commit 12/02/2026
This commit is contained in:
10
.idea/libraries/batoulapps_adhan.xml
generated
Normal file
10
.idea/libraries/batoulapps_adhan.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<component name="libraryTable">
|
||||
<library name="batoulapps.adhan" type="repository">
|
||||
<properties maven-id="com.batoulapps.adhan:adhan:1.2.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/batoulapps/adhan/adhan/1.2.1/adhan-1.2.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
16
.idea/libraries/batoulapps_adhan_adhan2.xml
generated
Normal file
16
.idea/libraries/batoulapps_adhan_adhan2.xml
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
<component name="libraryTable">
|
||||
<library name="batoulapps.adhan.adhan2" type="repository">
|
||||
<properties maven-id="com.batoulapps.adhan:adhan2:0.0.6" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/batoulapps/adhan/adhan2/0.0.6/adhan2-0.0.6.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-datetime/0.7.1/kotlinx-datetime-0.7.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-core/1.6.2/kotlinx-serialization-core-1.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.6.2/kotlinx-serialization-core-jvm-1.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.21/kotlin-stdlib-common-1.9.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
15
.idea/libraries/jetbrains_kotlinx_datetime.xml
generated
Normal file
15
.idea/libraries/jetbrains_kotlinx_datetime.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<component name="libraryTable">
|
||||
<library name="jetbrains.kotlinx.datetime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlinx:kotlinx-datetime:0.7.0-0.6.x-compat" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-datetime/0.7.0-0.6.x-compat/kotlinx-datetime-0.7.0-0.6.x-compat.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-core/1.6.2/kotlinx-serialization-core-1.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.6.2/kotlinx-serialization-core-jvm-1.6.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.21/kotlin-stdlib-common-1.9.21.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
@@ -10,6 +10,7 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/testResources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/html" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/audiofiles" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/OurAirports" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
@@ -38,5 +39,6 @@
|
||||
</orderEntry>
|
||||
<orderEntry type="library" name="google.cloud.texttospeech" level="project" />
|
||||
<orderEntry type="library" name="projectlombok.lombok" level="project" />
|
||||
<orderEntry type="library" name="batoulapps.adhan" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
84563
OurAirports/world-airports.csv
Normal file
84563
OurAirports/world-airports.csv
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
253
html/webpage/assets/css/bootstrap4-clockpicker.css
vendored
Normal file
253
html/webpage/assets/css/bootstrap4-clockpicker.css
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
/* !
|
||||
* ClockPicker v0.2.2 for Bootstrap (https://weareoutman.github.io/clockpicker/)
|
||||
* Copyright 2014 Wang Shenwei
|
||||
* Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
|
||||
* Bootstrap 4 compatibility by djibe (https://github.com/djibe/clockpicker) */
|
||||
|
||||
:root {
|
||||
--primary-color: 0, 123, 255;
|
||||
}
|
||||
|
||||
.clockpicker .input-group-addon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clockpicker-moving {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.clockpicker-align-left.popover > .arrow {
|
||||
left: 25px;
|
||||
}
|
||||
|
||||
.clockpicker-align-top.popover > .arrow {
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
.clockpicker-align-right.popover > .arrow {
|
||||
left: auto;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
.clockpicker-align-bottom.popover > .arrow {
|
||||
top: auto;
|
||||
bottom: 6px;
|
||||
}
|
||||
|
||||
.clockpicker-popover {
|
||||
-webkit-animation: pickerFadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
animation: pickerFadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
-webkit-transform-origin: center top 0px;
|
||||
transform-origin: center top 0px;
|
||||
-webkit-box-shadow: 0 24px 38px 3px rgba(0, 0, 0, 0.14), 0 9px 46px 8px rgba(0, 0, 0, 0.12), 0 11px 15px 0 rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 24px 38px 3px rgba(0, 0, 0, 0.14), 0 9px 46px 8px rgba(0, 0, 0, 0.12), 0 11px 15px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.clockpicker-popover.top {
|
||||
-webkit-transform-origin: center bottom 0px;
|
||||
transform-origin: center bottom 0px;
|
||||
}
|
||||
|
||||
.clockpicker-popover * {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.clockpicker-popover .popover-header {
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
background-color: var(--primary, #007bff);
|
||||
color: #fff;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
font-size: 3rem;
|
||||
font-weight: normal;
|
||||
letter-spacing: normal;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.clockpicker-popover .popover-header span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clockpicker-popover .popover-body {
|
||||
background-color: #fff;
|
||||
padding: 1rem 0.75rem 0.75rem;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.clockpicker-popover .btn {
|
||||
border: 0 !important;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
padding: 0.59375rem 1rem;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
margin-left: 0.25rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.clockpicker-popover .btn:focus, .clockpicker-popover .btn:hover, .clockpicker-popover .btn:active {
|
||||
outline: none !important;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.12)), to(rgba(0, 0, 0, 0.12)));
|
||||
background-image: linear-gradient(180deg, rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.12));
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.clockpicker-span-hours {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.clockpicker-span-minutes {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.clockpicker-close-block {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.clockpicker-plate {
|
||||
background-color: #ededee;
|
||||
border-radius: 50%;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.clockpicker-canvas, .clockpicker-dial {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.clockpicker-minutes {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.clockpicker-tick {
|
||||
border-radius: 50%;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clockpicker-tick.active, .clockpicker-tick:not(.disabled):hover {
|
||||
background-color: rgba(var(--primary-color, 0, 123, 255), 0.25);
|
||||
}
|
||||
|
||||
.clockpicker-tick.disabled {
|
||||
color: #eee;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.clockpicker-dial {
|
||||
-webkit-transition: opacity 350ms, -webkit-transform 350ms;
|
||||
transition: opacity 350ms, -webkit-transform 350ms;
|
||||
transition: transform 350ms, opacity 350ms;
|
||||
transition: transform 350ms, opacity 350ms, -webkit-transform 350ms;
|
||||
}
|
||||
|
||||
.clockpicker-dial-out {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.clockpicker-hours.clockpicker-dial-out {
|
||||
-webkit-transform: scale(1.2, 1.2);
|
||||
transform: scale(1.2, 1.2);
|
||||
}
|
||||
|
||||
.clockpicker-minutes.clockpicker-dial-out {
|
||||
-webkit-transform: scale(0.8, 0.8);
|
||||
transform: scale(0.8, 0.8);
|
||||
}
|
||||
|
||||
.clockpicker-canvas {
|
||||
-webkit-transition: opacity 175ms;
|
||||
transition: opacity 175ms;
|
||||
}
|
||||
|
||||
.clockpicker-canvas-out {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.clockpicker-canvas line {
|
||||
stroke: var(--primary, #007bff);
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
.clockpicker-canvas-bearing {
|
||||
stroke: none;
|
||||
fill: var(--primary, #007bff);
|
||||
}
|
||||
|
||||
.clockpicker-canvas-fg {
|
||||
stroke: none;
|
||||
fill: rgba(var(--primary-color, 0, 123, 255), 0.5);
|
||||
}
|
||||
|
||||
.clockpicker-canvas-bg {
|
||||
stroke: none;
|
||||
fill: rgba(var(--primary-color, 0, 123, 255), 0.25);
|
||||
}
|
||||
|
||||
.clockpicker-canvas-bg-trans {
|
||||
fill: rgba(var(--primary-color, 0, 123, 255), 0.25);
|
||||
}
|
||||
|
||||
.clockpicker-buttons-am-pm {
|
||||
color: white;
|
||||
display: none;
|
||||
-ms-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-ms-flex-pack: distribute;
|
||||
justify-content: space-around;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
font-size: 1rem;
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
@keyframes pickerFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(0.8);
|
||||
transform: scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
252
html/webpage/assets/css/bootstrap4-clockpicker.min.css
vendored
Normal file
252
html/webpage/assets/css/bootstrap4-clockpicker.min.css
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
/* !
|
||||
* ClockPicker v0.2.2 for Bootstrap (https://weareoutman.github.io/clockpicker/)
|
||||
* Copyright 2014 Wang Shenwei
|
||||
* Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
|
||||
* Bootstrap 4 compatibility by djibe (https://github.com/djibe/clockpicker) */
|
||||
|
||||
:root {
|
||||
--primary-color: 0,123,255;
|
||||
}
|
||||
|
||||
.clockpicker .input-group-addon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clockpicker-moving {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.clockpicker-align-left.popover > .arrow {
|
||||
left: 25px;
|
||||
}
|
||||
|
||||
.clockpicker-align-top.popover > .arrow {
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
.clockpicker-align-right.popover > .arrow {
|
||||
left: auto;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
.clockpicker-align-bottom.popover > .arrow {
|
||||
top: auto;
|
||||
bottom: 6px;
|
||||
}
|
||||
|
||||
.clockpicker-popover {
|
||||
-webkit-animation: pickerFadeIn .2s cubic-bezier(.4,0,.2,1);
|
||||
animation: pickerFadeIn .2s cubic-bezier(.4,0,.2,1);
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
-webkit-transform-origin: center top 0;
|
||||
transform-origin: center top 0;
|
||||
-webkit-box-shadow: 0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12),0 11px 15px 0 rgba(0,0,0,.2);
|
||||
box-shadow: 0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12),0 11px 15px 0 rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
.clockpicker-popover.top {
|
||||
-webkit-transform-origin: center bottom 0;
|
||||
transform-origin: center bottom 0;
|
||||
}
|
||||
|
||||
.clockpicker-popover * {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.clockpicker-popover .popover-header {
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
background-color: var(--primary,#007bff);
|
||||
color: #fff;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
font-size: 3rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: normal;
|
||||
text-align: center;
|
||||
padding: .5rem;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.clockpicker-popover .popover-header span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clockpicker-popover .popover-body {
|
||||
background-color: #fff;
|
||||
padding: 1rem .75rem .75rem;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.clockpicker-popover .btn {
|
||||
border: 0!important;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
font-size: .8125rem;
|
||||
font-weight: 500;
|
||||
padding: .59375rem 1rem;
|
||||
min-width: 0;
|
||||
margin: 0 0 0 .25rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.clockpicker-popover .btn:active, .clockpicker-popover .btn:focus, .clockpicker-popover .btn:hover {
|
||||
outline: 0!important;
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,.12)),to(rgba(0,0,0,.12)));
|
||||
background-image: linear-gradient(180deg,rgba(0,0,0,.12),rgba(0,0,0,.12));
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.clockpicker-span-hours {
|
||||
margin-right: .25rem;
|
||||
}
|
||||
|
||||
.clockpicker-span-minutes {
|
||||
margin-left: .25rem;
|
||||
}
|
||||
|
||||
.clockpicker-close-block {
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
.clockpicker-plate {
|
||||
background-color: #ededee;
|
||||
border-radius: 50%;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.clockpicker-canvas, .clockpicker-dial {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.clockpicker-minutes {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.clockpicker-tick {
|
||||
border-radius: 50%;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clockpicker-tick.active, .clockpicker-tick:not(.disabled):hover {
|
||||
background-color: rgba(var(--primary-color,0,123,255),.25);
|
||||
}
|
||||
|
||||
.clockpicker-tick.disabled {
|
||||
color: #eee;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.clockpicker-dial {
|
||||
-webkit-transition: opacity 350ms,-webkit-transform 350ms;
|
||||
transition: opacity 350ms,-webkit-transform 350ms;
|
||||
transition: transform 350ms,opacity 350ms;
|
||||
transition: transform 350ms,opacity 350ms,-webkit-transform 350ms;
|
||||
}
|
||||
|
||||
.clockpicker-dial-out {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.clockpicker-hours.clockpicker-dial-out {
|
||||
-webkit-transform: scale(1.2,1.2);
|
||||
transform: scale(1.2,1.2);
|
||||
}
|
||||
|
||||
.clockpicker-minutes.clockpicker-dial-out {
|
||||
-webkit-transform: scale(.8,.8);
|
||||
transform: scale(.8,.8);
|
||||
}
|
||||
|
||||
.clockpicker-canvas {
|
||||
-webkit-transition: opacity 175ms;
|
||||
transition: opacity 175ms;
|
||||
}
|
||||
|
||||
.clockpicker-canvas-out {
|
||||
opacity: .25;
|
||||
}
|
||||
|
||||
.clockpicker-canvas line {
|
||||
stroke: var(--primary,#007bff);
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
.clockpicker-canvas-bearing {
|
||||
stroke: none;
|
||||
fill: var(--primary,#007bff);
|
||||
}
|
||||
|
||||
.clockpicker-canvas-fg {
|
||||
stroke: none;
|
||||
fill: rgba(var(--primary-color,0,123,255),.5);
|
||||
}
|
||||
|
||||
.clockpicker-canvas-bg {
|
||||
stroke: none;
|
||||
fill: rgba(var(--primary-color,0,123,255),.25);
|
||||
}
|
||||
|
||||
.clockpicker-canvas-bg-trans {
|
||||
fill: rgba(var(--primary-color,0,123,255),.25);
|
||||
}
|
||||
|
||||
.clockpicker-buttons-am-pm {
|
||||
color: #fff;
|
||||
display: none;
|
||||
-ms-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-ms-flex-pack: distribute;
|
||||
justify-content: space-around;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
font-size: 1rem;
|
||||
margin-left: .75rem;
|
||||
}
|
||||
|
||||
@keyframes pickerFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(.8);
|
||||
transform: scale(.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
806
html/webpage/assets/css/flatpickr.min.css
vendored
Normal file
806
html/webpage/assets/css/flatpickr.min.css
vendored
Normal file
@@ -0,0 +1,806 @@
|
||||
.flatpickr-calendar {
|
||||
background: transparent;
|
||||
opacity: 0;
|
||||
display: none;
|
||||
text-align: center;
|
||||
visibility: hidden;
|
||||
padding: 0;
|
||||
-webkit-animation: none;
|
||||
animation: none;
|
||||
direction: ltr;
|
||||
border: 0;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
border-radius: 5px;
|
||||
position: absolute;
|
||||
width: 307.875px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-ms-touch-action: manipulation;
|
||||
touch-action: manipulation;
|
||||
background: #fff;
|
||||
-webkit-box-shadow: 1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,0.08);
|
||||
box-shadow: 1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.flatpickr-calendar.open, .flatpickr-calendar.inline {
|
||||
opacity: 1;
|
||||
max-height: 640px;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.open {
|
||||
display: inline-block;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.animate.open {
|
||||
-webkit-animation: fpFadeInDown 300ms cubic-bezier(.23,1,.32,1);
|
||||
animation: fpFadeInDown 300ms cubic-bezier(.23,1,.32,1);
|
||||
}
|
||||
|
||||
.flatpickr-calendar.inline {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.static {
|
||||
position: absolute;
|
||||
top: calc(100% + 2px);
|
||||
}
|
||||
|
||||
.flatpickr-calendar.static.open {
|
||||
z-index: 999;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n + 1) .flatpickr-day.inRange:nth-child(7n + 7) {
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n + 2) .flatpickr-day.inRange:nth-child(7n + 1) {
|
||||
-webkit-box-shadow: -2px 0 0 #e6e6e6,5px 0 0 #e6e6e6;
|
||||
box-shadow: -2px 0 0 #e6e6e6,5px 0 0 #e6e6e6;
|
||||
}
|
||||
|
||||
.flatpickr-calendar .hasWeeks .dayContainer, .flatpickr-calendar .hasTime .dayContainer {
|
||||
border-bottom: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.flatpickr-calendar .hasWeeks .dayContainer {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.hasTime .flatpickr-time {
|
||||
height: 40px;
|
||||
border-top: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.noCalendar.hasTime .flatpickr-time {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.flatpickr-calendar:before, .flatpickr-calendar:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
border: solid transparent;
|
||||
content: '';
|
||||
height: 0;
|
||||
width: 0;
|
||||
left: 22px;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.rightMost:before, .flatpickr-calendar.arrowRight:before, .flatpickr-calendar.rightMost:after, .flatpickr-calendar.arrowRight:after {
|
||||
left: auto;
|
||||
right: 22px;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowCenter:before, .flatpickr-calendar.arrowCenter:after {
|
||||
left: 50%;
|
||||
right: 50%;
|
||||
}
|
||||
|
||||
.flatpickr-calendar:before {
|
||||
border-width: 5px;
|
||||
margin: 0 -5px;
|
||||
}
|
||||
|
||||
.flatpickr-calendar:after {
|
||||
border-width: 4px;
|
||||
margin: 0 -4px;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowTop:before, .flatpickr-calendar.arrowTop:after {
|
||||
bottom: 100%;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowTop:before {
|
||||
border-bottom-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowTop:after {
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowBottom:before, .flatpickr-calendar.arrowBottom:after {
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowBottom:before {
|
||||
border-top-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowBottom:after {
|
||||
border-top-color: #fff;
|
||||
}
|
||||
|
||||
.flatpickr-calendar:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.flatpickr-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.flatpickr-months {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-month {
|
||||
background: transparent;
|
||||
color: rgba(0,0,0,0.9);
|
||||
fill: rgba(0,0,0,0.9);
|
||||
height: 34px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month, .flatpickr-months .flatpickr-next-month {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 34px;
|
||||
padding: 10px;
|
||||
z-index: 3;
|
||||
color: rgba(0,0,0,0.9);
|
||||
fill: rgba(0,0,0,0.9);
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-disabled, .flatpickr-months .flatpickr-next-month.flatpickr-disabled {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month i, .flatpickr-months .flatpickr-next-month i {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month, .flatpickr-months .flatpickr-next-month.flatpickr-prev-month {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* /*rtl:begin:ignore */
|
||||
|
||||
/* /*rtl:end:ignore */
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month.flatpickr-next-month, .flatpickr-months .flatpickr-next-month.flatpickr-next-month {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* /*rtl:begin:ignore */
|
||||
|
||||
/* /*rtl:end:ignore */
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month:hover, .flatpickr-months .flatpickr-next-month:hover {
|
||||
color: #959ea9;
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month:hover svg, .flatpickr-months .flatpickr-next-month:hover svg {
|
||||
fill: #f64747;
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month svg, .flatpickr-months .flatpickr-next-month svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month svg path, .flatpickr-months .flatpickr-next-month svg path {
|
||||
-webkit-transition: fill .1s;
|
||||
transition: fill .1s;
|
||||
fill: inherit;
|
||||
}
|
||||
|
||||
.numInputWrapper {
|
||||
position: relative;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.numInputWrapper input, .numInputWrapper span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.numInputWrapper input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.numInputWrapper input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.numInputWrapper input::-webkit-outer-spin-button, .numInputWrapper input::-webkit-inner-spin-button {
|
||||
margin: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.numInputWrapper span {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 14px;
|
||||
padding: 0 4px 0 2px;
|
||||
height: 50%;
|
||||
line-height: 50%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
border: 1px solid rgba(57,57,57,0.15);
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.numInputWrapper span:hover {
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.numInputWrapper span:active {
|
||||
background: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.numInputWrapper span:after {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.numInputWrapper span.arrowUp {
|
||||
top: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.numInputWrapper span.arrowUp:after {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-bottom: 4px solid rgba(57,57,57,0.6);
|
||||
top: 26%;
|
||||
}
|
||||
|
||||
.numInputWrapper span.arrowDown {
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.numInputWrapper span.arrowDown:after {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid rgba(57,57,57,0.6);
|
||||
top: 40%;
|
||||
}
|
||||
|
||||
.numInputWrapper span svg {
|
||||
width: inherit;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.numInputWrapper span svg path {
|
||||
fill: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.numInputWrapper:hover {
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.numInputWrapper:hover span {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.flatpickr-current-month {
|
||||
font-size: 135%;
|
||||
line-height: inherit;
|
||||
font-weight: 300;
|
||||
color: inherit;
|
||||
position: absolute;
|
||||
width: 75%;
|
||||
left: 12.5%;
|
||||
padding: 7.48px 0 0 0;
|
||||
line-height: 1;
|
||||
height: 34px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
-webkit-transform: translate3d(0,0,0);
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
|
||||
.flatpickr-current-month span.cur-month {
|
||||
font-family: inherit;
|
||||
font-weight: 700;
|
||||
color: inherit;
|
||||
display: inline-block;
|
||||
margin-left: .5ch;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.flatpickr-current-month span.cur-month:hover {
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.flatpickr-current-month .numInputWrapper {
|
||||
width: 6ch;
|
||||
width: 7ch\0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.flatpickr-current-month .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: rgba(0,0,0,0.9);
|
||||
}
|
||||
|
||||
.flatpickr-current-month .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: rgba(0,0,0,0.9);
|
||||
}
|
||||
|
||||
.flatpickr-current-month input.cur-year {
|
||||
background: transparent;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: inherit;
|
||||
cursor: text;
|
||||
padding: 0 0 0 .5ch;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: 300;
|
||||
line-height: inherit;
|
||||
height: auto;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
vertical-align: initial;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
|
||||
.flatpickr-current-month input.cur-year:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.flatpickr-current-month input.cur-year[disabled], .flatpickr-current-month input.cur-year[disabled]:hover {
|
||||
font-size: 100%;
|
||||
color: rgba(0,0,0,0.5);
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months {
|
||||
appearance: menulist;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-sizing: border-box;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: 300;
|
||||
height: auto;
|
||||
line-height: inherit;
|
||||
margin: -1px 0 0 0;
|
||||
outline: none;
|
||||
padding: 0 0 0 .5ch;
|
||||
position: relative;
|
||||
vertical-align: initial;
|
||||
-webkit-box-sizing: border-box;
|
||||
-webkit-appearance: menulist;
|
||||
-moz-appearance: menulist;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months:focus, .flatpickr-current-month .flatpickr-monthDropdown-months:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
|
||||
background: rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month {
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.flatpickr-weekdays {
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.flatpickr-weekdays .flatpickr-weekdaycontainer {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
span.flatpickr-weekday {
|
||||
cursor: default;
|
||||
font-size: 90%;
|
||||
background: transparent;
|
||||
color: rgba(0,0,0,0.54);
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
display: block;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.dayContainer, .flatpickr-weeks {
|
||||
padding: 1px 0 0 0;
|
||||
}
|
||||
|
||||
.flatpickr-days {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: start;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: start;
|
||||
align-items: flex-start;
|
||||
width: 307.875px;
|
||||
}
|
||||
|
||||
.flatpickr-days:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.dayContainer {
|
||||
padding: 0;
|
||||
outline: 0;
|
||||
text-align: left;
|
||||
width: 307.875px;
|
||||
min-width: 307.875px;
|
||||
max-width: 307.875px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
-ms-flex-wrap: wrap;
|
||||
-ms-flex-pack: justify;
|
||||
-webkit-justify-content: space-around;
|
||||
justify-content: space-around;
|
||||
-webkit-transform: translate3d(0,0,0);
|
||||
transform: translate3d(0,0,0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dayContainer + .dayContainer {
|
||||
-webkit-box-shadow: -1px 0 0 #e6e6e6;
|
||||
box-shadow: -1px 0 0 #e6e6e6;
|
||||
}
|
||||
|
||||
.flatpickr-day {
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 150px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: #393939;
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
width: 14.2857143%;
|
||||
-webkit-flex-basis: 14.2857143%;
|
||||
-ms-flex-preferred-size: 14.2857143%;
|
||||
flex-basis: 14.2857143%;
|
||||
max-width: 39px;
|
||||
height: 39px;
|
||||
line-height: 39px;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.flatpickr-day.inRange, .flatpickr-day.prevMonthDay.inRange, .flatpickr-day.nextMonthDay.inRange, .flatpickr-day.today.inRange, .flatpickr-day.prevMonthDay.today.inRange, .flatpickr-day.nextMonthDay.today.inRange, .flatpickr-day:hover, .flatpickr-day.prevMonthDay:hover, .flatpickr-day.nextMonthDay:hover, .flatpickr-day:focus, .flatpickr-day.prevMonthDay:focus, .flatpickr-day.nextMonthDay:focus {
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
background: #e6e6e6;
|
||||
border-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.flatpickr-day.today {
|
||||
border-color: #959ea9;
|
||||
}
|
||||
|
||||
.flatpickr-day.today:hover, .flatpickr-day.today:focus {
|
||||
border-color: #959ea9;
|
||||
background: #959ea9;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.flatpickr-day.selected, .flatpickr-day.startRange, .flatpickr-day.endRange, .flatpickr-day.selected.inRange, .flatpickr-day.startRange.inRange, .flatpickr-day.endRange.inRange, .flatpickr-day.selected:focus, .flatpickr-day.startRange:focus, .flatpickr-day.endRange:focus, .flatpickr-day.selected:hover, .flatpickr-day.startRange:hover, .flatpickr-day.endRange:hover, .flatpickr-day.selected.prevMonthDay, .flatpickr-day.startRange.prevMonthDay, .flatpickr-day.endRange.prevMonthDay, .flatpickr-day.selected.nextMonthDay, .flatpickr-day.startRange.nextMonthDay, .flatpickr-day.endRange.nextMonthDay {
|
||||
background: #569ff7;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
border-color: #569ff7;
|
||||
}
|
||||
|
||||
.flatpickr-day.selected.startRange, .flatpickr-day.startRange.startRange, .flatpickr-day.endRange.startRange {
|
||||
border-radius: 50px 0 0 50px;
|
||||
}
|
||||
|
||||
.flatpickr-day.selected.endRange, .flatpickr-day.startRange.endRange, .flatpickr-day.endRange.endRange {
|
||||
border-radius: 0 50px 50px 0;
|
||||
}
|
||||
|
||||
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n + 1)), .flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n + 1)), .flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n + 1)) {
|
||||
-webkit-box-shadow: -10px 0 0 #569ff7;
|
||||
box-shadow: -10px 0 0 #569ff7;
|
||||
}
|
||||
|
||||
.flatpickr-day.selected.startRange.endRange, .flatpickr-day.startRange.startRange.endRange, .flatpickr-day.endRange.startRange.endRange {
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.flatpickr-day.inRange {
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: -5px 0 0 #e6e6e6,5px 0 0 #e6e6e6;
|
||||
box-shadow: -5px 0 0 #e6e6e6,5px 0 0 #e6e6e6;
|
||||
}
|
||||
|
||||
.flatpickr-day.flatpickr-disabled, .flatpickr-day.flatpickr-disabled:hover, .flatpickr-day.prevMonthDay, .flatpickr-day.nextMonthDay, .flatpickr-day.notAllowed, .flatpickr-day.notAllowed.prevMonthDay, .flatpickr-day.notAllowed.nextMonthDay {
|
||||
color: rgba(57,57,57,0.3);
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.flatpickr-day.flatpickr-disabled, .flatpickr-day.flatpickr-disabled:hover {
|
||||
cursor: not-allowed;
|
||||
color: rgba(57,57,57,0.1);
|
||||
}
|
||||
|
||||
.flatpickr-day.week.selected {
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: -5px 0 0 #569ff7,5px 0 0 #569ff7;
|
||||
box-shadow: -5px 0 0 #569ff7,5px 0 0 #569ff7;
|
||||
}
|
||||
|
||||
.flatpickr-day.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.rangeMode .flatpickr-day {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.flatpickr-weekwrapper {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.flatpickr-weekwrapper .flatpickr-weeks {
|
||||
padding: 0 12px;
|
||||
-webkit-box-shadow: 1px 0 0 #e6e6e6;
|
||||
box-shadow: 1px 0 0 #e6e6e6;
|
||||
}
|
||||
|
||||
.flatpickr-weekwrapper .flatpickr-weekday {
|
||||
float: none;
|
||||
width: 100%;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.flatpickr-weekwrapper span.flatpickr-day, .flatpickr-weekwrapper span.flatpickr-day:hover {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
color: rgba(57,57,57,0.3);
|
||||
background: transparent;
|
||||
cursor: default;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.flatpickr-innerContainer {
|
||||
display: block;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.flatpickr-rContainer {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.flatpickr-time {
|
||||
text-align: center;
|
||||
outline: 0;
|
||||
display: block;
|
||||
height: 0;
|
||||
line-height: 40px;
|
||||
max-height: 40px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flatpickr-time:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.flatpickr-time .numInputWrapper {
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
width: 40%;
|
||||
height: 40px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.flatpickr-time .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: #393939;
|
||||
}
|
||||
|
||||
.flatpickr-time .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: #393939;
|
||||
}
|
||||
|
||||
.flatpickr-time.hasSeconds .numInputWrapper {
|
||||
width: 26%;
|
||||
}
|
||||
|
||||
.flatpickr-time.time24hr .numInputWrapper {
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
.flatpickr-time input {
|
||||
background: transparent;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: inherit;
|
||||
line-height: inherit;
|
||||
color: #393939;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
}
|
||||
|
||||
.flatpickr-time input.flatpickr-hour {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.flatpickr-time input.flatpickr-minute, .flatpickr-time input.flatpickr-second {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.flatpickr-time input:focus {
|
||||
outline: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.flatpickr-time .flatpickr-time-separator, .flatpickr-time .flatpickr-am-pm {
|
||||
height: inherit;
|
||||
float: left;
|
||||
line-height: inherit;
|
||||
color: #393939;
|
||||
font-weight: bold;
|
||||
width: 2%;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-align-self: center;
|
||||
-ms-flex-item-align: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.flatpickr-time .flatpickr-am-pm {
|
||||
outline: 0;
|
||||
width: 18%;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.flatpickr-time input:hover, .flatpickr-time .flatpickr-am-pm:hover, .flatpickr-time input:focus, .flatpickr-time .flatpickr-am-pm:focus {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.flatpickr-input[readonly] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@keyframes fpFadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0,-20px,0);
|
||||
transform: translate3d(0,-20px,0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translate3d(0,0,0);
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
406
html/webpage/assets/css/jquery-clockpicker.css
Normal file
406
html/webpage/assets/css/jquery-clockpicker.css
Normal file
@@ -0,0 +1,406 @@
|
||||
/* !
|
||||
* ClockPicker v0.0.7 for jQuery (http://weareoutman.github.io/clockpicker/)
|
||||
* Copyright 2014 Wang Shenwei.
|
||||
* Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
|
||||
*
|
||||
* Bootstrap v3.1.1 (http://getbootstrap.com)
|
||||
* Copyright 2011-2014 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */
|
||||
|
||||
/* Picked from bootstrap: .popover, .btn, .text-primary */
|
||||
|
||||
.popover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1010;
|
||||
display: none;
|
||||
max-width: 276px;
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid rgba(0, 0, 0, .2);
|
||||
border-radius: 6px;
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
|
||||
}
|
||||
|
||||
.popover.top {
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.popover.right {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.popover.bottom {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.popover.left {
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.popover-title {
|
||||
padding: 8px 14px;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 18px;
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
|
||||
.popover-content {
|
||||
padding: 9px 14px;
|
||||
}
|
||||
|
||||
.popover > .arrow, .popover > .arrow:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
overflow: visible;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
z-index: auto;
|
||||
background-color: transparent;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
right: auto;
|
||||
top: auto;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.popover > .arrow {
|
||||
border-width: 11px;
|
||||
}
|
||||
|
||||
.popover > .arrow:after {
|
||||
content: "";
|
||||
border-width: 10px;
|
||||
}
|
||||
|
||||
.popover.top > .arrow {
|
||||
bottom: -11px;
|
||||
left: 50%;
|
||||
margin-left: -11px;
|
||||
border-top-color: #999;
|
||||
border-top-color: rgba(0, 0, 0, .25);
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.popover.top > .arrow:after {
|
||||
bottom: 1px;
|
||||
margin-left: -10px;
|
||||
content: " ";
|
||||
border-top-color: #fff;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.popover.right > .arrow {
|
||||
top: 50%;
|
||||
left: -11px;
|
||||
margin-top: -11px;
|
||||
border-right-color: #999;
|
||||
border-right-color: rgba(0, 0, 0, .25);
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.popover.right > .arrow:after {
|
||||
bottom: -10px;
|
||||
left: 1px;
|
||||
content: " ";
|
||||
border-right-color: #fff;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.popover.bottom > .arrow {
|
||||
top: -11px;
|
||||
left: 50%;
|
||||
margin-left: -11px;
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #999;
|
||||
border-bottom-color: rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
.popover.bottom > .arrow:after {
|
||||
top: 1px;
|
||||
margin-left: -10px;
|
||||
content: " ";
|
||||
border-top-width: 0;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
.popover.left > .arrow {
|
||||
top: 50%;
|
||||
right: -11px;
|
||||
margin-top: -11px;
|
||||
border-right-width: 0;
|
||||
border-left-color: #999;
|
||||
border-left-color: rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
.popover.left > .arrow:after {
|
||||
right: 1px;
|
||||
bottom: -10px;
|
||||
content: " ";
|
||||
border-right-width: 0;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
background-image: none;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.btn:focus, .btn:active:focus, .btn.active:focus {
|
||||
outline: thin dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.btn:hover, .btn:focus {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn:active, .btn.active {
|
||||
background-image: none;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
border-color: #ccc;
|
||||
}
|
||||
|
||||
.btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default {
|
||||
color: #333;
|
||||
background-color: #ebebeb;
|
||||
border-color: #adadad;
|
||||
}
|
||||
|
||||
.btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: #428bca;
|
||||
}
|
||||
|
||||
/* !
|
||||
* ClockPicker v{package.version} for Bootstrap (http://weareoutman.github.io/clockpicker/)
|
||||
* Copyright 2014 Wang Shenwei.
|
||||
* Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE) */
|
||||
|
||||
.clockpicker .input-group-addon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clockpicker-moving {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.clockpicker-align-left.popover > .arrow {
|
||||
left: 25px;
|
||||
}
|
||||
|
||||
.clockpicker-align-top.popover > .arrow {
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
.clockpicker-align-right.popover > .arrow {
|
||||
left: auto;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
.clockpicker-align-bottom.popover > .arrow {
|
||||
top: auto;
|
||||
bottom: 6px;
|
||||
}
|
||||
|
||||
.clockpicker-popover .popover-title {
|
||||
background-color: #fff;
|
||||
color: #999;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.clockpicker-popover .popover-title span {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clockpicker-popover .popover-content {
|
||||
background-color: #f8f8f8;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.popover-content:last-child {
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.clockpicker-plate {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 50%;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.clockpicker-canvas, .clockpicker-dial {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.clockpicker-minutes {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.clockpicker-tick {
|
||||
border-radius: 50%;
|
||||
color: #666;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clockpicker-tick.active, .clockpicker-tick:hover {
|
||||
background-color: rgb(192, 229, 247);
|
||||
background-color: rgba(0, 149, 221, .25);
|
||||
}
|
||||
|
||||
.clockpicker-button {
|
||||
background-image: none;
|
||||
background-color: #fff;
|
||||
border-width: 1px 0 0;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.clockpicker-button:hover {
|
||||
background-image: none;
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
|
||||
.clockpicker-button:focus {
|
||||
outline: none!important;
|
||||
}
|
||||
|
||||
.clockpicker-dial {
|
||||
-webkit-transition: -webkit-transform 350ms, opacity 350ms;
|
||||
-moz-transition: -moz-transform 350ms, opacity 350ms;
|
||||
-ms-transition: -ms-transform 350ms, opacity 350ms;
|
||||
-o-transition: -o-transform 350ms, opacity 350ms;
|
||||
transition: transform 350ms, opacity 350ms;
|
||||
}
|
||||
|
||||
.clockpicker-dial-out {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.clockpicker-hours.clockpicker-dial-out {
|
||||
-webkit-transform: scale(1.2, 1.2);
|
||||
-moz-transform: scale(1.2, 1.2);
|
||||
-ms-transform: scale(1.2, 1.2);
|
||||
-o-transform: scale(1.2, 1.2);
|
||||
transform: scale(1.2, 1.2);
|
||||
}
|
||||
|
||||
.clockpicker-minutes.clockpicker-dial-out {
|
||||
-webkit-transform: scale(.8, .8);
|
||||
-moz-transform: scale(.8, .8);
|
||||
-ms-transform: scale(.8, .8);
|
||||
-o-transform: scale(.8, .8);
|
||||
transform: scale(.8, .8);
|
||||
}
|
||||
|
||||
.clockpicker-canvas {
|
||||
-webkit-transition: opacity 175ms;
|
||||
-moz-transition: opacity 175ms;
|
||||
-ms-transition: opacity 175ms;
|
||||
-o-transition: opacity 175ms;
|
||||
transition: opacity 175ms;
|
||||
}
|
||||
|
||||
.clockpicker-canvas-out {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.clockpicker-canvas-bearing, .clockpicker-canvas-fg {
|
||||
stroke: none;
|
||||
fill: rgb(0, 149, 221);
|
||||
}
|
||||
|
||||
.clockpicker-canvas-bg {
|
||||
stroke: none;
|
||||
fill: rgb(192, 229, 247);
|
||||
}
|
||||
|
||||
.clockpicker-canvas-bg-trans {
|
||||
fill: rgba(0, 149, 221, .25);
|
||||
}
|
||||
|
||||
.clockpicker-canvas line {
|
||||
stroke: rgb(0, 149, 221);
|
||||
stroke-width: 1;
|
||||
stroke-linecap: round;
|
||||
/*shape-rendering: crispEdges;*/
|
||||
}
|
||||
|
||||
.clockpicker-button.am-button {
|
||||
margin: 1px;
|
||||
padding: 5px;
|
||||
border: 1px solid rgba(0, 0, 0, .2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.clockpicker-button.pm-button {
|
||||
margin: 1px 1px 1px 136px;
|
||||
padding: 5px;
|
||||
border: 1px solid rgba(0, 0, 0, .2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
978
html/webpage/assets/js/bootstrap4-clockpicker.js
vendored
Normal file
978
html/webpage/assets/js/bootstrap4-clockpicker.js
vendored
Normal file
@@ -0,0 +1,978 @@
|
||||
/*!
|
||||
* ClockPicker v0.2.3 original by (http://weareoutman.github.io/clockpicker/)
|
||||
* Copyright 2014 Wang Shenwei.
|
||||
* Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
|
||||
* Bootstrap 4 support by djibe
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
var $win = $(window),
|
||||
$doc = $(document),
|
||||
$body;
|
||||
|
||||
// Can I use inline svg ?
|
||||
var svgNS = "http://www.w3.org/2000/svg",
|
||||
svgSupported =
|
||||
"SVGAngle" in window &&
|
||||
(function() {
|
||||
var supported,
|
||||
el = document.createElement("div");
|
||||
el.innerHTML = "<svg/>";
|
||||
supported = (el.firstChild && el.firstChild.namespaceURI) == svgNS;
|
||||
el.innerHTML = "";
|
||||
return supported;
|
||||
})();
|
||||
|
||||
// Can I use transition ?
|
||||
var transitionSupported = (function() {
|
||||
var style = document.createElement("div").style;
|
||||
return (
|
||||
"transition" in style ||
|
||||
"WebkitTransition" in style ||
|
||||
"MozTransition" in style ||
|
||||
"msTransition" in style ||
|
||||
"OTransition" in style
|
||||
);
|
||||
})();
|
||||
|
||||
// Listen touch events in touch screen device, instead of mouse events in desktop.
|
||||
var touchSupported = "ontouchstart" in window,
|
||||
mousedownEvent = "mousedown" + (touchSupported ? " touchstart" : ""),
|
||||
mousemoveEvent =
|
||||
"mousemove.clockpicker" +
|
||||
(touchSupported ? " touchmove.clockpicker" : ""),
|
||||
mouseupEvent =
|
||||
"mouseup.clockpicker" + (touchSupported ? " touchend.clockpicker" : "");
|
||||
|
||||
// Vibrate the device if supported
|
||||
var vibrate = navigator.vibrate
|
||||
? "vibrate"
|
||||
: navigator.webkitVibrate
|
||||
? "webkitVibrate"
|
||||
: null;
|
||||
|
||||
function createSvgElement(name) {
|
||||
return document.createElementNS(svgNS, name);
|
||||
}
|
||||
|
||||
function leadingZero(num) {
|
||||
return (num < 10 ? "0" : "") + num;
|
||||
}
|
||||
|
||||
// Get a unique id
|
||||
var idCounter = 0;
|
||||
function uniqueId(prefix) {
|
||||
var id = ++idCounter + "";
|
||||
return prefix ? prefix + id : id;
|
||||
}
|
||||
|
||||
// Clock size
|
||||
var dialRadius = 100,
|
||||
outerRadius = 80,
|
||||
// innerRadius = 80 on 12 hour clock
|
||||
innerRadius = 54,
|
||||
tickRadius = 13;
|
||||
(diameter = dialRadius * 2), (duration = transitionSupported ? 350 : 1);
|
||||
|
||||
// Popover template
|
||||
var tpl = [
|
||||
'<div class="popover clockpicker-popover">',
|
||||
'<div class="arrow"></div>',
|
||||
'<div class="popover-header">',
|
||||
'<span class="clockpicker-span-hours"></span>',
|
||||
":",
|
||||
'<span class="clockpicker-span-minutes text-white-50"></span>',
|
||||
'<span class="clockpicker-buttons-am-pm"></span>',
|
||||
"</div>",
|
||||
'<div class="popover-body">',
|
||||
'<div class="clockpicker-plate">',
|
||||
'<div class="clockpicker-canvas"></div>',
|
||||
'<div class="clockpicker-dial clockpicker-hours"></div>',
|
||||
'<div class="clockpicker-dial clockpicker-minutes clockpicker-dial-out"></div>',
|
||||
"</div>",
|
||||
'<div class="clockpicker-close-block justify-content-end"></div>',
|
||||
"</div>",
|
||||
"</div>"
|
||||
].join("");
|
||||
|
||||
// ClockPicker
|
||||
function ClockPicker(element, options) {
|
||||
var popover = $(tpl),
|
||||
plate = popover.find(".clockpicker-plate"),
|
||||
hoursView = popover.find(".clockpicker-hours"),
|
||||
minutesView = popover.find(".clockpicker-minutes"),
|
||||
isInput = element.prop("tagName") === "INPUT",
|
||||
input = isInput ? element : element.find("input"),
|
||||
isHTML5 = input.prop("type") === "time",
|
||||
addon = element.find(".input-group-addon"),
|
||||
popoverBody = popover.find(".popover-body"),
|
||||
closeBlock = popoverBody.find(".clockpicker-close-block"),
|
||||
self = this,
|
||||
timer;
|
||||
|
||||
this.id = uniqueId("cp");
|
||||
this.element = element;
|
||||
this.options = options;
|
||||
this.options.hourstep = this.parseStep(this.options.hourstep, 12);
|
||||
this.options.minutestep = this.parseStep(this.options.minutestep, 60);
|
||||
this.isAppended = false;
|
||||
this.isShown = false;
|
||||
this.currentView = "hours";
|
||||
this.isInput = isInput;
|
||||
this.isHTML5 = isHTML5;
|
||||
this.input = input;
|
||||
this.addon = addon;
|
||||
this.popover = popover;
|
||||
this.plate = plate;
|
||||
this.hoursView = hoursView;
|
||||
this.minutesView = minutesView;
|
||||
this.spanHours = popover.find(".clockpicker-span-hours");
|
||||
this.spanMinutes = popover.find(".clockpicker-span-minutes");
|
||||
this.buttonsAmPm = popover.find(".clockpicker-buttons-am-pm");
|
||||
this.currentPlacementClass = options.placement;
|
||||
this.raiseCallback = function() {
|
||||
raiseCallback.apply(self, arguments);
|
||||
};
|
||||
|
||||
// Setup for for 12 hour clock if option is selected
|
||||
if (options.twelvehour) {
|
||||
$(this.buttonsAmPm).css("display", "flex");
|
||||
|
||||
$('<a class="btn-am">AM</a>')
|
||||
.on("click", function() {
|
||||
self.amOrPm = "AM";
|
||||
$(this).removeClass("text-white-50");
|
||||
$(".btn-pm").addClass("text-white-50");
|
||||
if (options.ampmSubmit) {
|
||||
setTimeout(function() {
|
||||
self.done();
|
||||
}, duration / 2);
|
||||
}
|
||||
})
|
||||
.appendTo(this.buttonsAmPm);
|
||||
|
||||
$('<a class="btn-pm text-white-50">PM</a>')
|
||||
.on("click", function() {
|
||||
self.amOrPm = "PM";
|
||||
$(this).removeClass("text-white-50");
|
||||
$(".btn-am").addClass("text-white-50");
|
||||
if (options.ampmSubmit) {
|
||||
setTimeout(function() {
|
||||
self.done();
|
||||
}, duration / 2);
|
||||
}
|
||||
})
|
||||
.appendTo(this.buttonsAmPm);
|
||||
}
|
||||
|
||||
if (!options.autoclose) {
|
||||
// If autoclose is not setted, append a button
|
||||
closeBlock
|
||||
.append(
|
||||
'<button type="button" class="btn btn-sm btn-outline-primary cancel">' +
|
||||
options.canceltext +
|
||||
"</button>"
|
||||
)
|
||||
.on("click", ".cancel", function () {
|
||||
self.hide();
|
||||
});
|
||||
|
||||
closeBlock
|
||||
.css("display", "flex")
|
||||
.append(
|
||||
'<button type="button" class="btn btn-sm btn-outline-primary done">' +
|
||||
options.donetext +
|
||||
"</button>"
|
||||
)
|
||||
.on("click", ".done", $.proxy(this.done, this));
|
||||
}
|
||||
|
||||
// Placement and arrow align - make sure they make sense.
|
||||
if (
|
||||
/^(top|bottom)/.test(options.placement) &&
|
||||
(options.align === "top" || options.align === "bottom")
|
||||
)
|
||||
options.align = "left";
|
||||
if (
|
||||
(options.placement === "left" || options.placement === "right") &&
|
||||
(options.align === "left" || options.align === "right")
|
||||
)
|
||||
options.align = "top";
|
||||
|
||||
popover.addClass(options.placement);
|
||||
popover.addClass("clockpicker-align-" + options.align);
|
||||
|
||||
this.spanHours.click($.proxy(this.toggleView, this, "hours"));
|
||||
this.spanMinutes.click($.proxy(this.toggleView, this, "minutes"));
|
||||
|
||||
// Show or toggle
|
||||
if (!options.addonOnly) {
|
||||
input.on("focus.clockpicker click.clockpicker", $.proxy(this.show, this));
|
||||
}
|
||||
addon.on("click.clockpicker", $.proxy(this.toggle, this));
|
||||
|
||||
// Build ticks
|
||||
var tickTpl = $('<div class="clockpicker-tick"></div>'),
|
||||
i,
|
||||
tick,
|
||||
radian,
|
||||
radius;
|
||||
|
||||
// Hours view
|
||||
if (options.twelvehour) {
|
||||
for (i = 0; i < 12; i += options.hourstep) {
|
||||
tick = tickTpl.clone();
|
||||
radian = (i / 6) * Math.PI;
|
||||
radius = outerRadius;
|
||||
tick.css("font-size", "120%");
|
||||
tick.css({
|
||||
left: dialRadius + Math.sin(radian) * radius - tickRadius,
|
||||
top: dialRadius - Math.cos(radian) * radius - tickRadius
|
||||
});
|
||||
tick.html(i === 0 ? 12 : i);
|
||||
hoursView.append(tick);
|
||||
tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 24; i += options.hourstep) {
|
||||
var isDisabled = false;
|
||||
if (
|
||||
options.disabledhours &&
|
||||
$.inArray(i, options.disabledhours) != -1
|
||||
) {
|
||||
var isDisabled = true;
|
||||
}
|
||||
tick = tickTpl.clone();
|
||||
radian = (i / 6) * Math.PI;
|
||||
var inner = i > 0 && i < 13;
|
||||
radius = inner ? innerRadius : outerRadius;
|
||||
tick.css({
|
||||
left: dialRadius + Math.sin(radian) * radius - tickRadius,
|
||||
top: dialRadius - Math.cos(radian) * radius - tickRadius
|
||||
});
|
||||
if (inner) {
|
||||
tick.css("font-size", "120%");
|
||||
}
|
||||
if (isDisabled) {
|
||||
tick.addClass("disabled");
|
||||
}
|
||||
tick.html(i === 0 ? "00" : i);
|
||||
hoursView.append(tick);
|
||||
if (!isDisabled) {
|
||||
tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Minutes view
|
||||
var incrementValue = Math.max(options.minutestep, 5);
|
||||
for (i = 0; i < 60; i += incrementValue) {
|
||||
tick = tickTpl.clone();
|
||||
radian = (i / 30) * Math.PI;
|
||||
tick.css({
|
||||
left: dialRadius + Math.sin(radian) * outerRadius - tickRadius,
|
||||
top: dialRadius - Math.cos(radian) * outerRadius - tickRadius
|
||||
});
|
||||
tick.css("font-size", "120%");
|
||||
tick.html(leadingZero(i));
|
||||
minutesView.append(tick);
|
||||
tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
|
||||
// Clicking on minutes view space
|
||||
plate.on(mousedownEvent, function(e) {
|
||||
if ($(e.target).closest(".clockpicker-tick").length === 0) {
|
||||
mousedown(e, true);
|
||||
}
|
||||
});
|
||||
|
||||
// Mousedown or touchstart
|
||||
function mousedown(e, space) {
|
||||
var offset = plate.offset(),
|
||||
isTouch = /^touch/.test(e.type),
|
||||
x0 = offset.left + dialRadius,
|
||||
y0 = offset.top + dialRadius,
|
||||
dx = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
|
||||
dy = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0,
|
||||
z = Math.sqrt(dx * dx + dy * dy),
|
||||
moved = false;
|
||||
|
||||
// When clicking on minutes view space, check the mouse position
|
||||
if (
|
||||
space &&
|
||||
(z < outerRadius - tickRadius || z > outerRadius + tickRadius)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
// Set cursor style of body after 200ms
|
||||
var movingTimer = setTimeout(function() {
|
||||
$body.addClass("clockpicker-moving");
|
||||
}, 200);
|
||||
|
||||
// Place the canvas to top
|
||||
if (svgSupported) {
|
||||
plate.append(self.canvas);
|
||||
}
|
||||
|
||||
// Clock
|
||||
self.setHand(dx, dy, true);
|
||||
|
||||
// Mousemove on document
|
||||
$doc.off(mousemoveEvent).on(mousemoveEvent, function(e) {
|
||||
e.preventDefault();
|
||||
var isTouch = /^touch/.test(e.type),
|
||||
x = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
|
||||
y = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0;
|
||||
if (!moved && x === dx && y === dy) {
|
||||
// Clicking in chrome on windows will trigger a mousemove event
|
||||
return;
|
||||
}
|
||||
moved = true;
|
||||
self.setHand(x, y, true);
|
||||
});
|
||||
|
||||
// Mouseup on document
|
||||
$doc.off(mouseupEvent).on(mouseupEvent, function(e) {
|
||||
$doc.off(mouseupEvent);
|
||||
e.preventDefault();
|
||||
var isTouch = /^touch/.test(e.type),
|
||||
x = (isTouch ? e.originalEvent.changedTouches[0] : e).pageX - x0,
|
||||
y = (isTouch ? e.originalEvent.changedTouches[0] : e).pageY - y0;
|
||||
if ((space || moved) && x === dx && y === dy) {
|
||||
self.setHand(x, y);
|
||||
}
|
||||
if (self.currentView === "hours") {
|
||||
self.toggleView("minutes", duration / 2);
|
||||
} else {
|
||||
if (options.autoclose) {
|
||||
if (!options.ampmSubmit) {
|
||||
self.minutesView.addClass("clockpicker-dial-out");
|
||||
setTimeout(function() {
|
||||
self.done();
|
||||
}, duration / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
plate.prepend(canvas);
|
||||
|
||||
// Reset cursor style of body
|
||||
clearTimeout(movingTimer);
|
||||
$body.removeClass("clockpicker-moving");
|
||||
|
||||
// Unbind mousemove event
|
||||
$doc.off(mousemoveEvent);
|
||||
});
|
||||
}
|
||||
|
||||
if (svgSupported) {
|
||||
// Draw clock hands and others
|
||||
var canvas = popover.find(".clockpicker-canvas"),
|
||||
svg = createSvgElement("svg");
|
||||
svg.setAttribute("class", "clockpicker-svg");
|
||||
svg.setAttribute("width", diameter);
|
||||
svg.setAttribute("height", diameter);
|
||||
var g = createSvgElement("g");
|
||||
g.setAttribute(
|
||||
"transform",
|
||||
"translate(" + dialRadius + "," + dialRadius + ")"
|
||||
);
|
||||
var bearing = createSvgElement("circle");
|
||||
bearing.setAttribute("class", "clockpicker-canvas-bearing");
|
||||
bearing.setAttribute("cx", 0);
|
||||
bearing.setAttribute("cy", 0);
|
||||
bearing.setAttribute("r", 3);
|
||||
var hand = createSvgElement("line");
|
||||
hand.setAttribute("x1", 0);
|
||||
hand.setAttribute("y1", 0);
|
||||
var bg = createSvgElement("circle");
|
||||
bg.setAttribute("class", "clockpicker-canvas-bg");
|
||||
bg.setAttribute("r", tickRadius);
|
||||
var fg = createSvgElement("circle");
|
||||
fg.setAttribute("class", "clockpicker-canvas-fg");
|
||||
fg.setAttribute("r", 3.5);
|
||||
g.appendChild(hand);
|
||||
g.appendChild(bg);
|
||||
g.appendChild(fg);
|
||||
g.appendChild(bearing);
|
||||
svg.appendChild(g);
|
||||
canvas.append(svg);
|
||||
|
||||
this.hand = hand;
|
||||
this.bg = bg;
|
||||
this.fg = fg;
|
||||
this.bearing = bearing;
|
||||
this.g = g;
|
||||
this.canvas = canvas;
|
||||
}
|
||||
|
||||
this.raiseCallback(this.options.init, "init");
|
||||
}
|
||||
|
||||
function raiseCallback(callbackFunction, triggerName) {
|
||||
if (
|
||||
callbackFunction &&
|
||||
typeof callbackFunction === "function" &&
|
||||
this.element
|
||||
) {
|
||||
var time = this.getTime() || null;
|
||||
callbackFunction.call(this.element, time);
|
||||
}
|
||||
if (triggerName) {
|
||||
this.element.trigger("clockpicker." + triggerName || "NoName");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find most suitable vertical placement, doing our best to ensure it is inside of the viewport.
|
||||
*
|
||||
* First try to place the element according with preferredPlacement, then try the opposite
|
||||
* placement and as a last resort, popover will be placed on the very top of the viewport.
|
||||
*
|
||||
* @param {jQuery} element
|
||||
* @param {jQuery} popover
|
||||
* @param preferredPlacement Preferred placement, if there is enough room for it.
|
||||
* @returns {string} One of: 'top', 'bottom' or 'viewport-top'.
|
||||
*/
|
||||
function resolveAdaptiveVerticalPlacement(
|
||||
element,
|
||||
popover,
|
||||
preferredPlacement
|
||||
) {
|
||||
var popoverHeight = popover.outerHeight(),
|
||||
elementHeight = element.outerHeight(),
|
||||
elementTopOffset = element.offset().top,
|
||||
elementBottomOffset = element.offset().top + elementHeight,
|
||||
minVisibleY = elementTopOffset - element[0].getBoundingClientRect().top,
|
||||
maxVisibleY = minVisibleY + document.documentElement.clientHeight,
|
||||
isEnoughRoomAbove = elementTopOffset - popoverHeight >= minVisibleY,
|
||||
isEnoughRoomBelow = elementBottomOffset + popoverHeight <= maxVisibleY;
|
||||
|
||||
if (preferredPlacement === "top") {
|
||||
if (isEnoughRoomAbove) {
|
||||
return "top";
|
||||
} else if (isEnoughRoomBelow) {
|
||||
return "bottom";
|
||||
}
|
||||
} else {
|
||||
if (isEnoughRoomBelow) {
|
||||
return "bottom";
|
||||
} else if (isEnoughRoomAbove) {
|
||||
return "top";
|
||||
}
|
||||
}
|
||||
|
||||
return "viewport-top";
|
||||
}
|
||||
|
||||
ClockPicker.prototype.parseStep = function(givenStepSize, wholeSize) {
|
||||
return wholeSize % givenStepSize === 0 ? givenStepSize : 1;
|
||||
};
|
||||
|
||||
// Default options
|
||||
ClockPicker.DEFAULTS = {
|
||||
default: "", // default time, 'now' or '13:14' e.g.
|
||||
fromnow: 0, // set default time to * milliseconds from now (using with default = 'now')
|
||||
placement: "bottom", // clock popover placement
|
||||
align: "left", // popover arrow align
|
||||
donetext: "OK", // done button text
|
||||
canceltext: "Cancel", // cancel button text
|
||||
autoclose: false, // auto close when minute is selected
|
||||
twelvehour: false, // change to 12 hour AM/PM clock from 24 hour
|
||||
vibrate: true, // vibrate the device when dragging clock hand
|
||||
hourstep: 1, // allow to multi increment the hour
|
||||
minutestep: 1, // allow to multi increment the minute
|
||||
ampmSubmit: false, // allow submit with AM and PM buttons instead of the minute selection/picker
|
||||
addonOnly: false, // only open on clicking on the input-addon
|
||||
disabledhours: null // disabled hours (only 24 hour mode)
|
||||
};
|
||||
|
||||
// Show or hide popover
|
||||
ClockPicker.prototype.toggle = function() {
|
||||
this[this.isShown ? "hide" : "show"]();
|
||||
};
|
||||
|
||||
// Set new placement class for popover and remove the old one, if any.
|
||||
ClockPicker.prototype.updatePlacementClass = function(newClass) {
|
||||
if (this.currentPlacementClass) {
|
||||
this.popover.removeClass(this.currentPlacementClass);
|
||||
}
|
||||
if (newClass) {
|
||||
this.popover.addClass(newClass);
|
||||
}
|
||||
|
||||
this.currentPlacementClass = newClass;
|
||||
};
|
||||
|
||||
// Set popover position and update placement class, if needed
|
||||
ClockPicker.prototype.locate = function() {
|
||||
var element = this.element,
|
||||
popover = this.popover,
|
||||
offset = element.offset(),
|
||||
width = element.outerWidth(),
|
||||
height = element.outerHeight(),
|
||||
placement = this.options.placement,
|
||||
align = this.options.align,
|
||||
windowHeight = $win.height(),
|
||||
windowWidth = $win.width(),
|
||||
popoverHeight = popover.height(),
|
||||
popoverWidth = popover.width(),
|
||||
styles = {},
|
||||
self = this;
|
||||
|
||||
if (placement === "top-adaptive" || placement === "bottom-adaptive") {
|
||||
var preferredPlacement = placement.substr(0, placement.indexOf("-"));
|
||||
// Adaptive placement should be resolved into one of the "static" placement
|
||||
// options, that is best suitable for the current window scroll position.
|
||||
placement = resolveAdaptiveVerticalPlacement(
|
||||
element,
|
||||
popover,
|
||||
preferredPlacement
|
||||
);
|
||||
|
||||
this.updatePlacementClass(placement !== "viewport-top" ? placement : "");
|
||||
}
|
||||
|
||||
popover.show();
|
||||
|
||||
// Place the popover
|
||||
switch (placement) {
|
||||
case "bottom":
|
||||
styles.top = offset.top + height;
|
||||
break;
|
||||
case "right":
|
||||
styles.left = offset.left + width;
|
||||
break;
|
||||
case "top":
|
||||
styles.top = offset.top - popover.outerHeight();
|
||||
break;
|
||||
case "left":
|
||||
styles.left = offset.left - popover.outerWidth();
|
||||
break;
|
||||
case "viewport-top":
|
||||
styles.top = offset.top - element[0].getBoundingClientRect().top;
|
||||
break;
|
||||
}
|
||||
|
||||
// Align the popover arrow
|
||||
switch (align) {
|
||||
case "left":
|
||||
styles.left = offset.left;
|
||||
break;
|
||||
case "right":
|
||||
styles.left = offset.left + width - popover.outerWidth();
|
||||
break;
|
||||
case "top":
|
||||
styles.top = offset.top;
|
||||
break;
|
||||
case "bottom":
|
||||
styles.top = offset.top + height - popover.outerHeight();
|
||||
break;
|
||||
}
|
||||
|
||||
// Correct the popover position outside the window
|
||||
if (popoverHeight + styles.top > windowHeight) {
|
||||
styles.top = windowHeight - popoverHeight;
|
||||
}
|
||||
if (popoverWidth + styles.left > windowWidth) {
|
||||
styles.left = windowWidth - popoverWidth;
|
||||
}
|
||||
|
||||
popover.css(styles);
|
||||
};
|
||||
|
||||
// The input can be changed by the user
|
||||
// So before we can use this.hours/this.minutes we must update it
|
||||
ClockPicker.prototype.parseInputValue = function() {
|
||||
var value = this.input.prop("value") || this.options["default"] || "";
|
||||
|
||||
if (value === "now") {
|
||||
value = new Date(+new Date() + this.options.fromnow);
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
value = value.getHours() + ":" + value.getMinutes();
|
||||
}
|
||||
|
||||
value = value.split(":");
|
||||
|
||||
// Minutes can have AM/PM that needs to be removed
|
||||
this.hours = +value[0] || 0;
|
||||
this.minutes = +(value[1] + "").replace(/\D/g, "") || 0;
|
||||
|
||||
this.hours =
|
||||
Math.round(this.hours / this.options.hourstep) * this.options.hourstep;
|
||||
this.minutes =
|
||||
Math.round(this.minutes / this.options.minutestep) *
|
||||
this.options.minutestep;
|
||||
|
||||
if (this.options.twelvehour) {
|
||||
var period = (value[1] + "").replace(/\d+/g, "").toLowerCase();
|
||||
//this.amOrPm = this.hours > 12 || period === "pm" ? "PM" : "AM";
|
||||
this.amOrPm = this.hours < 12 || period === "am" ? "AM" : "PM";
|
||||
}
|
||||
};
|
||||
|
||||
// Show popover
|
||||
ClockPicker.prototype.show = function(e) {
|
||||
// Not show again
|
||||
if (this.isShown) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.raiseCallback(this.options.beforeShow, "beforeShow");
|
||||
|
||||
var self = this;
|
||||
|
||||
// Initialize
|
||||
if (!this.isAppended) {
|
||||
// Append popover to body
|
||||
$body = $(document.body).append(this.popover);
|
||||
|
||||
// Reset position when resize
|
||||
$win.on("resize.clockpicker" + this.id, function() {
|
||||
if (self.isShown) {
|
||||
self.locate();
|
||||
}
|
||||
});
|
||||
|
||||
this.isAppended = true;
|
||||
}
|
||||
|
||||
// Get the time from the input field
|
||||
this.parseInputValue();
|
||||
|
||||
this.spanHours.html(leadingZero(this.hours));
|
||||
this.spanMinutes.html(leadingZero(this.minutes));
|
||||
|
||||
// Toggle to hours view
|
||||
this.toggleView("hours");
|
||||
|
||||
// Set position
|
||||
this.locate();
|
||||
|
||||
this.isShown = true;
|
||||
|
||||
// Hide when clicking or tabbing on any element except the clock, input and addon
|
||||
$doc.on(
|
||||
"click.clockpicker." + this.id + " focusin.clockpicker." + this.id,
|
||||
function(e) {
|
||||
var target = $(e.target);
|
||||
if (
|
||||
target.closest(self.popover).length === 0 &&
|
||||
target.closest(self.addon).length === 0 &&
|
||||
target.closest(self.input).length === 0
|
||||
) {
|
||||
self.hide();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Hide when ESC is pressed
|
||||
$doc.on("keyup.clockpicker." + this.id, function(e) {
|
||||
if (e.keyCode === 27) {
|
||||
self.hide();
|
||||
}
|
||||
});
|
||||
|
||||
this.raiseCallback(this.options.afterShow, "afterShow");
|
||||
};
|
||||
|
||||
// Hide popover
|
||||
ClockPicker.prototype.hide = function() {
|
||||
this.raiseCallback(this.options.beforeHide, "beforeHide");
|
||||
|
||||
this.isShown = false;
|
||||
|
||||
// Unbinding events on document
|
||||
$doc.off(
|
||||
"click.clockpicker." + this.id + " focusin.clockpicker." + this.id
|
||||
);
|
||||
$doc.off("keyup.clockpicker." + this.id);
|
||||
|
||||
this.popover.hide();
|
||||
|
||||
this.raiseCallback(this.options.afterHide, "afterHide");
|
||||
};
|
||||
|
||||
// Toggle to hours or minutes view
|
||||
ClockPicker.prototype.toggleView = function(view, delay) {
|
||||
var raiseAfterHourSelect = false;
|
||||
if (
|
||||
view === "minutes" &&
|
||||
$(this.hoursView).css("visibility") === "visible"
|
||||
) {
|
||||
this.raiseCallback(this.options.beforeHourSelect, "beforeHourSelect");
|
||||
raiseAfterHourSelect = true;
|
||||
}
|
||||
var isHours = view === "hours",
|
||||
nextView = isHours ? this.hoursView : this.minutesView,
|
||||
hideView = isHours ? this.minutesView : this.hoursView;
|
||||
|
||||
this.currentView = view;
|
||||
|
||||
this.spanHours.toggleClass("text-white-50", !isHours);
|
||||
this.spanMinutes.toggleClass("text-white-50", isHours);
|
||||
|
||||
// Let's make transitions
|
||||
hideView.addClass("clockpicker-dial-out");
|
||||
nextView.css("visibility", "visible").removeClass("clockpicker-dial-out");
|
||||
|
||||
// Reset clock hand
|
||||
this.resetClock(delay);
|
||||
|
||||
// After transitions ended
|
||||
clearTimeout(this.toggleViewTimer);
|
||||
this.toggleViewTimer = setTimeout(function() {
|
||||
hideView.css("visibility", "hidden");
|
||||
}, duration);
|
||||
|
||||
if (raiseAfterHourSelect) {
|
||||
this.raiseCallback(this.options.afterHourSelect, "afterHourSelect");
|
||||
}
|
||||
};
|
||||
|
||||
// Reset clock hand
|
||||
ClockPicker.prototype.resetClock = function(delay) {
|
||||
var view = this.currentView,
|
||||
value = this[view],
|
||||
isHours = view === "hours",
|
||||
unit = Math.PI / (isHours ? 6 : 30),
|
||||
radian = value * unit,
|
||||
radius = isHours && value > 0 && value < 13 ? innerRadius : outerRadius,
|
||||
x = Math.sin(radian) * radius,
|
||||
y = -Math.cos(radian) * radius,
|
||||
self = this;
|
||||
if (svgSupported && delay) {
|
||||
self.canvas.addClass("clockpicker-canvas-out");
|
||||
setTimeout(function() {
|
||||
self.canvas.removeClass("clockpicker-canvas-out");
|
||||
self.setHand(x, y);
|
||||
}, delay);
|
||||
} else {
|
||||
this.setHand(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
// Set clock hand to (x, y)
|
||||
ClockPicker.prototype.setHand = function(x, y, dragging) {
|
||||
var radian = Math.atan2(x, -y),
|
||||
isHours = this.currentView === "hours",
|
||||
z = Math.sqrt(x * x + y * y),
|
||||
options = this.options,
|
||||
inner = isHours && z < (outerRadius + innerRadius) / 2,
|
||||
radius = inner ? innerRadius : outerRadius,
|
||||
unit,
|
||||
value;
|
||||
|
||||
// Calculate the unit
|
||||
if (isHours) {
|
||||
unit = (options.hourstep / 6) * Math.PI;
|
||||
} else {
|
||||
unit = (options.minutestep / 30) * Math.PI;
|
||||
}
|
||||
|
||||
if (options.twelvehour) {
|
||||
radius = outerRadius;
|
||||
}
|
||||
|
||||
// Radian should in range [0, 2PI]
|
||||
if (radian < 0) {
|
||||
radian = Math.PI * 2 + radian;
|
||||
}
|
||||
|
||||
// Get the round value
|
||||
value = Math.round(radian / unit);
|
||||
|
||||
// Get the round radian
|
||||
radian = value * unit;
|
||||
|
||||
// Correct the hours or minutes
|
||||
if (isHours) {
|
||||
value *= options.hourstep;
|
||||
|
||||
if (!options.twelvehour && !inner == value > 0) {
|
||||
value += 12;
|
||||
}
|
||||
if (options.twelvehour && value === 0) {
|
||||
value = 12;
|
||||
}
|
||||
if (value === 24) {
|
||||
value = 0;
|
||||
}
|
||||
if (
|
||||
dragging &&
|
||||
!options.twelvehour &&
|
||||
options.disabledhours &&
|
||||
$.inArray(value, options.disabledhours) != -1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
value *= options.minutestep;
|
||||
if (value === 60) {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Once hours or minutes changed, vibrate the device
|
||||
if (this[this.currentView] !== value) {
|
||||
if (vibrate && this.options.vibrate) {
|
||||
// Do not vibrate too frequently
|
||||
if (!this.vibrateTimer) {
|
||||
navigator[vibrate](10);
|
||||
this.vibrateTimer = setTimeout(
|
||||
$.proxy(function() {
|
||||
this.vibrateTimer = null;
|
||||
}, this),
|
||||
100
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this[this.currentView] = value;
|
||||
this[isHours ? "spanHours" : "spanMinutes"].html(leadingZero(value));
|
||||
|
||||
// If svg is not supported, just add an active class to the tick
|
||||
if (!svgSupported) {
|
||||
this[isHours ? "hoursView" : "minutesView"]
|
||||
.find(".clockpicker-tick")
|
||||
.each(function() {
|
||||
var tick = $(this);
|
||||
tick.toggleClass("active", value === +tick.html());
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Place clock hand at the top when dragging
|
||||
if (dragging || (!isHours && value % 5)) {
|
||||
this.g.insertBefore(this.hand, this.bearing);
|
||||
this.g.insertBefore(this.bg, this.fg);
|
||||
this.bg.setAttribute(
|
||||
"class",
|
||||
"clockpicker-canvas-bg clockpicker-canvas-bg-trans"
|
||||
);
|
||||
} else {
|
||||
// Or place it at the bottom
|
||||
this.g.insertBefore(this.hand, this.bg);
|
||||
this.g.insertBefore(this.fg, this.bg);
|
||||
this.bg.setAttribute("class", "clockpicker-canvas-bg");
|
||||
}
|
||||
|
||||
// Set clock hand and others' position
|
||||
var cx = Math.sin(radian) * radius,
|
||||
cy = -Math.cos(radian) * radius;
|
||||
this.hand.setAttribute("x2", cx);
|
||||
this.hand.setAttribute("y2", cy);
|
||||
this.bg.setAttribute("cx", cx);
|
||||
this.bg.setAttribute("cy", cy);
|
||||
this.fg.setAttribute("cx", cx);
|
||||
this.fg.setAttribute("cy", cy);
|
||||
};
|
||||
|
||||
// Allow user to get time time as Date object
|
||||
ClockPicker.prototype.getTime = function(callback) {
|
||||
var hours = this.hours;
|
||||
if (this.options.twelvehour && hours < 12 && this.amOrPm === "PM") {
|
||||
hours += 12;
|
||||
}
|
||||
|
||||
var selectedTime = new Date();
|
||||
selectedTime.setMinutes(this.minutes);
|
||||
selectedTime.setHours(hours);
|
||||
selectedTime.setSeconds(0);
|
||||
|
||||
return (
|
||||
(callback && callback.apply(this.element, selectedTime)) || selectedTime
|
||||
);
|
||||
};
|
||||
|
||||
// Hours and minutes are selected
|
||||
ClockPicker.prototype.done = function() {
|
||||
this.raiseCallback(this.options.beforeDone, "beforeDone");
|
||||
this.hide();
|
||||
var last = this.input.prop("value"),
|
||||
outHours = this.hours,
|
||||
value = ":" + leadingZero(this.minutes);
|
||||
|
||||
if (this.isHTML5 && this.options.twelvehour) {
|
||||
if (this.hours < 12 && this.amOrPm === "PM") {
|
||||
outHours += 12;
|
||||
}
|
||||
if (this.hours === 12 && this.amOrPm === "AM") {
|
||||
outHours = 0;
|
||||
}
|
||||
}
|
||||
|
||||
value = leadingZero(outHours) + value;
|
||||
|
||||
if (!this.isHTML5 && this.options.twelvehour) {
|
||||
value = value + this.amOrPm;
|
||||
}
|
||||
|
||||
this.input.prop("value", value);
|
||||
if (value !== last) {
|
||||
this.input.trigger("change");
|
||||
if (!this.isInput) {
|
||||
this.element.trigger("change");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.autoclose) {
|
||||
this.input.trigger("blur");
|
||||
}
|
||||
|
||||
this.raiseCallback(this.options.afterDone, "afterDone");
|
||||
};
|
||||
|
||||
// Remove clockpicker from input
|
||||
ClockPicker.prototype.remove = function() {
|
||||
this.element.removeData("clockpicker");
|
||||
this.input.off("focus.clockpicker click.clockpicker");
|
||||
this.addon.off("click.clockpicker");
|
||||
if (this.isShown) {
|
||||
this.hide();
|
||||
}
|
||||
if (this.isAppended) {
|
||||
$win.off("resize.clockpicker" + this.id);
|
||||
this.popover.remove();
|
||||
}
|
||||
};
|
||||
|
||||
// Extends $.fn.clockpicker
|
||||
$.fn.clockpicker = function(option) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
function handleClockPickerRequest() {
|
||||
var $this = $(this),
|
||||
data = $this.data("clockpicker");
|
||||
if (!data) {
|
||||
var options = $.extend(
|
||||
{},
|
||||
ClockPicker.DEFAULTS,
|
||||
$this.data(),
|
||||
typeof option == "object" && option
|
||||
);
|
||||
$this.data("clockpicker", new ClockPicker($this, options));
|
||||
} else {
|
||||
// Manual operations. show, hide, remove, getTime, e.g.
|
||||
if (typeof data[option] === "function") {
|
||||
return data[option].apply(data, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we explicitly do a call on a single element then we can return the value (if needed)
|
||||
// This allows us, for example, to return the value of getTime
|
||||
if (this.length == 1) {
|
||||
var returnValue = handleClockPickerRequest.apply(this[0]);
|
||||
|
||||
// If we do not have any return value then return the object itself so you can chain
|
||||
return returnValue !== undefined ? returnValue : this;
|
||||
}
|
||||
|
||||
// If we do have a list then we do not care about return values
|
||||
return this.each(handleClockPickerRequest);
|
||||
};
|
||||
})(jQuery);
|
||||
7
html/webpage/assets/js/bootstrap4-clockpicker.min.js
vendored
Normal file
7
html/webpage/assets/js/bootstrap4-clockpicker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
html/webpage/assets/js/flatpickr.js
Normal file
2
html/webpage/assets/js/flatpickr.js
Normal file
File diff suppressed because one or more lines are too long
729
html/webpage/assets/js/jquery-clockpicker.js
vendored
Normal file
729
html/webpage/assets/js/jquery-clockpicker.js
vendored
Normal file
@@ -0,0 +1,729 @@
|
||||
/*!
|
||||
* ClockPicker v0.0.7 (http://weareoutman.github.io/clockpicker/)
|
||||
* Copyright 2014 Wang Shenwei.
|
||||
* Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
|
||||
*/
|
||||
|
||||
;(function(){
|
||||
var $ = window.jQuery,
|
||||
$win = $(window),
|
||||
$doc = $(document),
|
||||
$body;
|
||||
|
||||
// Can I use inline svg ?
|
||||
var svgNS = 'http://www.w3.org/2000/svg',
|
||||
svgSupported = 'SVGAngle' in window && (function(){
|
||||
var supported,
|
||||
el = document.createElement('div');
|
||||
el.innerHTML = '<svg/>';
|
||||
supported = (el.firstChild && el.firstChild.namespaceURI) == svgNS;
|
||||
el.innerHTML = '';
|
||||
return supported;
|
||||
})();
|
||||
|
||||
// Can I use transition ?
|
||||
var transitionSupported = (function(){
|
||||
var style = document.createElement('div').style;
|
||||
return 'transition' in style ||
|
||||
'WebkitTransition' in style ||
|
||||
'MozTransition' in style ||
|
||||
'msTransition' in style ||
|
||||
'OTransition' in style;
|
||||
})();
|
||||
|
||||
// Listen touch events in touch screen device, instead of mouse events in desktop.
|
||||
var touchSupported = 'ontouchstart' in window,
|
||||
mousedownEvent = 'mousedown' + ( touchSupported ? ' touchstart' : ''),
|
||||
mousemoveEvent = 'mousemove.clockpicker' + ( touchSupported ? ' touchmove.clockpicker' : ''),
|
||||
mouseupEvent = 'mouseup.clockpicker' + ( touchSupported ? ' touchend.clockpicker' : '');
|
||||
|
||||
// Vibrate the device if supported
|
||||
var vibrate = navigator.vibrate ? 'vibrate' : navigator.webkitVibrate ? 'webkitVibrate' : null;
|
||||
|
||||
function createSvgElement(name) {
|
||||
return document.createElementNS(svgNS, name);
|
||||
}
|
||||
|
||||
function leadingZero(num) {
|
||||
return (num < 10 ? '0' : '') + num;
|
||||
}
|
||||
|
||||
// Get a unique id
|
||||
var idCounter = 0;
|
||||
function uniqueId(prefix) {
|
||||
var id = ++idCounter + '';
|
||||
return prefix ? prefix + id : id;
|
||||
}
|
||||
|
||||
// Clock size
|
||||
var dialRadius = 100,
|
||||
outerRadius = 80,
|
||||
// innerRadius = 80 on 12 hour clock
|
||||
innerRadius = 54,
|
||||
tickRadius = 13,
|
||||
diameter = dialRadius * 2,
|
||||
duration = transitionSupported ? 350 : 1;
|
||||
|
||||
// Popover template
|
||||
var tpl = [
|
||||
'<div class="popover clockpicker-popover">',
|
||||
'<div class="arrow"></div>',
|
||||
'<div class="popover-title">',
|
||||
'<span class="clockpicker-span-hours text-primary"></span>',
|
||||
' : ',
|
||||
'<span class="clockpicker-span-minutes"></span>',
|
||||
'<span class="clockpicker-span-am-pm"></span>',
|
||||
'</div>',
|
||||
'<div class="popover-content">',
|
||||
'<div class="clockpicker-plate">',
|
||||
'<div class="clockpicker-canvas"></div>',
|
||||
'<div class="clockpicker-dial clockpicker-hours"></div>',
|
||||
'<div class="clockpicker-dial clockpicker-minutes clockpicker-dial-out"></div>',
|
||||
'</div>',
|
||||
'<span class="clockpicker-am-pm-block">',
|
||||
'</span>',
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('');
|
||||
|
||||
// ClockPicker
|
||||
function ClockPicker(element, options) {
|
||||
var popover = $(tpl),
|
||||
plate = popover.find('.clockpicker-plate'),
|
||||
hoursView = popover.find('.clockpicker-hours'),
|
||||
minutesView = popover.find('.clockpicker-minutes'),
|
||||
amPmBlock = popover.find('.clockpicker-am-pm-block'),
|
||||
isInput = element.prop('tagName') === 'INPUT',
|
||||
input = isInput ? element : element.find('input'),
|
||||
addon = element.find('.input-group-addon'),
|
||||
self = this,
|
||||
timer;
|
||||
|
||||
this.id = uniqueId('cp');
|
||||
this.element = element;
|
||||
this.options = options;
|
||||
this.isAppended = false;
|
||||
this.isShown = false;
|
||||
this.currentView = 'hours';
|
||||
this.isInput = isInput;
|
||||
this.input = input;
|
||||
this.addon = addon;
|
||||
this.popover = popover;
|
||||
this.plate = plate;
|
||||
this.hoursView = hoursView;
|
||||
this.minutesView = minutesView;
|
||||
this.amPmBlock = amPmBlock;
|
||||
this.spanHours = popover.find('.clockpicker-span-hours');
|
||||
this.spanMinutes = popover.find('.clockpicker-span-minutes');
|
||||
this.spanAmPm = popover.find('.clockpicker-span-am-pm');
|
||||
this.amOrPm = "PM";
|
||||
|
||||
// Setup for for 12 hour clock if option is selected
|
||||
if (options.twelvehour) {
|
||||
|
||||
var amPmButtonsTemplate = ['<div class="clockpicker-am-pm-block">',
|
||||
'<button type="button" class="btn btn-sm btn-default clockpicker-button clockpicker-am-button">',
|
||||
'AM</button>',
|
||||
'<button type="button" class="btn btn-sm btn-default clockpicker-button clockpicker-pm-button">',
|
||||
'PM</button>',
|
||||
'</div>'].join('');
|
||||
|
||||
var amPmButtons = $(amPmButtonsTemplate);
|
||||
//amPmButtons.appendTo(plate);
|
||||
|
||||
////Not working b/c they are not shown when this runs
|
||||
//$('clockpicker-am-button')
|
||||
// .on("click", function() {
|
||||
// self.amOrPm = "AM";
|
||||
// $('.clockpicker-span-am-pm').empty().append('AM');
|
||||
// });
|
||||
//
|
||||
//$('clockpicker-pm-button')
|
||||
// .on("click", function() {
|
||||
// self.amOrPm = "PM";
|
||||
// $('.clockpicker-span-am-pm').empty().append('PM');
|
||||
// });
|
||||
|
||||
$('<button type="button" class="btn btn-sm btn-default clockpicker-button am-button">' + "AM" + '</button>')
|
||||
.on("click", function() {
|
||||
self.amOrPm = "AM";
|
||||
$('.clockpicker-span-am-pm').empty().append('AM');
|
||||
}).appendTo(this.amPmBlock);
|
||||
|
||||
|
||||
$('<button type="button" class="btn btn-sm btn-default clockpicker-button pm-button">' + "PM" + '</button>')
|
||||
.on("click", function() {
|
||||
self.amOrPm = 'PM';
|
||||
$('.clockpicker-span-am-pm').empty().append('PM');
|
||||
}).appendTo(this.amPmBlock);
|
||||
|
||||
}
|
||||
|
||||
if (! options.autoclose) {
|
||||
// If autoclose is not setted, append a button
|
||||
$('<button type="button" class="btn btn-sm btn-default btn-block clockpicker-button">' + options.donetext + '</button>')
|
||||
.click($.proxy(this.done, this))
|
||||
.appendTo(popover);
|
||||
}
|
||||
|
||||
// Placement and arrow align - make sure they make sense.
|
||||
if ((options.placement === 'top' || options.placement === 'bottom') && (options.align === 'top' || options.align === 'bottom')) options.align = 'left';
|
||||
if ((options.placement === 'left' || options.placement === 'right') && (options.align === 'left' || options.align === 'right')) options.align = 'top';
|
||||
|
||||
popover.addClass(options.placement);
|
||||
popover.addClass('clockpicker-align-' + options.align);
|
||||
|
||||
this.spanHours.click($.proxy(this.toggleView, this, 'hours'));
|
||||
this.spanMinutes.click($.proxy(this.toggleView, this, 'minutes'));
|
||||
|
||||
// Show or toggle
|
||||
input.on('focus.clockpicker click.clockpicker', $.proxy(this.show, this));
|
||||
addon.on('click.clockpicker', $.proxy(this.toggle, this));
|
||||
|
||||
// Build ticks
|
||||
var tickTpl = $('<div class="clockpicker-tick"></div>'),
|
||||
i, tick, radian, radius;
|
||||
|
||||
// Hours view
|
||||
if (options.twelvehour) {
|
||||
for (i = 1; i < 13; i += 1) {
|
||||
tick = tickTpl.clone();
|
||||
radian = i / 6 * Math.PI;
|
||||
radius = outerRadius;
|
||||
tick.css('font-size', '120%');
|
||||
tick.css({
|
||||
left: dialRadius + Math.sin(radian) * radius - tickRadius,
|
||||
top: dialRadius - Math.cos(radian) * radius - tickRadius
|
||||
});
|
||||
tick.html(i === 0 ? '00' : i);
|
||||
hoursView.append(tick);
|
||||
tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 24; i += 1) {
|
||||
tick = tickTpl.clone();
|
||||
radian = i / 6 * Math.PI;
|
||||
var inner = i > 0 && i < 13;
|
||||
radius = inner ? innerRadius : outerRadius;
|
||||
tick.css({
|
||||
left: dialRadius + Math.sin(radian) * radius - tickRadius,
|
||||
top: dialRadius - Math.cos(radian) * radius - tickRadius
|
||||
});
|
||||
if (inner) {
|
||||
tick.css('font-size', '120%');
|
||||
}
|
||||
tick.html(i === 0 ? '00' : i);
|
||||
hoursView.append(tick);
|
||||
tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
}
|
||||
|
||||
// Minutes view
|
||||
for (i = 0; i < 60; i += 5) {
|
||||
tick = tickTpl.clone();
|
||||
radian = i / 30 * Math.PI;
|
||||
tick.css({
|
||||
left: dialRadius + Math.sin(radian) * outerRadius - tickRadius,
|
||||
top: dialRadius - Math.cos(radian) * outerRadius - tickRadius
|
||||
});
|
||||
tick.css('font-size', '120%');
|
||||
tick.html(leadingZero(i));
|
||||
minutesView.append(tick);
|
||||
tick.on(mousedownEvent, mousedown);
|
||||
}
|
||||
|
||||
// Clicking on minutes view space
|
||||
plate.on(mousedownEvent, function(e){
|
||||
if ($(e.target).closest('.clockpicker-tick').length === 0) {
|
||||
mousedown(e, true);
|
||||
}
|
||||
});
|
||||
|
||||
// Mousedown or touchstart
|
||||
function mousedown(e, space) {
|
||||
var offset = plate.offset(),
|
||||
isTouch = /^touch/.test(e.type),
|
||||
x0 = offset.left + dialRadius,
|
||||
y0 = offset.top + dialRadius,
|
||||
dx = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
|
||||
dy = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0,
|
||||
z = Math.sqrt(dx * dx + dy * dy),
|
||||
moved = false;
|
||||
|
||||
// When clicking on minutes view space, check the mouse position
|
||||
if (space && (z < outerRadius - tickRadius || z > outerRadius + tickRadius)) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
// Set cursor style of body after 200ms
|
||||
var movingTimer = setTimeout(function(){
|
||||
$body.addClass('clockpicker-moving');
|
||||
}, 200);
|
||||
|
||||
// Place the canvas to top
|
||||
if (svgSupported) {
|
||||
plate.append(self.canvas);
|
||||
}
|
||||
|
||||
// Clock
|
||||
self.setHand(dx, dy, ! space, true);
|
||||
|
||||
// Mousemove on document
|
||||
$doc.off(mousemoveEvent).on(mousemoveEvent, function(e){
|
||||
e.preventDefault();
|
||||
var isTouch = /^touch/.test(e.type),
|
||||
x = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
|
||||
y = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0;
|
||||
if (! moved && x === dx && y === dy) {
|
||||
// Clicking in chrome on windows will trigger a mousemove event
|
||||
return;
|
||||
}
|
||||
moved = true;
|
||||
self.setHand(x, y, false, true);
|
||||
});
|
||||
|
||||
// Mouseup on document
|
||||
$doc.off(mouseupEvent).on(mouseupEvent, function(e){
|
||||
$doc.off(mouseupEvent);
|
||||
e.preventDefault();
|
||||
var isTouch = /^touch/.test(e.type),
|
||||
x = (isTouch ? e.originalEvent.changedTouches[0] : e).pageX - x0,
|
||||
y = (isTouch ? e.originalEvent.changedTouches[0] : e).pageY - y0;
|
||||
if ((space || moved) && x === dx && y === dy) {
|
||||
self.setHand(x, y);
|
||||
}
|
||||
if (self.currentView === 'hours') {
|
||||
self.toggleView('minutes', duration / 2);
|
||||
} else {
|
||||
if (options.autoclose) {
|
||||
self.minutesView.addClass('clockpicker-dial-out');
|
||||
setTimeout(function(){
|
||||
self.done();
|
||||
}, duration / 2);
|
||||
}
|
||||
}
|
||||
plate.prepend(canvas);
|
||||
|
||||
// Reset cursor style of body
|
||||
clearTimeout(movingTimer);
|
||||
$body.removeClass('clockpicker-moving');
|
||||
|
||||
// Unbind mousemove event
|
||||
$doc.off(mousemoveEvent);
|
||||
});
|
||||
}
|
||||
|
||||
if (svgSupported) {
|
||||
// Draw clock hands and others
|
||||
var canvas = popover.find('.clockpicker-canvas'),
|
||||
svg = createSvgElement('svg');
|
||||
svg.setAttribute('class', 'clockpicker-svg');
|
||||
svg.setAttribute('width', diameter);
|
||||
svg.setAttribute('height', diameter);
|
||||
var g = createSvgElement('g');
|
||||
g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');
|
||||
var bearing = createSvgElement('circle');
|
||||
bearing.setAttribute('class', 'clockpicker-canvas-bearing');
|
||||
bearing.setAttribute('cx', 0);
|
||||
bearing.setAttribute('cy', 0);
|
||||
bearing.setAttribute('r', 2);
|
||||
var hand = createSvgElement('line');
|
||||
hand.setAttribute('x1', 0);
|
||||
hand.setAttribute('y1', 0);
|
||||
var bg = createSvgElement('circle');
|
||||
bg.setAttribute('class', 'clockpicker-canvas-bg');
|
||||
bg.setAttribute('r', tickRadius);
|
||||
var fg = createSvgElement('circle');
|
||||
fg.setAttribute('class', 'clockpicker-canvas-fg');
|
||||
fg.setAttribute('r', 3.5);
|
||||
g.appendChild(hand);
|
||||
g.appendChild(bg);
|
||||
g.appendChild(fg);
|
||||
g.appendChild(bearing);
|
||||
svg.appendChild(g);
|
||||
canvas.append(svg);
|
||||
|
||||
this.hand = hand;
|
||||
this.bg = bg;
|
||||
this.fg = fg;
|
||||
this.bearing = bearing;
|
||||
this.g = g;
|
||||
this.canvas = canvas;
|
||||
}
|
||||
|
||||
raiseCallback(this.options.init);
|
||||
}
|
||||
|
||||
function raiseCallback(callbackFunction) {
|
||||
if (callbackFunction && typeof callbackFunction === "function") {
|
||||
callbackFunction();
|
||||
}
|
||||
}
|
||||
|
||||
// Default options
|
||||
ClockPicker.DEFAULTS = {
|
||||
'default': '', // default time, 'now' or '13:14' e.g.
|
||||
fromnow: 0, // set default time to * milliseconds from now (using with default = 'now')
|
||||
placement: 'bottom', // clock popover placement
|
||||
align: 'left', // popover arrow align
|
||||
donetext: '完成', // done button text
|
||||
autoclose: false, // auto close when minute is selected
|
||||
twelvehour: false, // change to 12 hour AM/PM clock from 24 hour
|
||||
vibrate: true // vibrate the device when dragging clock hand
|
||||
};
|
||||
|
||||
// Show or hide popover
|
||||
ClockPicker.prototype.toggle = function(){
|
||||
this[this.isShown ? 'hide' : 'show']();
|
||||
};
|
||||
|
||||
// Set popover position
|
||||
ClockPicker.prototype.locate = function(){
|
||||
var element = this.element,
|
||||
popover = this.popover,
|
||||
offset = element.offset(),
|
||||
width = element.outerWidth(),
|
||||
height = element.outerHeight(),
|
||||
placement = this.options.placement,
|
||||
align = this.options.align,
|
||||
styles = {},
|
||||
self = this;
|
||||
|
||||
popover.show();
|
||||
|
||||
// Place the popover
|
||||
switch (placement) {
|
||||
case 'bottom':
|
||||
styles.top = offset.top + height;
|
||||
break;
|
||||
case 'right':
|
||||
styles.left = offset.left + width;
|
||||
break;
|
||||
case 'top':
|
||||
styles.top = offset.top - popover.outerHeight();
|
||||
break;
|
||||
case 'left':
|
||||
styles.left = offset.left - popover.outerWidth();
|
||||
break;
|
||||
}
|
||||
|
||||
// Align the popover arrow
|
||||
switch (align) {
|
||||
case 'left':
|
||||
styles.left = offset.left;
|
||||
break;
|
||||
case 'right':
|
||||
styles.left = offset.left + width - popover.outerWidth();
|
||||
break;
|
||||
case 'top':
|
||||
styles.top = offset.top;
|
||||
break;
|
||||
case 'bottom':
|
||||
styles.top = offset.top + height - popover.outerHeight();
|
||||
break;
|
||||
}
|
||||
|
||||
popover.css(styles);
|
||||
};
|
||||
|
||||
// Show popover
|
||||
ClockPicker.prototype.show = function(e){
|
||||
// Not show again
|
||||
if (this.isShown) {
|
||||
return;
|
||||
}
|
||||
|
||||
raiseCallback(this.options.beforeShow);
|
||||
|
||||
var self = this;
|
||||
|
||||
// Initialize
|
||||
if (! this.isAppended) {
|
||||
// Append popover to body
|
||||
$body = $(document.body).append(this.popover);
|
||||
|
||||
// Reset position when resize
|
||||
$win.on('resize.clockpicker' + this.id, function(){
|
||||
if (self.isShown) {
|
||||
self.locate();
|
||||
}
|
||||
});
|
||||
|
||||
this.isAppended = true;
|
||||
}
|
||||
|
||||
// Get the time
|
||||
var value = ((this.input.prop('value') || this.options['default'] || '') + '').split(':');
|
||||
if (value[0] === 'now') {
|
||||
var now = new Date(+ new Date() + this.options.fromnow);
|
||||
value = [
|
||||
now.getHours(),
|
||||
now.getMinutes()
|
||||
];
|
||||
}
|
||||
this.hours = + value[0] || 0;
|
||||
this.minutes = + value[1] || 0;
|
||||
this.spanHours.html(leadingZero(this.hours));
|
||||
this.spanMinutes.html(leadingZero(this.minutes));
|
||||
|
||||
// Toggle to hours view
|
||||
this.toggleView('hours');
|
||||
|
||||
// Set position
|
||||
this.locate();
|
||||
|
||||
this.isShown = true;
|
||||
|
||||
// Hide when clicking or tabbing on any element except the clock, input and addon
|
||||
$doc.on('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id, function(e){
|
||||
var target = $(e.target);
|
||||
if (target.closest(self.popover).length === 0 &&
|
||||
target.closest(self.addon).length === 0 &&
|
||||
target.closest(self.input).length === 0) {
|
||||
self.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Hide when ESC is pressed
|
||||
$doc.on('keyup.clockpicker.' + this.id, function(e){
|
||||
if (e.keyCode === 27) {
|
||||
self.hide();
|
||||
}
|
||||
});
|
||||
|
||||
raiseCallback(this.options.afterShow);
|
||||
};
|
||||
|
||||
// Hide popover
|
||||
ClockPicker.prototype.hide = function(){
|
||||
raiseCallback(this.options.beforeHide);
|
||||
|
||||
this.isShown = false;
|
||||
|
||||
// Unbinding events on document
|
||||
$doc.off('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id);
|
||||
$doc.off('keyup.clockpicker.' + this.id);
|
||||
|
||||
this.popover.hide();
|
||||
|
||||
raiseCallback(this.options.afterHide);
|
||||
};
|
||||
|
||||
// Toggle to hours or minutes view
|
||||
ClockPicker.prototype.toggleView = function(view, delay){
|
||||
var raiseAfterHourSelect = false;
|
||||
if (view === 'minutes' && $(this.hoursView).css("visibility") === "visible") {
|
||||
raiseCallback(this.options.beforeHourSelect);
|
||||
raiseAfterHourSelect = true;
|
||||
}
|
||||
var isHours = view === 'hours',
|
||||
nextView = isHours ? this.hoursView : this.minutesView,
|
||||
hideView = isHours ? this.minutesView : this.hoursView;
|
||||
|
||||
this.currentView = view;
|
||||
|
||||
this.spanHours.toggleClass('text-primary', isHours);
|
||||
this.spanMinutes.toggleClass('text-primary', ! isHours);
|
||||
|
||||
// Let's make transitions
|
||||
hideView.addClass('clockpicker-dial-out');
|
||||
nextView.css('visibility', 'visible').removeClass('clockpicker-dial-out');
|
||||
|
||||
// Reset clock hand
|
||||
this.resetClock(delay);
|
||||
|
||||
// After transitions ended
|
||||
clearTimeout(this.toggleViewTimer);
|
||||
this.toggleViewTimer = setTimeout(function(){
|
||||
hideView.css('visibility', 'hidden');
|
||||
}, duration);
|
||||
|
||||
if (raiseAfterHourSelect) {
|
||||
raiseCallback(this.options.afterHourSelect);
|
||||
}
|
||||
};
|
||||
|
||||
// Reset clock hand
|
||||
ClockPicker.prototype.resetClock = function(delay){
|
||||
var view = this.currentView,
|
||||
value = this[view],
|
||||
isHours = view === 'hours',
|
||||
unit = Math.PI / (isHours ? 6 : 30),
|
||||
radian = value * unit,
|
||||
radius = isHours && value > 0 && value < 13 ? innerRadius : outerRadius,
|
||||
x = Math.sin(radian) * radius,
|
||||
y = - Math.cos(radian) * radius,
|
||||
self = this;
|
||||
if (svgSupported && delay) {
|
||||
self.canvas.addClass('clockpicker-canvas-out');
|
||||
setTimeout(function(){
|
||||
self.canvas.removeClass('clockpicker-canvas-out');
|
||||
self.setHand(x, y);
|
||||
}, delay);
|
||||
} else {
|
||||
this.setHand(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
// Set clock hand to (x, y)
|
||||
ClockPicker.prototype.setHand = function(x, y, roundBy5, dragging){
|
||||
var radian = Math.atan2(x, - y),
|
||||
isHours = this.currentView === 'hours',
|
||||
unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
|
||||
z = Math.sqrt(x * x + y * y),
|
||||
options = this.options,
|
||||
inner = isHours && z < (outerRadius + innerRadius) / 2,
|
||||
radius = inner ? innerRadius : outerRadius,
|
||||
value;
|
||||
|
||||
if (options.twelvehour) {
|
||||
radius = outerRadius;
|
||||
}
|
||||
|
||||
// Radian should in range [0, 2PI]
|
||||
if (radian < 0) {
|
||||
radian = Math.PI * 2 + radian;
|
||||
}
|
||||
|
||||
// Get the round value
|
||||
value = Math.round(radian / unit);
|
||||
|
||||
// Get the round radian
|
||||
radian = value * unit;
|
||||
|
||||
// Correct the hours or minutes
|
||||
if (options.twelvehour) {
|
||||
if (isHours) {
|
||||
if (value === 0) {
|
||||
value = 12;
|
||||
}
|
||||
} else {
|
||||
if (roundBy5) {
|
||||
value *= 5;
|
||||
}
|
||||
if (value === 60) {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isHours) {
|
||||
if (value === 12) {
|
||||
value = 0;
|
||||
}
|
||||
value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
|
||||
} else {
|
||||
if (roundBy5) {
|
||||
value *= 5;
|
||||
}
|
||||
if (value === 60) {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Once hours or minutes changed, vibrate the device
|
||||
if (this[this.currentView] !== value) {
|
||||
if (vibrate && this.options.vibrate) {
|
||||
// Do not vibrate too frequently
|
||||
if (! this.vibrateTimer) {
|
||||
navigator[vibrate](10);
|
||||
this.vibrateTimer = setTimeout($.proxy(function(){
|
||||
this.vibrateTimer = null;
|
||||
}, this), 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this[this.currentView] = value;
|
||||
this[isHours ? 'spanHours' : 'spanMinutes'].html(leadingZero(value));
|
||||
|
||||
// If svg is not supported, just add an active class to the tick
|
||||
if (! svgSupported) {
|
||||
this[isHours ? 'hoursView' : 'minutesView'].find('.clockpicker-tick').each(function(){
|
||||
var tick = $(this);
|
||||
tick.toggleClass('active', value === + tick.html());
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Place clock hand at the top when dragging
|
||||
if (dragging || (! isHours && value % 5)) {
|
||||
this.g.insertBefore(this.hand, this.bearing);
|
||||
this.g.insertBefore(this.bg, this.fg);
|
||||
this.bg.setAttribute('class', 'clockpicker-canvas-bg clockpicker-canvas-bg-trans');
|
||||
} else {
|
||||
// Or place it at the bottom
|
||||
this.g.insertBefore(this.hand, this.bg);
|
||||
this.g.insertBefore(this.fg, this.bg);
|
||||
this.bg.setAttribute('class', 'clockpicker-canvas-bg');
|
||||
}
|
||||
|
||||
// Set clock hand and others' position
|
||||
var cx = Math.sin(radian) * radius,
|
||||
cy = - Math.cos(radian) * radius;
|
||||
this.hand.setAttribute('x2', cx);
|
||||
this.hand.setAttribute('y2', cy);
|
||||
this.bg.setAttribute('cx', cx);
|
||||
this.bg.setAttribute('cy', cy);
|
||||
this.fg.setAttribute('cx', cx);
|
||||
this.fg.setAttribute('cy', cy);
|
||||
};
|
||||
|
||||
// Hours and minutes are selected
|
||||
ClockPicker.prototype.done = function() {
|
||||
raiseCallback(this.options.beforeDone);
|
||||
this.hide();
|
||||
var last = this.input.prop('value'),
|
||||
value = leadingZero(this.hours) + ':' + leadingZero(this.minutes);
|
||||
if (this.options.twelvehour) {
|
||||
value = value + this.amOrPm;
|
||||
}
|
||||
|
||||
this.input.prop('value', value);
|
||||
if (value !== last) {
|
||||
this.input.triggerHandler('change');
|
||||
if (! this.isInput) {
|
||||
this.element.trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.autoclose) {
|
||||
this.input.trigger('blur');
|
||||
}
|
||||
|
||||
raiseCallback(this.options.afterDone);
|
||||
};
|
||||
|
||||
// Remove clockpicker from input
|
||||
ClockPicker.prototype.remove = function() {
|
||||
this.element.removeData('clockpicker');
|
||||
this.input.off('focus.clockpicker click.clockpicker');
|
||||
this.addon.off('click.clockpicker');
|
||||
if (this.isShown) {
|
||||
this.hide();
|
||||
}
|
||||
if (this.isAppended) {
|
||||
$win.off('resize.clockpicker' + this.id);
|
||||
this.popover.remove();
|
||||
}
|
||||
};
|
||||
|
||||
// Extends $.fn.clockpicker
|
||||
$.fn.clockpicker = function(option){
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
return this.each(function(){
|
||||
var $this = $(this),
|
||||
data = $this.data('clockpicker');
|
||||
if (! data) {
|
||||
var options = $.extend({}, ClockPicker.DEFAULTS, $this.data(), typeof option == 'object' && option);
|
||||
$this.data('clockpicker', new ClockPicker($this, options));
|
||||
} else {
|
||||
// Manual operatsions. show, hide, remove, e.g.
|
||||
if (typeof data[option] === 'function') {
|
||||
data[option].apply(data, args);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}());
|
||||
@@ -139,6 +139,8 @@ function reloadTodaySchedule(APIURL = "ScheduleBank/") {
|
||||
|
||||
dayViewMode = 'all'; // all, everyday, monday, tuesday, wednesday, thursday, friday, saturday, sunday
|
||||
scheduledate = null; // Litepicker instance for schedule date selection
|
||||
scheduletime = null; // time picker instance for schedule time selection
|
||||
$schedulemodal = null; // schedule modal jQuery object
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("schedulebank.js loaded successfully");
|
||||
@@ -153,6 +155,8 @@ $(document).ready(function () {
|
||||
$btnEdit.prop('disabled', true);
|
||||
$btnRemove.prop('disabled', true);
|
||||
let APIURL = "ScheduleBank/";
|
||||
|
||||
|
||||
|
||||
if (dtScheduleBank === null) {
|
||||
dtScheduleBank = new DataTable('#schedulebanktable', {
|
||||
@@ -239,15 +243,24 @@ $(document).ready(function () {
|
||||
})
|
||||
|
||||
|
||||
let $schedulemodal = $('#schedulemodal');
|
||||
$schedulemodal = $('#schedulemodal');
|
||||
// text input
|
||||
let $scheduleid = $schedulemodal.find('#scheduleid');
|
||||
// text input
|
||||
let $scheduledescription = $schedulemodal.find('#scheduledescription');
|
||||
|
||||
// number input 0-23
|
||||
let $schedulehour = $schedulemodal.find('#schedulehour');
|
||||
//let $schedulehour = $schedulemodal.find('#schedulehour');
|
||||
// number input 0-59
|
||||
let $scheduleminute = $schedulemodal.find('#scheduleminute');
|
||||
//let $scheduleminute = $schedulemodal.find('#scheduleminute');
|
||||
scheduletime = flatpickr("#scheduletime",{
|
||||
enableTime: true,
|
||||
noCalendar: true, // time only
|
||||
dateFormat: "H:i", // HH:mm format
|
||||
time_24hr: true, // firce 24-hour format
|
||||
minuteIncrement: 1,
|
||||
defaultDate: new Date()
|
||||
});
|
||||
// select2 for message
|
||||
let $schedulemessage = $schedulemodal.find('#schedulemessage');
|
||||
// number input 0-5
|
||||
@@ -264,6 +277,7 @@ $(document).ready(function () {
|
||||
let $weeklyselect = $schedulemodal.find('#weeklyselect');
|
||||
// radio button for specific date
|
||||
let $schedulespecialdate = $schedulemodal.find('#schedulespecialdate');
|
||||
|
||||
|
||||
scheduledate = new Litepicker({
|
||||
element: document.getElementById('scheduledate'),
|
||||
@@ -276,6 +290,8 @@ $(document).ready(function () {
|
||||
console.log("Selected special date: " + date.format('DD/MM/YYYY'));
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// date input
|
||||
//let $scheduledate = $schedulemodal.find('#scheduledate');
|
||||
// select2 for language
|
||||
@@ -294,8 +310,8 @@ $(document).ready(function () {
|
||||
function clearScheduleModal() {
|
||||
$scheduleid.prop('disabled', true).val('');
|
||||
$scheduledescription.val('');
|
||||
$schedulehour.val('0');
|
||||
$scheduleminute.val('0');
|
||||
//$schedulehour.val('0');
|
||||
//$scheduleminute.val('0');
|
||||
$schedulerepeat.val('1');
|
||||
$scheduleenable.prop('checked', true);
|
||||
$scheduleeveryday.prop('checked', false);
|
||||
@@ -403,9 +419,9 @@ $(document).ready(function () {
|
||||
const Language = $languageselect.val().join(';');
|
||||
const broadcastZones = $schedulezones.val().join(';');
|
||||
// Format time as HH:mm
|
||||
const hour = parseInt($schedulehour.val(), 10);
|
||||
const minute = parseInt($scheduleminute.val(), 10);
|
||||
const _Time = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
|
||||
//const hour = parseInt($schedulehour.val(), 10);
|
||||
//const minute = parseInt($scheduleminute.val(), 10);
|
||||
const _Time = $('#scheduletime').val();
|
||||
if (Description.length > 0) {
|
||||
if (_Day.length > 0) {
|
||||
if (Message.length > 0) {
|
||||
@@ -488,8 +504,9 @@ $(document).ready(function () {
|
||||
$scheduleid.val(sr.index);
|
||||
$scheduledescription.val(sr.Description);
|
||||
let [hour, minute] = sr.Time.split(':').map(num => parseInt(num, 10));
|
||||
$schedulehour.val(hour.toString());
|
||||
$scheduleminute.val(minute.toString());
|
||||
//$schedulehour.val(hour.toString());
|
||||
//$scheduleminute.val(minute.toString());
|
||||
scheduletime.setDate(sr.Time,true, "H:i");
|
||||
$schedulemessage.val(sr.Soundpath).trigger('change');
|
||||
$schedulerepeat.val(sr.Repeat);
|
||||
$scheduleenable.prop('checked', sr.Enable);
|
||||
@@ -562,9 +579,9 @@ $(document).ready(function () {
|
||||
const BroadcastZones = $schedulezones.val().join(';');
|
||||
const Language = $languageselect.val().join(';');
|
||||
// Format time as HH:mm
|
||||
const hour = parseInt($schedulehour.val(), 10);
|
||||
const minute = parseInt($scheduleminute.val(), 10);
|
||||
const Time = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
|
||||
// const hour = parseInt($schedulehour.val(), 10);
|
||||
//const minute = parseInt($scheduleminute.val(), 10);
|
||||
const Time = $('#scheduletime').val();
|
||||
if (Description && Description.length > 0) {
|
||||
if (Day && Day.length > 0) {
|
||||
if (Soundpath && Soundpath.length > 0) {
|
||||
|
||||
@@ -67,6 +67,275 @@ function load_default_voice(){
|
||||
});
|
||||
}
|
||||
|
||||
function ValidLatitude(lat){
|
||||
const num = parseFloat(lat);
|
||||
return !isNaN(num) && num >= -90 && num <= 90;
|
||||
}
|
||||
|
||||
function ValidLongitude(lon){
|
||||
const num = parseFloat(lon);
|
||||
return !isNaN(num) && num >= -180 && num <= 180;
|
||||
}
|
||||
|
||||
function ValidTimezone(tz){
|
||||
try{
|
||||
Intl.DateTimeFormat(undefined, { timeZone: tz });
|
||||
return true;
|
||||
} catch(e){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function ValidHHMM(time){
|
||||
const regex = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
||||
return regex.test(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a date string is valid in DD/MM/YYYY format
|
||||
* @param {string} dateStr date to check in DD/MM/YYYY format
|
||||
* @returns {boolean} true if valid date in DD/MM/YYYY format, false otherwise
|
||||
*/
|
||||
function ValidDateDDMMYYYY(dateStr){
|
||||
const regex = /^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[0-2])\/\d{4}$/;
|
||||
if (!regex.test(dateStr)) return false;
|
||||
const [day, month, year] = dateStr.split('/').map(Number);
|
||||
const date = new Date(year, month - 1, day);
|
||||
return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day;
|
||||
}
|
||||
|
||||
function ValidFilePath(path){
|
||||
if (typeof path !== 'string' || path.trim() === '') return false;
|
||||
// test if ends with .wav or .mp3
|
||||
const regex = /\.(wav|mp3)$/i;
|
||||
return regex.test(path);
|
||||
}
|
||||
|
||||
function IsEnabled(value){
|
||||
// accept "true", "false", true, false, 1, 0
|
||||
// detect if value is null or undefined
|
||||
if (value === null || value === undefined) return false;
|
||||
if (typeof value === 'boolean') return value;
|
||||
if (typeof value === 'number') return value === 1;
|
||||
if (typeof value === 'string') return value.toLowerCase() === "true" || value === "1";
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} AdzanSetting
|
||||
* @property {string} latitude
|
||||
* @property {string} longitude
|
||||
* @property {string} timezone
|
||||
* @property {string} fajar_sound
|
||||
* @property {string} dzuhur_sound
|
||||
* @property {string} ashar_sound
|
||||
* @property {string} maghrib_sound
|
||||
* @property {string} isya_sound
|
||||
* @property {string} fajar_time
|
||||
* @property {string} dzuhur_time
|
||||
* @property {string} ashar_time
|
||||
* @property {string} maghrib_time
|
||||
* @property {string} isya_time
|
||||
* @property {boolean} fajar_enable
|
||||
* @property {boolean} dzuhur_enable
|
||||
* @property {boolean} ashar_enable
|
||||
* @property {boolean} maghrib_enable
|
||||
* @property {boolean} isya_enable
|
||||
*/
|
||||
|
||||
function Get_AdzanSetting(){
|
||||
fetchAPI("Settings/AdzanSetting", "GET", {}, null,
|
||||
/**
|
||||
* returned AdzanSetting data
|
||||
* @param {AdzanSetting} okdata
|
||||
*/
|
||||
(okdata) => {
|
||||
// text input for latitude, longitude, timezone
|
||||
if (ValidLatitude(okdata.latitude)) {
|
||||
$('#adzanlatitude').val(okdata.latitude);
|
||||
} else {
|
||||
$('#adzanlatitude').val("N/A");
|
||||
}
|
||||
if (ValidLongitude(okdata.longitude)) {
|
||||
$('#adzanlongitude').val(okdata.longitude);
|
||||
} else {
|
||||
$('#adzanlongitude').val("N/A");
|
||||
}
|
||||
if (ValidTimezone(okdata.timezone)) {
|
||||
$('#adzantimezone').val(okdata.timezone);
|
||||
} else {
|
||||
$('#adzantimezone').val("N/A");
|
||||
}
|
||||
// text input for adzan sound file for each prayer time
|
||||
if (ValidFilePath(okdata.fajar_sound)) {
|
||||
$('#fajar .adzanfile').val(okdata.fajar_sound);
|
||||
} else {
|
||||
$('#fajar .adzanfile').val("N/A");
|
||||
}
|
||||
if (ValidFilePath(okdata.dzuhur_sound)) {
|
||||
$('#dzuhur .adzanfile').val(okdata.dzuhur_sound);
|
||||
} else {
|
||||
$('#dzuhur .adzanfile').val("N/A");
|
||||
}
|
||||
if (ValidFilePath(okdata.ashar_sound)) {
|
||||
$('#ashar .adzanfile').val(okdata.ashar_sound);
|
||||
} else {
|
||||
$('#ashar .adzanfile').val("N/A");
|
||||
}
|
||||
if (ValidFilePath(okdata.maghrib_sound)) {
|
||||
$('#maghrib .adzanfile').val(okdata.maghrib_sound);
|
||||
} else {
|
||||
$('#maghrib .adzanfile').val("N/A");
|
||||
}
|
||||
if (ValidFilePath(okdata.isya_sound)) {
|
||||
$('#isya .adzanfile').val(okdata.isya_sound);
|
||||
} else {
|
||||
$('#isya .adzanfile').val("N/A");
|
||||
}
|
||||
// checkbox adzanenable for each prayer time
|
||||
$('#fajar .adzanenable').prop('checked', IsEnabled(okdata.fajar_enable));
|
||||
$('#dzuhur .adzanenable').prop('checked', IsEnabled(okdata.dzuhur_enable));
|
||||
$('#ashar .adzanenable').prop('checked', IsEnabled(okdata.ashar_enable));
|
||||
$('#maghrib .adzanenable').prop('checked', IsEnabled(okdata.maghrib_enable));
|
||||
$('#isya .adzanenable').prop('checked', IsEnabled(okdata.isya_enable));
|
||||
|
||||
// adzantime for each prayer time
|
||||
// if valid HH:MM will set <input type="time"> value, else set to undefined
|
||||
if (ValidHHMM(okdata.fajar_time)) {
|
||||
$('#fajar .adzantime').val(okdata.fajar_time);
|
||||
} else {
|
||||
$('#fajar .adzantime').val("");
|
||||
}
|
||||
if (ValidHHMM(okdata.dzuhur_time)) {
|
||||
$('#dzuhur .adzantime').val(okdata.dzuhur_time);
|
||||
} else {
|
||||
$('#dzuhur .adzantime').val("");
|
||||
}
|
||||
if (ValidHHMM(okdata.ashar_time)) {
|
||||
$('#ashar .adzantime').val(okdata.ashar_time);
|
||||
} else {
|
||||
$('#ashar .adzantime').val("");
|
||||
}
|
||||
if (ValidHHMM(okdata.maghrib_time)) {
|
||||
$('#maghrib .adzantime').val(okdata.maghrib_time);
|
||||
} else {
|
||||
$('#maghrib .adzantime').val("");
|
||||
}
|
||||
if (ValidHHMM(okdata.isya_time)) {
|
||||
$('#isya .adzantime').val(okdata.isya_time);
|
||||
} else {
|
||||
$('#isya .adzantime').val("");
|
||||
}
|
||||
|
||||
}, (errdata) => {
|
||||
alert("Error getting Adzan settings : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
function Set_AdzanSetting(){
|
||||
let latitude = $('#adzanlatitude').val();
|
||||
if (!ValidLatitude(latitude)){
|
||||
alert("Please enter a valid latitude between -90 and 90.");
|
||||
return;
|
||||
}
|
||||
let longitude = $('#adzanlongitude').val();
|
||||
if (!ValidLongitude(longitude)){
|
||||
alert("Please enter a valid longitude between -180 and 180.");
|
||||
return;
|
||||
}
|
||||
let timezone = $('#adzantimezone').val();
|
||||
if (!ValidTimezone(timezone)){
|
||||
alert("Please enter a valid timezone.");
|
||||
return;
|
||||
}
|
||||
let fajar_sound = $('#fajar .adzanfile').val();
|
||||
if (!ValidFilePath(fajar_sound)){
|
||||
alert("Please enter a valid file path for Fajar adzan sound (must end with .wav or .mp3).");
|
||||
return;
|
||||
}
|
||||
let dzuhur_sound = $('#dzuhur .adzanfile').val();
|
||||
if (!ValidFilePath(dzuhur_sound)){
|
||||
alert("Please enter a valid file path for Dzuhur adzan sound (must end with .wav or .mp3).");
|
||||
return;
|
||||
}
|
||||
let ashar_sound = $('#ashar .adzanfile').val();
|
||||
if (!ValidFilePath(ashar_sound)){
|
||||
alert("Please enter a valid file path for Ashar adzan sound (must end with .wav or .mp3).");
|
||||
return;
|
||||
}
|
||||
let maghrib_sound = $('#maghrib .adzanfile').val();
|
||||
if (!ValidFilePath(maghrib_sound)){
|
||||
alert("Please enter a valid file path for Maghrib adzan sound (must end with .wav or .mp3).");
|
||||
return;
|
||||
}
|
||||
let isya_sound = $('#isya .adzanfile').val();
|
||||
if (!ValidFilePath(isya_sound)){
|
||||
alert("Please enter a valid file path for Isya adzan sound (must end with .wav or .mp3).");
|
||||
return;
|
||||
}
|
||||
let fajar_enable = $('#fajar .adzanenable').prop('checked');
|
||||
let dzuhur_enable = $('#dzuhur .adzanenable').prop('checked');
|
||||
let ashar_enable = $('#ashar .adzanenable').prop('checked');
|
||||
let maghrib_enable = $('#maghrib .adzanenable').prop('checked');
|
||||
let isya_enable = $('#isya .adzanenable').prop('checked');
|
||||
let fajar_time = $('#fajar .adzantime').val();
|
||||
if (!ValidHHMM(fajar_time)){
|
||||
alert("Please enter a valid time for Fajar adzan (HH:MM).");
|
||||
return;
|
||||
}
|
||||
let dzuhur_time = $('#dzuhur .adzantime').val();
|
||||
if (!ValidHHMM(dzuhur_time)){
|
||||
alert("Please enter a valid time for Dzuhur adzan (HH:MM).");
|
||||
return;
|
||||
}
|
||||
let ashar_time = $('#ashar .adzantime').val();
|
||||
if (!ValidHHMM(ashar_time)){
|
||||
alert("Please enter a valid time for Ashar adzan (HH:MM).");
|
||||
return;
|
||||
}
|
||||
let maghrib_time = $('#maghrib .adzantime').val();
|
||||
if (!ValidHHMM(maghrib_time)){
|
||||
alert("Please enter a valid time for Maghrib adzan (HH:MM).");
|
||||
return;
|
||||
}
|
||||
let isya_time = $('#isya .adzantime').val();
|
||||
if (!ValidHHMM(isya_time)){
|
||||
alert("Please enter a valid time for Isya adzan (HH:MM).");
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {AdzanSetting}
|
||||
*/
|
||||
let data = {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
timezone: timezone,
|
||||
fajar_sound: fajar_sound,
|
||||
dzuhur_sound: dzuhur_sound,
|
||||
ashar_sound: ashar_sound,
|
||||
maghrib_sound: maghrib_sound,
|
||||
isya_sound: isya_sound,
|
||||
fajar_enable: fajar_enable,
|
||||
dzuhur_enable: dzuhur_enable,
|
||||
ashar_enable: ashar_enable,
|
||||
maghrib_enable: maghrib_enable,
|
||||
isya_enable: isya_enable,
|
||||
fajar_time: fajar_time,
|
||||
dzuhur_time: dzuhur_time,
|
||||
ashar_time: ashar_time,
|
||||
maghrib_time: maghrib_time,
|
||||
isya_time: isya_time
|
||||
};
|
||||
fetchAPI("Settings/AdzanSetting", "POST", {}, data, (okdata) => {
|
||||
alert("Adzan settings updated successfully.");
|
||||
}, (errdata) => {
|
||||
alert("Error updating Adzan settings : " + errdata.message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Get_WebAccessSetting(){
|
||||
fetchAPI("Settings/WebAccess", "GET", {}, null, (okdata) => {
|
||||
let adminpass = okdata.adminpass || "password";
|
||||
@@ -109,8 +378,10 @@ $(document).ready(function () {
|
||||
load_default_voice();
|
||||
Get_OldResultDays();
|
||||
Get_WebAccessSetting();
|
||||
Get_AdzanSetting();
|
||||
load_messagebank(() => load_remark_selection());
|
||||
$("#fiscodesave").off('click').on('click', function () {
|
||||
|
||||
$('#fiscodesave').click(function () {
|
||||
Set_OldResultDays();
|
||||
let gop = $("#input_GOP").val();
|
||||
let gbd = $("#input_GBD").val();
|
||||
@@ -133,10 +404,13 @@ $(document).ready(function () {
|
||||
} else {
|
||||
alert("Please select all FIS codes (GOP, GBD, GFC, FLD) and Default Voice before saving.");
|
||||
}
|
||||
|
||||
});
|
||||
$("#webaccesssave").off('click').on('click', function () {
|
||||
Set_WebAccessSetting();
|
||||
$('#webaccesssave').click(function () {
|
||||
Set_WebAccessSetting();
|
||||
});
|
||||
|
||||
$('#adzansave').click(function () {
|
||||
Set_AdzanSetting();
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -292,7 +291,6 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/soundchannel.js"></script>
|
||||
<script src="assets/js/broadcastzones.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -156,7 +155,6 @@
|
||||
<div><audio class="invisible" id="audioplayer" controls=""></audio></div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/filemanagement.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/all.min.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/flatpickr.min.css">
|
||||
<link rel="stylesheet" href="assets/css/litepicker.css">
|
||||
<link rel="stylesheet" href="assets/css/select2.min.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
@@ -40,26 +42,26 @@
|
||||
<div>
|
||||
<hr class="mt-0">
|
||||
<ul class="nav nav-pills flex-column mb-auto">
|
||||
<li class="nav-item"><a class="nav-link active link-light text-menu" id="homelink" href="#" aria-current="page"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-house-door me-2" style="font-size: 20px;">
|
||||
<li class="nav-item"><a class="nav-link active link-light text-menu" id="homelink" href="#" aria-current="page"><svg class="bi bi-house-door me-2" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" style="font-size: 20px;">
|
||||
<path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4.5a.5.5 0 0 0 .5-.5v-4h2v4a.5.5 0 0 0 .5.5H14a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293zM2.5 14V7.707l5.5-5.5 5.5 5.5V14H10v-4a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v4z"></path>
|
||||
</svg> Overview</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="soundbanklink" href="#"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-soundwave me-2 icon-menu pad-icon-menu" style="font-size: 20px;">
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="soundbanklink" href="#"><svg class="bi bi-soundwave me-2 icon-menu pad-icon-menu" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" style="font-size: 20px;">
|
||||
<path fill-rule="evenodd" d="M8.5 2a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-1 0v-11a.5.5 0 0 1 .5-.5m-2 2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m4 0a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m-6 1.5A.5.5 0 0 1 5 6v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m8 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m-10 1A.5.5 0 0 1 3 7v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5m12 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5"></path>
|
||||
</svg> Sound Bank</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="messagebanklink" href="#"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="me-2 icon-menu" style="font-size: 20px;">
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="messagebanklink" href="#"><svg class="me-2 icon-menu" xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="font-size: 20px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"></path>
|
||||
</svg> Message Bank</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="languagelink" href="#"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="me-2 icon-menu" style="font-size: 20px;">
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="languagelink" href="#"><svg class="me-2 icon-menu" xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="font-size: 20px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path>
|
||||
</svg> Language Link</a></li>
|
||||
<li class="nav-item .icon-menu"><a class="nav-link link-body-emphasis text-menu" id="timerlink" href="#"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="me-2 icon-menu pad-icon-menu" style="font-size: 20px;">
|
||||
<li class="nav-item .icon-menu"><a class="nav-link link-body-emphasis text-menu" id="timerlink" href="#"><svg class="me-2 icon-menu pad-icon-menu" xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="font-size: 20px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"></path>
|
||||
<path d="M12.5 7H11v6l5.25 3.15.75-1.23-4.5-2.67z"></path>
|
||||
</svg> Timer</a></li>
|
||||
<li class="nav-item .icon-menu"><a class="nav-link link-body-emphasis text-menu" id="broadcastzonelink" href="#"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="me-2 icon-menu" style="font-size: 20px;">
|
||||
<li class="nav-item .icon-menu"><a class="nav-link link-body-emphasis text-menu" id="broadcastzonelink" href="#"><svg class="me-2 icon-menu" xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="font-size: 20px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M18.2 1H9.8C8.81 1 8 1.81 8 2.8v14.4c0 .99.81 1.79 1.8 1.79l8.4.01c.99 0 1.8-.81 1.8-1.8V2.8c0-.99-.81-1.8-1.8-1.8zM14 3c1.1 0 2 .89 2 2s-.9 2-2 2-2-.89-2-2 .9-2 2-2zm0 13.5c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"></path>
|
||||
<circle cx="14" cy="12.5" r="2.5"></circle>
|
||||
@@ -71,11 +73,11 @@
|
||||
<path d="M19,7H9C7.9,7,7,7.9,7,9v10c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V9C21,7.9,20.1,7,19,7z M19,9v2H9V9H19z M13,15v-2h2v2H13z M15,17v2h-2v-2H15z M11,15H9v-2h2V15z M17,13h2v2h-2V13z M9,17h2v2H9V17z M17,19v-2h2v2H17z M6,17H5c-1.1,0-2-0.9-2-2V5 c0-1.1,0.9-2,2-2h10c1.1,0,2,0.9,2,2v1h-2V5H5v10h1V17z"></path>
|
||||
</g>
|
||||
</svg> Log</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="usermanagement" href="#"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="me-2 icon-menu pad-icon-menu" style="font-size: 20px;">
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="usermanagement" href="#"><svg class="me-2 icon-menu pad-icon-menu" xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="font-size: 20px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"></path>
|
||||
</svg> User Management</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="filemanagement" href="#"><svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="me-2 icon-menu pad-icon-menu" style="font-size: 20px;">
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="filemanagement" href="#"><svg class="me-2 icon-menu pad-icon-menu" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="font-size: 20px;">
|
||||
<g>
|
||||
<rect fill="none" height="24" width="24"></rect>
|
||||
</g>
|
||||
@@ -83,13 +85,13 @@
|
||||
<path d="M14,2H6C4.9,2,4.01,2.9,4.01,4L4,20c0,1.1,0.89,2,1.99,2H18c1.1,0,2-0.9,2-2V8L14,2z M16,13h-3v3.75 c0,1.24-1.01,2.25-2.25,2.25S8.5,17.99,8.5,16.75c0-1.24,1.01-2.25,2.25-2.25c0.46,0,0.89,0.14,1.25,0.38V11h4V13z M13,9V3.5 L18.5,9H13z"></path>
|
||||
</g>
|
||||
</svg> File Management</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="settinglink" href="#"><svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="me-2 icon-menu pad-icon-menu" style="font-size: 20px;">
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="settinglink" href="#"><svg class="me-2 icon-menu pad-icon-menu" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="font-size: 20px;">
|
||||
<g>
|
||||
<path d="M0,0h24v24H0V0z" fill="none"></path>
|
||||
<path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"></path>
|
||||
</g>
|
||||
</svg> Setting</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="logoutlink" href="#"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="me-2 icon-menu" style="font-size: 20px;">
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="logoutlink" href="#"><svg class="me-2 icon-menu" xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="font-size: 20px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"></path>
|
||||
</svg> Logout</a></li>
|
||||
@@ -160,9 +162,11 @@
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/jquery-3.7.1.min.js"></script>
|
||||
<script src="assets/js/select2.min.js"></script>
|
||||
<script src="assets/js/litepicker.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/all.min.js"></script>
|
||||
<script src="assets/js/script.js"></script>
|
||||
<script src="assets/js/flatpickr.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -16,6 +16,8 @@
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/all.min.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/flatpickr.min.css">
|
||||
<link rel="stylesheet" href="assets/css/litepicker.css">
|
||||
<link rel="stylesheet" href="assets/css/select2.min.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
@@ -40,7 +42,7 @@
|
||||
<div>
|
||||
<hr class="mt-0">
|
||||
<ul class="nav nav-pills flex-column mb-auto">
|
||||
<li class="nav-item"><a class="nav-link active link-light text-menu" id="homelink" href="#" aria-current="page"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-house-door me-2" style="font-size: 20px;">
|
||||
<li class="nav-item"><a class="nav-link active link-light text-menu" id="homelink" href="#" aria-current="page"><svg class="bi bi-house-door me-2" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" style="font-size: 20px;">
|
||||
<path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4.5a.5.5 0 0 0 .5-.5v-4h2v4a.5.5 0 0 0 .5.5H14a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293zM2.5 14V7.707l5.5-5.5 5.5 5.5V14H10v-4a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v4z"></path>
|
||||
</svg> Overview</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="loglink" href="#"><svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="icon-menu pad-icon-menu" style="font-size: 20px;">
|
||||
@@ -49,7 +51,7 @@
|
||||
<path d="M19,7H9C7.9,7,7,7.9,7,9v10c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V9C21,7.9,20.1,7,19,7z M19,9v2H9V9H19z M13,15v-2h2v2H13z M15,17v2h-2v-2H15z M11,15H9v-2h2V15z M17,13h2v2h-2V13z M9,17h2v2H9V17z M17,19v-2h2v2H17z M6,17H5c-1.1,0-2-0.9-2-2V5 c0-1.1,0.9-2,2-2h10c1.1,0,2,0.9,2,2v1h-2V5H5v10h1V17z"></path>
|
||||
</g>
|
||||
</svg> Log</a></li>
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="logoutlink" href="#"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" class="me-2 icon-menu" style="font-size: 20px;">
|
||||
<li class="nav-item"><a class="nav-link link-body-emphasis text-menu" id="logoutlink" href="#"><svg class="me-2 icon-menu" xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="font-size: 20px;">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"></path>
|
||||
</svg> Logout</a></li>
|
||||
@@ -120,9 +122,11 @@
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/jquery-3.7.1.min.js"></script>
|
||||
<script src="assets/js/select2.min.js"></script>
|
||||
<script src="assets/js/litepicker.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/all.min.js"></script>
|
||||
<script src="assets/js/script.js"></script>
|
||||
<script src="assets/js/flatpickr.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -14,7 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -99,7 +98,6 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/languagelink.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/litepicker.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -71,8 +69,6 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/litepicker.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/log.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -27,7 +26,7 @@
|
||||
<div class="col-md-6 col-xl-4">
|
||||
<div class="card mb-5 card-login">
|
||||
<div class="card-body d-flex flex-column align-items-center">
|
||||
<div class="bs-icon-xl bs-icon-circle bs-icon-primary my-4 bs-icon bg-icon-login"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-person bg-icon-login">
|
||||
<div class="bs-icon-xl bs-icon-circle bs-icon-primary my-4 bs-icon bg-icon-login"><svg class="bi bi-person bg-icon-login" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664z"></path>
|
||||
</svg></div>
|
||||
<h2 class="mb-3 h-login">Login</h2>
|
||||
@@ -46,8 +45,6 @@
|
||||
</section>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/jquery-3.7.1.min.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/login.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -114,16 +113,13 @@
|
||||
<div class="row">
|
||||
<div class="col bg-light"><select class="w-100 h-100 overflow-scroll" id="messageavailablevariables" size="10"></select></div>
|
||||
<div class="col-3 col-sm-3 col-md-3 col-lg-2 col-xl-2">
|
||||
<div class="row pad-row-btn"><button class="btn btn-round-basic color-remove" data-bs-toggle="tooltip" data-bss-tooltip="" id="btnclearlist" type="button" title="Clear List"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
|
||||
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
||||
<div class="row pad-row-btn"><button class="btn btn-round-basic color-remove" data-bs-toggle="tooltip" data-bss-tooltip="" id="btnclearlist" type="button" title="Clear List"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c-9.4 9.4-9.4 24.6 0 33.9l47 47-47 47c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l47-47 47 47c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-47-47 47-47c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-47 47-47-47c-9.4-9.4-24.6-9.4-33.9 0z"></path>
|
||||
</svg></button></div>
|
||||
<div class="row pad-row-btn"><button class="btn btn-round-basic color-edit" data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="right" id="btnremovefromlist" type="button" title="Remove"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
|
||||
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
||||
<div class="row pad-row-btn"><button class="btn btn-round-basic color-edit" data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="right" id="btnremovefromlist" type="button" title="Remove"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path d="M48 256a208 208 0 1 1 416 0A208 208 0 1 1 48 256zm464 0A256 256 0 1 0 0 256a256 256 0 1 0 512 0zM217.4 376.9c4.2 4.5 10.1 7.1 16.3 7.1c12.3 0 22.3-10 22.3-22.3V304h96c17.7 0 32-14.3 32-32V240c0-17.7-14.3-32-32-32H256V150.3c0-12.3-10-22.3-22.3-22.3c-6.2 0-12.1 2.6-16.3 7.1L117.5 242.2c-3.5 3.8-5.5 8.7-5.5 13.8s2 10.1 5.5 13.8l99.9 107.1z"></path>
|
||||
</svg></button></div>
|
||||
<div class="row pad-row-btn"><button class="btn btn-round-basic color-import" data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="right" id="btnaddtolist" type="button" title="Add"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;">
|
||||
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
||||
<div class="row pad-row-btn"><button class="btn btn-round-basic color-import" data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="right" id="btnaddtolist" type="button" title="Add"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" style="font-size: 32;"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path d="M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM294.6 135.1c-4.2-4.5-10.1-7.1-16.3-7.1C266 128 256 138 256 150.3V208H160c-17.7 0-32 14.3-32 32v32c0 17.7 14.3 32 32 32h96v57.7c0 12.3 10 22.3 22.3 22.3c6.2 0 12.1-2.6 16.3-7.1l99.9-107.1c3.5-3.8 5.5-8.7 5.5-13.8s-2-10.1-5.5-13.8L294.6 135.1z"></path>
|
||||
</svg></button></div>
|
||||
</div>
|
||||
@@ -136,7 +132,6 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/messagebank.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/fonts/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -4684,7 +4683,6 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/overview.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -123,9 +122,76 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Adzan Setting</h4>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="container">
|
||||
<div class="row py-1">
|
||||
<div class="col-auto"><label class="col-form-label">Latitude</label></div>
|
||||
<div class="col-2"><input class="w-100 h-100" type="text" id="adzanlatitude"></div>
|
||||
<div class="col-auto"><label class="col-form-label">Longitude</label></div>
|
||||
<div class="col-2"><input class="w-100 h-100" type="text" id="adzanlongitude"></div>
|
||||
<div class="col-auto"><label class="col-form-label">TimeZone</label></div>
|
||||
<div class="col"><input class="w-100 h-100" type="text" id="adzantimezone"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p class="py-1" id="adzantoday">Today's Adzan</p>
|
||||
</div>
|
||||
<div class="row py-1" id="fajar">
|
||||
<div class="col-2"><label class="col-form-label w-100 h-100 align-content-center">Fajar</label></div>
|
||||
<div class="col-2"><input class="w-100 h-100 align-content-center adzantime" id="fajar_time" type="time"></div>
|
||||
<div class="col d-flex"><input class="flex-grow-1 me-1 adzanfile" type="text"><button class="btn btn-primary col-auto adzanbrowse" type="button">Browse</button></div>
|
||||
<div class="col-2">
|
||||
<div class="form-check w-100 h-100 align-content-center"><input class="form-check-input adzanenable" type="checkbox" id="enable_fajr"><label class="form-check-label" for="enable_fajr">Enable</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row py-1" id="dzuhur">
|
||||
<div class="col-2"><label class="col-form-label w-100 h-100 align-content-center">Dzuhur</label></div>
|
||||
<div class="col-2"><input class="w-100 h-100 align-content-center adzantime" id="fajar_time-4" type="time"></div>
|
||||
<div class="col d-flex"><input class="flex-grow-1 me-1 adzanfile" type="text"><button class="btn btn-primary col-auto adzanbrowse" type="button">Browse</button></div>
|
||||
<div class="col-2">
|
||||
<div class="form-check w-100 h-100 align-content-center"><input class="form-check-input adzanenable" type="checkbox" id="enable_fajr-4"><label class="form-check-label" for="enable_fajr-4">Enable</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row py-1" id="ashar">
|
||||
<div class="col-2"><label class="col-form-label w-100 h-100 align-content-center">Ashar</label></div>
|
||||
<div class="col-2"><input class="w-100 h-100 align-content-center adzantime" id="fajar_time-3" type="time"></div>
|
||||
<div class="col d-flex"><input class="flex-grow-1 me-1 adzanfile" type="text"><button class="btn btn-primary col-auto adzanbrowse" type="button">Browse</button></div>
|
||||
<div class="col-2">
|
||||
<div class="form-check w-100 h-100 align-content-center"><input class="form-check-input adzanenable" type="checkbox" id="enable_fajr-3"><label class="form-check-label" for="enable_fajr-3">Enable</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row py-1" id="magrib">
|
||||
<div class="col-2"><label class="col-form-label w-100 h-100 align-content-center">Magrib</label></div>
|
||||
<div class="col-2"><input class="w-100 h-100 align-content-center adzantime" id="fajar_time-2" type="time"></div>
|
||||
<div class="col d-flex"><input class="flex-grow-1 me-1 adzanfile" type="text"><button class="btn btn-primary col-auto adzanbrowse" type="button">Browse</button></div>
|
||||
<div class="col-2">
|
||||
<div class="form-check w-100 h-100 align-content-center"><input class="form-check-input adzanenable" type="checkbox" id="enable_fajr-2"><label class="form-check-label" for="enable_fajr-2">Enable</label></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row py-1" id="isya">
|
||||
<div class="col-2"><label class="col-form-label w-100 h-100 align-content-center">Isya</label></div>
|
||||
<div class="col-2"><input class="w-100 h-100 align-content-center adzantime" id="fajar_time-1" type="time"></div>
|
||||
<div class="col d-flex"><input class="flex-grow-1 me-1 adzanfile" type="text"><button class="btn btn-primary col-auto adzanbrowse" type="button">Browse</button></div>
|
||||
<div class="col-2">
|
||||
<div class="form-check w-100 h-100 align-content-center"><input class="form-check-input adzanenable" type="checkbox" id="enable_fajr-1"><label class="form-check-label" for="enable_fajr-1">Enable</label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4 col-sm-3 col-md-2 col-lg-2 col-xl-2"><button class="btn w-100 h-100 pad-button btn-round-basic color-add" id="adzansave" type="button">Save</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/setting.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -120,7 +119,6 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/soundbank.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -91,7 +90,6 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -14,8 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/litepicker.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -80,13 +78,8 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row w-100 h-100">
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"><input type="number" id="schedulehour" class="input-add form-control class100" value="0" min="0" max="23" step="1"></div>
|
||||
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2">
|
||||
<p class="pad-time">(H)</p>
|
||||
</div>
|
||||
<div class="col-4 col-sm-4 col-md-4 col-lg-4 col-xl-4"><input class="w-100 input-add form-control" type="number" id="scheduleminute" value="0" min="0" max="59" step="1"></div>
|
||||
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2">
|
||||
<p class="pad-time">(M)</p>
|
||||
<div class="row w-100 pad-day">
|
||||
<div class="col w-100"><input type="text" id="scheduletime" placeholder="HH:mm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -213,8 +206,6 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/litepicker.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/schedulebank.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -124,7 +123,6 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/tts.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<link rel="stylesheet" href="assets/css/Font%20Awesome%206%20Pro.css">
|
||||
<link rel="stylesheet" href="assets/css/FontAwesome.css">
|
||||
<link rel="stylesheet" href="assets/css/bss-overrides.css">
|
||||
<link rel="stylesheet" href="assets/css/datatables.css">
|
||||
<link rel="stylesheet" href="assets/css/Login-Form-Basic-icons.css">
|
||||
<link rel="stylesheet" href="assets/css/styles.css">
|
||||
</head>
|
||||
@@ -238,7 +237,6 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/datatables.js"></script>
|
||||
<script src="assets/js/usermanagement.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
17
src/Main.kt
17
src/Main.kt
@@ -16,6 +16,7 @@ import content.Language
|
||||
import content.VoiceType
|
||||
import database.data.Log
|
||||
import database.MariaDB
|
||||
import database.table.Table_Adzan
|
||||
import database.table.Table_BroadcastZones
|
||||
import database.table.Table_Logs
|
||||
import database.table.Table_Messagebank
|
||||
@@ -33,6 +34,7 @@ import web.WebApp
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.TimeZone
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.io.path.exists
|
||||
@@ -43,7 +45,7 @@ lateinit var audioPlayer: AudioPlayer
|
||||
val StreamerOutputs: MutableMap<String, BarixConnection> = HashMap()
|
||||
lateinit var udpreceiver: UDPReceiver
|
||||
lateinit var tcpreceiver: TCPReceiver
|
||||
const val version = "0.0.29 (09/02/2026)"
|
||||
const val version = "0.0.30 (12/02/2026)"
|
||||
// AAS 64 channels
|
||||
const val max_channel = 64
|
||||
|
||||
@@ -55,7 +57,7 @@ lateinit var soundchannelDB: Table_SoundChannel
|
||||
lateinit var messageDB: Table_Messagebank
|
||||
lateinit var logDB: Table_Logs
|
||||
|
||||
|
||||
lateinit var adzanTable : Table_Adzan
|
||||
val contentCache = ContentCache()
|
||||
/**
|
||||
* Create necessary folders if not exist
|
||||
@@ -176,6 +178,11 @@ fun main(args: Array<String>) {
|
||||
|
||||
db = MariaDB()
|
||||
|
||||
adzanTable = Table_Adzan(latitude = config.Get(configKeys.LATITUDE.key).toDouble(),
|
||||
longitude = config.Get(configKeys.LONGITUDE.key).toDouble(),
|
||||
timezone = TimeZone.getTimeZone(config.Get(configKeys.TIMEZONE.key))
|
||||
)
|
||||
|
||||
|
||||
val subcode01 = MainExtension01()
|
||||
|
||||
@@ -305,8 +312,12 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
|
||||
logDB.Add("AAS"," Application started")
|
||||
adzanTable = Table_Adzan(latitude = config.Get(configKeys.LATITUDE.key).toDouble(),
|
||||
longitude = config.Get(configKeys.LONGITUDE.key).toDouble(),
|
||||
timezone = TimeZone.getTimeZone(config.Get(configKeys.TIMEZONE.key))
|
||||
)
|
||||
|
||||
logDB.Add("AAS"," Application started")
|
||||
|
||||
// shutdown hook
|
||||
Runtime.getRuntime().addShutdownHook(Thread ({
|
||||
|
||||
@@ -39,8 +39,8 @@ class TCP_Barix_Command_Server {
|
||||
socketMap[key] = socket
|
||||
Logger.info { "Start communicating with Streamer Output with IP : $key" }
|
||||
try{
|
||||
|
||||
val din = DataInputStream(socket.getInputStream())
|
||||
|
||||
var VuZeroCounter = 0L
|
||||
while (isActive) {
|
||||
|
||||
@@ -50,6 +50,7 @@ class TCP_Barix_Command_Server {
|
||||
Logger.info { "Connection closed by Streamer Output with IP $key" }
|
||||
break
|
||||
}
|
||||
|
||||
if (readbytes == 0) continue
|
||||
var stringlength = 0
|
||||
try{
|
||||
|
||||
@@ -24,11 +24,13 @@ import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.function.BiConsumer
|
||||
import java.util.function.Consumer
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.name
|
||||
import kotlin.streams.asSequence
|
||||
|
||||
|
||||
@Suppress("unused")
|
||||
@@ -352,6 +354,7 @@ class Somecodes {
|
||||
// and find them recursively
|
||||
if (Files.exists(p) && Files.isDirectory(p)){
|
||||
Files.walk(p)
|
||||
.asSequence()
|
||||
// cari file regular saja
|
||||
.filter { Files.isRegularFile(it)}
|
||||
// size lebih dari 1KB
|
||||
@@ -707,6 +710,8 @@ class Somecodes {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Find a schedule day by its name.
|
||||
* @param value The name of the schedule day to find.
|
||||
@@ -752,6 +757,56 @@ class Somecodes {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a valid latitude.
|
||||
* @param value The string to check.
|
||||
* @return True if the string is a valid latitude, false otherwise.
|
||||
*/
|
||||
fun ValidLatitude(value: String) : Boolean {
|
||||
return try {
|
||||
val lat = value.toDouble()
|
||||
lat in -90.0..90.0
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun ValidLatitude(value: Double) : Boolean {
|
||||
return value in -90.0..90.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a valid longitude.
|
||||
* @param value The string to check.
|
||||
* @return True if the string is a valid longitude, false otherwise.
|
||||
*/
|
||||
fun ValidLongitude(value: String) : Boolean {
|
||||
return try {
|
||||
val lon = value.toDouble()
|
||||
lon in -180.0..180.0
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun ValidLongitude(value: Double) : Boolean {
|
||||
return value in -180.0..180.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a valid time zone.
|
||||
* @param value The string to check.
|
||||
* @return True if the string is a valid time zone, false otherwise.
|
||||
*/
|
||||
fun ValidTimeZone(value: String) : Boolean {
|
||||
return try {
|
||||
ZoneId.of(value)
|
||||
true
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a WAV file name with the current date and time.
|
||||
* The file name format is: [prefix]_ddMMyyyy_HHmmss_[postfix].wav
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package codes
|
||||
|
||||
import content.VoiceType
|
||||
import database.table.AdzanSetting
|
||||
import org.tinylog.Logger
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
@@ -44,6 +44,44 @@ class configFile {
|
||||
}
|
||||
}
|
||||
|
||||
class ChangedSetting(
|
||||
val key: String,
|
||||
val oldValue: String,
|
||||
val newValue: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Compare current Adzan settings with the provided AdzanSetting object.
|
||||
* @param adz The AdzanSetting object to compare with.
|
||||
* @return true if some settings are different, false if all are the same.
|
||||
*/
|
||||
fun CompareWithAdzanSetting(adz : AdzanSetting) : List<ChangedSetting>{
|
||||
val changes = mutableListOf<ChangedSetting>()
|
||||
configKeys.entries.forEach { ck ->
|
||||
val currentValue = Get(ck.key)
|
||||
val newValue = when (ck) {
|
||||
configKeys.ADZAN_FAJR_ENABLED -> adz.fajar_enable.toString()
|
||||
configKeys.ADZAN_DHUHR_ENABLED -> adz.dzuhur_enable.toString()
|
||||
configKeys.ADZAN_ASR_ENABLED -> adz.ashar_enable.toString()
|
||||
configKeys.ADZAN_MAGHRIB_ENABLED -> adz.maghrib_enable.toString()
|
||||
configKeys.ADZAN_ISHA_ENABLED -> adz.isya_enable.toString()
|
||||
configKeys.ADZAN_FAJR_SOUND -> adz.fajar_sound
|
||||
configKeys.ADZAN_DHUHR_SOUND -> adz.dzuhur_sound
|
||||
configKeys.ADZAN_ASR_SOUND -> adz.ashar_sound
|
||||
configKeys.ADZAN_MAGHRIB_SOUND -> adz.maghrib_sound
|
||||
configKeys.ADZAN_ISHA_SOUND -> adz.isya_sound
|
||||
configKeys.LATITUDE -> adz.latitude.toString()
|
||||
configKeys.LONGITUDE -> adz.longitude.toString()
|
||||
configKeys.TIMEZONE -> adz.timezone
|
||||
else -> return@forEach
|
||||
}
|
||||
if (currentValue != newValue){
|
||||
changes.add(ChangedSetting(key=ck.key, oldValue = currentValue, newValue = newValue))
|
||||
}
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
private fun HaveAllKeys() : Boolean{
|
||||
return configKeys.entries.all { config.containsKey(it.key) }
|
||||
|
||||
@@ -52,23 +90,9 @@ class configFile {
|
||||
private fun CreateDefaultConfig(){
|
||||
config.clear()
|
||||
// create default config file
|
||||
config[configKeys.DATABASE_HOST.key] = "localhost"
|
||||
config[configKeys.DATABASE_PORT.key] = "3306"
|
||||
config[configKeys.DATABASE_USER.key] = "admin"
|
||||
config[configKeys.DATABASE_PASSWORD.key] = "admin"
|
||||
config[configKeys.DATABASE_NAME.key] = "aas"
|
||||
config[configKeys.SOUNDBANK_DIRECTORY.key] = Paths.get(Somecodes.current_directory, "soundbank").toString()
|
||||
config[configKeys.REMARK_GOP.key] = ""
|
||||
config[configKeys.REMARK_GBD.key] = ""
|
||||
config[configKeys.REMARK_GFC.key] = ""
|
||||
config[configKeys.REMARK_FLD.key] = ""
|
||||
config[configKeys.WEBAPP_ADMIN_USERNAME.key] = "admin"
|
||||
config[configKeys.WEBAPP_ADMIN_PASSWORD.key] = "password"
|
||||
config[configKeys.WEBAPP_VIEWER_USERNAME.key] = "viewer"
|
||||
config[configKeys.WEBAPP_VIEWER_PASSWORD.key] = "password"
|
||||
config[configKeys.WEBAPP_PORT.key] = "3030"
|
||||
config[configKeys.DEFAULT_VOICE_TYPE.key] = VoiceType.VOICE_1.name
|
||||
config[configKeys.AUTO_DELETE_RESULT_DAYS.key] = "7"
|
||||
configKeys.entries.forEach { ck ->
|
||||
config[ck.key] = ck.defaultValue
|
||||
}
|
||||
Save()
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,42 @@
|
||||
package codes
|
||||
|
||||
enum class configKeys(val key: String) {
|
||||
DATABASE_HOST("database.host"),
|
||||
DATABASE_PORT("database.port"),
|
||||
DATABASE_USER("database.user"),
|
||||
DATABASE_PASSWORD("database.password"),
|
||||
DATABASE_NAME("database.name"),
|
||||
SOUNDBANK_DIRECTORY("soundbank.directory"),
|
||||
REMARK_GOP("remark.GOP"),
|
||||
REMARK_GBD("remark.GBD"),
|
||||
REMARK_GFC("remark.GFC"),
|
||||
REMARK_FLD("remark.FLD"),
|
||||
DEFAULT_VOICE_TYPE("default.voice.type"),
|
||||
WEBAPP_ADMIN_USERNAME("webapp.admin.username"),
|
||||
WEBAPP_ADMIN_PASSWORD("webapp.admin.password"),
|
||||
WEBAPP_VIEWER_USERNAME("webapp.viewer.username"),
|
||||
WEBAPP_VIEWER_PASSWORD("webapp.viewer.password"),
|
||||
WEBAPP_PORT("webapp.port"),
|
||||
AUTO_DELETE_RESULT_DAYS("auto.delete.result.days")
|
||||
import content.VoiceType
|
||||
import kotlin.io.path.Path
|
||||
|
||||
enum class configKeys(val key: String, val defaultValue: String) {
|
||||
DATABASE_HOST("database.host", "localhost"),
|
||||
DATABASE_PORT("database.port", "3306"),
|
||||
DATABASE_USER("database.user", "admin"),
|
||||
DATABASE_PASSWORD("database.password", "admin"),
|
||||
DATABASE_NAME("database.name", "aas"),
|
||||
SOUNDBANK_DIRECTORY("soundbank.directory", Path(Somecodes.current_directory, "soundbank").toString()),
|
||||
REMARK_GOP("remark.GOP", "GOP"),
|
||||
REMARK_GBD("remark.GBD", "GBD"),
|
||||
REMARK_GFC("remark.GFC", "GFC"),
|
||||
REMARK_FLD("remark.FLD", "FLD"),
|
||||
DEFAULT_VOICE_TYPE("default.voice.type", VoiceType.VOICE_1.name),
|
||||
WEBAPP_ADMIN_USERNAME("webapp.admin.username", "admin"),
|
||||
WEBAPP_ADMIN_PASSWORD("webapp.admin.password", "password"),
|
||||
WEBAPP_VIEWER_USERNAME("webapp.viewer.username", "viewer"),
|
||||
WEBAPP_VIEWER_PASSWORD("webapp.viewer.password", "password"),
|
||||
WEBAPP_PORT("webapp.port", "3030"),
|
||||
AUTO_DELETE_RESULT_DAYS("auto.delete.result.days", "7"),
|
||||
LATITUDE("latitude", "-6.1751"),
|
||||
LONGITUDE("longitude", "106.8272"),
|
||||
TIMEZONE("timezone", "Asia/Jakarta"),
|
||||
ADZAN_FAJR_ENABLED("adzan.fajr.enabled", "false"),
|
||||
ADZAN_DHUHR_ENABLED("adzan.dhuhr.enabled", "false"),
|
||||
ADZAN_ASR_ENABLED("adzan.asr.enabled", "false"),
|
||||
ADZAN_MAGHRIB_ENABLED("adzan.maghrib.enabled", "false"),
|
||||
ADZAN_ISHA_ENABLED("adzan.isha.enabled", "false"),
|
||||
ADZAN_FAJR_SOUND("adzan.fajr.sound", "adzan_fajr.mp3"),
|
||||
ADZAN_DHUHR_SOUND("adzan.dhuhr.sound", "adzan_dhuhr.mp3"),
|
||||
ADZAN_ASR_SOUND("adzan.asr.sound", "adzan_asr.mp3"),
|
||||
ADZAN_MAGHRIB_SOUND("adzan.maghrib.sound", "adzan_maghrib.mp3"),
|
||||
ADZAN_ISHA_SOUND("adzan.isha.sound", "adzan_isha.mp3"),
|
||||
ADZAN_FAJR_MINUTE_OFFSET("adzan.fajr.minute.offset", "0"),
|
||||
ADZAN_DHUHR_MINUTE_OFFSET("adzan.dhuhr.minute.offset", "0"),
|
||||
ADZAN_ASR_MINUTE_OFFSET("adzan.asr.minute.offset", "0"),
|
||||
ADZAN_MAGHRIB_MINUTE_OFFSET("adzan.maghrib.minute.offset", "0"),
|
||||
ADZAN_ISHA_MINUTE_OFFSET("adzan.isha.minute.offset", "0")
|
||||
}
|
||||
12
src/database/table/AdzanPrayerTime.kt
Normal file
12
src/database/table/AdzanPrayerTime.kt
Normal file
@@ -0,0 +1,12 @@
|
||||
package database.table
|
||||
|
||||
/**
|
||||
* Data class representing Adzan prayer times for a specific date.
|
||||
* @param date The date for the prayer times in format DD/MM/YYYY.
|
||||
* @param fajr The Fajr prayer time in format HH:MM.
|
||||
* @param dhuhr The Dhuhr prayer time in format HH:MM.
|
||||
* @param asr The Asr prayer time in format HH:MM.
|
||||
* @param maghrib The Maghrib prayer time in format HH:MM.
|
||||
* @param isha The Isha prayer time in format HH:MM.
|
||||
*/
|
||||
data class AdzanPrayerTime(val date: String, val fajr: String, val dhuhr: String, val asr: String, val maghrib: String, val isha: String)
|
||||
69
src/database/table/AdzanSetting.kt
Normal file
69
src/database/table/AdzanSetting.kt
Normal file
@@ -0,0 +1,69 @@
|
||||
package database.table
|
||||
|
||||
import codes.Somecodes.Companion.ValidLatitude
|
||||
import codes.Somecodes.Companion.ValidLongitude
|
||||
import codes.Somecodes.Companion.ValidScheduleTime
|
||||
import codes.Somecodes.Companion.ValidTimeZone
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
|
||||
data class AdzanSetting(
|
||||
val latitude: Double,
|
||||
val longitude: Double,
|
||||
val timezone: String,
|
||||
val fajar_sound: String,
|
||||
val fajar_enable : Boolean,
|
||||
val fajar_time : String,
|
||||
val dzuhur_sound: String,
|
||||
val dzuhur_enable : Boolean,
|
||||
val dzuhur_time : String,
|
||||
val ashar_sound: String,
|
||||
val ashar_enable : Boolean,
|
||||
val ashar_time : String,
|
||||
val maghrib_sound: String,
|
||||
val maghrib_enable : Boolean,
|
||||
val maghrib_time : String,
|
||||
val isya_sound: String,
|
||||
val isya_enable : Boolean,
|
||||
val isya_time : String
|
||||
) {
|
||||
companion object{
|
||||
/**
|
||||
* Create AdzanSetting from JsonNode
|
||||
* @param json JsonNode object
|
||||
* @return AdzanSetting object
|
||||
* @throws Exception if any required field is missing
|
||||
*/
|
||||
fun FromJsonNode(json: JsonNode): AdzanSetting{
|
||||
val xx = AdzanSetting(
|
||||
latitude = json.get("latitude")?.asDouble() ?: throw Exception("latitude is missing"),
|
||||
longitude = json.get("longitude")?.asDouble() ?: throw Exception("longitude is missing"),
|
||||
timezone = json.get("timezone")?.asText() ?: throw Exception("timezone is missing"),
|
||||
fajar_sound = json.get("fajar_sound")?.asText("") ?: throw Exception("fajar_sound is missing"),
|
||||
fajar_enable = json.get("fajar_enable")?.asBoolean(false) ?: throw Exception("fajar_enable is missing"),
|
||||
fajar_time = json.get("fajar_time")?.asText() ?: throw Exception("fajar_time is missing"),
|
||||
dzuhur_sound = json.get("dzuhur_sound")?.asText() ?: throw Exception("dzuhur_sound is missing"),
|
||||
dzuhur_enable = json.get("dzuhur_enable")?.asBoolean() ?: throw Exception("dzuhur_enable is missing"),
|
||||
dzuhur_time = json.get("dzuhur_time")?.asText() ?: throw Exception("dzuhur_time is missing"),
|
||||
ashar_sound = json.get("ashar_sound")?.asText() ?: throw Exception("ashar_sound is missing"),ashar_enable = json.get("ashar_enable").asBoolean(false),
|
||||
ashar_time = json.get("ashar_time")?.asText() ?: throw Exception("ashar_time is missing"),
|
||||
maghrib_sound = json.get("maghrib_sound")?.asText() ?: throw Exception("maghrib_sound is missing"),
|
||||
maghrib_enable = json.get("maghrib_enable")?.asBoolean() ?: throw Exception("maghrib_enable is missing"),
|
||||
maghrib_time = json.get("maghrib_time")?.asText() ?: throw Exception("maghrib_time is missing"),
|
||||
isya_sound = json.get("isya_sound")?.asText() ?: throw Exception("isya_sound is missing"),
|
||||
isya_enable = json.get("isya_enable")?.asBoolean() ?: throw Exception("isya_enable is missing"),
|
||||
isya_time = json.get("isya_time")?.asText() ?: throw Exception("isya_time is missing")
|
||||
)
|
||||
if (!ValidLatitude(xx.latitude)) throw Exception("Invalid latitude value")
|
||||
if (!ValidLongitude(xx.longitude)) throw Exception("Invalid longitude value")
|
||||
if (!ValidTimeZone(xx.timezone)) throw Exception("Invalid timezone value")
|
||||
if (!ValidScheduleTime(xx.fajar_time)) throw Exception("Invalid fajar_time value")
|
||||
if (!ValidScheduleTime(xx.dzuhur_time)) throw Exception("Invalid dzuhur_time value")
|
||||
if (!ValidScheduleTime(xx.ashar_time)) throw Exception("Invalid ashar_time value")
|
||||
if (!ValidScheduleTime(xx.maghrib_time)) throw Exception("Invalid maghrib_time value")
|
||||
if (!ValidScheduleTime(xx.isya_time)) throw Exception("Invalid isya_time value")
|
||||
return xx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
src/database/table/AdzanTimeZone.kt
Normal file
15
src/database/table/AdzanTimeZone.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package database.table
|
||||
|
||||
enum class AdzanTimeZone {
|
||||
WIB,
|
||||
WITA,
|
||||
WIT;
|
||||
|
||||
fun toTimeZoneString(): String {
|
||||
return when (this) {
|
||||
WIB -> "Asia/Jakarta"
|
||||
WITA -> "Asia/Makassar"
|
||||
WIT -> "Asia/Jayapura"
|
||||
}
|
||||
}
|
||||
}
|
||||
155
src/database/table/Table_Adzan.kt
Normal file
155
src/database/table/Table_Adzan.kt
Normal file
@@ -0,0 +1,155 @@
|
||||
package database.table
|
||||
|
||||
import com.batoulapps.adhan.CalculationMethod
|
||||
import com.batoulapps.adhan.CalculationParameters
|
||||
import com.batoulapps.adhan.Coordinates
|
||||
import com.batoulapps.adhan.Madhab
|
||||
import com.batoulapps.adhan.PrayerTimes
|
||||
import com.batoulapps.adhan.data.DateComponents
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.TimeZone
|
||||
|
||||
/**
|
||||
* Class for calculating prayer times (Adzan) based on latitude, longitude, and time zone.
|
||||
* @param latitude The latitude of the location. Default is Monas, Jakarta (latitude: -6.1751).
|
||||
* @param longitude The longitude of the location. Default is Monas, Jakarta (longitude: 106.8272).
|
||||
* @param timezone The time zone for formatting prayer times. Default is "Asia/Jakarta".
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class Table_Adzan(val latitude: Double = -6.1751, val longitude: Double = 106.8272, val timezone : TimeZone = TimeZone.getTimeZone("Asia/Jakarta")) {
|
||||
|
||||
var coordinate: Coordinates = Coordinates(latitude, longitude)
|
||||
val params : CalculationParameters = CalculationMethod.OTHER.parameters
|
||||
val timeformatter = SimpleDateFormat("HH:mm")
|
||||
val dateformatter = SimpleDateFormat("dd/MM/yyyy")
|
||||
|
||||
var fajar_enable = false
|
||||
var dzuhur_enable = false
|
||||
var ashar_enable = false
|
||||
var maghrib_enable = false
|
||||
var isya_enable = false
|
||||
|
||||
var fajar_sound = ""
|
||||
var dzuhur_sound = ""
|
||||
var ashar_sound = ""
|
||||
var maghrib_sound = ""
|
||||
var isya_sound = ""
|
||||
|
||||
init{
|
||||
// sumber chatgpt Kemenag
|
||||
params.fajrAngle = 20.0
|
||||
params.ishaAngle = 18.0
|
||||
params.madhab = Madhab.SHAFI
|
||||
timeformatter.timeZone = timezone
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the time zone used for formatting prayer times.
|
||||
* @param timezone The new AdzanTimeZone.
|
||||
*/
|
||||
fun ChangeTimezone(timezone: AdzanTimeZone) {
|
||||
ChangeTimeZone(timezone.toTimeZoneString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the time zone used for formatting prayer times.
|
||||
* @param timezoneString The new time zone string (e.g., "Asia/Jakarta").
|
||||
*/
|
||||
fun ChangeTimeZone(timezoneString: String) {
|
||||
timeformatter.timeZone = TimeZone.getTimeZone(timezoneString)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set prayer time adjustments in minutes.
|
||||
* @param fajrMinute Adjustment for Fajr prayer time in minutes.
|
||||
* @param dhuhrMinute Adjustment for Dhuhr prayer time in minutes.
|
||||
* @param asrMinute Adjustment for Asr prayer time in minutes.
|
||||
* @param maghribMinute Adjustment for Maghrib prayer time in minutes.
|
||||
* @param ishaMinute Adjustment for Isha prayer time in minutes.
|
||||
*/
|
||||
fun SetPrayerAdjustment(fajrMinute: Int = 0, dhuhrMinute: Int = 0, asrMinute: Int = 0,maghribMinute: Int = 0, ishaMinute: Int = 0) {
|
||||
params.adjustments.fajr = fajrMinute
|
||||
params.adjustments.dhuhr = dhuhrMinute
|
||||
params.adjustments.asr = asrMinute
|
||||
params.adjustments.maghrib = maghribMinute
|
||||
params.adjustments.isha = ishaMinute
|
||||
}
|
||||
/**
|
||||
* Change the coordinates used for Adzan calculations.
|
||||
* @param lat The new latitude.
|
||||
* @param long The new longitude.
|
||||
*/
|
||||
fun ChangeCoordinate(lat: Double, long: Double) {
|
||||
coordinate = Coordinates(lat, long)
|
||||
}
|
||||
|
||||
fun ChangeLatitude(lat: Double) {
|
||||
coordinate = Coordinates(lat, coordinate.longitude)
|
||||
}
|
||||
|
||||
fun ChangeLongitude(long: Double) {
|
||||
coordinate = Coordinates(coordinate.latitude, long)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prayer times for a specific date string in the format "dd/MM/yyyy".
|
||||
* @param date_string The date string for which to get prayer times.
|
||||
* @return An AdzanPrayerTime object containing the prayer times, or null if the date string is invalid.
|
||||
*/
|
||||
fun GetPrayerTimes(date_string: String) : AdzanPrayerTime?{
|
||||
try{
|
||||
val date = dateformatter.parse(date_string)
|
||||
return GetPrayerTimes(date)
|
||||
} catch (e: Exception){
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prayer times for a specific date.
|
||||
* @param date The date for which to get prayer times.
|
||||
* @return An AdzanPrayerTime object containing the prayer times.
|
||||
*/
|
||||
fun GetPrayerTimes(date: Date) : AdzanPrayerTime{
|
||||
val prayer = PrayerTimes(coordinate, DateComponents.from(date), params)
|
||||
return AdzanPrayerTime(
|
||||
dateformatter.format(date),
|
||||
timeformatter.format(prayer.fajr),
|
||||
timeformatter.format(prayer.dhuhr),
|
||||
timeformatter.format(prayer.asr),
|
||||
timeformatter.format(prayer.maghrib),
|
||||
timeformatter.format(prayer.isha)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prayer times for the current date.
|
||||
* @return An AdzanPrayerTime object containing the prayer times for today.
|
||||
*/
|
||||
fun GetTodayPrayerTimes() : AdzanPrayerTime{
|
||||
return GetPrayerTimes(Date())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prayer times for all days in a specific month and year.
|
||||
* @param month The month (1-12) for which to get prayer times.
|
||||
* @param year The year for which to get prayer times.
|
||||
* @return A list of AdzanPrayerTime objects for each day in the specified month and year.
|
||||
*/
|
||||
fun GetMonthlyPrayerTimes(month: Int, year: Int) : List<AdzanPrayerTime>{
|
||||
val prayerTimesList = mutableListOf<AdzanPrayerTime>()
|
||||
val calendar = java.util.Calendar.getInstance()
|
||||
calendar.set(year, month - 1, 1) // Month is 0-based in Calendar
|
||||
val daysInMonth = calendar.getActualMaximum(java.util.Calendar.DAY_OF_MONTH)
|
||||
|
||||
for (day in 1..daysInMonth) {
|
||||
calendar.set(year, month - 1, day)
|
||||
val date = calendar.time
|
||||
val prayerTimes = GetPrayerTimes(date)
|
||||
prayerTimesList.add(prayerTimes)
|
||||
}
|
||||
|
||||
return prayerTimesList
|
||||
}
|
||||
}
|
||||
@@ -47,26 +47,27 @@ class Table_LogSemiAuto(connection: Connection) : dbFunctions<LogSemiauto>("logs
|
||||
|
||||
|
||||
override fun Get(cbOK: Consumer<Unit>?, cbFail: Consumer<String>?) {
|
||||
List.clear()
|
||||
try {
|
||||
val statement = connection.createStatement()
|
||||
val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
|
||||
while (resultSet?.next() == true) {
|
||||
val log = LogSemiauto(
|
||||
resultSet.getLong("index").toULong(),
|
||||
resultSet.getString("date"),
|
||||
resultSet.getString("time"),
|
||||
resultSet.getString("source"),
|
||||
resultSet.getString("description")
|
||||
)
|
||||
List.add(log)
|
||||
}
|
||||
cbOK?.accept(Unit)
|
||||
} catch (e: Exception) {
|
||||
cbFail?.accept("Error fetching ${super.dbName}: ${e.message}")
|
||||
Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any)
|
||||
|
||||
}
|
||||
throw Exception("Getting all LogSemiauto entries is not supported")
|
||||
// List.clear()
|
||||
// try {
|
||||
// val statement = connection.createStatement()
|
||||
// val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
|
||||
// while (resultSet?.next() == true) {
|
||||
// val log = LogSemiauto(
|
||||
// resultSet.getLong("index").toULong(),
|
||||
// resultSet.getString("date"),
|
||||
// resultSet.getString("time"),
|
||||
// resultSet.getString("source"),
|
||||
// resultSet.getString("description")
|
||||
// )
|
||||
// List.add(log)
|
||||
// }
|
||||
// cbOK?.accept(Unit)
|
||||
// } catch (e: Exception) {
|
||||
// cbFail?.accept("Error fetching ${super.dbName}: ${e.message}")
|
||||
// Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any)
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
fun Add(source: String, description: String){
|
||||
@@ -182,31 +183,32 @@ class Table_LogSemiAuto(connection: Connection) : dbFunctions<LogSemiauto>("logs
|
||||
}
|
||||
|
||||
override fun Resort(): Boolean {
|
||||
try{
|
||||
val statement = connection.createStatement()
|
||||
val tempdb_name = "temp_${super.dbName}"
|
||||
// use a temporary table to reorder the index
|
||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||
statement?.executeUpdate(
|
||||
"INSERT INTO $tempdb_name (date, time, source, description) " +
|
||||
"SELECT date, time, source, description FROM ${super.dbName} " +
|
||||
"ORDER BY date , time , source "
|
||||
)
|
||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||
statement?.executeUpdate(
|
||||
"INSERT INTO ${super.dbName} (date, time, source, description) " +
|
||||
"SELECT date, time, source, description FROM $tempdb_name"
|
||||
)
|
||||
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||
Logger.info("${super.dbName} table resorted by date, time, source" as Any)
|
||||
// reload the local list
|
||||
Get()
|
||||
return true
|
||||
} catch (e : Exception){
|
||||
Logger.error { "Error resorting logsemiauto table: ${e.message}" }
|
||||
return false
|
||||
}
|
||||
throw Exception("Resorting LogSemiauto table is not supported")
|
||||
// try{
|
||||
// val statement = connection.createStatement()
|
||||
// val tempdb_name = "temp_${super.dbName}"
|
||||
// // use a temporary table to reorder the index
|
||||
// statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||
// statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||
// statement?.executeUpdate(
|
||||
// "INSERT INTO $tempdb_name (date, time, source, description) " +
|
||||
// "SELECT date, time, source, description FROM ${super.dbName} " +
|
||||
// "ORDER BY date , time , source "
|
||||
// )
|
||||
// statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||
// statement?.executeUpdate(
|
||||
// "INSERT INTO ${super.dbName} (date, time, source, description) " +
|
||||
// "SELECT date, time, source, description FROM $tempdb_name"
|
||||
// )
|
||||
// statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||
// Logger.info("${super.dbName} table resorted by date, time, source" as Any)
|
||||
// // reload the local list
|
||||
// Get()
|
||||
// return true
|
||||
// } catch (e : Exception){
|
||||
// Logger.error { "Error resorting logsemiauto table: ${e.message}" }
|
||||
// return false
|
||||
// }
|
||||
}
|
||||
|
||||
override fun Import_XLSX(workbook: XSSFWorkbook): Boolean {
|
||||
|
||||
@@ -2,9 +2,6 @@ package database.table
|
||||
|
||||
import database.data.Log
|
||||
import database.dbFunctions
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook
|
||||
import org.tinylog.Logger
|
||||
import java.sql.Connection
|
||||
@@ -116,28 +113,29 @@ class Table_Logs(connection: Connection) : dbFunctions<Log>("logs", connection,l
|
||||
* @param cbFail Optional callback invoked upon failure with an error message.
|
||||
*/
|
||||
override fun Get(cbOK: Consumer<Unit>?, cbFail: Consumer<String>?) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
List.clear()
|
||||
try {
|
||||
val statement = connection.createStatement()
|
||||
val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
|
||||
while (resultSet?.next() == true) {
|
||||
val log = Log(
|
||||
resultSet.getLong("index").toULong(),
|
||||
resultSet.getString("datenya"),
|
||||
resultSet.getString("timenya"),
|
||||
resultSet.getString("machine"),
|
||||
resultSet.getString("description")
|
||||
)
|
||||
List.add(log)
|
||||
}
|
||||
cbOK?.accept(Unit)
|
||||
} catch (e: Exception) {
|
||||
cbFail?.accept("Error fetching ${super.dbName}: ${e.message}")
|
||||
Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any)
|
||||
|
||||
}
|
||||
}
|
||||
throw Exception("Get all logs is not supported for Logs")
|
||||
// CoroutineScope(Dispatchers.IO).launch {
|
||||
// List.clear()
|
||||
// try {
|
||||
// val statement = connection.createStatement()
|
||||
// val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}")
|
||||
// while (resultSet?.next() == true) {
|
||||
// val log = Log(
|
||||
// resultSet.getLong("index").toULong(),
|
||||
// resultSet.getString("datenya"),
|
||||
// resultSet.getString("timenya"),
|
||||
// resultSet.getString("machine"),
|
||||
// resultSet.getString("description")
|
||||
// )
|
||||
// List.add(log)
|
||||
// }
|
||||
// cbOK?.accept(Unit)
|
||||
// } catch (e: Exception) {
|
||||
// cbFail?.accept("Error fetching ${super.dbName}: ${e.message}")
|
||||
// Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any)
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -196,31 +194,32 @@ class Table_Logs(connection: Connection) : dbFunctions<Log>("logs", connection,l
|
||||
}
|
||||
|
||||
override fun Resort(): Boolean {
|
||||
try {
|
||||
val statement = connection.createStatement()
|
||||
val tempdb_name = "temp_${super.dbName}"
|
||||
// use a temporary table to reorder the index
|
||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||
statement?.executeUpdate(
|
||||
"INSERT INTO $tempdb_name (datenya, timenya, machine, description) " +
|
||||
"SELECT datenya, timenya, machine, description FROM ${super.dbName} " +
|
||||
"ORDER BY datenya , timenya , machine "
|
||||
)
|
||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||
statement?.executeUpdate(
|
||||
"INSERT INTO ${super.dbName} (datenya, timenya, machine, description) " +
|
||||
"SELECT datenya, timenya, machine, description FROM $tempdb_name"
|
||||
)
|
||||
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||
Logger.info("${super.dbName} table resorted by datenya, timenya, machine" as Any)
|
||||
// reload the local list
|
||||
Get()
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
Logger.error("Error resorting ${super.dbName} table by datenya, timenya, machine: ${e.message}" as Any)
|
||||
}
|
||||
return false
|
||||
throw Exception("Resorting Logs is not supported")
|
||||
// try {
|
||||
// val statement = connection.createStatement()
|
||||
// val tempdb_name = "temp_${super.dbName}"
|
||||
// // use a temporary table to reorder the index
|
||||
// statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||
// statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||
// statement?.executeUpdate(
|
||||
// "INSERT INTO $tempdb_name (datenya, timenya, machine, description) " +
|
||||
// "SELECT datenya, timenya, machine, description FROM ${super.dbName} " +
|
||||
// "ORDER BY datenya , timenya , machine "
|
||||
// )
|
||||
// statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||
// statement?.executeUpdate(
|
||||
// "INSERT INTO ${super.dbName} (datenya, timenya, machine, description) " +
|
||||
// "SELECT datenya, timenya, machine, description FROM $tempdb_name"
|
||||
// )
|
||||
// statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||
// Logger.info("${super.dbName} table resorted by datenya, timenya, machine" as Any)
|
||||
// // reload the local list
|
||||
// Get()
|
||||
// return true
|
||||
// } catch (e: Exception) {
|
||||
// Logger.error("Error resorting ${super.dbName} table by datenya, timenya, machine: ${e.message}" as Any)
|
||||
// }
|
||||
// return false
|
||||
}
|
||||
|
||||
override fun Import_XLSX(workbook: XSSFWorkbook): Boolean {
|
||||
|
||||
@@ -32,12 +32,14 @@ class Table_Schedule(connection: Connection) : dbFunctions<ScheduleBank>("schedu
|
||||
|
||||
fun Find_Enabled_Schedules() : List<ScheduleBank>{
|
||||
return List
|
||||
.asSequence()
|
||||
.filter{it.Enable} // yang enabled saja
|
||||
.filter{ValidScheduleTime(it.Time)} // yang timenya dalam format HH:MM
|
||||
.filter{ValidLanguage(it.Language)} // yang bahasanya valid
|
||||
.filter{broadcastDB.AllBroadcastZonesValid(it.BroadcastZones, false)} // yang broadcastzonesnya valid
|
||||
// Soundpath ini coding typo, aslinya Messagebank description
|
||||
.filter{messageDB.Messagebank_Exists(it.Soundpath, it.Language)}
|
||||
.toList()
|
||||
}
|
||||
val eligibleSchedule = Find_Enabled_Schedules()
|
||||
|
||||
@@ -57,7 +59,6 @@ class Table_Schedule(connection: Connection) : dbFunctions<ScheduleBank>("schedu
|
||||
// masukin ke todaySchedule yang sudah di sort by Time
|
||||
todaySchedule.addAll(tempMap.values.sortedBy { it.Time })
|
||||
|
||||
println("Todays schedule : $todaySchedule")
|
||||
}
|
||||
|
||||
override fun Create() {
|
||||
|
||||
37
src/ourAirport/AirportData.kt
Normal file
37
src/ourAirport/AirportData.kt
Normal file
@@ -0,0 +1,37 @@
|
||||
package ourAirport
|
||||
|
||||
import codes.Somecodes
|
||||
|
||||
data class AirportData(val description: String, val latitude: Double, val longitude: Double, val country: String, val IATA: String, val ICAO: String)
|
||||
{
|
||||
companion object{
|
||||
/**
|
||||
* create AirportData from CSV line
|
||||
* CSV format : id,ident,type,name,latitude_deg,longitude_deg,elevation_ft,continent,country_name,iso_country,region_name,iso_region,local_region,municipality,scheduled_service,gps_code,icao_code,iata_code,local_code,home_link,wikipedia_link,keywords,score,last_updated
|
||||
* @param line CSV line
|
||||
* @return AirportData or null if failed
|
||||
*/
|
||||
fun fromString(line: String) : AirportData? {
|
||||
if (Somecodes.Companion.ValidString(line)){
|
||||
try{
|
||||
val values = line.split(",")
|
||||
// description on index 3
|
||||
val description = values[3].trim()
|
||||
// latitude on index 4
|
||||
val latitude = values[4].toDoubleOrNull() ?: return null
|
||||
// longitude on index 5
|
||||
val longitude = values[5].toDoubleOrNull() ?: return null
|
||||
// country on index 8
|
||||
val country = values[8].trim()
|
||||
// ICAO on index 16
|
||||
val ICAO = values[16].trim()
|
||||
// IATA on index 17
|
||||
val IATA = values[17].trim()
|
||||
return AirportData(description, latitude, longitude, country, IATA, ICAO)
|
||||
} catch (_ : Exception){
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/ourAirport/OurAirport.kt
Normal file
47
src/ourAirport/OurAirport.kt
Normal file
@@ -0,0 +1,47 @@
|
||||
package ourAirport
|
||||
|
||||
import codes.Somecodes
|
||||
import org.tinylog.Logger
|
||||
import java.nio.file.Files
|
||||
import kotlin.io.path.Path
|
||||
|
||||
/**
|
||||
* this class read CSV from world-airports.csv included in resources folder
|
||||
* and filter only the airports in the InterestedCountries list
|
||||
* @param InterestedCountries vararg list of country names to filter airports. Default is "Indonesia"
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class OurAirport(vararg InterestedCountries: String = arrayOf("Indonesia")) {
|
||||
val List: MutableList<AirportData> = mutableListOf()
|
||||
init{
|
||||
// extract world-airports.csv from resources to current folder
|
||||
try{
|
||||
val current = Path(Somecodes.current_directory)
|
||||
if (!Files.exists(current.resolve("world-airports.csv"))){
|
||||
val inputStream = this::class.java.getResourceAsStream("/world-airports.csv")
|
||||
if (inputStream != null) {
|
||||
Files.copy(inputStream, current)
|
||||
Logger.info { "Extracted world-airports.csv to ${Somecodes.current_directory}" }
|
||||
} else throw Exception("Resource world-airports.csv not found")
|
||||
}
|
||||
val lines = Files.readAllLines(current.resolve("world-airports.csv"))
|
||||
for (line in lines.drop(1)) { // skip header
|
||||
AirportData.fromString(line)?.let { ad ->
|
||||
if (InterestedCountries.contains(ad.country)) {
|
||||
List.add(ad)
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch(ex: Exception){
|
||||
Logger.error { "Failed to copy world-airports.csv: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
fun GetFromIATA(iata: String): AirportData? {
|
||||
return List.find { it.IATA.equals(iata, ignoreCase = true) }
|
||||
}
|
||||
|
||||
fun GetFromICAO(icao: String): AirportData? {
|
||||
return List.find { it.ICAO.equals(icao, ignoreCase = true) }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package web
|
||||
|
||||
import StreamerOutputs
|
||||
import adzanTable
|
||||
import barix.BarixConnection
|
||||
import broadcastDB
|
||||
import codes.Somecodes
|
||||
@@ -46,6 +47,7 @@ import codes.configKeys
|
||||
import config
|
||||
import database.data.LogSemiauto
|
||||
import database.data.QueueTable
|
||||
import database.table.AdzanSetting
|
||||
import google.GoogleTTS
|
||||
import google.autoadd
|
||||
import google.fileoperation
|
||||
@@ -58,6 +60,7 @@ import version
|
||||
import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.file.Path
|
||||
import java.util.TimeZone
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
@@ -2467,6 +2470,76 @@ class WebApp(val listenPort: Int, var userlist: List<Pair<String, String>>, val
|
||||
}
|
||||
}
|
||||
path("Settings") {
|
||||
path("AdzanSetting"){
|
||||
get{
|
||||
val todayadzan = adzanTable.GetTodayPrayerTimes()
|
||||
val value = AdzanSetting(
|
||||
latitude = _config.Get(configKeys.LATITUDE.key).toDoubleOrNull() ?: 0.0,
|
||||
longitude = _config.Get(configKeys.LONGITUDE.key).toDoubleOrNull() ?: 0.0,
|
||||
timezone = _config.Get(configKeys.TIMEZONE.key),
|
||||
fajar_sound = _config.Get(configKeys.ADZAN_FAJR_SOUND.key),
|
||||
fajar_enable = _config.Get(configKeys.ADZAN_FAJR_ENABLED.key).toBoolean(),
|
||||
fajar_time = todayadzan.fajr,
|
||||
dzuhur_sound = _config.Get(configKeys.ADZAN_DHUHR_SOUND.key),
|
||||
dzuhur_enable = _config.Get(configKeys.ADZAN_DHUHR_ENABLED.key).toBoolean(),
|
||||
dzuhur_time = todayadzan.dhuhr,
|
||||
ashar_sound = _config.Get(configKeys.ADZAN_ASR_SOUND.key),
|
||||
ashar_enable = _config.Get(configKeys.ADZAN_FAJR_ENABLED.key).toBoolean(),
|
||||
ashar_time = todayadzan.asr,
|
||||
maghrib_sound = _config.Get(configKeys.ADZAN_MAGHRIB_SOUND.key),
|
||||
maghrib_enable = _config.Get(configKeys.ADZAN_MAGHRIB_ENABLED.key).toBoolean(),
|
||||
maghrib_time = todayadzan.maghrib,
|
||||
isya_sound = _config.Get(configKeys.ADZAN_ISHA_SOUND.key),
|
||||
isya_enable = _config.Get(configKeys.ADZAN_ISHA_ENABLED.key).toBoolean(),
|
||||
isya_time = todayadzan.isha
|
||||
)
|
||||
it.json(value)
|
||||
}
|
||||
post{
|
||||
val json: JsonNode = objectmapper.readTree(it.body())
|
||||
try{
|
||||
val newsetting = AdzanSetting.FromJsonNode(json)
|
||||
_config.CompareWithAdzanSetting(newsetting).let { changes ->
|
||||
if (changes.isNotEmpty()){
|
||||
// something changes
|
||||
changes.forEach { change ->
|
||||
_config.Set(change.key, change.newValue)
|
||||
Logger.info{"AdzanSetting change: ${change.key} from ${change.oldValue} to ${change.newValue}"}
|
||||
when(change.key){
|
||||
configKeys.LATITUDE.key -> adzanTable.ChangeLatitude(change.newValue.toDouble())
|
||||
configKeys.LONGITUDE.key -> adzanTable.ChangeLongitude(change.newValue.toDouble())
|
||||
configKeys.TIMEZONE.key -> adzanTable.ChangeTimeZone(change.newValue)
|
||||
configKeys.ADZAN_FAJR_ENABLED.key -> adzanTable.fajar_enable = change.newValue.toBoolean()
|
||||
configKeys.ADZAN_DHUHR_ENABLED.key -> adzanTable.dzuhur_enable = change.newValue.toBoolean()
|
||||
configKeys.ADZAN_ASR_ENABLED.key -> adzanTable.ashar_enable = change.newValue.toBoolean()
|
||||
configKeys.ADZAN_MAGHRIB_ENABLED.key -> adzanTable.maghrib_enable = change.newValue.toBoolean()
|
||||
configKeys.ADZAN_ISHA_ENABLED.key -> adzanTable.isya_enable = change.newValue.toBoolean()
|
||||
configKeys.ADZAN_FAJR_SOUND.key -> adzanTable.fajar_sound = change.newValue
|
||||
configKeys.ADZAN_DHUHR_SOUND.key -> adzanTable.dzuhur_sound = change.newValue
|
||||
configKeys.ADZAN_ASR_SOUND.key -> adzanTable.ashar_sound = change.newValue
|
||||
configKeys.ADZAN_MAGHRIB_SOUND.key -> adzanTable.maghrib_sound = change.newValue
|
||||
configKeys.ADZAN_ISHA_SOUND.key -> adzanTable.isya_sound = change.newValue
|
||||
|
||||
else -> {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
}
|
||||
_config.Save()
|
||||
Logger.info { "Updated AdzanSetting configuration" }
|
||||
} else {
|
||||
Logger.info { "No changes detected in AdzanSetting"}
|
||||
}
|
||||
}
|
||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||
|
||||
|
||||
} catch (e : Exception){
|
||||
it.status(400)
|
||||
.result(objectmapper.writeValueAsString(resultMessage("Incomplete AdzanSetting data: ${e.message}")))
|
||||
}
|
||||
}
|
||||
}
|
||||
path("OldResultDays") {
|
||||
get {
|
||||
it.result(objectmapper.writeValueAsString(resultMessage(_config.Get(configKeys.AUTO_DELETE_RESULT_DAYS.key))))
|
||||
|
||||
Reference in New Issue
Block a user