initial input
authorKilian Saffran <ksaffran@dks.lu>
Tue, 19 Feb 2019 16:26:44 +0000 (17:26 +0100)
committerKilian Saffran <ksaffran@dks.lu>
Tue, 19 Feb 2019 16:26:44 +0000 (17:26 +0100)
81 files changed:
.config/lxsession/LXDE-pi/autostart [new file with mode: 0644]
.profile [new file with mode: 0644]
app/clock.html [new file with mode: 0644]
app/css/admin.css [new file with mode: 0644]
app/css/clock.css [new file with mode: 0644]
app/index.html [new file with mode: 0644]
app/js/admin.js [new file with mode: 0644]
app/js/app.js [new file with mode: 0644]
app/js/database.js [new file with mode: 0644]
app/js/jquery-3.2.1.min.js [new file with mode: 0644]
app/js/login.js [new file with mode: 0644]
app/js/request.js [new file with mode: 0644]
app/js/sysconfig.js [new file with mode: 0644]
app/js/track.js [new file with mode: 0644]
app/main.js [new file with mode: 0644]
app/package.json [new file with mode: 0644]
app/supported.html [new file with mode: 0644]
app/test.html [new file with mode: 0644]
bin/Hourtraxsrv.pl [new file with mode: 0644]
bin/Module/FileSystem.pm [new file with mode: 0644]
bin/Module/Function/encrypt.pm [new file with mode: 0644]
bin/Module/OpenVPN.pm [new file with mode: 0644]
bin/Module/SQLite.pm [new file with mode: 0644]
bin/Module/Service.pm [new file with mode: 0644]
bin/Module/System.pm [new file with mode: 0644]
bin/Module/Test.pm [new file with mode: 0644]
bin/mountdrives.pl [new file with mode: 0644]
bin/startapp.sh [new file with mode: 0644]
bin/supportmnt.pl [new file with mode: 0644]
bin/syncdb.pl [new file with mode: 0644]
data/data.txt [new file with mode: 0644]
data/hourtrax.json [new file with mode: 0644]
data/hourtrax.passwd [new file with mode: 0644]
data/hourtrax.pid [new file with mode: 0644]
data/hourtrax.sqlite [new file with mode: 0644]
data/test/hourtrax.json [new file with mode: 0644]
data/test/hourtrax.passwd [new file with mode: 0644]
data/test/hourtrax.sqlite [new file with mode: 0644]
db/hourtrax.sql [new file with mode: 0644]
db/hourtrax.sqlite [new file with mode: 0644]
db/hourtrax_dev.sql [new file with mode: 0644]
icons/Add_New.svg [new file with mode: 0644]
icons/Sand_Clock__03.svg [new file with mode: 0644]
icons/Stop_Clock.svg [new file with mode: 0644]
icons/splash.png [new file with mode: 0644]
icons/splash.xcf [new file with mode: 0644]
icons/test.png [new file with mode: 0644]
new_install/fileto_change.txt [new file with mode: 0644]
new_install/history.txt [new file with mode: 0644]
new_install/hourtrax.json [new file with mode: 0644]
new_install/hourtrax.passwd [new file with mode: 0644]
new_install/hourtrax.sqlite [new file with mode: 0644]
splash.png [new file with mode: 0644]
sys/boot/config.txt [new file with mode: 0644]
sys/etc/lightdm/lightdm.conf [new file with mode: 0644]
sys/etc/modprobe.d/raspi-blacklist.conf [new file with mode: 0644]
sys/etc/modules [new file with mode: 0644]
sys/etc/openvpn/DKS-VPN-dks-hourtrax.conf [new file with mode: 0644]
sys/etc/ssh/sshd_config [new file with mode: 0644]
sys/etc/sudoers [new file with mode: 0644]
sys/etc/systemd/system/autologin@.service [new file with mode: 0644]
sys/etc/xdg/lxsession/LXDE-pi/autostart [new file with mode: 0644]
sys/etc/xdg/lxsession/LXDE-pi/sshpwd.sh [new file with mode: 0644]
sys/etc/xdg/lxsession/LXDE/autostart [new file with mode: 0644]
sys/etc/xdg/openbox/menu.xml [new file with mode: 0644]
sys/usr/share/plymouth/themes/pix/splash.png [new file with mode: 0644]
sys/var/spool/cron/crontabs/dks [new file with mode: 0644]
web/clock.html [new file with mode: 0644]
web/css/admin.css [new file with mode: 0644]
web/css/clock.css [new file with mode: 0644]
web/index.html [new file with mode: 0644]
web/js/admin.js [new file with mode: 0644]
web/js/app.js [new file with mode: 0644]
web/js/database.js [new file with mode: 0644]
web/js/jquery-3.2.1.min.js [new file with mode: 0644]
web/js/login.js [new file with mode: 0644]
web/js/request.js [new file with mode: 0644]
web/js/sysconfig.js [new file with mode: 0644]
web/js/track.js [new file with mode: 0644]
web/supported.html [new file with mode: 0644]
web/test.html [new file with mode: 0644]

diff --git a/.config/lxsession/LXDE-pi/autostart b/.config/lxsession/LXDE-pi/autostart
new file mode 100644 (file)
index 0000000..2c98ce9
--- /dev/null
@@ -0,0 +1,6 @@
+#@lxpanel --profile LXDE-pi
+#@pcmanfm --desktop --profile LXDE-pi
+#@xscreensaver -no-splash
+#@point-rpi
+@/home/dks/bin/startapp.sh
+
diff --git a/.profile b/.profile
new file mode 100644 (file)
index 0000000..5b8d9b5
--- /dev/null
+++ b/.profile
@@ -0,0 +1,6 @@
+PATH="/home/dks/bin:/home/dks/perl5/bin${PATH:+:${PATH}}"; export PATH;
+PERL5LIB="/home/dks/perl5/lib/perl5${PERL5LIB:+:${PERL5LIB}}"; export PERL5LIB;
+PERL_LOCAL_LIB_ROOT="/home/dks/perl5${PERL_LOCAL_LIB_ROOT:+:${PERL_LOCAL_LIB_ROOT}}"; export PERL_LOCAL_LIB_ROOT;
+PERL_MB_OPT="--install_base \"/home/dks/perl5\""; export PERL_MB_OPT;
+PERL_MM_OPT="INSTALL_BASE=/home/dks/perl5"; export PERL_MM_OPT;
+
diff --git a/app/clock.html b/app/clock.html
new file mode 100644 (file)
index 0000000..f4c27ce
--- /dev/null
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>DKS Hourtrax</title>
+<meta http-equiv="cache-control" content="max-age=0" />
+  <meta http-equiv="cache-control" content="no-cache" />
+  <meta http-equiv="expires" content="0" />
+  <meta http-equiv="expires" content="Tue, 01 Jan 1970 1:00:00 GMT" />
+  <meta http-equiv="pragma" content="no-cache" />
+<link href="css/clock.css" rel="stylesheet" type="text/css">
+</head>
+<body>
+<header>
+       <h1 style="float: left;"><span style="color: #52638e">DKS</span> HourTrax &nbsp;</h1> 
+       <button id="reload" style="top: -20px; height: 50px; width: 100px;" onclick="location.href='clock.html';">Logout</button>&nbsp;
+       <div id="mnuviews" style="float: left;">
+               <button id="mnubtnnewpincode" style="top: -20px; height: 50px; width: 100px;" onclick="login.gotoNewLogin(null);">Changer PIN</button>&nbsp;
+               <button id="mnubtnstamp" style="top: -20px; height: 50px; width: 100px;" onclick="track.loadtrackscreen();">Pointer</button>&nbsp;
+       </div>
+       
+</header>
+<div id="scruserlist" style="float: left; text-align: center;">
+               
+       </div>
+       <div id="scruserpin">
+               <div class="usergreeting" id="usergreeting1">Bonjour Prename,</div>
+               <div class="pincode"><input type="password" value="" id="pincode" readonly="1"/></div>
+               <div id="pinmessage"></div>
+               <div id="keypad">
+               <button class="btnkeypad" onclick="login.setPin('1');">1</button>
+               <button class="btnkeypad" onclick="login.setPin('2');">2</button> 
+               <button class="btnkeypad" onclick="login.setPin('3');">3</button> 
+               <button class="btnkeypad" onclick="login.setPin('4');">4</button> 
+               <button class="btnkeypad" onclick="login.setPin('5');">5</button>
+               <button class="btnkeypad" onclick="login.setPin('6');">6</button>
+               <button class="btnkeypad" onclick="login.setPin('7');">7</button>
+               <button class="btnkeypad" onclick="login.setPin('8');">8</button>
+               <button class="btnkeypad" onclick="login.setPin('9');">9</button>
+               <button class="btnkeypad" onclick="login.setPin('COR');" style="color: red;">COR</button>
+               <button class="btnkeypad" onclick="login.setPin('0');">0</button>
+               <button class="btnkeypad" onclick="login.setPin('OK');" style="color: green;">OK</button>
+               </div>
+       </div>
+       <div id="scrnewuserpin">
+               <div class="usergreeting" id="usergreeting2">Bonjour Prename,</div>
+               <div class="pincode"><input type="password" value="" id="newpincode1" readonly="1" placeholder="nouveau pin..."/><input type="password" value="" id="newpincode2"  readonly="1" placeholder="confirmez pin.."/></div>
+               <div id="pinmessage2"></div>
+               <div id="keypad">
+               <button class="btnkeypad" onclick="login.setPin('1');">1</button>
+               <button class="btnkeypad" onclick="login.setPin('2');">2</button> 
+               <button class="btnkeypad" onclick="login.setPin('3');">3</button> 
+               <button class="btnkeypad" onclick="login.setPin('4');">4</button> 
+               <button class="btnkeypad" onclick="login.setPin('5');">5</button>
+               <button class="btnkeypad" onclick="login.setPin('6');">6</button>
+               <button class="btnkeypad" onclick="login.setPin('7');">7</button>
+               <button class="btnkeypad" onclick="login.setPin('8');">8</button>
+               <button class="btnkeypad" onclick="login.setPin('9');">9</button>
+               <button class="btnkeypad" onclick="login.setPin('COR');" style="color: red;">COR</button>
+               <button class="btnkeypad" onclick="login.setPin('0');">0</button>
+               <button class="btnkeypad" onclick="login.setPin('OK');" style="color: green;">OK</button>
+               </div>
+       </div>
+       <div id="scrtimetracker">
+               <div class="usergreeting" id="usergreeting3">PrĂ©nom Nom</div>
+               <div class="usergreeting"><strong>Total heures mois courrant:</strong>&nbsp;<span id="monthhours">00:00 h</span></div>
+               <div id="timetracker">
+                       <button class="btntrack" id="btntrackin" onclick="track.setTrack('in');">EntrĂ©e<br/><span id="lasttrackin"></span></button><button class="btntrack" id="btntrackout" onclick="track.setTrack('out');">Sortie<br/><span id="lasttrackout"></span></button>
+               </div>
+       </div>
+       <div id="scrloader" style="text-align: center;">
+               
+               <progress style=" margin: auto; margin-top: 200px; width: 300px;" min="0" max="100"></progress>
+               
+       </div>
+       <div id="scrstatus" style="text-align: center;">
+               
+               <div id="statusmsg" style=" margin: auto; margin-top: 100px; width: 600px; font-size: 20px;" ></div>
+               
+       </div>
+       
+
+<script src="js/jquery-3.2.1.min.js"></script>
+<!-- <script src="js/database.js"></script> -->
+<script src="js/login.js"></script>
+<script src="js/track.js"></script>
+<script src="js/app.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/app/css/admin.css b/app/css/admin.css
new file mode 100644 (file)
index 0000000..1487d61
--- /dev/null
@@ -0,0 +1,240 @@
+       * {
+               font-family: sans-sherif, Arial, Helvetica !important;
+       }
+       body,html {
+               margin: 0px;
+       }
+header {
+               overflow: hidden;
+               position: fixed; /* Set the navbar to fixed position */
+       top: 0; /* Position the navbar at the top of the page */
+       width: 100%; /* Full width */
+               height: 60px;
+               padding-left: 5px;
+               background: linear-gradient(to right, purple, gray);
+}
+
+header > h1 {
+       color: yellow;
+       margin-top: 10px;
+       float: left;
+       text-shadow: 0px -1px 0px rgba(0,0,0,.5);
+}
+div.headerlinks {
+       margin-left: 10px;
+       margin-top: 5px;
+       float: left;
+}
+button {
+       /* display: inline-block;
+        */
+        text-decoration: none;
+       color: #fff;
+       font-weight: bold;
+       background-color: #538fbe;
+       /* padding: 20px 70px;
+       font-size: 24px; */
+       border: 1px solid #2d6898;
+       background-image: linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -o-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -moz-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -webkit-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -ms-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       
+       background-image: -webkit-gradient(
+               linear,
+               left bottom,
+               left top,
+               color-stop(0, rgb(73,132,180)),
+               color-stop(1, rgb(97,155,203))
+       );
+       -webkit-border-radius: 5px;
+       -moz-border-radius: 5px;
+       border-radius: 5px;
+       text-shadow: 0px -1px 0px rgba(0,0,0,.5);
+       /* -webkit-box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       -moz-box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5); */
+       /* -webkit-transition: all .1s ease-in-out;
+       -moz-transition: all .2s ease-in-out;
+       transition: all .2s ease-in-out;
+       -webkit-transform: rotateX(20deg); */
+}
+
+
+button {
+       margin-left: 3px;
+       height: 30px;
+}
+
+div.headerlinks button {
+       height: 50px;
+}
+
+
+section.main {
+       width: 1024px;
+       margin: auto;
+       margin-top: 70px;
+       
+}
+table {
+       margin-top: 10px;
+       width: 100%;
+       border: 1px solid silver;
+       border-collapse: collapse;
+}
+table thead {
+       background-color: gray;
+}
+
+table thead th {
+       padding: 5px 10px;
+       border: 1px solid silver;
+}
+
+table tbody tr:nth-child(even){
+       background-color: rgba(128,0,128,0.3);
+       
+}
+
+table tbody tr:hover {
+       background-color: #79bcff;
+}
+
+table tbody td {
+       border: 1px solid silver;
+       padding: 5px 10px;
+}
+input[type=number]::-webkit-inner-spin-button, 
+input[type=number]::-webkit-outer-spin-button { 
+  -webkit-appearance: none; 
+  margin: 0; 
+}
+
+
+
+input, select {
+       border: 1px solid silver;
+       -webkit-border-radius: 5px;
+       -moz-border-radius: 5px;
+       border-radius: 5px;
+       padding: 5px 10px;
+}
+
+input:disabled {
+       border: 0;
+       color: #000000;
+       background: transparent !important;
+}
+
+.page {
+       display: none;
+}
+
+input[type=number] {
+       text-align: right;
+}
+
+
+
+tbody > tr.selected {
+       background-color: #0080ff;
+}
+
+.panel {
+       border: 1px solid silver;
+       width: 100%;
+       display: block;
+       border: 1px solid silver;
+       padding: 5px;
+       -webkit-border-radius: 5px;
+       -moz-border-radius: 5px;
+       border-radius: 5px;
+       margin: 4px;
+}
+
+.panel-head {
+       margin: 0px;
+       padding: 3px;
+       height: 20px;
+       
+}
+
+h4 {
+       margin: 0px;
+       padding: 2px;
+}
+
+.tabset > input[type="radio"] {
+  position: absolute;
+  left: -200vw;
+}
+
+.tabset .tab-panel {
+  display: none;
+}
+
+.tabset > input:first-child:checked ~ .tab-panels > .tab-panel:first-child,
+.tabset > input:nth-child(3):checked ~ .tab-panels > .tab-panel:nth-child(2),
+.tabset > input:nth-child(5):checked ~ .tab-panels > .tab-panel:nth-child(3),
+.tabset > input:nth-child(7):checked ~ .tab-panels > .tab-panel:nth-child(4),
+.tabset > input:nth-child(9):checked ~ .tab-panels > .tab-panel:nth-child(5),
+.tabset > input:nth-child(11):checked ~ .tab-panels > .tab-panel:nth-child(6) {
+  display: block;
+}
+
+.tabset > label {
+  position: relative;
+  display: inline-block;
+  padding: 15px 15px 15px;
+  border: 1px solid transparent;
+  border-bottom: 0;
+  cursor: pointer;
+  font-weight: 600;
+}
+
+/* .tabset > label::after {
+  position: absolute;
+  left: 15px;
+  bottom: 10px;
+  width: 22px;
+  height: 4px;
+  background: #8d8d8d;
+} */
+
+.tabset > label:hover,
+.tabset > input:focus + label {
+  color: #06c;
+}
+
+.tabset > label:hover::after,
+.tabset > input:focus + label::after,
+.tabset > input:checked + label::after {
+  background: #06c;
+} 
+
+.tabset > input:checked + label {
+  border-color: #ccc;
+  border-bottom: 1px solid #fff;
+  margin-bottom: -1px;
+}
+
+.tab-panel {
+  padding: 30px 0;
+  border-top: 1px solid #ccc;
+}
+
+label.formlabel {
+       width: 120px;
+}
+
+div.row {
+       margin-top: 3px;
+       margin-bottom: 3px;
+}
+
+section.sysconfig {
+       display: none;
+}
+
diff --git a/app/css/clock.css b/app/css/clock.css
new file mode 100644 (file)
index 0000000..86cd02a
--- /dev/null
@@ -0,0 +1,236 @@
+       
+       body,html {
+               overflow: hidden;
+               margin: 0px;
+               
+       }
+       div {
+               margin: auto;
+       }
+       header {
+               height: 60px;
+               padding-left: 5px;
+               background: linear-gradient(to right, purple, gray);
+       }
+       h1 {
+               padding-top: 5px;
+               margin-top: 0px;
+               color: yellow;
+       }
+       button {
+       display: inline-block;
+       text-decoration: none;
+       color: #fff;
+       font-weight: bold;
+       background-color: #538fbe;
+       /* padding: 20px 70px;
+       font-size: 24px; */
+       border: 1px solid #2d6898;
+       background-image: linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -o-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -moz-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -webkit-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -ms-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       
+       background-image: -webkit-gradient(
+               linear,
+               left bottom,
+               left top,
+               color-stop(0, rgb(73,132,180)),
+               color-stop(1, rgb(97,155,203))
+       );
+       -webkit-border-radius: 5px;
+       -moz-border-radius: 5px;
+       border-radius: 5px;
+       text-shadow: 0px -1px 0px rgba(0,0,0,.5);
+       -webkit-box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       -moz-box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       -webkit-transition: all .1s ease-in-out;
+       -moz-transition: all .2s ease-in-out;
+       transition: all .2s ease-in-out;
+       -webkit-transform: rotateX(20deg);
+}
+
+button:hover {
+       background-image: linear-gradient(bottom, rgb(79,142,191) 0%, rgb(102,166,214) 100%);
+       background-image: -o-linear-gradient(bottom, rgb(79,142,191) 0%, rgb(102,166,214) 100%);
+       background-image: -moz-linear-gradient(bottom, rgb(79,142,191) 0%, rgb(102,166,214) 100%);
+       background-image: -webkit-linear-gradient(bottom, rgb(79,142,191) 0%, rgb(102,166,214) 100%);
+       background-image: -ms-linear-gradient(bottom, rgb(79,142,191) 0%, rgb(102,166,214) 100%);
+       
+       /* background-image: -webkit-gradient(
+               linear,
+               left bottom,
+               left top,
+               color-stop(0, rgb(79,142,191)),
+               color-stop(1, rgb(102,166,214))
+       ); */
+}
+
+button:active {
+-webkit-box-shadow: 0px 2px 0px #2b638f, 0px 1px 6px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+-moz-box-shadow: 0px 2px 0px #2b638f, 0px 1px 6px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+box-shadow: 0px 2px 0px #2b638f, 0px 1px 6px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       background-image: linear-gradient(bottom, rgb(88,154,204) 0%, rgb(90,150,199) 100%);
+       background-image: -o-linear-gradient(bottom, rgb(88,154,204) 0%, rgb(90,150,199) 100%);
+       background-image: -moz-linear-gradient(bottom, rgb(88,154,204) 0%, rgb(90,150,199) 100%);
+       background-image: -webkit-linear-gradient(bottom, rgb(88,154,204) 0%, rgb(90,150,199) 100%);
+       background-image: -ms-linear-gradient(bottom, rgb(88,154,204) 0%, rgb(90,150,199) 100%);
+       
+       background-image: -webkit-gradient(
+               linear,
+               left bottom,
+               left top,
+               color-stop(0, rgb(88,154,204)),
+               color-stop(1, rgb(90,150,199))
+       );
+    -webkit-transform: translate(0, 4px) rotateX(20deg);  
+    -moz-transform: translate(0, 4px);  
+    transform: translate(0, 4px);  
+}
+
+button:disabled {
+       background-image: none;
+       background-color: #f4f4f4;
+       color: #000;
+       font-weight: bold;
+       box-shadow: none;
+       -webkit-transform: none;
+}
+       /* button {
+               background-color: lightgrey;
+               border: 1px solid silver;
+               outline: none;
+       } */
+       button.user {
+               height: 90px;
+               width: 90px;
+               margin: 10px;
+                white-space: nowrap;
+                overflow: hidden;
+       }
+       button.btnkeypad {
+               height: 70px;
+               width: 70px;
+               margin: 5px;
+               font-weight: bold;
+               font-size: 20px;
+                white-space: nowrap;
+                overflow: hidden;
+       }
+       button.btntrack {
+               background-color: 
+               height: 150px;
+               width: 150px;
+               margin: 15px;
+               font-weight: bold;
+               font-size: 30px;
+                white-space: nowrap;
+                overflow: hidden;
+       }
+       #scrloader {
+               display: none;
+               margin: auto;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #scruserlist {
+               display: none;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #scruserpin {
+               display: none;
+               margin: auto;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #scrnewuserpin {
+               display: none;
+               margin: auto;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #scrtimetracker{
+               display: none;
+               margin: auto;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #scrstatus{
+               display: none;
+               margin: auto;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #keypad {
+               border: 1px solid silver;
+               width: 270px;
+               margin: auto;
+       }
+       #timetracker{
+               border: 1px solid silver;
+               width: 400px;
+               margin: auto;
+               margin-top: 20px;
+               text-align: center;
+       }
+       div.pincode {
+               text-align: center;
+       }
+       #pincode,#newpincode1,#newpincode2 {
+               min-height: 30px;
+               border: 1px solid silver;
+               width: 270px;
+               margin: auto;
+               text-align: center;
+               font-weight: bold;
+               font-size: 10px;
+       }
+       #newpincode1,#newpincode2{
+               width: 135px;
+       }
+       .usergreeting {
+               text-align: center;
+       }
+       #pinmessage, #pinmessage2 {
+               height: 30px;
+               display: block;
+               color: red;
+               font-weight: bold;
+               text-align: center;
+       }
+       #lasttrackin,#lasttrackout {
+               font-size: 12px; 
+               color: #000;
+               font-weight: bold;
+       }
+       
+       
+/* screenheight: 480;*/
+/* screenwidth: 80px;*/
\ No newline at end of file
diff --git a/app/index.html b/app/index.html
new file mode 100644 (file)
index 0000000..04b5024
--- /dev/null
@@ -0,0 +1,281 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Hourtrax - Admin</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<meta http-equiv="cache-control" content="max-age=0" />
+<meta http-equiv="cache-control" content="no-cache" />
+<meta http-equiv="expires" content="0" />
+<meta http-equiv="expires" content="Tue, 01 Jan 1970 1:00:00 GMT" />
+<meta http-equiv="pragma" content="no-cache" />
+<link href="css/admin.css" rel="stylesheet" type="text/css">
+</head>
+<body>
+       <header>
+               <h1>Hourtrax</h1>
+               <div class="headerlinks">
+                       <button onclick="admin.loadpage('page_track');">Pointages</button>
+                       <button onclick="admin.loadpage('page_staff');">EmployĂ©(e)s</button>
+
+
+                       <button onclick="admin.loadpage('page_sysconfig');">Configuration</button>
+               </div>
+       </header>
+       <section class="main">
+               <div id="page_track" class="page">
+
+                       <div id="pagenav_track" class="pagenav">
+                               <span id="tracklabel">Pointages:</span> <select id="select_table"
+                                       onchange="admin.change_tracktable();">
+                                       <option value="trackincomplete">incomplet</option>
+                                       <option value="trackbyday">par jour</option>
+                                       <option value="trackbystaff">par employĂ©(e)</option>
+                               </select> <input type="date" class="trackbyday" id="cmb_trackdate"
+                                       onchange="admin.change_tracktable();" /> <input type="month"
+                                       class="trackbystaff" id="cmb_trackmonth"
+                                       onchange="admin.change_tracktable();" /> <select
+                                       class="trackbystaff" id="select_staff"
+                                       onchange="admin.change_tracktable();">
+                                       <option value="employe">Prename Surname</option>
+                               </select>
+                       </div>
+                       <table id="table_trackbyday" style="display: none;">
+                               <thead>
+                                       <tr>
+                                               <th>Nom</th>
+                                               <th>EntrĂ©e</th>
+                                               <th>Sortie</th>
+                                       </tr>
+
+                               </thead>
+                               <tbody id="table_trackbyday_tbody">
+                               </tbody>
+                       </table>
+                       <table id="table_trackbystaff" style="display: none;">
+                               <thead>
+                                       <tr>
+                                               <th>Date</th>
+                                               <th>EntrĂ©e</th>
+                                               <th>Sortie</th>
+                                       </tr>
+
+                               </thead>
+                               <tbody id="table_trackbystaff_tbody">
+                               </tbody>
+                       </table>
+                       <table id="table_trackincomplete" style="display: none;">
+                               <thead>
+                                       <tr>
+                                               <th>Date</th>
+                                               <th>Nom</th>
+                                               <th>EntrĂ©e</th>
+                                               <th>Sortie</th>
+                                       </tr>
+
+                               </thead>
+                               <tbody id="table_trackincomplete_tbody">
+                               </tbody>
+                       </table>
+               </div>
+               <div id="page_staff" class="page">
+                       <div class="pagenav">
+                               <button onclick="admin.addstaff();">Ajouter EmployĂ©(e)</button>
+                       </div>
+                       <table id="table_staff">
+                               <thead>
+                                       <tr>
+                                               <th>Nom</th>
+                                               <th>PrĂ©nom</th>
+                                               <th>date debut</th>
+                                               <th>H/(Semaine)</th>
+                                               <th>bloquĂ©</th>
+                                               <th>inactive</th>
+                                               <th>Code PIN</th>
+                                               <th>&nbsp;</th>
+                                       </tr>
+
+                               </thead>
+                               <tbody id="table_staff_tbody">
+                               </tbody>
+                       </table>
+               </div>
+               <div id="page_sysconfig" class="page">
+                       <div  class="pagenav">
+                               <span id="tracklabel">Configuration:</span> <select id="select_sysconfig"
+                                       onchange="sysconfig.showsection();">
+                                       <option value="sysexport">Export des donnĂ©es</option>
+                                       <option value="sysaccess">Accès admin</option>
+                                       <option value="syspause">Pause obligatoire</option>
+                                       <option value="syssync">Sychronisation</option>
+                                       <option value="sysbackup">Backup</option>
+                                       <option value="syssystem">WLAN & Hostname</option>
+                               </select> 
+                       </div>
+                       <hr/>
+                       <section id="sysexport" class="sysconfig" style="display: block;">
+                                       <h4>Export des donnĂ©es comme fichier CSV:</h4>
+                                                       <div class="row">
+                                                       du <input type="date" id="cmb_exportdatestart" />
+                                                       </div>
+                                                       <div class="row">
+                                                       au <input type="date" id="cmb_exportdatestop" />
+                                                       <button onclick="sysconfig.exportdata();">Exporter</button>
+                                                       </div>
+                               </section>
+                       <section id="syspause" class="sysconfig">
+                                                       <h4>pause obligatoire</h4>
+                                                       <div class="row">
+                                                               max longueur pause: <input type="number"
+                                                                       id="sysconfig_pausetime" style="width: 40px;"> minutes
+                                                       </div>
+                                                       <div class="row">
+                                                               pause de <input type="time" id="sysconfig_pausestarttime"
+                                                                       style="width: 70px"> Ă  <input type="time"
+                                                                       id="sysconfig_pauseendtime" style="width: 70px">
+                                                       </div>
+                                                       <div class="row">
+                                                                       <input type="checkbox" name="radiopausecond"
+                                                                               id="sysconfig_alwaysremovepause"> dĂ©duire toujours
+                                                               </div>
+                                                               <div class="row">
+                                                                       <input type="checkbox" name="radiopausecond"
+                                                                               id="sysconfig_onlywhennottracked"> si pointĂ© dans la
+                                                                       periode marquĂ©, dĂ©duire ce qui manque
+                                                               </div>
+                                                               <button onclick="sysconfig.savepause();">Sauvegarder</button>
+                                                       
+                       </section>
+                       <section id="syssync" class="sysconfig">                                
+                                                               <h4>Synchronisation base de donnĂ©e</h4>
+                                                               <div class="row">
+                                                                       DB-Type:<select id="sysconfig_syncdbtype">
+                                                                               <option value="sqlite">SQLite</option>
+                                                                       </select> <label>Host:</label><input type="text" id="sysconfig_synchost">
+                                                                       <label>Dossier PartagĂ©:</label><input type="text"
+                                                                               id="sysconfig_syncpath">
+                                                               </div>
+
+                                                               <div class="row">
+                                                                       Utilisateur: <input type="text" id="sysconfig_syncuser">&nbsp;Mot
+                                                                       de passe: <input type="password" id="sysconfig_syncpwd" />
+                                                               </div>
+                                                               <div class="row">
+                                                                       Synchronisation quotidienne Ă  <input type="time"
+                                                                               id="sysconfig_synctime" />
+                                                               </div>
+                                                               <div class="row">
+                                                                       <span style="font-weight: bold;">Base de donnĂ©e</span>
+                                                               </div>
+                                                               <div class="row">
+                                                                       Nom: <input type="text" id="sysconfig_syncdbname">
+                                                               </div>
+                                                               <div class="row">
+                                                                       <span style="font-weight: bold;">Champs:</span>
+                                                               </div>
+                                                               <div class="row">
+                                                                       <table style="width: 60%;">
+                                                                               <thead>
+                                                                                       <tr>
+                                                                                               <th>Champ hourtrax</th>
+                                                                                               <th>Table</th>
+                                                                                               <th>Colonne</th>
+                                                                                       </tr>
+                                                                               </thead>
+                                                                               <tbody>
+                                                                                       <tr>
+                                                                                               <td>ID EmployĂ©</td>
+                                                                                               <td><input type="text" id="sysconfig_synctableidstaff" /></td>
+                                                                                               <td><input type="text" id="sysconfig_synccolidstaff" /></td>
+                                                                                       </tr>
+                                                                                       <tr>
+                                                                                               <td>PrĂ©nom</td>
+                                                                                               <td><input type="text" id="sysconfig_synctableprename" /></td>
+                                                                                               <td><input type="text" id="sysconfig_synccolprename" /></td>
+                                                                                       </tr>
+                                                                                       <tr>
+                                                                                               <td>Nom</td>
+                                                                                               <td><input type="text" id="sysconfig_synctablesurname" /></td>
+                                                                                               <td><input type="text" id="sysconfig_synccolsurname" /></td>
+                                                                                       </tr>
+                                                                                       <tr>
+                                                                                               <td>Date DĂ©but</td>
+                                                                                               <td><input type="text"
+                                                                                                       id="sysconfig_synctablestartdate" /></td>
+                                                                                               <td><input type="text" id="sysconfig_synccolstartdate" /></td>
+                                                                                       </tr>
+                                                                                       <tr>
+                                                                                               <td>Heures par Semaine</td>
+                                                                                               <td><input type="text"
+                                                                                                       id="sysconfig_synctableweekhours" /></td>
+                                                                                               <td><input type="text" id="sysconfig_synccolweekhours" /></td>
+                                                                                       </tr>
+                                                                                       <tr>
+                                                                                               <td>Code PIN</td>
+                                                                                               <td><input type="text" id="sysconfig_synctablepin" /></td>
+                                                                                               <td><input type="text" id="sysconfig_synccolpin" /></td>
+                                                                                       </tr>
+                                                                               </tbody>
+                                                                       </table>
+                                                               </div>
+                                                               <div class="row">
+                                                               <button onclick="sysconfig.setSyncData();">Sauvegarder</button>
+                                                               &nbsp;
+                                                               <button onclick="">Synchroniser</button>&nbsp;
+                                                               <button onclick="">Test Connection</button>
+                                                               </div>
+                                                       </section>
+                                       <section id="sysbackup" class="sysconfig">
+                                                       <h4>Backup</h4>
+                                                       <div>
+                                                       <select id="sysconfig_backuptype"><option>Samba
+                                                                       / Windows Share</option></select> Host: <input type="text"
+                                                               id="sysconfig_backuphost"> Dossier partagĂ©: <input
+                                                               type="text" id="sysconfig_backuppath">
+                                               </div>
+                                               <div>
+                                                       Utilisateur: <input type="text" id="sysconfig_backupuser">&nbsp;Mot
+                                                       de passe: <input type="password" id="sysconfig_backupPwd" />
+                                               </div>
+                                               <div>
+                                                       Backup quotidien Ă  <input type="time" id="sysconfig_backuptime" />
+                                               </div>
+                                               <div>
+                                                       Max nombre de backups Ă  garder: <input type="number" id="sysconfig_backupmaxnum" />
+                                               </div>
+                                               <button onclick="">Test Connection</button><button onclick="">Backup Maintenant</button><button onclick="">Voir les backups</button>
+                                       </section>
+                                       <section id="sysaccess" class="sysconfig">
+                                               <h4>Accès Admin</h4>
+                                               <div class="row">
+                                                       <label>Login:</label><input type="text" id="sysconfig_user" placeholder="login"> 
+                                               </div>
+                                               <div class="row">
+                                                       <label>Mot de passe:</label>
+                                                       <input type="password" id="sysconfig_pwd1" placeholder="mot de passe.." />
+                                                       <input type="password" id="sysconfig_pwd2" placeholder="confirmer mot de passe" />
+                                               </div>
+                                               <button onclick="sysconfig.setSystemUser();">Sauvegarder</button>
+       
+                                       </section>
+                                       <section id="syssystem" class="sysconfig">
+
+                                               <div>
+                                                       Hostname:<input type="text" id="sysconfig_hostname">
+                                               </div>
+                                               <div>
+                                                       Wifi SSID:<select id="sysconfig_wlanssid"></select>
+                                                       Mot de passe: <input type="password" id="sysconfig_wlanpwd" />
+                                               </div>
+                                               <button onclick="sysconfig.setNetworkConfig();">Sauvegarder</button>
+                                       </section>
+                                       
+                               </div>
+
+       </section>
+
+       <script src="js/admin.js"></script>
+       <script src="js/request.js"></script>
+       <script src="js/sysconfig.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/app/js/admin.js b/app/js/admin.js
new file mode 100644 (file)
index 0000000..918e428
--- /dev/null
@@ -0,0 +1,247 @@
+var admin = {
+               lastrow: null,
+               lastpage: null,
+               lasttracktable: null,
+               currentrowdata: null,
+               loadpage: function (pagename){
+                       //disable all first
+                       admin.lastrow = null;
+                       document.getElementById(admin.lastpage).style.display = 'none';
+                       if (pagename == 'page_track'){
+                               document.getElementById("select_table").style.display = 'inline';
+                               
+                               document.getElementById("page_track").style.display = 'block';
+                               admin.change_tracktable();
+                               
+                       } else if (pagename == 'page_staff'){
+                               document.getElementById("page_staff").style.display = 'block';
+                               admin.loadtable_staff();
+                       }else if (pagename == 'page_exportdata'){
+                               document.getElementById("page_exportdata").style.display = 'block';
+                       } else if (pagename == 'page_sysconfig'){
+                               document.getElementById("page_sysconfig").style.display = 'block';
+                       }
+                       admin.lastpage=pagename;
+               },
+               change_tracktable: function(){
+                       document.getElementById("cmb_trackdate").style.display = 'none';
+                       document.getElementById("cmb_trackmonth").style.display = 'none';
+                       document.getElementById("select_staff").style.display = 'none';
+                       if (admin.lasttracktable != null){
+                               document.getElementById(admin.lasttracktable).style.display = 'none';
+                       }
+                       var tablesel = document.getElementById("select_table").value;
+                       //console.log(tablesel);
+                       document.getElementById('table_' + tablesel).style.display = 'block';
+                       admin.lasttracktable = 'table_' + tablesel;
+                       if (tablesel == "trackbyday"){
+                               document.getElementById('cmb_trackdate').style.display = 'inline';
+                               admin.loadtable_trackbyday();
+                       }else if (tablesel == "trackbystaff"){
+                               document.getElementById("cmb_trackmonth").style.display = 'inline';
+                               document.getElementById("select_staff").style.display = 'inline';
+                               admin.loadtable_trackbystaff();
+                       } else {
+                               admin.loadtable_trackincomplete();
+                       }
+               },
+               loadtable_staff: function(){
+                       admin.lastrow = null;
+                       document.getElementById("table_staff_tbody").innerHTML="";
+                       var xq = {"type":"querysorted","sql":"select st.id,st.prename,st.surname,st.pin,st.disabled,st.blocked, co.startdate, co.weekhours from staff st left join contract co on (st.id=co.idstaff) order by st.surname,st.prename,st.id;"};
+                       var tbldata = req.reqdata('POST','sqlite/query',xq,null);
+                       var ntbldata = ''; 
+                       for (var i in tbldata.sqldata){
+                               var row = '<tr data-id="'+tbldata.sqldata[i].id+'" onclick="admin.setRowEditable(this);" >';
+                               //row += '<td><input type="text" value="'+ tbldata.sqldata[i].id+'" onfocusout="admin.changeData(this);" style="width: 50px;" class="textright" placeholder="Id" id="staff_id_'+ i+'"  disabled="1"/></td>';
+                               row += '<td><input type="text" value="'+ tbldata.sqldata[i].surname+'" onfocusout="admin.changeData(this);" placeholder="Nom" id="staff_surname_'+ i+'"  disabled="1"/></td>';
+                               row += '<td><input type="text" value="'+ tbldata.sqldata[i].prename+'" onfocusout="admin.changeData(this);" placeholder="PrĂ©nom" id="staff_prename_'+ i+'"  disabled="1"/></td>';
+                               row += '<td><input type="date" value="'+ tbldata.sqldata[i].startdate+'" onfocusout="admin.changeData(this);" style="width: 150px;" id="contract_startdate_'+ i+'"  disabled="1"/></td>';
+                               row += '<td><input type="number" value="'+ tbldata.sqldata[i].weekhours+'" onfocusout="admin.changeData(this);" style="width: 40px;" class="textright" id="contract_weekhours_'+ i+'"  disabled="1"/>h</td>';
+                               row += '<td><input type="checkbox" value="true" onchange="admin.changeData(this);" id="staff_blocked_'+ i+'" '+((tbldata.sqldata[i].blocked == true)?'checked="1"':'')+'  disabled="1"/></td>';
+                               row += '<td><input type="checkbox" value="true" onchange="admin.changeData(this);" id="staff_disabled_'+ i+'" '+((tbldata.sqldata[i].disabled == true)?'checked="1"':'')+'  disabled="1"/></td>';
+                               row += '<td><input type="password" ' + ((tbldata.sqldata[i].pin!=null)?'value="'+ tbldata.sqldata[i].pin +'"':"")+' onfocusout="admin.changeData(this);" placeholder="Code Pin" id="staff_pin_'+ i+'"  disabled="1"/></td>';
+                               row += '<td><button id="staff_delete" onclick="admin.deleteRow(\'staff\',\''+ tbldata.sqldata[i].id +'\');">Supprimer</button></td>';
+                               row += '</tr>';
+                               ntbldata += row;
+                       }
+                       document.getElementById("table_staff_tbody").innerHTML=ntbldata;
+               },
+               loadtable_trackbyday: function(){
+                       admin.lastrow = null;
+                       document.getElementById("table_trackbyday_tbody").innerHTML="";
+                       var trackdate = document.getElementById("cmb_trackdate").value;
+                       //console.log("trackdate" + trackdate);
+                       var xq = {"type":"querysorted","sql":"select hr.id,co.idstaff,st.prename,st.surname,hr.stamp_in, hr.stamp_out from staff st left join contract co on (st.id=co.idstaff) left join (select id,idstaff,stamp_in,stamp_out from hours where date(stamp_in) = date('"+trackdate +"')) hr on (st.id=hr.idstaff) where st.disabled is null and co.startdate <= date('"+ trackdate +"') order by st.surname,st.prename,co.idstaff;"};
+                       var tbldata = req.reqdata('POST','sqlite/query',xq,null);
+                       var ntbldata = ''; 
+                       for (var i in tbldata.sqldata){
+                               var row='<tr data-id="'+tbldata.sqldata[i].id+'" data-idstaff="'+tbldata.sqldata[i].idstaff+'" onclick="admin.setRowEditable(this);">';
+                               row += '<td>'+tbldata.sqldata[i].surname+' '+tbldata.sqldata[i].prename+'</td>';
+                               row += '<td><input type="time" id="hours_stampin_'+ i +'" value="'+((tbldata.sqldata[i].stamp_out != null)?tbldata.sqldata[i].stamp_in.substring(11,16):'')+'" onfocusout="admin.changeData(this);" disabled="1"/></td>';
+                               row += '<td><input type="time" id="hours_stampout_'+ i +'" value="'+((tbldata.sqldata[i].stamp_out != null)?tbldata.sqldata[i].stamp_out.substring(11,16):'')+'" onfocusout="admin.changeData(this);" disabled="1"/></td>';
+                               row +='</tr>';
+                               ntbldata += row;
+                       }
+                       document.getElementById("table_trackbyday_tbody").innerHTML=ntbldata;
+               },
+               loadtable_trackbystaff: function(){
+                       admin.lastrow = null;
+                       document.getElementById("table_trackbystaff_tbody").innerHTML="";
+                       var trackdate = document.getElementById("cmb_trackmonth").value;
+                       var staffid = document.getElementById("select_staff").value;
+                       var bdate = new Date(trackdate + "-01");
+                       var edate = new Date(bdate.getFullYear(),bdate.getMonth()+1,1);
+                       var sqldays = new Array();
+                   var cdate2 = new Date();
+                   for (var cdate2=bdate;cdate2<=edate;cdate2.setDate(cdate2.getDate() +1)){
+                       sqldays.push ("SELECT date('" + cdate2.toISOString().substring(0,10) +"') as daydate");
+                   }
+                   var  sqlret = sqldays.join(' UNION ALL ');
+                   
+                       var sql = "select md.daydate,hr.id,hr.stamp_in,hr.stamp_out,hr.idstaff from  (" + sqlret +") md left join (select * from hours where idstaff='"+staffid+"' and date(stamp_in) between date('"+trackdate+"-01') and date('"+trackdate+"-01','+1 month','-1 day')) hr on (date(hr.stamp_in)= md.daydate);";
+                       var xq = {"type":"querysorted","sql":sql};
+                       var tbldata = req.reqdata('POST','sqlite/query',xq,null);
+                       var ntbldata = ''; 
+                       
+//                     var cdate = new Date(bdate);
+//                     
+//                     while (cdate <= enddate){
+//                             console.log("cdate:" + cdate.toISOString());
+//                             cdate.setDate(cdate.getDate() + 1);
+//                     } 
+                       for (var i in tbldata.sqldata){
+                               
+                               var row='<tr data-id="'+((tbldata.sqldata[i].id != null)?tbldata.sqldata[i].id:'')+'" data-idstaff="'+staffid+'" onclick="admin.setRowEditable(this);">';
+                               row += '<td><input type="date" id="hours_stampdate_'+ i +'" value="'+tbldata.sqldata[i].daydate+'" disabled="1" readonly="1"/></td>';
+                               row += '<td><input type="time" id="hours_stampin_'+ i+'" value="'+((tbldata.sqldata[i].stamp_in != null)?tbldata.sqldata[i].stamp_in.substring(11,16):"")+'" onfocusout="admin.changeData(this);" disabled="1"/></td>';
+                               row += '<td><input type="time" id="hours_stampout_'+ i +'" value="'+((tbldata.sqldata[i].stamp_out != null)?tbldata.sqldata[i].stamp_out.substring(11,16):'')+'" onfocusout="admin.changeData(this);" disabled="1"/></td>';
+                               row +='</tr>';
+                               ntbldata += row;
+                       }
+                       document.getElementById("table_trackbystaff_tbody").innerHTML=ntbldata;
+               },
+               loadtable_trackincomplete: function(){
+                       admin.lastrow = null;
+                       document.getElementById("table_trackincomplete_tbody").innerHTML="";
+                       var xq = {"type":"querysorted","sql":"select hr.id,hr.idstaff,st.prename,st.surname,hr.stamp_in,hr.stamp_out from hours hr left join staff st on (hr.idstaff=st.id) where stamp_out is null order by hr.stamp_in,st.surname,st.prename,st.id; "};
+                       var tbldata = req.reqdata('POST','sqlite/query',xq,null);
+                       var ntbldata = ''; 
+                       for (var i in tbldata.sqldata){
+                               
+                               var row='<tr data-id="'+tbldata.sqldata[i].id+'" data-idstaff="'+tbldata.sqldata[i].idstaff+'" onclick="admin.setRowEditable(this);">';
+                               row += '<td><input type="date" id="hours_stampdate_'+ i+'" value="'+tbldata.sqldata[i].stamp_in.substring(0,10)+'" disabled="1" onfocusout="admin.changeData(this);" readonly="1"/></td>';
+                               row += '<td>'+tbldata.sqldata[i].surname+' '+tbldata.sqldata[i].prename+'</td>';
+                               row += '<td><input type="time" id="hours_stampin_'+ i+'" value="'+tbldata.sqldata[i].stamp_in.substring(11,16)+'" disabled="1"/></td>';
+                               row += '<td><input type="time" id="hours_stampout_'+ i+'" value="'+((tbldata.sqldata[i].stamp_out != null)?tbldata.sqldata[i].stamp_out.substring(11,16):'')+'" onfocusout="admin.changeData(this);" disabled="1"/></td>';
+                               row +='</tr>';
+                               ntbldata += row;
+                       }
+                       document.getElementById("table_trackincomplete_tbody").innerHTML=ntbldata;
+               },
+               deleteRow: function(table,id){
+                       
+                       var xq = {"type":"exec","sql":"delete from "+ table+" WHERE id='"+ id +"';"};
+                       var ret = req.reqdata('POST','sqlite/exec',xq,null);
+                       if (ret){
+                               admin.loadpage(admin.lastpage);
+                       }
+               },
+               addRow: function(){
+                       
+               },
+               export_data: function(){
+                       
+               },
+               save_config: function(){
+                       
+               },
+               changeData: function(field){
+                       var dbfield = field.id.split("_");
+                       
+                       var where=[];
+                       for (var i in admin.currentrowdata){
+                               where.push( i + "='" +admin.currentrowdata[i] + "'");
+                       }
+                       var sql = "";
+                       var value= "'" + field.value + "'";
+                       if (field.type == "date"){
+                               value= "date('"+ field.value+"')";
+                       } else if (field.type == "number"){
+                               value= field.value;
+                       } else if (field.type == "checkbox"){
+                               if (field.checked == true){
+                                       value= "'1'";
+                               } else {
+                                       value= "null";
+                               }
+                               
+                       }
+                       if (field.value == ''){
+                               value = "null";
+                       }
+                       if (where.length > 0 ){
+                               sql = "UPDATE " + dbfield[0] +" SET " + dbfield[1] + "=" + value + " WHERE "+ where.join(" AND ") +";";
+                       } else {
+                               sql = "INSERT INTO " + dbfield[0] + " (" + dbfield[1] + ") VALUES (" + value + ");";
+                       }
+                       console.log(sql);
+                       var xu = {"type":"exec","sql":sql};
+                       var tbldata = req.reqdata('POST','sqlite/query',xu,req.asyncNoEvent);
+                       //console.log(field.type);
+                       //console.log(field.id + " " + field.tagName +  " " + field.type);
+                       
+               },
+               
+               load_select_staff: function(selected){
+                       document.getElementById("select_staff").innerHTML='';
+                       var xq = {"type":"querysorted","sql":"select id,prename,surname from staff order by surname,prename,id"};
+                       var cmbdata = req.reqdata('POST','sqlite/query',xq,null);
+                       var tcmbdata = '';
+                       for (var i in cmbdata.sqldata){
+                               tcmbdata += '<option value="'+cmbdata.sqldata[i].id+'">'+cmbdata.sqldata[i].surname+' '+cmbdata.sqldata[i].prename+'</option>';
+                       }
+                       document.getElementById("select_staff").innerHTML=tcmbdata;
+               },
+               setRowEditable: function(row){
+                       //console.log("set Editable!");
+                       if (row == admin.lastrow){
+                               return false;
+                       }
+                       if (admin.lastrow != null){
+                               var lins = admin.lastrow.getElementsByTagName('input');
+                               for (var z=0;z < lins.length; z++){
+                                       //console.log("YUppi " + z);
+                                       document.getElementById(lins[z].id).setAttribute('disabled',true);
+                               }
+                       }
+                       admin.currentrowdata = row.dataset;
+                       var ins = row.getElementsByTagName('input');
+                       for (var z=0;z < ins.length; z++){
+                               //console.log("Yeah " + z);
+                               if (document.getElementById(ins[z].id).getAttribute('readonly') == null){
+                                       document.getElementById(ins[z].id).removeAttribute('disabled');
+                               }
+                               
+                       }
+                       
+                       ////console.log(row.dataset);
+                       admin.lastrow = row;
+                       return true;
+               }
+       }
+window.onload = function() {
+         //console.log('window - onload');
+         admin.lastpage = 'page_track';
+         admin.loadpage('page_track');
+         var cdate = new Date();
+         //console.log(cdate.toISOString().substring(0,10));
+         document.getElementById("cmb_trackdate").value = cdate.toISOString().substring(0,10);
+         document.getElementById("cmb_trackmonth").value = cdate.toISOString().substring(0,7);
+         admin.load_select_staff(null);
+         var nav = navigator.userAgent
+         
+         if (nav.indexOf("Chrome/65") == -1){
+                 location.href='/supported.html';
+         }
+};
\ No newline at end of file
diff --git a/app/js/app.js b/app/js/app.js
new file mode 100644 (file)
index 0000000..b326e98
--- /dev/null
@@ -0,0 +1,99 @@
+var app={
+               interval: null,
+               intervaltime: 60,
+               currentscreen: 'scrloader',
+               showscreen: function screen(screenid){
+                       //console.log("old screen:" + app.currentscreen);
+                       document.getElementById(app.currentscreen).style.display = 'none';
+                       document.getElementById(screenid).style.display = 'block';
+                       app.currentscreen = screenid;
+                       //console.log("new screen:" + app.currentscreen);
+               },
+               checkIdle: function(){
+                       if (app.intervaltime < 0){
+                               location.href = "clock.html";
+                       } else {
+                               app.intervaltime = app.intervaltime - 1; 
+                       }
+               },
+               startCheckIdle: function(){
+                       window.setInterval(app.checkIdle(),1000);
+                       
+               },
+               stopCheckIdle: function(){
+                       window.clearInterval(app.interval);
+               },
+               database: 'hourtrax',
+               reqdata: function(method,url,data,callback){
+                       var host = '';
+                       var ret = null;
+                       var rdata = null;
+                       var async = true;
+                       if (callback == null){
+                               async = false;
+                       }
+                       if (location.protocol === 'file:'){
+                               host = 'http://localhost:6060';
+                       }
+                       var request = new XMLHttpRequest();
+                       if (typeof data == 'object'){
+                               var xdata = [];
+                               for (var i in data){
+                                        var value = '';
+                                        if (typeof(data[i]) == 'object'){
+                                                value = encodeURICOmponent(JSON.stringify(data[i]));
+                                        } else {
+                                                value = encodeURIComponent(data[i]);
+                                        }
+                                        xdata.push(i + "=" + value);
+                               }
+                               rdata = xdata.join("&");
+                       }else {
+                               rdata = data;
+                       }
+                       //console.log("Data to send: " + decodeURIComponent(rdata));
+                       var sendurl = host + '/' + url;
+                       if (method.toUpperCase() == 'GET'){
+                               sendurl = sendurl + '?' + rdata;
+                       }
+                       //console.log("sending URL: " + method + " => " +sendurl);
+                       request.open(method.toUpperCase(), sendurl, false);
+                       request.onload = function(){
+                               if (request.status >= 200 && request.status <= 400){
+                                       //console.log("Status returned: " + request.status + "resp:" + request.getResponseHeader("Content-Type"));
+                                       if (request.getResponseHeader("Content-Type").indexOf('application/json') == 0){
+                                               var xparse = JSON.parse(request.responseText);
+                                               ret = xparse.result;
+                                       }else {
+                                               ret = request.responseText;
+                                       }
+                                       ////console.log("data returned: " + request.responseText);
+                                       if (callback){
+                                               callback(ret);
+                                       }
+                                       
+                               } else {
+                                       //console.log("ServerERROR: " + request.status + "\n" + request.responseText);
+                                       alert("ServerERROR:" + request.status + "\n" + request.responseText);
+                               }
+                       };
+                       request.onerror = function(){
+                               //console.log("ERROR: connection ERROR\n" + url);
+                               alert("Connection ERROR!\n" + url);
+                       };
+                       if (method.toUpperCase() == 'POST'){
+                               request.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8');
+                               request.send(rdata);
+                       } else {
+                               //request.withCredentials = true;
+                               request.send();
+                       }
+                       return ret;
+               }
+}
+
+window.onload = function() {
+         //console.log('window - onload');
+         login.gotoUsers();
+};
+
diff --git a/app/js/database.js b/app/js/database.js
new file mode 100644 (file)
index 0000000..e4f904f
--- /dev/null
@@ -0,0 +1,71 @@
+var appdb = {
+               dbfile: null,
+               url: null,
+               dbquery: function(sQuery){
+                       var type='querysorted';
+                       var result= {sqldata:[]};
+                       //console.log(this.url + 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery));
+                       //dump(this.url + 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery) + "\n");
+                       $.ajax({
+                               encoding:"UTF-8",
+                               method: "POST",
+                               url:this.url + '/query', 
+                               data: 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery),
+                               crossDomain: true,
+                               success: function (data){
+                                       //dump(data + "\n");
+                                               result=data.result;
+                                       },
+                                       error: function(data){
+                                               alert("Error:" + JSON.stringify(data));
+                                               console.log("Error:" + JSON.stringify(data));
+                                       },
+                               async:false
+                       });
+                       return result;
+               },
+               dbqueryarray: function(sQuery){
+                       var type='queryarray';
+                       var result= {sqldata:[]};
+                       //console.log(this.url + 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery));
+                       //alert(this.url + 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery) + "\n");
+                       $.ajax({
+                               encoding:"UTF-8",
+                               method: "POST",
+                               url:this.url + '/query', 
+                               data: 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery),
+                               crossDomain: true,
+                               success: function (data){
+                                               result=data.result;
+                                       },
+                               error: function(data){
+                                       alert("Error:" + JSON.stringify(data));
+                                       console.log("Error:" + JSON.stringify(data));
+                               },
+                               async:false
+                       });
+                       return result;
+               },
+               dbexec: function(sQuery){
+                       var type='exec';
+                       var result= {sqldata:[]};
+                       //dump(this.url + 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery) + "\n");
+                       //console.log(this.url + '&db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery) );
+                       $.ajax({
+                               encoding:"UTF-8",
+                               method: "POST",
+                               url:this.url + '/exec', 
+                               data: 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery),
+                               crossDomain: true,
+                               success: function (data){
+                                               result=data.result;
+                                       },
+                                       error: function(data){
+                                               alert("Error:" + JSON.stringify(data));
+                                               console.log("Error:" + JSON.stringify(data));
+                                       },
+                               async:false
+                       });
+                       return result;
+               }
+}
\ No newline at end of file
diff --git a/app/js/jquery-3.2.1.min.js b/app/js/jquery-3.2.1.min.js
new file mode 100644 (file)
index 0000000..644d35e
--- /dev/null
@@ -0,0 +1,4 @@
+/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=Array.isArray(d)))?(e?(e=!1,f=c&&Array.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,N,e),g(f,c,O,e)):(f++,j.call(a,g(f,c,N,e),g(f,c,O,e),g(f,c,N,c.notifyWith))):(d!==N&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S),
+a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},U=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function V(){this.expando=r.expando+V.uid++}V.uid=1,V.prototype={cache:function(a){var b=a[this.expando];return b||(b={},U(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){Array.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(L)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var W=new V,X=new V,Y=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Z=/[A-Z]/g;function $(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:Y.test(a)?JSON.parse(a):a)}function _(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Z,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=$(c)}catch(e){}X.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return X.hasData(a)||W.hasData(a)},data:function(a,b,c){return X.access(a,b,c)},removeData:function(a,b){X.remove(a,b)},_data:function(a,b,c){return W.access(a,b,c)},_removeData:function(a,b){W.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=X.get(f),1===f.nodeType&&!W.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),_(f,d,e[d])));W.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){X.set(this,a)}):T(this,function(b){var c;if(f&&void 0===b){if(c=X.get(f,a),void 0!==c)return c;if(c=_(f,a),void 0!==c)return c}else this.each(function(){X.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=W.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var aa=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ba=new RegExp("^(?:([+-])=|)("+aa+")([a-z%]*)$","i"),ca=["Top","Right","Bottom","Left"],da=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},ea=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function fa(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&ba.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var ga={};function ha(a){var b,c=a.ownerDocument,d=a.nodeName,e=ga[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),ga[d]=e,e)}function ia(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=W.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&da(d)&&(e[f]=ha(d))):"none"!==c&&(e[f]="none",W.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ia(this,!0)},hide:function(){return ia(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){da(this)?r(this).show():r(this).hide()})}});var ja=/^(?:checkbox|radio)$/i,ka=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c<d;c++)W.set(a[c],"globalEval",!b||W.get(b[c],"globalEval"))}var pa=/<|&#?\w+;/;function qa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(pa.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ka.exec(f)||["",""])[1].toLowerCase(),i=ma[h]||ma._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g,h=[],i=b.delegateCount,j=a.target;if(i&&j.nodeType&&!("click"===a.type&&a.button>=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c<i;c++)d=b[c],e=d.selector+" ",void 0===g[e]&&(g[e]=d.needsContext?r(e,this).index(j)>-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i<b.length&&h.push({elem:j,handlers:b.slice(i)}),h},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==xa()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===xa()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&B(this,"input"))return this.click(),!1},_default:function(a){return B(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?va:wa,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:wa,isPropagationStopped:wa,isImmediatePropagationStopped:wa,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=va,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=va,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=va,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&sa.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&ta.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return ya(this,a,b,c,d)},one:function(a,b,c,d){return ya(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=wa),this.each(function(){r.event.remove(this,a,c,b)})}});var za=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/<script|<style|<link/i,Ba=/checked\s*(?:[^=]|=\s*.checked.)/i,Ca=/^true\/(.*)/,Da=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}X.hasData(a)&&(h=X.access(a),i=r.extend({},h),X.set(b,i))}}function Ia(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ja.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ja(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,na(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Ga),l=0;l<i;l++)j=h[l],la.test(j.type||"")&&!W.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Da,""),k))}return a}function Ka(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(na(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&oa(na(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(za,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d<e;d++)Ia(f[d],g[d]);if(b)if(c)for(f=f||na(a),g=g||na(h),d=0,e=f.length;d<e;d++)Ha(f[d],g[d]);else Ha(a,h);return g=na(h,"script"),g.length>0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(na(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ja(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(na(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var La=/^margin/,Ma=new RegExp("^("+aa+")(?!px)[a-z%]+$","i"),Na=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",ra.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,ra.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Oa(a,b,c){var d,e,f,g,h=a.style;return c=c||Na(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&Ma.test(g)&&La.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Pa(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Qa=/^(none|table(?!-c[ea]).+)/,Ra=/^--/,Sa={position:"absolute",visibility:"hidden",display:"block"},Ta={letterSpacing:"0",fontWeight:"400"},Ua=["Webkit","Moz","ms"],Va=d.createElement("div").style;function Wa(a){if(a in Va)return a;var b=a[0].toUpperCase()+a.slice(1),c=Ua.length;while(c--)if(a=Ua[c]+b,a in Va)return a}function Xa(a){var b=r.cssProps[a];return b||(b=r.cssProps[a]=Wa(a)||a),b}function Ya(a,b,c){var d=ba.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Za(a,b,c,d,e){var f,g=0;for(f=c===(d?"border":"content")?4:"width"===b?1:0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+ca[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+ca[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+ca[f]+"Width",!0,e))):(g+=r.css(a,"padding"+ca[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+ca[f]+"Width",!0,e)));return g}function $a(a,b,c){var d,e=Na(a),f=Oa(a,b,e),g="border-box"===r.css(a,"boxSizing",!1,e);return Ma.test(f)?f:(d=g&&(o.boxSizingReliable()||f===a.style[b]),"auto"===f&&(f=a["offset"+b[0].toUpperCase()+b.slice(1)]),f=parseFloat(f)||0,f+Za(a,b,c||(g?"border":"content"),d,e)+"px")}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Oa(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=Ra.test(b),j=a.style;return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:j[b]:(f=typeof c,"string"===f&&(e=ba.exec(c))&&e[1]&&(c=fa(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(j[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i?j.setProperty(b,c):j[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b),i=Ra.test(b);return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Oa(a,b,d)),"normal"===e&&b in Ta&&(e=Ta[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Qa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?$a(a,b,d):ea(a,Sa,function(){return $a(a,b,d)})},set:function(a,c,d){var e,f=d&&Na(a),g=d&&Za(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=ba.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Ya(a,c,g)}}}),r.cssHooks.marginLeft=Pa(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Oa(a,"marginLeft"))||a.getBoundingClientRect().left-ea(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+ca[d]+b]=f[d]||f[d-2]||f[0];return e}},La.test(a)||(r.cssHooks[a+b].set=Ya)}),r.fn.extend({css:function(a,b){return T(this,function(a,b,c){var d,e,f={},g=0;if(Array.isArray(b)){for(d=Na(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f<g;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&da(a),q=W.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],cb.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=W.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ia([a],!0),j=a.style.display||j,k=r.css(a,"display"),ia([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=W.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ia([a],!0),m.done(function(){p||ia([a]),W.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=hb(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],Array.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=kb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=ab||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;g<i;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),f<1&&i?c:(i||h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:ab||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;c<d;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);f<g;f++)if(d=kb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,hb,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j}r.Animation=r.extend(kb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return fa(c.elem,a,ba.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(L);for(var c,d=0,e=a.length;d<e;d++)c=a[d],kb.tweeners[c]=kb.tweeners[c]||[],kb.tweeners[c].unshift(b)},prefilters:[ib],prefilter:function(a,b){b?kb.prefilters.unshift(a):kb.prefilters.push(a)}}),r.speed=function(a,b,c){var d=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off?d.duration=0:"number"!=typeof d.duration&&(d.duration in r.fx.speeds?d.duration=r.fx.speeds[d.duration]:d.duration=r.fx.speeds._default),null!=d.queue&&d.queue!==!0||(d.queue="fx"),d.old=d.complete,d.complete=function(){r.isFunction(d.old)&&d.old.call(this),d.queue&&r.dequeue(this,d.queue)},d},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(da).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=kb(this,r.extend({},a),f);(e||W.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=W.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&db.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=W.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;b<g;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gb(b,!0),a,d,e)}}),r.each({slideDown:gb("show"),slideUp:gb("hide"),slideToggle:gb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(ab=r.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||r.fx.stop(),ab=void 0},r.fx.timer=function(a){r.timers.push(a),r.fx.start()},r.fx.interval=13,r.fx.start=function(){bb||(bb=!0,eb())},r.fx.stop=function(){bb=null},r.fx.speeds={slow:600,fast:200,_default:400},r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var lb,mb=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return T(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),
+null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!B(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("<script>").prop({charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&f("error"===a.type?404:200,a.type)}),d.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Tb=[],Ub=/(=)\?(?=&|$)|\?\?/;r.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Tb.pop()||r.expando+"_"+ub++;return this[a]=!0,a}}),r.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Ub.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ub.test(b.data)&&"data");if(h||"jsonp"===b.dataTypes[0])return e=b.jsonpCallback=r.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Ub,"$1"+e):b.jsonp!==!1&&(b.url+=(vb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||r.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?r(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Tb.push(e)),g&&r.isFunction(f)&&f(g[0]),g=f=void 0}),"script"}),o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=C.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=qa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.fn.load=function(a,b,c){var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=pb(a.slice(h)),a=a.slice(0,h)),r.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&r.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?r("<div>").append(r.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},r.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){r.fn[b]=function(a){return this.on(b,a)}}),r.expr.pseudos.animated=function(a){return r.grep(r.timers,function(b){return a===b.elem}).length},r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),b=f.ownerDocument,c=b.documentElement,e=b.defaultView,{top:d.top+e.pageYOffset-c.clientTop,left:d.left+e.pageXOffset-c.clientLeft}):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),B(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||ra})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return T(this,function(a,d,e){var f;return r.isWindow(a)?f=a:9===a.nodeType&&(f=a.defaultView),void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Pa(o.pixelPosition,function(a,c){if(c)return c=Oa(a,b),Ma.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return T(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.holdReady=function(a){a?r.readyWait++:r.ready(!0)},r.isArray=Array.isArray,r.parseJSON=JSON.parse,r.nodeName=B,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var Vb=a.jQuery,Wb=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=Wb),b&&a.jQuery===r&&(a.jQuery=Vb),r},b||(a.jQuery=a.$=r),r});
diff --git a/app/js/login.js b/app/js/login.js
new file mode 100644 (file)
index 0000000..d5ab186
--- /dev/null
@@ -0,0 +1,148 @@
+var login = {
+               userid: null,
+               pinfield: null,
+               gotoLogin: function (btn){
+                       document.getElementById("reload").style.visibility = 'visible';
+                       document.getElementById("mnuviews").style.visibility = 'hidden';
+                       login.pinfield = 'pincode';
+                       console.log(typeof(btn));
+                       console.log(btn);
+                       app.intervaltime = 60;
+                       
+                       if (typeof(btn) == "object"){
+                               if (btn.sqldata){
+                                       document.getElementById("pinmessage").innerHTML = '';
+                                       document.getElementById('pincode').value = '';
+                                       document.getElementById("usergreeting1").innerHTML ="Bonjour, " + document.getElementById("btn_user_" + login.userid).getAttribute('data-prename') + " entrez votre code pin:";
+                               } else {
+                                       login.userid= btn.getAttribute('data-id');
+                                       document.getElementById("usergreeting1").innerHTML="Bonjour, " + btn.getAttribute('data-prename')  + "! Entrez votre code pin:";
+                                       document.getElementById("pinmessage").innerHTML = '';
+                                       app.startCheckIdle();
+                               }
+                               
+                       } else if ((typeof(btn) == "string") && (btn == 'notpasswd')) {
+                               document.getElementById("usergreeting1").innerHTML ="Bonjour, " + document.getElementById("btn_user_" + login.userid).getAttribute('data-prename') + " entrez votre code pin:";
+                               
+                       }
+                       app.showscreen('scruserpin');   
+               },
+               gotoNewLogin: function (btn){
+                       document.getElementById("reload").style.visibility = 'visible';
+                       document.getElementById("mnuviews").style.visibility = 'hidden';
+                       console.log("New Login!");
+                       console.log(btn);
+                       app.intervaltime = 60;
+                       if (btn != null){
+                               login.userid= btn.getAttribute('data-id');
+                               document.getElementById("usergreeting2").innerHTML="Bonjour, " + btn.getAttribute('data-prename')  + "! Entrez votre nouveau code pin:";
+                               app.startCheckIdle();
+                       }
+                       
+                       login.pinfield = "newpincode1";
+                       document.getElementById("newpincode1").value = "";
+                       document.getElementById("newpincode2").value = "";
+                       document.getElementById("pinmessage2").innerHTML = '';
+                       app.showscreen('scrnewuserpin');
+                       
+               },
+               gotoUsers: function(){
+                       var sqlq = "select id,prename,surname,case when pin is null then 'nopin' else null end as haspin, case when blocked is not null then 'blocked' else null end as isblocked from staff st left join (select idstaff,max(startdate) as startdate from contract where startdate <= CURRENT_DATE group by idstaff) co on (st.id=co.idstaff);";
+                       app.reqdata('POST','sqlite/query',{"type": "querysorted","sql":sqlq},login.loadUsers);
+                       
+               },
+               setPin:function (key){
+                       app.intervaltime = 60;
+                       if (key == 'COR'){
+                               document.getElementById('pincode').value = '';
+                               document.getElementById('newpincode1').value = '';
+                               document.getElementById('newpincode2').value = '';
+                               if (login.pinfield == "newpincode2"){
+                                       login.pinfield = "newpincode1";
+                               }
+                               document.getElementById("pinmessage").innerHTML = '';
+                               document.getElementById("pinmessage2").innerHTML = '';
+                       } else if (key == 'OK'){
+                               if (login.pinfield == 'pincode'){
+                                       app.showscreen('scrloader');
+                                       var xa = {"type":"querysorted","sql":"select id from staff where id='"+login.userid+"' and pin='"+document.getElementById("pincode").value+"';"};
+                                               app.reqdata('POST','sqlite/query',xa,login.getAccess);
+                               } else if (login.pinfield == 'newpincode1'){
+                                       login.pinfield = 'newpincode2';
+                               } else if (login.pinfield == 'newpincode2'){
+                                       var pin1 = document.getElementById("newpincode1").value;
+                                       var pin2 = document.getElementById("newpincode2").value;
+                                       if ((pin1.length < 4) || (pin2.length < 4)){
+                                               document.getElementById("pinmessage2").innerHTML = 'minimum 4 nombre requis!';
+                                       } 
+                                       else if (pin1 != pin2){
+                                               document.getElementById("pinmessage2").innerHTML = 'les codes pin sont pas identiques!';
+                                       } else {
+                                               app.showscreen('scrloader');
+                                               var xa = {"type":"exec","sql":"UPDATE staff SET pin='"+ pin1 +"' WHERE id='"+login.userid+"';"};
+                                                       app.reqdata('POST','sqlite/exec',xa,login.gotoLogin);
+                                       }
+                               }
+                               
+                       } else {
+                               //console.log("Add number " + key);
+                               var cobj = document.getElementById(login.pinfield);
+                               var cpin = cobj.value;
+                               
+                               cobj.value = cpin + key;
+                       }
+               },
+               getAccess: function(data){
+                       app.intervaltime = 60;
+                       console.log(data);
+                       console.log('getAccess');
+                       if ((data.sqldata) && (data.sqldata[0])){
+                               var xa = {"type":"exec","sql":"update staff set loginattemps=null where id='"+login.userid+"';"};
+                               var ret = app.reqdata('POST','sqlite/exec',xa,null);
+                               console.log("Access OK!")
+                               console.log(ret);
+                               if (ret){
+                                       var xq = {"type":"querysorted","sql":"select last_id, case when current_stamp_in is not null then strftime('%d.%m.%Y<br/>%H:%M',current_stamp_in) else null end as current_stamp_in, case when current_stamp_out is not null then strftime('%d.%m.%Y<br/>%H:%M',current_stamp_out) else  null end as current_stamp_out,cast((daytrack  / 3600.0) as integer) || ':' || case when cast(((daytrack % 3600.0) / 60.0) as integer) < 10 then '0'  else '' end || cast(((daytrack % 3600.0) / 60.0) as integer) as monthtrack from (select max(id) as last_id, case when date(max(stamp_in)) = CURRENT_DATE then max(stamp_in) else null end as current_stamp_in,case when date(max(stamp_out)) = CURRENT_DATE then max(stamp_out) else null end as current_stamp_out,sum(case when stamp_out is not null then cast(strftime('%s',stamp_out) as integer) - cast(strftime('%s',stamp_in) as integer) else 0 end) as daytrack from hours where idstaff='"+ login.userid+"' and stamp_in between date('now','start of month') and date('now','start of month','+1 month','-1 day'));"};
+                                       app.reqdata('POST','sqlite/query',xq,track.loadtrackscreen);
+                               }
+                       } else {
+                               var xa = {"type":"exec","sql":"update staff set loginattemps=(select case when max(loginattemps) is null then 1 else loginattemps+1 end as newattemps from staff where id='"+login.userid+"') where id='"+login.userid+'";'};
+                               app.reqdata('POST','sqlite/exec',xa,null);
+                               document.getElementById("pinmessage").innerHTML = 'code pin pas correcte!';
+                               login.gotoLogin('notpassed');
+                       }
+                       
+                       //console.log(data);
+//                     if(login.curpincode == '1234'){
+//                             
+//                     } else {
+//                             login.curpincode = '';
+//                             login.pinenc = '';
+//                             document.getElementById("pincode").innerHTML = login.pinenc;
+//                             document.getElementById("pinmessage").innerHTML = 'Code pas correcte';
+//                     }
+               },
+               loadUsers: function(data){
+                       document.getElementById("reload").style.visibility = 'hidden';
+                       document.getElementById("mnuviews").style.visibility = 'hidden';
+                       var ulist = document.getElementById("scruserlist");
+                       ulist.innerHTML = '';
+                       console.log('loadUsers');
+                       //console.log("TEST" + data);
+                       if (data != null){
+                               var btns = '';
+                               for (var i in data.sqldata){
+                                       console.log(data.sqldata[i]);
+                                       var disabled = ";"
+                                       if (data.sqldata[i].isblocked != null){
+                                               diabled='disabled="1"';
+                                       }
+                                       btns += '<button class="user" onclick="login.goto'+((data.sqldata[i].haspin == null)?"":"New")+'Login(this);" id="btn_user_'+data.sqldata[i].id+'" data-id="'+ data.sqldata[i].id+'" data-prename="'+data.sqldata[i].prename+'" data-surname="'+data.sqldata[i].surname+'" data-haspin="'+data.sqldata[i].haspin+'" '+ disabled +'>'+ data.sqldata[i].prename+'<br/>'+ data.sqldata[i].surname+'</button>';
+                               }
+                               ulist.innerHTML = btns;
+                       }
+                       app.stopCheckIdle();
+                       app.showscreen('scruserlist');
+               }
+}
+
diff --git a/app/js/request.js b/app/js/request.js
new file mode 100644 (file)
index 0000000..119ba84
--- /dev/null
@@ -0,0 +1,105 @@
+var req = {
+               reqdata: function(method,url,data,callback){
+                       var host = '';
+                       var ret = null;
+                       var rdata = null;
+                       var async = true;
+                       if (callback == null){
+                               async = false;
+                       }
+                       if (location.protocol === 'file:'){
+                               host = 'http://localhost:6060';
+                       }
+                       else {
+                               host = location.origin;
+                       }
+                       var request = new XMLHttpRequest();
+                       if (typeof data == 'object'){
+                               var xdata = [];
+                               for (var i in data){
+                                        var value = '';
+                                        if (typeof(data[i]) == 'object'){
+                                                value = encodeURIComponent(JSON.stringify(data[i]));
+                                        } else {
+                                                value = encodeURIComponent(data[i]);
+                                        }
+                                        xdata.push(i + "=" + value);
+                               }
+                               rdata = xdata.join("&");
+                       }else {
+                               rdata = data;
+                       }
+                       //console.log("Data to send: " + decodeURIComponent(rdata));
+                       var sendurl = host + '/' + url;
+                       if (method.toUpperCase() == 'GET'){
+                               sendurl = sendurl + '?' + rdata;
+                       }
+                       //console.log("sending URL: " + method + " => " +sendurl);
+                       request.open(method.toUpperCase(), sendurl, false);
+                       request.onload = function(){
+                               if (request.status >= 200 && request.status <= 400){
+                                       //console.log("Status returned: " + request.status + "resp:" + request.getResponseHeader("Content-Type"));
+                                       if (request.getResponseHeader("Content-Type").indexOf('application/json') == 0){
+                                               var xparse = JSON.parse(request.responseText);
+                                               ret = xparse.result;
+                                       }else if (request.getResponseHeader("Content-Type").indexOf('application/vnd.ms-excel') == 0){
+                                               var filename = "";
+                                       var disposition = request.getResponseHeader('Content-Disposition');
+                                       if (disposition && disposition.indexOf('attachment') !== -1) {
+                                           var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
+                                           var matches = filenameRegex.exec(disposition);
+                                           if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
+                                       }
+                                       var type = request.getResponseHeader('Content-Type');
+                                       var blob = new Blob([this.response], { type: type });
+                                       var URL = window.URL;
+                                       var downloadUrl = URL.createObjectURL(blob);
+                                       if (filename) {
+                                               var a = document.createElement("a");
+                                               if (typeof a.download === 'undefined') {
+                                                       window.location = downloadUrl;
+                                               } else {
+                                                       a.href = downloadUrl;
+                                                       a.download = filename;
+                                                       document.body.appendChild(a);
+                                                       a.click();
+                                               }
+                                       } else {
+                                               window.location = downloadUrl;
+                                       }
+                                       //setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100);
+                                       }
+                                       else {
+                                               ret = request.responseText;
+                                               console.log(ret);
+                                       }
+                                       ////console.log("data returned: " + request.responseText);
+                                       if (callback){
+                                               callback(ret);
+                                       }
+                                       
+                               } else {
+                                       //console.log("ServerERROR: " + request.status + "\n" + request.responseText);
+                                       alert("ServerERROR:" + request.status + "\n" + request.responseText);
+                               }
+                       };
+                       request.onerror = function(){
+                               //console.log("ERROR: connection ERROR\n" + url);
+                               alert("Connection ERROR!\n" + url);
+                       };
+                       if (method.toUpperCase() == 'POST'){
+                               request.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8');
+                               request.send(rdata);
+                       } else {
+                               //request.withCredentials = true;
+                               request.send();
+                       }
+                       return ret;
+               },
+               asyncNoEvent: function(data){
+                       console.log("query done");
+                       console.log(data);
+                       console.log("done");
+               }
+               
+}
\ No newline at end of file
diff --git a/app/js/sysconfig.js b/app/js/sysconfig.js
new file mode 100644 (file)
index 0000000..80efbb0
--- /dev/null
@@ -0,0 +1,118 @@
+var sysconfig={
+               lastsection: null,
+               showsection: function(){
+                       var sectionsel = document.getElementById("select_sysconfig").value;
+                       if (sysconfig.lastsection != null){
+                               document.getElementById(sysconfig.lastsection).style.display = 'none';
+                               document.getElementById(sectionsel).style.display = 'block';
+                               sysconfig.lastsection = sectionsel;
+                       } else {
+                               document.getElementById("sysexport").style.display = 'block';
+                               sysconfig.lastsection = "sysexport";
+                       }
+                       console.log(sysconfig.lastsection);
+                       if (sysconfig.lastsection == 'sysaccess'){
+                               req.reqdata("GET",'app/getaccess','',sysconfig.getSystemUser);
+//                             var cdate = new Date();
+//                             var bdate= new Date(cdate.getFullYear(),cdate.getMonth()+1,1);
+//                             var strbdate = bdate.getFullYear() + "-" + ((bdate.getMonth() < 10)?"0":"") + bdate.getMonth() + "-"+ ((bdate.getDate() < 10)?"0":"") + bdate.getDate(); 
+//                             var edate= new Date(bdate.getFullYear(),bdate.getMonth() + 1,1);
+//                             edate.setDate(edate.getDate() - 1);
+//                             var stredate = edate.getFullYear() + "-" + ((edate.getMonth() < 10)?"0":"") + edate.getMonth() + "-"+ ((edate.getDate() < 10)?"0":"") + edate.getDate(); 
+//                             document.getElementById('cmb_exportdatestart').value=strbdate;
+//                             document.getElementById('cmb_exportdatestop').value=stredate;
+                       } else if (sysconfig.lastsection == 'syssync'){
+                               req.reqdata("GET",'app/preferences/hourtrax','',sysconfig.getSyncData);
+                       }
+                       
+               },
+               getSyncData: function(data){
+                       console.log("getSyncData");
+                       console.log(data);
+                       //var syncdata={ "sync":{}};
+                       var section = document.getElementById("syssync");
+                       var elements = section.querySelectorAll('input');
+                       
+                       //console.log(syncdata);
+//                     for (var e in elements){
+//                             console.log(e);
+//                     }
+                       if ((data) && (data.sync)){
+                               elements.forEach( function (index, value) {
+                                         var ident = index.id.substring(14);
+                                         if (data.sync[ident] != null){
+                                                 index.value=data.sync[ident];
+                                         }
+                               });
+                       }
+               },
+               setSyncData: function(){
+                       var section = document.getElementById("syssync");
+                       var elements = section.querySelectorAll('input');
+                       var syncdata={ "sync":{}};
+                       elements.forEach( function (index, value) {
+                                 var ident = index.id.substring(14);
+                                 syncdata.sync[ident] = ((index.value)?index.value:null);
+                       });
+                       console.log(syncdata);
+                       req.reqdata("POST",'app/preferences/hourtrax',syncdata,req.asyncNoEvent);
+               },
+               getSystemUser: function(data){
+                       console.log(data);
+                       document.getElementById("sysconfig_user").value = data.login;
+               },
+               setSystemUser: function(){
+                       var pwd1 = document.getElementById("sysconfig_pwd1").value;
+                       var pwd2 = document.getElementById("sysconfig_pwd2").value;
+                       var user = document.getElementById("sysconfig_user").value;
+                       if (user == ""){
+                               alert("Nom du utilisateir requis!");
+                               return;
+                       }
+                       if (pwd1 != pwd2){
+                               alert("les mot de passe ne sont pas identiques");
+                               return;
+                       }
+                       if (pwd1.length < 8){
+                               alert("le mot de passe doit avoir au moin 8 charactères!");
+                               return;
+                       }
+                       var xa = {"login":user,"passwd":pwd1};
+                       req.reqdata('POST','app/setaccess',xa,req.asyncNoEvent);
+               },
+               setNetworkConfig: function(){
+                       var hostname  = document.getElementById("sysconfig_hostname").value;
+                       var wlanssid  = document.getElementById("sysconfig_wlanssid").value;
+                       var wlanpwd  = document.getElementById("sysconfig_wlanpwd").value;
+                       var xa = {"hostname":hostname,"passwd":pwd1};
+                       req.reqdata('POST','app/access',xa,req.asyncNoEvent);
+               },
+               getNetworkConfig: function(){
+                       var ssids = ['wlan 1','wlan 2'];
+                       var hostname = "";//getNetworkSSIDs
+                       var currentssid = '';
+                       //gethostname
+                       document.getElementById("sysconfig_hostname").value = "";
+                       document.getElementById("sysconfig_wlanssid").innerHTML  ="";
+                       var strssids ="";
+                       for (var i in ssids){
+                               strssids += '<option value="'+ ssids[i] +'">'+ ssids[i] + '</option>'; 
+                       }
+                       document.getElementById("sysconfig_wlanssid").innerHTML = strssids;
+                       document.getElementById("sysconfig_wlanssid").value = currentssid;
+                       document.getElementById("sysconfig_wlanpwd").value = "";
+               },
+               
+               exportdata: function(){
+                       var bdate = document.getElementById('cmb_exportdatestart').value;
+                       var edate = document.getElementById('cmb_exportdatestop').value;
+                       if (bdate == null || edate == null || bdate == '' || edate==''){
+                               alert("Dates bornes requis!");
+                               return;
+                       };
+                       var xa = {"type":"CSV","sql":"select st.surname || ' ' ||st.prename as name, hr.stamp_in as entry, hr.stamp_out as exit from hours hr left join staff st on (st.id=hr.idstaff) where hr.stamp_in between date('" + bdate+"') and date('" + edate+"') and hr.stamp_out between date('" + bdate+"') and date('" + edate+"');"}
+               
+                       req.reqdata("POST",'sqlite/export',xa,null);
+                       
+               }
+}
\ No newline at end of file
diff --git a/app/js/track.js b/app/js/track.js
new file mode 100644 (file)
index 0000000..22a72dc
--- /dev/null
@@ -0,0 +1,57 @@
+var track = {
+               trackid: null,
+               setTrack: function(type){
+                       app.showscreen('scrloader');
+                       console.log(type);
+                       var tracksql = "";
+                       if (type == 'in'){
+                               tracksql = "insert into hours (idstaff,stamp_in) VALUES ('"+ login.userid+"',CURRENT_TIMESTAMP);";
+                       } else if (type == 'out'){
+                               tracksql = "update hours set stamp_out=CURRENT_TIMESTAMP where idstaff='"+ login.userid+"' and id='"+track.trackid+"';";
+                       }
+                       var xt = {"type":"exec","sql":tracksql};
+                       app.reqdata('POST','sqlite/exec',xt,track.loadsuccessscreen);
+               },
+               loadtrackscreen: function(data){
+                       
+                       console.log('loadtrackscreen');
+                       document.getElementById("mnuviews").style.visibility = 'visible';
+                       console.log(data);
+                       if (data) {
+                               var btn=document.getElementById("btn_user_" + login.userid); 
+                               document.getElementById("usergreeting3").innerHTML = btn.getAttribute("data-prename") + " " + btn.getAttribute("data-surname"); 
+                               document.getElementById("monthhours").innerHTML = data.sqldata[0].monthtrack + "h";
+                               document.getElementById("btntrackin").setAttribute("disabled",false);
+                               document.getElementById("btntrackout").setAttribute("disabled",false);
+                               document.getElementById("lasttrackin").innerHTML = '&nbsp;<br/>&nbsp;';
+                               document.getElementById("lasttrackout").innerHTML = '&nbsp;<br/>&nbsp;';
+                               track.trackid = data.sqldata[0].last_id;
+                               if (data.sqldata[0].current_stamp_in != null){
+                                       document.getElementById("lasttrackin").innerHTML = data.sqldata[0].current_stamp_in;
+                               }
+                               if (data.sqldata[0].current_stamp_out != null){
+                                       document.getElementById("lasttrackout").innerHTML = data.sqldata[0].current_stamp_out;
+                               }
+                               if ((data.sqldata[0].current_stamp_in == null) && (data.sqldata[0].current_stamp_out == null)){
+                                       document.getElementById("btntrackin").removeAttribute("disabled");
+                               } else if ((data.sqldata[0].current_stamp_in != null) && (data.sqldata[0].current_stamp_out == null)){
+                                       document.getElementById("btntrackout").removeAttribute("disabled");
+                               } else if ((data.sqldata[0].current_stamp_in != null) && (data.sqldata[0].current_stamp_out != null)) {
+                                       if (data.sqldata[0].current_stamp_in > data.sqldata[0].current_stamp_out){
+                                               document.getElementById("btntrackout").removeAttribute("disabled");
+                                       } else {
+                                               document.getElementById("btntrackin").removeAttribute("disabled");
+                                       }
+                               }
+                       }
+                       //document.getElementById("usergreeting3").value = ""
+                       app.showscreen('scrtimetracker');
+               },
+               loadsuccessscreen: function(data){
+                       console.log(data);
+                       document.getElementById("statusmsg").innerHTML = "L'heure est enregistrĂ©";
+                       app.showscreen('scrstatus');
+                       track.trackid = null;
+                       setTimeout("location.href='clock.html'",3000);
+               }
+}
\ No newline at end of file
diff --git a/app/main.js b/app/main.js
new file mode 100644 (file)
index 0000000..525d492
--- /dev/null
@@ -0,0 +1,85 @@
+const {app, BrowserWindow} = require('electron')
+const path = require('path')
+const url = require('url')
+const os = require('os')
+const http = require('http')
+const dialog = require('electron').dialog
+const {ipcMain} = require('electron')
+//var child = require('child_process').execFile;
+
+// Keep a global reference of the window object, if you don't, the window will
+// be closed automatically when the JavaScript object is garbage collected.
+let win
+
+function createWindow () {
+       var executablePath = "";
+       var parameters = [];
+//     if (os.platform() == "win32"){
+//             executablePath = "C:\\Strawberry\\perl\\bin\\perl.exe";
+//             parameters = ["C:\\Users\\ksaff\\Workspace\\creorga\\Tools\\Creorgasrv.pl"];
+//     } else { //os.platform() == "darwin"
+//             executablePath = "/usr/bin/perl";
+//             parameters = ["/Users/kilian/Workspace/creorga/Tools/Creorgasrv.pl"];
+//     }
+       win = new BrowserWindow({show: false})
+       win.setMenu(null)
+       win.maximize()
+       win.setFullScreen(true)
+//     child(executablePath, parameters, function(err, data) {
+//         if(err){
+//             console.error('stderr',err);
+//         } else {
+//             var test = app.getPath('USERPROFILE');
+//             console.log("Path:" + test);
+//             console.log(data.toString());
+//                 win.show()
+//         } 
+//             
+//     });
+       
+  // Create the browser window.
+  
+  
+  
+  // and load the index.html of the app.
+  win.loadURL(url.format({
+    pathname: path.join(__dirname, 'clock.html'),
+    protocol: 'file:'
+  }))
+  
+  // Open the DevTools.
+  //win.webContents.openDevTools()
+
+  // Emitted when the window is closed.
+  win.on('closed', () => {
+    // Dereference the window object, usually you would store windows
+    // in an array if your app supports multi windows, this is the time
+    // when you should delete the corresponding element.
+    win = null
+  })
+}
+
+// This method will be called when Electron has finished
+// initialization and is ready to create browser windows.
+// Some APIs can only be used after this event occurs.
+app.on('ready', createWindow)
+
+// Quit when all windows are closed.
+app.on('window-all-closed', () => {
+//     http.get('http://localhost:6060/openvpn/disconnect', (resp) => {
+//             resp.on('end',() => {console.log("vpn disconnected!")});
+//     });
+  // On macOS it is common for applications and their menu bar
+  // to stay active until the user quits explicitly with Cmd + Q
+  if (process.platform !== 'darwin') {
+    app.quit()
+  }
+})
+
+app.on('activate', () => {
+  // On macOS it's common to re-create a window in the app when the
+  // dock icon is clicked and there are no other windows open.
+  if (win === null) {
+    createWindow()
+  }
+})
diff --git a/app/package.json b/app/package.json
new file mode 100644 (file)
index 0000000..5d6bda3
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "name"    : "Hourtrax",
+  "version" : "1.0",
+  "main"    : "main.js"
+}
\ No newline at end of file
diff --git a/app/supported.html b/app/supported.html
new file mode 100644 (file)
index 0000000..4fc8a27
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Hourtrax - Supported Browsers</title>
+</head>
+<body>
+       <h1>Only Google Chrome Browser is supported!</h1>
+       <p> minimum Version: 65.*<br/>Dowmload Google Chrome here: <br/><a  href="https://www.google.com/chrome/"><img src="https://www.google.com/chrome/assets/common/images/chrome_logo_2x.png" style="border: 1px solid silver" alt="Download Google Chrome"></a></p>
+       
+</body>
+</html>
\ No newline at end of file
diff --git a/app/test.html b/app/test.html
new file mode 100644 (file)
index 0000000..a758cc5
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test</title>
+</head>
+<body>
+       <div id="data"></div>
+       <progress max="100" min="0">
+<script type="text/javascript">
+window.onload = function() {
+         getlocation();
+};
+
+function getlocation(){
+       var test = "location.hash =" + location.hash + "<br/>";
+       test += "location.host = " + location.host + "<br/>";
+       test += "location.hostname = " + location.hostname + "<br/>";
+       test += "location.href = " + location.href + "<br/>";
+       test += "location.origin = " + location.origin + "<br/>";
+       test += "location.pathname = " + location.pathname + "<br/>";
+       test += "location.port = " + location.port + "<br/>";
+       test += "location.protocol = " + location.protocol + "<br/>";
+       test += "location.search = " + location.search + "<br/>";
+       document.getElementById("data").innerHTML = test;
+}
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/bin/Hourtraxsrv.pl b/bin/Hourtraxsrv.pl
new file mode 100644 (file)
index 0000000..18f4ec4
--- /dev/null
@@ -0,0 +1,103 @@
+#!/usr/bin/perl
+use strict;
+use File::Basename;
+use Getopt::Long;
+use Time::HiRes;
+use Data::Dumper;
+use lib ($ENV{HOME}.'/perl5/lib/perl5');
+use FindBin qw($RealBin); 
+use lib $RealBin;
+
+use Plack::Builder;
+use Plack::App::File;
+use Plack::App::WrapCGI;
+use Plack::Middleware::Auth::Basic;
+use Plack::Middleware::DirIndex; 
+use Plack::Request; 
+use Plack::Runner;
+use Module::Service;
+#use Module::Test;
+use Module::SQLite; 
+use Module::FileSystem; 
+use Module::System; 
+
+print $^O."\n";
+print dirname($RealBin)."\n";
+my $datadir = dirname($RealBin).'/data';
+my $webdir = dirname($RealBin).'/web';
+print "Datadir:".$datadir."\n"; 
+my @match =  grep { /par-.*inc$/} @INC;
+
+if ($^O ne "MSWin32"){
+       use POSIX qw(setsid);
+       open STDIN, '/dev/null'   or die "Can't read /dev/null: $!";
+    open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
+    open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!";
+    
+       die "cannot start process $!" unless defined (my $child = fork);
+       exit 0 if $child;
+       setsid() or die "cannot start new session $!";
+       chdir('/');
+       umask(0);
+       system("echo ".$$." > /home/dks/data/hourtrax.pid");
+}
+
+my $basedir = dirname($0);
+if (scalar(@match) > 0){
+       $basedir = $match[0];
+}
+
+my $cfgpath = "";
+#print "BASEDIR:".$basedir."\n";
+
+
+sub version {
+    require Twiggy;
+    print "Twiggy $Twiggy::VERSION\n";
+}
+
+
+
+my $name = basename($0);
+$name =~ s/srv\.pl$//;
+$name =~ s/srv\.exe$//;
+$name = lc($name);
+
+sub authen_cb {
+      my($username, $password, $env) = @_;
+      my $auth = 0;
+      #print "Check AUTH\n";
+      if (-e $datadir.'/'.$name.'.passwd'){ 
+        open(AUTH,$datadir.'/'.$name.'.passwd');
+        while (my $l = <AUTH>){
+                chomp($l);
+                if ($l eq $username.'='.$password){
+                        $auth = 1;
+                        last;
+                }
+        }
+        close(AUTH);
+      }
+      return $auth;
+ }
+
+
+my $allapp = builder {
+   enable_if { $_[0]->{REMOTE_ADDR} ne 'localhost' && $_[0]->{REMOTE_ADDR} ne '127.0.0.1' } "Auth::Basic", authenticator => \&authen_cb;
+   enable "Plack::Middleware::DirIndex", dir_index => 'index.html';    
+   mount "/app" => Module::Service->new(); 
+   mount "/system" => Module::System->new();
+   #mount "/test" => Module::Test->new();
+   mount "/sqlite" => Module::SQLite->new(dbfile => $datadir.'/'.$name.".sqlite");
+   mount "/" => Plack::App::File->new(root => $webdir)->to_app;
+};
+
+
+my @args = ("-p","6060");
+my $runner = Plack::Runner->new(server => 'Twiggy', env => 'deployment', version_cb => \&version);#env => development, test 
+$runner->parse_options(@args);
+$runner->run($allapp);
+
+print "Started\n";
+
+
diff --git a/bin/Module/FileSystem.pm b/bin/Module/FileSystem.pm
new file mode 100644 (file)
index 0000000..c3d93e9
--- /dev/null
@@ -0,0 +1,394 @@
+package Module::FileSystem;
+
+use strict;
+use warnings;
+use parent qw(Plack::Component);
+use Plack::Request;
+use Data::Dumper;
+use File::Find::Rule;
+use File::Basename;
+use JSON::PP;
+use File::Path qw(make_path remove_tree);
+use File::Copy;
+use MIME::Types;
+if ($^O eq "MSWin32"){
+       eval('use Win32::File; use Win32::GUI;');
+}
+
+sub call {
+    my($self, $env) = @_;
+      if (($env->{REMOTE_ADDR} =~ "^127\.0\.") && 
+               ($env->{REMOTE_ADDR} =~ "^10\.") && 
+               ($env->{REMOTE_ADDR} =~ "^172\.16\.") && 
+               ($env->{REMOTE_ADDR} =~ "^192\.168\.")) {
+               return [
+       404,
+       [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate' ], 
+               [ "Sorry no remote access allowed!" ]
+       ];
+       }
+       if ($env->{PATH_INFO} =~ /^\/search/){
+               return $self->search($env);     
+       } elsif ($env->{PATH_INFO} =~ /^\/directory/) {
+               return $self->directory($env);
+       } elsif ($env->{PATH_INFO} =~ /^\/file/) {
+               return $self->file($env);
+       } elsif ($env->{PATH_INFO} =~ /^\/userenv/){
+               return $self->userenv($env);
+       } 
+       return [
+       404,
+       [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate' ], 
+               [ "Unknown System Request!" ]
+       ];
+}
+
+sub search() {
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = ();
+       my $ct="application/json";
+       my $status=200;
+       my $req = Plack::Request->new($env);
+       my $ff  = File::Find::Rule->new;
+    if (exists($req->query_parameters->{name})){
+               my $namesearch = $req->query_parameters->{name};
+       $ff->name($req->query_parameters->{name})
+    }
+    if (exists($req->query_parameters->{type})){
+       if ($req->query_parameters->{type} eq 'd'){
+               $ff->directory; 
+       } else {
+               $ff->file;
+       }
+    }
+    if (exists($req->query_parameters->{relative})){
+       $ff->relative;
+    }
+    if (exists($req->query_parameters->{osspec})){
+       $ff->canonpath;
+    }
+    my @data = $ff->in($req->query_parameters->{path});
+    if (exists($req->query_parameters->{sorted})){
+       @data = sort {$a cmp $b} @data;
+       if ($req->query_parameters->{sorted} eq "desc"){
+               @data = reverse(@data);
+       } 
+    }
+    $html->{result} = \@data;
+    return [
+    200,
+     [ 'Content-Type' => $ct,'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub directory() {
+       my $self = shift;
+       my $env = shift;
+       my $html;
+       my $ct="application/json";
+       my $status=200;
+       
+       my $req = Plack::Request->new($env);
+       if ($env->{PATH_INFO} =~ /^\/directory\/list/){
+       my $mt = MIME::Types->new();
+       $html->{result} = 0;
+       if (exists($req->query_parameters->{path})){
+               my $dir = $req->query_parameters->{path};
+               #print $dir."\n";
+               $html->{result} = {'path' => $dir};
+               if (-d $dir){
+               my @dirs = ();
+               my @files = ();
+               opendir(LDIR,$dir);
+               while (my $f = readdir(LDIR)){
+                       if ($f =~ /^\./){ next; }
+                       
+                       if (-d $dir.'/'.$f){
+                               my $bok =1 ;
+                               if ($^O eq "MSWin32"){
+                                       eval ('my $attr;
+                                       Win32::File::GetAttributes($dir.\'/\'.$f,$attr);
+                                       if ($attr & HIDDEN){
+                                               $bok = 0;
+                                       }');
+                               }
+                               if ($bok == 1){
+                                       push(@dirs,$f); 
+                               }
+                               
+                       } elsif (-f $dir.'/'.$f) {
+                               my $bok =1 ;
+                               if ($^O eq "MSWin32"){
+                                       eval ('my $attr;
+                                       Win32::File::GetAttributes($dir.\'/\'.$f,$attr);
+                                       if ($attr & HIDDEN){
+                                               $bok = 0;
+                                       }');
+                               }
+                               if ($bok == 1){
+                                       print $f."\n";
+                                       my $fi->{name} = $f;
+                                       my $mtf = $mt->mimeTypeOf($f);
+                                       $fi->{mimetype} = (exists($mtf->{MT_simplified})?$mtf->{MT_simplified}:'unknown');  
+       
+                                       push(@files,$fi);
+                               }
+                       }
+               }
+               closedir(LDIR);
+               $html->{result}->{directory} = \@dirs;
+               $html->{result}->{file} = \@files;
+               }
+       }
+    }
+       if ($env->{PATH_INFO} =~ /^\/directory\/exists/){
+       $html->{result} = 0;
+       if (exists($req->query_parameters->{path})){
+               if (-d $req->query_parameters->{path}){
+               $html->{result} = 1;
+               }
+       }
+    }
+    if ($env->{PATH_INFO} =~ /^\/directory\/make/){
+       make_path($req->query_parameters->{path});
+       $html->{result} = 0;
+       if (-d $req->query_parameters->{path}){
+               $html->{result} = 1;
+       }
+       }
+       if ($env->{PATH_INFO} =~ /^\/directory\/delete/){
+       my $keep_root = 0;
+       if (exists($req->query_parameters->{keep_root})){
+               $keep_root = 1;
+       }
+       $html->{result} = 0;
+       if (-d $req->query_parameters->{path}){
+               remove_tree( $req->query_parameters->{path}, {keep_root => $keep_root} );
+               $html->{result} = 1;
+       }
+       }
+       return [
+    200,
+     [ 'Content-Type' => $ct,'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub file() {
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = ();
+       my $ct="application/json";
+       my $status=200;
+       
+       my $req = Plack::Request->new($env);
+       if ($env->{PATH_INFO} =~ /^\/file\/exists/){
+       $html->{result} = 0;
+       if (exists($req->query_parameters->{path})){
+               if (-f $req->query_parameters->{path}){
+               $html->{result} = 1;
+           }
+       }
+    }
+    if ($env->{PATH_INFO} =~ /^\/file\/write/){
+               $html->{result} = 0;
+       if (exists($req->query_parameters->{path})){
+           if (! -d (dirname($req->query_parameters->{path}))){
+               make_path(dirname($req->query_parameters->{path}))
+           }
+           my $fwrite = ">";
+           if (exists($req->query_parameters->{append})){
+               $fwrite = ">>"; 
+           }
+           my $datax = $req->body_parameters->{data};
+           print $req->body_parameters->{data}."\n";
+           open(WFI,$fwrite.$req->query_parameters->{path});
+               print WFI $req->body_parameters->{data};
+           close(WFI);
+           $html->{result} = 1;
+          }
+         }
+         if ($env->{PATH_INFO} =~ /^\/file\/read/){
+               $html->{result} = "";
+          if (exists($req->query_parameters->{path})){
+           if (-f $req->query_parameters->{path}){
+               my $rdata = "";
+               open(RFI,$req->query_parameters->{path});
+               while ( my $l = <RFI>){
+                       $rdata .= $l; 
+               } 
+               close(RFI);
+               $html->{result} = $rdata;
+           }
+          }
+         }
+         if ($env->{PATH_INFO} =~ /^\/file\/copy/){
+               $html->{result} = "";
+          if (exists($req->query_parameters->{src})){ 
+           if (-f $req->query_parameters->{src}){
+               my $dest = $req->query_parameters->{dest};
+               if (! -d dirname($req->query_parameters->{dest})){
+                       make_path(dirname($req->query_parameters->{dest}))
+               }
+               if ($req->query_parameters->{src} ne $req->query_parameters->{dest}){
+                       my $cp = copy($req->query_parameters->{src},$req->query_parameters->{dest});
+                       $html->{result} = $cp;  
+               } else {
+                       $html->{result} = 1;
+               }
+           }
+          }
+          
+                 }
+                 if ($env->{PATH_INFO} =~ /^\/file\/move/){
+               $html->{result} = "";
+          if (exists($req->query_parameters->{src})){ 
+           if (-f $req->query_parameters->{src}){
+               my $dest = $req->query_parameters->{dest};
+               if (! -d dirname($req->query_parameters->{dest})){
+                       make_path(dirname($req->query_parameters->{dest}))
+               }
+               if ($req->query_parameters->{src} ne $req->query_parameters->{dest}){
+                       my $cp = move($req->query_parameters->{src},$req->query_parameters->{dest});
+                       $html->{result} = $cp;  
+               } else {
+                       $html->{result} = 0;
+               }
+           }
+          }
+          
+                 }
+                 if ($env->{PATH_INFO} =~ /^\/file\/dialog/){
+               if ($^O eq "MSWin32"){
+                               my $multisel = 0;
+                               if (exists($req->query_parameters->{multisel})){
+                                       $multisel = $req->query_parameters->{multisel};
+                               }
+                               my $title = "select file";
+                               if (exists($req->query_parameters->{title})){
+                                       $title = $req->query_parameters->{title};
+                               } 
+                               my $lastdir = $ENV{USERPROFILE};
+                               if (exists($req->query_parameters->{dir})){
+                                       $lastdir = $req->query_parameters->{dir}
+                               }
+                               my @filters = ['All Files - *', '*'];
+                               if(exists($req->query_parameters->{filters})){
+                                       my @newfilters = split(',',$req->query_parameters->{filters});
+                                       push(@newfilters,'All Files - *','*');
+                                       $filters[0] = \@newfilters;
+                               }
+                               my ( @file);
+                               my ( @parms );
+                               push (@parms,-filter => @filters,-directory => $lastdir,-title => $title,-multisel => $multisel,-filemustexist => 1,-pathmustexist => 1);
+                               @file = Win32::GUI::GetOpenFileName ( @parms );
+                               $html->{result}->{files} = \@file;
+                       }
+          }
+          if ($env->{PATH_INFO} =~ /^\/file\/open/){
+               if ( (exists($req->query_parameters->{file})) && (-e $req->query_parameters->{file}) ){
+                       if ($^O eq "MSWin32"){
+                               my $st = system('start /b "" "'.$req->query_parameters->{file}.'"');
+                                       $html->{result} = $st;  
+                       }
+               } else {
+                       $html->{result} = -1000;
+               }
+               
+          }
+          if ($env->{PATH_INFO} =~ /^\/file\/delete/){
+               if ( (exists($req->query_parameters->{file})) && (-e $req->query_parameters->{file}) ){
+                       unlink($req->query_parameters->{file});
+                               $html->{result} = 1;    
+               } else {
+                       $html->{result} = 0;
+               }
+               
+          }
+          if ($env->{PATH_INFO} =~ /^\/file\/rename/){
+               $html->{result} = "";
+          if (exists($req->query_parameters->{src})){ 
+           if (-f $req->query_parameters->{src}){
+               my $dest = $req->query_parameters->{dest};
+               if (! -d dirname($req->query_parameters->{dest})){
+                       make_path(dirname($req->query_parameters->{dest}))
+               }
+               if ($req->query_parameters->{src} ne $req->query_parameters->{dest}){
+                       my $cp = rename($req->query_parameters->{src},$req->query_parameters->{dest});
+                       $html->{result} = $cp;  
+               } else {
+                       $html->{result} = 1;
+               }
+           }
+          }
+          
+                 }
+          
+
+  return [
+    200,
+     [ 'Content-Type' => $ct,'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+
+sub userenv() {
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = ();
+       my $ct="application/json";
+       my $status=200;
+       
+       my $req = Plack::Request->new($env);
+       foreach my $k (keys(%ENV)){
+               $html->{result}->{$k} = $ENV{$k};
+       }
+        
+       return [
+    200,
+     [ 'Content-Type' => $ct,'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+#sub filedialog() {
+#      my $self = shift;
+#      my $env = shift;
+#      my $html->{result} = ();
+#      my $ct="application/json";
+#      my $status=200;
+#      my $req = Plack::Request->new($env);
+#      
+#      if ($^O eq "MSWin32"){
+#              my $multisel = 0;
+#              if (exists($req->query_parameters->{multisel})){
+#                      $multisel = $req->query_parameters->{multisel};
+#              }
+#              my $title = "select file";
+#              if (exists($req->query_parameters->{title})){
+#                      $title = $req->query_parameters->{title};
+#              } 
+#              my $lastdir = $ENV{USERPROFILE};
+#              if (exists($req->query_parameters->{dir})){
+#                      $lastdir = $req->query_parameters->{dir}
+#              }
+#              my @filters = ['All Files - *', '*'];
+#              if(exists($req->query_parameters->{filters})){
+#                      unshift(@filters,$req->query_parameters->{filters});
+#              }
+#              my ( @file);
+#              my ( @parms );
+#              push (@parms,-filter => @filters,-directory => $lastdir,-title => $title,-multisel => $multisel,-filemustexist => 1,-pathmustexist => 1,);
+#              @file = Win32::GUI::GetOpenFileName ( @parms );
+#              $html->{result}->{files} = \@file;
+#      }
+#      return [
+#    200,
+#     [ 'Content-Type' => $ct,'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+#     [ JSON::PP::encode_json($html) ]
+#  ];
+#}
+1;
\ No newline at end of file
diff --git a/bin/Module/Function/encrypt.pm b/bin/Module/Function/encrypt.pm
new file mode 100644 (file)
index 0000000..303c630
--- /dev/null
@@ -0,0 +1,56 @@
+package Module::Function::encrypt;
+
+use strict;
+
+sub encrypt_file(){
+       my $file = shift;
+       my $key = shift;
+       my $encfile = $file.'.enc';
+       my $st = system('openssl enc -aes-256-cbc -salt -in "'.$file.'" -out "'.$enc.'" -k "'.$key.'"');
+       if ($st == ){
+               unlink($file);
+       }
+       return $encfile; 
+}
+
+sub decrypt_file(){
+       my $encfile = shift;
+       my $key = shift;
+       my $decfile = substr($encfile,0,-4);
+       my $st = system('openssl enc -aes-256-cbc -d -in "'.$encfile.'" -out "'.$decfile.'" -k "'.$key.'"');
+       if ($st == 0){
+               unlink($enc);
+       }
+       return $decfile;
+}
+
+sub encrypt_device(){
+       my $dev = shift;
+       my $key = shift;
+       my $keysize=2048;
+       #sudo umount $dev
+       #sudo echo 'start=2048, type=83' | sudo sfdisk $dev
+       #sudo mkfs -t ext3 $dev
+       #head -c $keysize /dev/urandom > private.key
+       
+       #cryptsetup -yvh sha256 -caes-xts-plain -s 256 luksFormat $dev (asks for password)
+       #cryptsetup luksFormat $dev --key-file=private.key --use-urandom --batch-mode -v --key-size=$keysize --cipher=aes
+       #check: cryptsetup -v isLuks $dev ~: blkid -p $dev
+       #check dump: ryptsetup luksDump $dev
+       #ls -la /dev/mapper/encdev -> mkfs.ext3 /dev/mapper/encdev 
+       #mount -t ext3 /dev/mapper/encdev /home/dks/data
+       #chown -R dks:dks /home/dks/data 
+       #cryptsetup luksClose /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8
+       #umount dev: umount /home/dks/data -> cryptsetup luksClose encdev --key-file=private.key -v --batch-mode
+       #backup header: cryptsetup luksHeaderBackup --header-backup-file headerBackup /dev/sdi
+       #restore header: cryptsetup luksHeaderRestore --header-backup-file headerBackup /dev/sdi
+       
+}
+
+sub mount_encdev(){
+
+}
+
+sub unmount_encdev(){
+
+}       
\ No newline at end of file
diff --git a/bin/Module/OpenVPN.pm b/bin/Module/OpenVPN.pm
new file mode 100644 (file)
index 0000000..e2b68a3
--- /dev/null
@@ -0,0 +1,207 @@
+package Module::OpenVPN;
+
+use strict;
+use warnings;
+use parent qw(Plack::Component);
+use Plack::Request;
+use Data::Dumper;
+use File::Find::Rule;
+use File::Basename;
+use JSON::PP;
+use File::Copy;
+use File::Path qw(make_path);
+
+sub call {
+    my($self, $env) = @_;
+    #$self->_app->($env);
+    if (($env->{REMOTE_ADDR} =~ "^127\.0\.") && 
+               ($env->{REMOTE_ADDR} =~ "^10\.") && 
+               ($env->{REMOTE_ADDR} =~ "^172\.16\.") && 
+               ($env->{REMOTE_ADDR} =~ "^192\.168\.")) {
+               return [
+       404,
+       [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate' ], 
+               [ "Sorry no remote access allowed!" ]
+       ];
+       }
+       if ($env->{PATH_INFO} =~ /^\/connect/){
+       return $self->vpnconnect($env);
+    } elsif ($env->{PATH_INFO} =~ /^\/disconnect/){
+       return $self->vpndisconnect($env);
+    } elsif ($env->{PATH_INFO} =~ /^\/installprofile/){
+       return $self->vpninstallprofile($env);
+    } elsif ($env->{PATH_INFO} =~ /^\/listprofiles/){
+       return $self->vpnprofilelist($env);
+    }
+    return [
+       404,
+       [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate' ], 
+               [ "Unknown System Request!" ]
+       ];
+}
+
+sub vpnconnect(){
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = 0;
+       my $req = Plack::Request->new($env);
+       my $uprofile = "";
+       #is gui or vpn running
+       
+       if (exists($req->query_parameters->{vpnprofile})){
+               my $status = $self->vpnstatus();
+               if (!exists($status->{active}->{$req->query_parameters->{vpnprofile}})){
+                       if ($^O eq "MSWin32"){
+                               if (exists($status->{gui})){
+                                       system('taskkill.exe /F /IM openvpn.exe');
+                                       system('taskkill.exe /F /IM openvpn-gui.exe');
+                                       sleep(1);
+                               }
+                               my $st = system(1,'start /b "" "C:\Program Files\OpenVPN\bin\openvpn-gui.exe" --connect "'.$req->query_parameters->{vpnprofile}.'.ovpn"');
+                               if ($st == 0){
+                                       
+                                       my $bconn = 0;
+                                       my $i = 30;
+                                       while ($bconn == 0 || $i > 0){
+                                               $status = $self->vpnstatus();
+                                               if (exists($status->{active}->{$req->query_parameters->{vpnprofile}})){
+                                                       $html->{result} = $status;
+                                                       $bconn = 1;
+                                               }
+                                               $i--;
+                                               sleep(1);
+                                       }
+                               }
+                       }       
+               } else {
+                       $html->{result} = $status;
+               }
+       }
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub vpnstatus(){
+       my $self = shift;
+       my $status = ();
+       if ($^O eq "MSWin32"){
+               
+               my $tasklist = `tasklist`;
+               my @task = split("\n",$tasklist);
+               my @ovpntasks = grep(/openvpn-gui\.exe/,@task);
+               if (scalar(@ovpntasks) > 0){
+                       $status->{gui} = "running";
+               }
+               @ovpntasks = grep(/openvpn.exe/,@task);
+               #$status->{active_connections} = scalar(@ovpntasks);
+               if (scalar(@ovpntasks) > 0){
+                       my $ff = File::Find::Rule->new();
+                       $ff->file;
+                       $ff->name('*.log');
+                       my @loglist =$ff->in($ENV{USERPROFILE}.'/OpenVPN/log');
+                       foreach my $c (@loglist){
+                               open(CLOG,$c);
+                               my @data = <CLOG>;
+                               close(CLOG);
+                               my $laststate=$data[scalar(@data)-1];
+                               chomp($laststate);
+                               if ($laststate =~ /CONNECTED/){
+                                       my ($time,$ip,$server,$port) = $laststate =~ /.+MANAGEMENT:\s>STATE:(\d+),CONNECTED,SUCCESS,(.+),(.+),(.+),,$/;
+                                       if (!exists($status->{connection}->{$ip})){
+                                               $status->{connection}->{$ip}->{config} = substr(basename($c),0,-4);;
+                                               $status->{connection}->{$ip}->{server} = $server; 
+                                               $status->{connection}->{$ip}->{port} = $port;
+                                               $status->{connection}->{$ip}->{connected_since} = $time;
+                                       }else {
+                                               if ($time >= $status->{connection}->{$ip}->{connected_since}){
+                                                       $status->{connection}->{$ip}->{config} = substr(basename($c),0,-4);
+                                                       $status->{connection}->{$ip}->{server}= $server; 
+                                                       $status->{connection}->{$ip}->{port} = $port;
+                                                       $status->{connection}->{$ip}->{connected_since} = $time;
+                                               }
+                                       }
+                               }
+                       }
+                       my @notactive = ();
+                       my $active = ();
+                       foreach my $c (keys(%{$status->{connection}})){
+                               my $routeslist = `route print -4`;      
+                               my @routes = split("\n",$routeslist);
+                               my @activetest = grep(/$c/,@routes);
+                               if (scalar(@activetest) == 0){
+                                       push @notactive,$c;
+                               } else {
+                                       $active->{$status->{connection}->{$c}->{config}} = $c;
+                               }
+                       }
+                       foreach my $na (@notactive){
+                               delete $status->{connection}->{$na};
+                       }
+                       $status->{active} = $active;
+               }
+       }
+       return $status;
+}
+
+sub vpndisconnect(){
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = 1;
+       if ($^O eq "MSWin32"){
+               system('taskkill.exe /F /IM openvpn.exe');
+               system('taskkill.exe /F /IM openvpn-gui.exe');
+       }
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub vpninstallprofile(){
+       my $self = shift;
+       my $env = shift;
+       my $req = Plack::Request->new($env);
+       my $html->{result} = 0;
+       if ($^O eq "MSWin32"){
+               if ( ! -d $ENV{USERPROFILE}.'/OpenVPN'){
+                       make_path($ENV{USERPROFILE}.'/OpenVPN');
+               }
+               if (exists($req->query_parameters->{vpnprofile}) && (-e $req->query_parameters->{vpnprofile}) && ($req->query_parameters->{vpnprofile} =~ /\.ovpn$/)){
+                       copy(req->query_parameters->{vpnprofile},$ENV{USERPROFILE}.'/OpenVPN/'.basename($req->query_parameters->{vpnprofile}));
+                               $html->{result} = 1;
+               }
+       }
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub vpnprofilelist(){
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = ();
+       if ($^O eq "MSWin32"){
+               my $ff = File::Find::Rule->new();
+               $ff->file;
+               $ff->name('*.ovpn');
+               my @vpnlist =$ff->in($ENV{USERPROFILE}.'/OpenVPN');
+               foreach (my $p=0;$p<scalar(@vpnlist);$p++){
+                       $vpnlist[$p] = substr(basename($vpnlist[$p]),0,-5);
+               } 
+               $html->{result} = \@vpnlist;
+       }
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+
+1;
\ No newline at end of file
diff --git a/bin/Module/SQLite.pm b/bin/Module/SQLite.pm
new file mode 100644 (file)
index 0000000..89d0ad9
--- /dev/null
@@ -0,0 +1,422 @@
+package Module::SQLite;
+
+use strict;
+use warnings;
+use parent qw(Plack::Component);
+use Plack::Request;
+#use Data::Dumper;
+use DBI;
+use DBD::SQLite;
+use Encode;
+use JSON::PP;
+use File::Copy;
+use Plack::Util;
+use Plack::Util::Accessor qw ( dbfile );
+
+sub call {
+    my($self, $env) = @_;
+    if (($env->{REMOTE_ADDR} =~ "^127\.0\.") && 
+               ($env->{REMOTE_ADDR} =~ "^10\.") && 
+               ($env->{REMOTE_ADDR} =~ "^172\.16\.") && 
+               ($env->{REMOTE_ADDR} =~ "^192\.168\.")) {
+               return [
+       404,
+       [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate' ], 
+               [ "Sorry no remote access allowed!" ]
+       ];
+       }
+       #return $self->sqlite($env);
+       #if (! -e )
+       if ($env->{PATH_INFO} =~ /^\/query/){
+               return $self->sqlite($env);
+       } elsif ($env->{PATH_INFO} =~ /^\/exec/){
+               return $self->sqlite($env);
+       } elsif ($env->{PATH_INFO} =~ /^\/createdb/){
+               return $self->createdb($env);
+       } elsif ($env->{PATH_INFO} =~ /^\/checkdb/){
+               return $self->checkdb($env);
+       } elsif ($env->{PATH_INFO} =~ /^\/export/){
+               return $self->export($env);
+       }
+    return [
+       404,
+       [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate' ], 
+               [ "no such function!" ]
+       ];
+}
+
+sub sqlite {
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = ();
+       my $ct="application/json";
+       my $status=200;
+       my $req = Plack::Request->new($env);
+       my $res = ();
+       #print $req->query_parameters->{db}.":".$req->query_parameters->{type}.":".decode_base64($req->query_parameters->{sql})."\n------------------\n";
+       #$html->{req}->{db} = $req->body_parameters->{db};
+       my $test = $req->body_parameters();
+       $html->{req}->{type} = $req->body_parameters->{type};
+       $html->{req}->{sql} = $req->body_parameters->{sql};
+       #$html->{req}->{sqldecoded} = $req->query_parameters->{sql}; 
+       if ((exists($req->parameters->{sql})) && (exists($req->parameters->{type})) ) {
+               
+               #$self->{dbfile} = $req->body_parameters->{db};
+               #my $db = sqlite->new();
+               my $q = $req->body_parameters->{sql};
+               my $t = $req->body_parameters->{type};
+               if ($t eq "query"){
+                       $res = $self->dbquery($req->body_parameters->{key},$q);
+               } elsif ($t eq "querysorted"){
+                       $res = $self->dbquerysorted($q);
+               } elsif ($t eq "queryarray"){
+                       $res = $self->dbqueryarray($q);
+               } elsif ($t eq "exec"){
+                       $res = $self->dbexec($q);
+               } 
+               
+       }
+       $html->{result}->{sqldata} = $res;
+       return [
+    200,
+     [ 'Content-Type' => $ct.'; charset=utf-8','Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+};
+
+
+
+sub strreplace(){
+    my $self = shift;
+    my $text = shift;
+    $text =~ s/'/''/g;
+    return $text;
+}
+
+sub dbquery(){
+    my $self = shift;
+    my $key = shift;
+    my $stat = shift;
+    my $retdata =();
+    my $dbh = DBI->connect('DBI:SQLite:dbname='.$self->dbfile,"","",{PrintError=>1,RaiseError=>1,AutoCommit=>1})  or return $retdata->{error} = "dbquery Connection Error!".$!;
+    #$stat = encode("utf8", $stat);
+
+   #open FILE,">>/tmp/sql.log";
+   #print "Query with key: $stat\n";
+   #  close FILE;
+    my $sth = $dbh->prepare($stat);
+   $sth->execute() or print "dbquery: ".$sth->errstr;
+   while(my $data = $sth->fetchrow_hashref())
+   {
+     if (exists $data->{$key}){
+        foreach my $k (keys %{$data}){
+            $retdata->{$data->{$key}}{$k} = decode( "utf8", $data->{$k});
+        }
+     }
+   }
+   if (keys(%{$retdata}) == 0){
+    $retdata =();
+   }
+   $sth->finish();
+   $dbh->disconnect();
+   return $retdata;
+}
+
+sub dbquerysorted(){
+    my $self = shift;
+    my $stat = shift;
+    my $retdata = ();
+     my $dbh = DBI->connect('DBI:SQLite:dbname='.$self->dbfile,"","",{PrintError=>1,RaiseError=>1,AutoCommit=>1})  or return $retdata->{error} =  "dbquery Connection Error!".$!;
+    #$stat = encode("utf8", $stat);
+    #open FILE,">>/tmp/sql.log";
+    #print "Query Sorted: $stat\n";
+    # close FILE;
+    my $sth = $dbh->prepare($stat);
+
+   $sth->execute() or print "dbquery: ".$sth->errstr;
+   my $count = 0;
+   while(my $data = $sth->fetchrow_hashref())
+   {
+        foreach my $k (keys %{$data}){
+                $retdata->{$count}->{$k} = decode( "utf8", $data->{$k});
+        }
+     $count++;
+   }
+
+   $sth->finish();
+   $dbh->disconnect();
+   #%retdata = sort {$a <=> $b} keys %retdata;
+   return $retdata;
+}
+
+sub dbqueryarray(){
+    my $self = shift;
+    my $stat = shift;
+    my @retdata = ();
+     my $dbh = DBI->connect('DBI:SQLite:dbname='.$self->dbfile,"","",{PrintError=>1,RaiseError=>1,AutoCommit=>1})  or return $retdata[0]->{error} =  "dbquery Connection Error!".$!;
+    #$stat = encode("utf8", $stat);
+    #open FILE,">>/tmp/sql.log";
+    #print "Query Array: $stat\n";
+    # close FILE;
+    my $sth = $dbh->prepare($stat);
+
+   $sth->execute() or print "dbquery: ".$sth->errstr;
+   my $count = 0;
+   
+   while(my $valdata = $sth->fetchrow_arrayref())
+   {
+               if ($valdata == undef){ last;}
+           my @rdata = ();
+        foreach my $k (@{$valdata}){
+                push @rdata,decode( "utf8", $k);
+        }
+        push @retdata,\@rdata;
+   }
+
+   $sth->finish();
+   $dbh->disconnect();
+   #%retdata = sort {$a <=> $b} keys %retdata;
+   return \@retdata;
+}
+
+sub dbexec(){
+    my $self = shift;
+    my $stat = shift;
+    my $dbh = DBI->connect('DBI:SQLite:dbname='.$self->dbfile,"","",{PrintError=>1,AutoCommit=>1})  or return  "dbexec Connection Error!".$!;
+    #$stat = encode("utf8", $stat);
+    #print $stat."\n";
+    #open FILE,">>/Users/kilian/sql.log";
+    print "Exec: $stat\n";
+    #close FILE;
+    my $sth = $dbh->prepare($stat);
+   my $rv =$dbh->do($stat) or print "Failed dbexec:\n'".$stat. "'\n\n";
+   $dbh->disconnect();
+   return $rv;
+}
+
+sub createdb(){
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = 0;
+       my $ct="application/json";
+       my $status=200;
+       my $req = Plack::Request->new($env);
+       if (exists($req->query_parameters->{templatedb}) && exists($req->query_parameters->{newdb}) && (-f $req->query_parameters->{templatedb})){
+               my $r = copy($req->query_parameters->{templatedb},$req->query_parameters->{newdb});
+               $html->{result} = $r;
+       }
+       return [
+    200,
+     [ 'Content-Type' => $ct.'; charset=utf-8','Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub checkdb(){
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = 1;
+       my $ct="application/json";
+       my $status=200;
+       my $req = Plack::Request->new($env);
+       if (exists($req->query_parameters->{templatedb}) && exists($req->query_parameters->{db}) && (-f $req->query_parameters->{templatedb}) && (-f $req->query_parameters->{db})){
+               my $templatedb = $req->query_parameters->{templatedb};
+               my $dborig = $req->query_parameters->{templatedb};
+               $self->dbfile = $templatedb;
+               my $dbdefsql = "SELECT type, name,tbl_name,sql FROM sqlite_master order by name,tbl_name,type;";
+               my $defdbschemacfg = $self->dbquerysorted($dbdefsql);
+               $self->dbfile = $dborig; 
+               my $tcurcfg = $self->dbquerysorted($dbdefsql);
+               my $keycnt = keys(%{$defdbschemacfg});
+               my $bvaccum = 0;
+               my $stexec = 0;
+               foreach my $pd (sort {$a <=> $b} keys(%{$defdbschemacfg})){
+                       if ($defdbschemacfg->{$pd}->{'type'} eq 'table') {
+                               my $bupdate = 0;
+                               my $bexists = 0;
+                               my $cucols = '';
+                               my $oldobj = ();
+                               foreach my $pc (keys(%{$tcurcfg})){
+                                       if (($tcurcfg->{$pc}->{tbl_name} eq $defdbschemacfg->{$pd}->{tbl_name}) && ($tcurcfg->{$pc}->{type} eq $defdbschemacfg->{$pd}->{type})){
+                                               #print $defdbschemacfg->{$pd}->{type}.": ".$defdbschemacfg->{$pd}->{tbl_name}. "\n";
+                                               if ($tcurcfg->{$pc}->{sql} ne $defdbschemacfg->{$pd}->{sql}){ $bupdate = 1; $oldobj= $self->getcoldef($tcurcfg->{$pc}->{sql}); }
+                                               $bexists = 1; last;
+                                       }
+                               }
+                               if (($bexists==1) && ($bupdate== 1)){
+                                       my $sql_installnew = $defdbschemacfg->{$pd}->{sql}; 
+                                       my $newobj = $self->getcoldef($defdbschemacfg->{$pd}->{sql});
+                                       my @copycols = ();
+                                       for my $x (keys(%{$newobj})){
+                                               if (exists($oldobj->{$x})) { push @copycols,$x; }
+                                       }   
+                                       my @ainssql = ();
+                                       push(@ainssql,"DROP TABLE IF EXISTS new_".$defdbschemacfg->{$pd}->{tbl_name}. ";");
+                                       my $sql_tmptbl = $sql_installnew;
+                                       $sql_tmptbl =~ s/CREATE\ TABLE\ /CREATE TABLE new_/;
+                                       $sql_tmptbl =~ s/"//g;
+                                       push(@ainssql,$sql_tmptbl);
+                                       push(@ainssql,"INSERT INTO new_".$defdbschemacfg->{$pd}->{tbl_name}." (".join(',',@copycols).") SELECT ".join(',',@copycols)." FROM ".$defdbschemacfg->{$pd}->{tbl_name}.";");  
+                                       push(@ainssql,"DROP TABLE ".$defdbschemacfg->{$pd}->{tbl_name}.";");
+                                       push(@ainssql,"ALTER TABLE new_".$defdbschemacfg->{$pd}->{tbl_name}. " RENAME TO ".$defdbschemacfg->{$pd}->{tbl_name}.";");
+                                       $bvaccum = 1;
+                                       #print Dumper(@ainssql);
+                                       my $stexec = 0;
+                                       for(my $s=0;$s<scalar(@ainssql);$s++){
+                                               if (defined($stexec)) { $stexec = $self->dbexec($ainssql[$s]); }
+                                       }
+                                       #print "tbl done\n";
+                               }
+                               elsif ($bexists == 0){
+                                       my $sql_installnew = $defdbschemacfg->{$pd}->{sql};
+                                       $self->dbexec($sql_installnew);
+                               }
+                       } elsif (($defdbschemacfg->{$pd}->{'type'} eq 'trigger') || ($defdbschemacfg->{$pd}->{'type'} eq 'index')) {
+                               my $bexists = 0; my $bupdate = 0;
+                               foreach my $pc (keys(%{$tcurcfg})){
+                                       if (($tcurcfg->{$pc}->{tbl_name} eq $defdbschemacfg->{$pd}->{tbl_name}) && ($tcurcfg->{$pc}->{type} eq $defdbschemacfg->{$pd}->{type})){
+                                               #        print $defdbschemacfg->{$pd}->{type}.": ".$defdbschemacfg->{$pd}->{tbl_name}. "\n";
+                                       if ($tcurcfg->{$pc}->{sql} ne $defdbschemacfg->{$pd}->{sql}){ $bupdate = 1; }
+                                       $bexists = 1; last;
+                                       }
+                               }
+                               if (($bexists==1) && ($bupdate== 1)){
+                                       $bvaccum = 1;
+                                       my @ainssql = ();
+                                       if ($defdbschemacfg->{$pd}->{type} eq 'trigger'){
+                                               push @ainssql,"DROP TRIGGER IF EXISTS ".$defdbschemacfg->{$pd}->{'name'}.";";
+                                       } elsif ($defdbschemacfg->{$pd}->{type} eq 'index') {
+                                               push @ainssql,"DROP INDEX IF EXISTS ".$defdbschemacfg->{$pd}->{'name'}.";";
+                                       }
+                                       push @ainssql,$defdbschemacfg->{$pd}->{'sql'};
+                                       my $stexec = 0;
+                                       for(my $s=0;$s<scalar(@ainssql);$s++){
+                                               if (defined($stexec)) { $stexec = $self->dbexec($ainssql[$s]); }
+                                       }
+                               }elsif ($bexists == 0) {
+                                       my $sql_installnew = $defdbschemacfg->{$pd}->{sql};
+                                       $self->dbexec($sql_installnew); #Test check
+                               }
+                       }
+               }
+               if ($bvaccum == 1) { $self->dbexec("vacuum;"); }
+       }
+       return [
+    200,
+     [ 'Content-Type' => $ct.'; charset=utf-8','Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub export(){
+       my $self = shift;
+       my $env = shift;
+       my $req = Plack::Request->new($env);
+       my $datares = ();
+       print $req->body_parameters->{'sql'}."\n";
+       if (exists($req->body_parameters->{'sql'})){
+               $datares = $self->dbquerysorted($req->body_parameters->{'sql'});
+       }
+
+       my $retdata = "";
+       if (keys(%{$datares}) > 0){
+               $retdata = "";
+               my @columns = ();
+               foreach my $k (keys(%{$datares->{0}})){
+                       push(@columns,$k);
+               }
+               $retdata .= '"'.join('","',@columns).'"'."\n";
+               foreach my $d (keys(%{$datares})){
+                       my @r = ();
+                       foreach my $k (@columns){
+                                push @r,$datares->{$d}->{$k};
+                       }
+                       $retdata .= '"'.join('","',@r).'"'."\n";
+               }
+       }
+       return [
+    200,
+     [ 'Content-Type' => 'application/vnd.ms-excel','Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*', 'Content-Disposition' => 'attachment; filename="'.$req->body_parameters->{'filename'}.'"'], 
+     [ $retdata ]
+  ];
+}
+
+sub    getcoldef($){
+       my $self = shift;
+       my $strddl = shift;
+       my $curddl = $strddl;
+       $curddl =~ s/\s+/\ /g;
+       my $bi = index($curddl,'(')+1;
+       my $ei = rindex($curddl,')');
+       
+       $curddl = substr($curddl,$bi,length($curddl)-$bi-(length($curddl)-$ei));#curddl.substring(curddl.indexOf('(')+1,curddl.lastIndexOf(')')).trim().replace(/\s+/g," ");
+       my @colsfull = split(/,/,$curddl);#     curddl =curddl.replace(new RegExp("\\b(" + appdb.keywords.join("|") + ")\\b", "g"), "");
+       my $tblobj = ();
+       foreach my $c (@colsfull){
+         $c =~ s/^\s+//;
+         $c =~ s/\s+$//;
+               my @coldef = split(/\ /,$c);
+               my $type = uc($coldef[1]);
+               if (($type =~ /^TEXT/) || ($type =~ /^REAL/) || ($type =~ /^INTEGER/) || ($type =~ /^BOOLEAN/) || ($type =~ /^DATE/) || ($type =~ /^DATETIME/)) {
+                       $tblobj->{$coldef[0]} = $type;
+               } 
+       }
+       return $tblobj;
+}
+
+#sub dbbackup(){
+#    my $self = shift;
+#    my $path = shift;
+#    my $type = shift;
+#    
+#    my @dx = localtime();
+#    $dx[5] = $dx[5] +1900;
+#    $dx[4] = $dx[4] +1;
+#    if ($dx[4] < 10){$dx[4] = '0'.$dx[4];}
+#    if ($dx[3] < 10){$dx[3] = '0'.$dx[3];}
+#    if ($dx[2] < 10){$dx[2] = '0'.$dx[2];}
+#    if ($dx[1] < 10){$dx[1] = '0'.$dx[1];}
+#    if ($dx[0] < 10){$dx[0] = '0'.$dx[0];}
+#    my $xdd = $dx[5].$dx[4].$dx[3].'_'.$dx[2].$dx[1].$dx[0];
+#    my $bfile = "";
+#    if ($type eq "binary" ) {
+#        $bfile = $path.'/'.basename(substr($self->{dbfile},0,rindex($self->{dbfile},'.'))).'_'.$xdd.'.sqlite';
+#        my $dbh = DBI->connect('DBI:SQLite:dbname='.$self->{dbfile},"","",{PrintError=>1,RaiseError=>1,AutoCommit=>1})  or die "dbexec Connection Error!".$!;
+#        $dbh->sqlite_backup_to_file($bfile);
+#        $dbh->disconnect();
+#    }elsif($type eq "sql"){
+#        $bfile = $path.'/'.basename($self->{dbfile}).'_'.$xdd.'.sql';
+#        my $st = system('sqlite3 "'.$self->{dbfile}.'" ".dump" > '.$bfile);
+#    }
+#    return $bfile;
+#}
+#
+#sub dbrestore(){
+#    my $self = shift;
+#    my $file = shift;
+#    my $type = shift;
+#    if ($type eq "binary" ) {
+#        my $dbh = DBI->connect('DBI:SQLite:dbname='.$self->{dbfile},"","",{PrintError=>1,RaiseError=>1,AutoCommit=>1})  or die "dbexec Connection Error!".$!;
+#        $dbh->sqlite_backup_from_file($file);
+#        $dbh->disconnect();
+#    }elsif($type eq "sql"){
+#        open(REST,$file) or die "cannot open restore file $file!\n";
+#        my $rsql = "";
+#        while (my $l = <REST>) {
+#            $rsql .= $l;
+#        }
+#        close(REST);
+#        unlink($self->{dbfile});
+#        $self->dbexec($rsql);
+#    }
+#}
+#
+#sub dbrepair(){
+#    my $self = shift;
+#    my $bfile = $self->dbbackup($ENV{'TMPDIR'},'sql');
+#    $self->dbrestore($bfile,'sql');
+#    unlink($bfile);
+#}
+
+
+1;
\ No newline at end of file
diff --git a/bin/Module/Service.pm b/bin/Module/Service.pm
new file mode 100644 (file)
index 0000000..264d5c2
--- /dev/null
@@ -0,0 +1,241 @@
+package Module::Service;
+
+use strict;
+use warnings;
+use File::Path qw(make_path);
+use File::Basename;
+use parent qw(Plack::Component);
+use FindBin qw($RealBin); 
+use Data::Dumper;
+
+sub call {
+    my($self, $env) = @_;
+    if (($env->{REMOTE_ADDR} =~ "^127\.0\.") && 
+               ($env->{REMOTE_ADDR} =~ "^10\.") && 
+               ($env->{REMOTE_ADDR} =~ "^172\.16\.") && 
+               ($env->{REMOTE_ADDR} =~ "^192\.168\.")) {
+       return [
+    404,
+     [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate' ], 
+     [ "Sorry no access allowed!" ]
+     ];
+  }
+    return $self->service($env);
+}
+
+sub service() {
+  my $self = shift;
+  my $env = shift;
+  my $html = "Unknown service!";
+  my $ct="application/json";
+  my $status=200;
+  
+  if ($env->{PATH_INFO} =~ /^\/info/){
+       return $self->appinfo($env);
+  }
+  if ($env->{PATH_INFO} =~ /^\/preferences/){
+       return $self->preferences($env);
+  }
+  if ($env->{PATH_INFO} =~ /^\/setaccess/){
+       return $self->setWebLogin($env);
+  }
+  if ($env->{PATH_INFO} =~ /^\/getaccess/){
+       return $self->getWebLogin($env);
+  }
+  if (($env->{PATH_INFO} =~ /^\/stop/) || ($env->{PATH_INFO} =~ /^\/unload/)){
+    exit(0);    
+  }
+  return [
+    200,
+     [ 'Content-Type' => 'text/html','Cache-Control' =>  'no-store, no-cache, must-revalidate' , 'Access-Control-Allow-Origin'=> '*'], 
+     [ $html ]
+  ];
+};
+
+sub preferences(){
+       my $self = shift;
+       my $env =shift;
+       my $name = $self->getAppName();
+       my $pref->{result}= ();
+       my $appcfgpath = $self->getappconfigpath();
+       if (basename($env->{PATH_INFO})  ne 'preferences'){
+               $appcfgpath =~ s/\\/\//g;
+               my $page = basename($env->{PATH_INFO});
+               my $req = Plack::Request->new($env);
+               if (-e $appcfgpath.'/'.$page.'.json'){
+                       open(PREF,$appcfgpath.'/'.$page.'.json');
+                       my $strpref = "";
+                       while (my $l = <PREF>){
+                               $strpref .= $l;
+                       }
+                       close(PREF);
+                       $pref->{result}=JSON::PP::decode_json($strpref); 
+               }
+               my @kk = $req->body_parameters->keys;
+               my $setcron = '';
+               #print "keys.".Dumper(@kk)."\n";
+               if (scalar(@kk) >  0){
+               foreach my $p (@kk){
+                       my $newpref = JSON::PP::decode_json($req->body_parameters->{$p});
+                       if ($p eq 'sync'){
+                               $setcron = $p;
+                       }
+                       #if (exists($pref->{result}->{$p})){
+                               $pref->{result}->{$p} = $newpref;
+                       #}
+               }
+               open(PREF,">".$appcfgpath.'/'.$page.'.json');
+               print PREF JSON::PP::encode_json($pref->{result});
+               close(PREF);
+               
+               }
+               if ($setcron ne ''){
+                               if ($setcron eq 'sync'){
+                                       $self->setcron($ENV{HOME}.'/bin/syncdb.pl',$pref->{result}->{sync}->{time});    
+                               }
+               }
+       }
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($pref) ]
+  ]; 
+}
+
+sub appinfo(){
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = ();
+       my $req = Plack::Request->new($env);
+       my $name = basename($0);
+       $name =~ s/srv\.pl$//;
+       $name =~ s/srv\.exe$//;
+       $html->{result}->{OS} = $^O;
+       $html->{result}->{app} = $name;
+       $html->{result}->{appcfgpath} = $self->getappconfigpath();
+       if ($^O eq "MSWin32"){
+               $html->{result}->{home} = $ENV{USERPROFILE};  
+               $html->{result}->{user} = $ENV{USERNAME};  
+               $html->{result}->{hostname} = $ENV{COMPUTERNAME};
+               $html->{result}->{arch} = $ENV{PROCESSOR_ARCHITEW6432};
+               $html->{result}->{appcfgpath} =~ s/\\/\//g; 
+               $html->{result}->{home}  =~ s/\\/\//g; 
+       } else {
+               $html->{result}->{home} = $ENV{HOME};  
+               $html->{result}->{user} = $ENV{USER}; 
+               $html->{result}->{hostname} = `hostname -s`;
+               chomp($html->{result}->{hostname});
+               $html->{result}->{arch} = `uname -m`;
+               chomp($html->{result}->{arch});
+       }
+       if (! -e $html->{result}->{appcfgpath}){
+                       make_path($html->{result}->{appcfgpath});
+       }
+       if (-e $html->{result}->{appcfgpath}.'/'.$name.'.json'){
+               open(LCFG,$html->{result}->{appcfgpath}.'/'.$name.'.json');
+               my $strprofile = "";
+               while (my $l = <LCFG>){
+                       $strprofile .= $l;
+               }
+               close(LCFG);
+               if ($strprofile ne ""){
+                       $html->{result}->{appconfig} = JSON::PP::decode_json($strprofile);
+               }
+       }
+       if (!exists($html->{result}->{appconfig})){
+               $html->result->{appconfig} = undef;
+       }
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub getWebLogin(){
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = ();
+       my $req = Plack::Request->new($env);
+       
+       my $apppath = $self->getappconfigpath();
+       my $loginname= "";
+       print "read:". $apppath.'/'.$self->getAppName().'.passwd'."\n";
+       if (-e $apppath.'/'.$self->getAppName().'.passwd'){
+               open(AUTH,$apppath.'/'.$self->getAppName().'.passwd');
+               while (my $l = <AUTH>){
+                       chomp($l);
+                       if ($l eq ""){ next;}
+                       ($loginname) = $l =~ m/^(\w+)\=.*/;
+               }
+               close(AUTH);
+       }
+       $html->{result}->{login} = $loginname;
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub setWebLogin(){
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = -1;
+       my $req = Plack::Request->new($env);
+       my $ret = -1;
+       my $apppath = $self->getappconfigpath();
+       if (exists($req->body_parameters->{login}) && exists($req->body_parameters->{passwd})){
+               open(AUTH,">".$apppath.'/'.$self->getAppName().'.passwd');
+               print AUTH $req->body_parameters->{login}.'='.$req->body_parameters->{passwd};
+               close(AUTH);
+               $ret = 0;
+       }
+       $html->{result} = $ret;
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub getappconfigpath(){
+        my $self = shift;
+        my $datadir = dirname($RealBin).'/data';
+        return $datadir;
+}
+
+sub getAppName(){
+       my $self = shift;
+        my $name = basename($0);
+     $name =~ s/srv\.pl$//;
+     $name =~ s/srv\.exe$//;
+     return lc($name);
+}
+
+sub setcron(){
+       my $self = shift;
+       my $script =shift;
+       my $time = shift;
+       print "setcron\n";
+       my $strcron = `crontab -l`;
+       my @lines = split("\n",$strcron);
+       my $crontabline = "";
+       if ((defined($time) && $time =~ /\d\d:\d\d/)){
+               $crontabline = int(substr($time,3,2)).' '.int(substr($time,0,2)).' * * * '.$script;
+       }
+       my $newcrontab = "";
+       foreach my $l (@lines){
+               chomp($l);
+               if ($l !~ $script){
+                       $newcrontab .= $l."\n";
+               }
+       }
+       if ($crontabline ne ""){
+               $newcrontab .= $crontabline."\n";
+       }
+       print "\n----\n".$newcrontab."\n----\n";
+       system('sudo echo "'.$newcrontab.'" > /var/spool/cron/crontabs/'.$ENV{USER});
+}
+
+1;
\ No newline at end of file
diff --git a/bin/Module/System.pm b/bin/Module/System.pm
new file mode 100644 (file)
index 0000000..f7f31be
--- /dev/null
@@ -0,0 +1,184 @@
+package Module::System;
+
+use strict;
+use warnings;
+use File::Path qw(make_path);
+use File::Basename;
+use parent qw(Plack::Component);
+use JSON::PP;
+use Data::Dumper;
+
+sub call {
+    my($self, $env) = @_;
+    if (($env->{REMOTE_ADDR} =~ "^127\.0\.") && 
+               ($env->{REMOTE_ADDR} =~ "^10\.") && 
+               ($env->{REMOTE_ADDR} =~ "^172\.16\.") && 
+               ($env->{REMOTE_ADDR} =~ "^192\.168\.")) {
+       return [
+    404,
+     [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate' ], 
+     [ "Sorry no access allowed!" ]
+     ];
+       }
+       my $html->{result}  = undef;
+    if ($env->{PATH_INFO} =~ /^\/getconfig/){
+               return $self->getconfig($env);
+       } elsif ($env->{PATH_INFO} =~ /^\/setconfig/) {
+               return $self->setconfig($env);
+       } elsif ($env->{PATH_INFO} =~ /^\/shutdown/){
+               print "Shutdown launched!\n";
+        system('sudo shutdown -h now');
+        $html->{result} = "shutdown launched";
+       } elsif ($env->{PATH_INFO} =~ /^\/restart/){
+        print "Restart launched!\n";
+        system('sudo shutdown -r now');
+        $html->{result} = "restart launched";
+       } 
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub getconfig(){
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = undef;
+       my $req = Plack::Request->new($env);
+       my @sections = ('lan','wlan','hostname','activenet');
+       if (exists($req->query_parameters->{section})){
+               @sections = split(',',$req->query_parameters->{section});
+       }
+       foreach my $s (@sections){
+               if ($s eq 'wlan') {
+                       $html->{result}->{$s} = $self->getwifinetworks();
+               } elsif ($s eq 'hostname') {
+                       $html->{result}->{$s} = $self->gethostname();
+               } elsif ($s eq 'activenet'){
+                       $html->{result}->{$s} = $self->getactivenet();
+               }
+       }
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+sub setconfig(){
+       my $self = shift;
+       my $env = shift;
+       my $html->{result} = {};
+       my $req = Plack::Request->new($env);
+       my @sections = ('wlan','hostname');
+       if (exists($req->query_parameters->{section})){
+               @sections = split(',',$req->query_parameters->{section});
+       }
+       foreach my $k (@sections){
+               my $values = JSON::PP::decode_json($req->body_parameters->{$k});
+               #print Dumper($values);
+               if ($k eq 'wlan'){
+                       $html->{result}->{$k} = $self->setwifinetwork($values);
+               }
+               if ($k eq 'hostname'){
+                       $html->{result}->{$k} = $self->sethostname($values);
+               }
+       }
+       return [
+    200,
+     [ 'Content-Type' => "application/json",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ JSON::PP::encode_json($html) ]
+  ];
+}
+
+#weblogin
+
+sub getactivenet(){
+       my $self = shift;
+       my $netconfig = `ip -4 a show`;
+my @rows = split("\n",$netconfig);
+my $net = ();
+my $cnet = "";
+foreach my $r (@rows){
+       chomp($r);
+       if ($r =~ /^\d:/){
+               my ($tmp) = $r =~ m/^\d:\s(.+):/;
+               $cnet = $tmp; 
+       } elsif ($r =~ /^\s+inet\s/){
+               my ($ip) = $r =~ m/^\s+inet\s([\d|\.]+)/; 
+               $net->{$cnet} = $ip;
+       }
+}
+       return $net;
+}
+
+#hostname
+sub gethostname(){
+       my $self = shift;
+       my $hostname = `hostname -s`;
+       chomp($hostname);
+       return $hostname;
+}
+
+sub sethostname(){
+       my $self = shift;
+       my $data = shift;
+       my $ret = -1;
+       if (exists($data->{hostname}) && $data->{hostname} ne ""){
+               system("sudo hostnamectl set-hostname ".$data->{hostname});
+               my $ret = system('sudo sed -i "s/^127.0.1.1.*/127.0.1.1\t'.$data->{hostname}.'/" /etc/hosts');  
+       }
+       return $ret;
+}
+
+#wlan
+sub getwifinetworks(){
+       my $self = shift;
+       my $cmd = "sudo wpa_cli -i wlan0 status | grep -e \"^ssid=\" | awk -F \"=\" '\{  print \$2 \}'";
+       my $current = `$cmd`;
+        chomp($current);
+        my $wifi->{ssid} = undef;
+        if ($current ne ""){
+                $wifi->{ssid} = $current;
+        }
+        my $strlist = `sudo iw wlan0 scan | grep SSID | sed \'s/.*SSID: //g\'`;
+        my @list = split("\n",$strlist);
+        $wifi->{networks} = \@list;
+        return $wifi;
+        
+}
+
+sub setwifinetwork(){
+        my $self = shift;
+        my $data = shift; 
+        my $strwpa = 'ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev'."\n";
+               $strwpa .= 'update_config=1'."\n";
+               $strwpa .= 'country=GB'."\n";
+        
+        if (exists($data->{hiddenssid})){
+                $strwpa .= "\t".'scan_ssid=1'."\n";
+        }
+        if (exists($data->{passphrase})){
+                my $cmd = 'wpa_passphrase "'.$data->{ssid}.'" "'.$data->{passphrase}.'"';
+                my $enc = `$cmd`;
+                #chomp($enc);
+                $strwpa .= $enc;
+        } else {
+               $strwpa .= 'network={'."\n";
+               $strwpa .= "\t".'ssid="'.$data->{ssid}."\"\n";
+            $strwpa .= "\t".'key_mgmt=NONE'."\n";
+               $strwpa .= '}'."\n";
+        }
+       
+        print $strwpa."\n---\n";
+        my $st = system('echo "'.$strwpa.'" > /tmp/wpa_supplicant.conf');
+        $st = system('sudo cp /tmp/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf');
+        my $cmd = "sudo wpa_cli -i wlan0 reconfigure";
+        my $ret = system($cmd);
+        return 1;
+}
+
+
+
+1;
\ No newline at end of file
diff --git a/bin/Module/Test.pm b/bin/Module/Test.pm
new file mode 100644 (file)
index 0000000..7874a0f
--- /dev/null
@@ -0,0 +1,53 @@
+package Module::Test;
+
+use strict;
+use warnings;
+use parent qw(Plack::Component);
+use Plack::Request;
+use Data::Dumper;
+
+sub call {
+    my($self, $env) = @_;
+    #$self->_app->($env);
+    if (($env->{REMOTE_ADDR} =~ "^127\.0\.") && 
+               ($env->{REMOTE_ADDR} =~ "^10\.") && 
+               ($env->{REMOTE_ADDR} =~ "^172\.16\.") && 
+               ($env->{REMOTE_ADDR} =~ "^192\.168\.")) {
+               return [
+       404,
+       [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate' ], 
+               [ "Sorry no remote access allowed!" ]
+       ];
+       }
+    return $self->test($env);
+}
+
+sub test(){
+       my $self = shift;
+       my $env = shift;
+       my $html = "";
+       my $ct="application/json";
+       my $status=200;
+       my $request = Plack::Request->new($env);
+       $html .= "<h1>System Environement</h1>";
+       foreach my $k (keys(%ENV)){
+               $html.= '<strong>'.$k.':</strong>'.$ENV{$k}."<br/>\n";
+       }
+       $html .= "<h1>Request Header</h1>";
+       foreach my $k (keys(%{$env})){
+               $html .= '<strong>'.$k.':</strong>'.$env->{$k}."<br/>\n";
+       }
+       $html .= "<h1>GET PARAMETERS</h1>";
+       $html .= Dumper($request->query_parameters);
+       $html .= "<h1>POST PARAMETERS</h1>";
+       $html .= Dumper($request->body_parameters);
+       print "Test Called!\n"; 
+       return [
+    200,
+     [ 'Content-Type' => "text/html",'Cache-Control' =>  'no-store, no-cache, must-revalidate', 'Access-Control-Allow-Origin'=> '*' ], 
+     [ $html ]
+  ];
+};
+
+
+1;
\ No newline at end of file
diff --git a/bin/mountdrives.pl b/bin/mountdrives.pl
new file mode 100644 (file)
index 0000000..cde1c2c
--- /dev/null
@@ -0,0 +1,117 @@
+#!/usr/bin/perl
+
+use strict;
+use File::Basename;
+use Data::Dumper;
+use JSON;
+use Getopt::Long;
+my $mount = 0;
+my $unmount = 0;
+my $format = 0;
+my $dev = "";
+GetOptions("unmount|u"=> \$unmount,"mount|m"=> \$mount,"format|f" => \$format,"dev|d=s" => \$dev);
+my @drives = &getdrives();
+if (($mount == 0) && ($format == 0) && ($unmount == 0)){
+       print "nooptions selected!\n";
+       #print Dumper(@drives);
+       exit(0);        
+}
+#
+#my $fstypes = { ntfs => 'ntfs-3g',exfat => 'exfat', fat32 => 'vfat',''};
+#print Dumper($connecteddrives);
+print $ENV{HOME}."\n";
+if ($format == 1){
+       foreach my $dev (@drives){
+               #print Dumper($dev)."\n";
+                       if ($dev->{name} eq $dev){
+                               my @cmds = ("sfdisk --delete /dev/".$dev, 
+                               "echo 'start=2048, type=83' | sudo sfdisk /dev/".$dev,
+                               'echo "'.$dev->{serial}.'" | sudo cryptsetup -yvh sha256 -caes-xts-plain -s 256 luksFormat /dev/'.$dev.'1',
+                               'echo "'.$dev->{serial}.'" | sudo cryptsetup luksOpen /dev/'.$dev.'1 '.$dev->{serial},
+                               'sudo mkfs.ext3 /dev/mapper/'.$dev->{serial});
+                               foreach my $c (@cmds){
+                                       print $c."\n";
+                                       my $ret = `$c`;
+                                       print $ret."\n--\n";    
+                               }
+                               $mount = 1;
+                       } 
+       }
+}
+if ($mount == 1){
+       my $mounts = &getmounts();
+       if (keys(%{$mounts}) > 0){
+               foreach my $k (keys(%{$mounts})){
+                       print $k." mounted already to ".$mounts->{$k}."\n";             
+               }
+               exit(0);        
+       } 
+       
+       foreach my $dev (@drives){
+               #print Dumper($dev)."\n";
+                       if ($dev->{name} =~ /^sd/){
+                               my $serial = $dev->{serial};
+                               my @partitions = @{$dev->{children}};
+                               
+                               foreach my $p (@partitions){
+                                       if (($p->{fstype} eq 'crypto_LUKS') && ($p->{mountpoint} ne $ENV{HOME}.'/data')){
+                                               print "device to mount ".$serial."!\n";
+                                               my @cmds = ('echo "'.$serial.'" | sudo cryptsetup luksOpen /dev/'.$p->{name}.' '.$serial,
+                                               'sudo mount /dev/mapper/'.$serial.' '.$ENV{HOME}.'/data',
+                                               'sudo chown -R dks:dks '.$ENV{HOME}.'/data'
+                                               );
+                                               foreach my $c (@cmds){
+                                                       print $c."\n";
+                                                       my $ret = `$c`;
+                                                       print $ret."\n--\n";    
+                                               }
+                                                       
+                                       }
+                               }
+                       } 
+       }
+}
+if ($unmount == 1){
+       my $mounts = &getmounts();
+       if (keys(%{$mounts}) > 0){
+               foreach my $k (keys(%{$mounts})){
+                       print "unmounted and close encryption for to ".$mounts->{$k}."\n";
+                       system("sudo umount ".$mounts->{$k});
+                       system("sudo cryptsetup luksClose /dev/".$k);           
+               }
+       } 
+}
+
+
+
+sub getdrives(){
+       my $cmd = 'lsblk -J -o name,label,size,mountpoint,fstype,SERIAL';# | grep -e "sd."';
+       my $strdrives = `$cmd`;
+       my $dr = JSON::decode_json($strdrives); 
+       return @{$dr->{blockdevices}};
+}
+
+#sub getcurrentmountpoints(){
+#      my $cmd = 'mount | grep "/dev/sd"';
+#      my $mpoints = ();
+#      my $strmounts = `$cmd`;
+#      #print $strmounts;
+#      my @mdrives = split("\n",$strmounts);
+#      foreach my $m (@mdrives){
+#              my ($drv,$path) = $m =~ m/^\/dev\/(.+)\son\s(.*)\stype.*$/;
+#              $mpoints->{$drv} = $path;
+#      }       
+#      return $mpoints;
+#}
+sub getmounts(){
+       my $cmd = 'mount | grep "/dev/mapper"';
+       my $mpoints = ();
+       my $strmounts = `$cmd`;
+       #print $strmounts;
+       my @mdrives = split("\n",$strmounts);
+       foreach my $m (@mdrives){
+               my ($drv,$path) = $m =~ m/^\/dev\/(.+)\son\s(.*)\stype.*$/;
+               $mpoints->{$drv} = $path;
+       }       
+       return $mpoints;
+}
\ No newline at end of file
diff --git a/bin/startapp.sh b/bin/startapp.sh
new file mode 100644 (file)
index 0000000..a7edb84
--- /dev/null
@@ -0,0 +1,4 @@
+#/bin/bash
+/home/dks/bin/mountdrives.pl -m &&
+/home/dks/bin/Hourtraxsrv.pl &&
+/home/dks/bin/electron/electron --disable-gpu &
diff --git a/bin/supportmnt.pl b/bin/supportmnt.pl
new file mode 100644 (file)
index 0000000..a915ba0
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+
+use strict;
+use Data::Dumper;
+use JSON::PP;
+use Encode;
+my $strcfg = "";
+open(CFG,$ENV{HOME}.'/data/hourtrax.json');
+while (my $l = <CFG>){
+       chomp($l);
+       $strcfg .= $l;
+}
+close(CFG);
+if ("$ARGV[0]" eq "mount"){
+
+       my $cmd = "mount | grep ".$ENV{HOME}."/sync"; 
+       my $ismnt = `$cmd`;
+       chomp($ismnt);
+       #print "Test 1: mounted:".$ismnt."\n";
+       if ($ismnt ne ""){
+               my $cmd = "sudo umount ".$ENV{HOME}."/sync";
+               system($cmd);
+       } 
+       my $data = JSON::PP::decode_json($strcfg);
+       my $conn = "sudo mount -t cifs -o username=".$data->{sync}->{user}.",password=".$data->{sync}->{pwd}." //".$data->{sync}->{host}.'/'.$data->{sync}->{path}." ".$ENV{HOME}.'/sync';
+       #print $conn."\n";
+       my $ret = `$conn`;
+       #print $ret."\n";
+
+       if ($ret =~ /unable to/){
+               print "$ret\n";
+               exit(1);
+       } 
+}
+if ("$ARGV[0]" eq "unmount"){
+       my $cmd = "sudo umount ".$ENV{HOME}."/sync";
+       system($cmd);
+}
diff --git a/bin/syncdb.pl b/bin/syncdb.pl
new file mode 100644 (file)
index 0000000..bf00a89
--- /dev/null
@@ -0,0 +1,108 @@
+#!/usr/bin/perl
+
+use strict;
+use Data::Dumper;
+use JSON::PP;
+use DBI;
+use DBD::SQLite;
+use Encode;
+my $strcfg = "";
+open(CFG,$ENV{HOME}.'/data/hourtrax.json');
+while (my $l = <CFG>){
+       chomp($l);
+       $strcfg .= $l;
+}
+close(CFG);
+my $cmd = "mount | grep ".$ENV{HOME}."/sync"; 
+my $ismnt = `$cmd`;
+chomp($ismnt);
+#print "Test 1: mounted:".$ismnt."\n";
+if ($ismnt ne ""){
+       my $cmd = "sudo umount ".$ENV{HOME}."/sync";
+       system($cmd);
+} 
+my $data = JSON::PP::decode_json($strcfg);
+my $conn = "sudo mount -t cifs -o username=".$data->{sync}->{user}.",password=".$data->{sync}->{pwd}." //".$data->{sync}->{host}.'/'.$data->{sync}->{path}." ".$ENV{HOME}.'/sync';
+#print $conn."\n";
+my $ret = `$conn`;
+#print $ret."\n";
+
+if ($ret =~ /unable to/){
+       print "$ret\n";
+       exit(1);
+} else {
+       my $localdb = $ENV{HOME}.'/data/hourtrax.sqlite';
+       my $remotedb = $ENV{HOME}.'/sync/'.$data->{sync}->{dbname}.".sqlite";
+       if (-e $remotedb){
+               my $rd = &dbquery('id',$data->{sync}->{sql},$remotedb) ;
+       foreach my $r (keys(%{$rd})){
+               my $sql1= "select id,prename,surname from staff where id='".$r."'";
+               my $rl = &dbquery('id',$sql1,$localdb);
+               if (keys(%{$rl}) > 0){
+                       if (($rd->{$r}->{prename} ne $rl->{$r}->{prename}) || 
+                               ($rd->{$r}->{surname} ne $rl->{$r}->{surname})){
+                                       &dbexec("UPDATE staff set prename='".$rd->{$r}->{prename}."',surname='".$rd->{$r}->{surname}."' where id='".$r."'",$localdb);
+                       }
+                       my $sql2= "select id,idstaff,weekhours,startdate from contract where idstaff='".$r."' and startdate='".$rd->{$r}->{startdate}."'";
+                       my $rl2 = &dbquery('idstaff',$sql2,$localdb);
+                   if (keys(%{$rl2}) > 0){
+                       if (($rl2->{$rd->{$r}->{id}}->{weekhours} ne  $rl->{$r}->{weekhours}) && ($rl->{$r}->{weekhours} ne '')){
+                                &dbexec("UPDATE contract SET weekhours='".$rl->{$r}->{weekhours}."' WHERE id='".$rl2->{$rd->{$r}->{id}}->{id}."'",,$localdb);
+                       }
+                   }else {
+                       &dbexec("INSERT INTO contract (idstaff,weekhours,startdate) VALUES ('".$rd->{$r}->{id}."','".$rd->{$r}->{weekhours}."',date('".$rd->{$r}->{startdate}."');",$localdb);
+                   }
+               }else {
+                       &dbexec("INSERT INTO staff (id,prename,surname) VALUES ('".$rd->{$r}->{id}."','".$rd->{$r}->{prename}."','".$rd->{$r}->{surname}."')",$data->{sync}->{sql},$localdb);
+                       &dbexec("INSERT INTO contract (idstaff,weekhours,startdate) VALUES ('".$rd->{$r}->{id}."','".$rd->{$r}->{weekhours}."',date('".$rd->{$r}->{startdate}."');",$localdb);
+               }
+       }                               
+       }
+       
+}
+$cmd = "sudo umount ".$ENV{HOME}."/sync";
+system($cmd);
+print "Normal END!\n";
+
+
+sub dbquery(){
+    my $key = shift;
+    my $stat = shift;
+    my $file = shift;
+    my $retdata =();
+    my $dbh = DBI->connect('DBI:SQLite:dbname='.$file,"","",{PrintError=>1,RaiseError=>1,AutoCommit=>1})  or exit(2);
+    #$stat = encode("utf8", $stat);
+
+   #open FILE,">>/tmp/sql.log";
+   print "Query with key: $stat\n";
+   #  close FILE;
+    my $sth = $dbh->prepare($stat);
+   $sth->execute() or exit(2);
+   while(my $data = $sth->fetchrow_hashref())
+   {
+     if (exists $data->{$key}){
+        foreach my $k (keys %{$data}){
+            $retdata->{$data->{$key}}{$k} = decode( "utf8", $data->{$k});
+        }
+     }
+   }
+   if (keys(%{$retdata}) == 0){
+    $retdata =();
+   }
+   $sth->finish();
+   $dbh->disconnect();
+   return $retdata;
+}
+
+sub dbexec(){
+    my $stat = shift;
+    my $file =shift;
+#    my $dbh = DBI->connect('DBI:SQLite:dbname='.$$file,"","",{PrintError=>1,AutoCommit=>1})  or exit(2);
+    print "Exec: $stat\n";
+#    my $sth = $dbh->prepare($stat);
+#   my $rv =$dbh->do($stat) or exit(2);
+#   $dbh->disconnect();
+   return 1;
+#   return $rv;
+}
+
diff --git a/data/data.txt b/data/data.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/data/hourtrax.json b/data/hourtrax.json
new file mode 100644 (file)
index 0000000..3321e5e
--- /dev/null
@@ -0,0 +1 @@
+{"sync":{"path":"creorga/test","dbname":"9475a95d-e2ad-4586-8432-d44a604b3fd3","pwd":"fb1ia1ka","host":"dks-laptop","time":"01:00","user":"kilian","sql":"select uuid as id,prename,surname,weekhours,entrydate as startdate from staff;\t\t\t\t"}}
\ No newline at end of file
diff --git a/data/hourtrax.passwd b/data/hourtrax.passwd
new file mode 100644 (file)
index 0000000..565877f
--- /dev/null
@@ -0,0 +1 @@
+kilian=fb1ia1ka
\ No newline at end of file
diff --git a/data/hourtrax.pid b/data/hourtrax.pid
new file mode 100644 (file)
index 0000000..0b2a2c0
--- /dev/null
@@ -0,0 +1 @@
+1190
diff --git a/data/hourtrax.sqlite b/data/hourtrax.sqlite
new file mode 100644 (file)
index 0000000..17b102b
Binary files /dev/null and b/data/hourtrax.sqlite differ
diff --git a/data/test/hourtrax.json b/data/test/hourtrax.json
new file mode 100644 (file)
index 0000000..c700640
--- /dev/null
@@ -0,0 +1 @@
+{"sync":{"path":"differdange.nas/WD-Elements25A2-01/Differdange","host":"FRITZ.NAS","dbname":"5531423c-b85a-4305-9372-c62a293d0c84","user":"manager","sql":"select uuid as id,prename,surname,weekhours,entrydate as startdate from staff;","time":null,"pwd":"diff2016"}}
\ No newline at end of file
diff --git a/data/test/hourtrax.passwd b/data/test/hourtrax.passwd
new file mode 100644 (file)
index 0000000..f6617e5
--- /dev/null
@@ -0,0 +1 @@
+manager=diff2016
\ No newline at end of file
diff --git a/data/test/hourtrax.sqlite b/data/test/hourtrax.sqlite
new file mode 100644 (file)
index 0000000..cac70fc
Binary files /dev/null and b/data/test/hourtrax.sqlite differ
diff --git a/db/hourtrax.sql b/db/hourtrax.sql
new file mode 100644 (file)
index 0000000..26169f4
--- /dev/null
@@ -0,0 +1,31 @@
+DROP TABLE "staff";
+DROP TABLE "hours";
+
+CREATE table "staff" (
+       id TEXT primary key not null,
+       prename TEXT,
+       surname TEXT,
+       pin TEXT,
+       loginattemps integer,
+       blocked integer,
+       lastmodified DATETIME DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE table "hours" (
+       id integer primary key autoincrement not null,
+       idstaff TEXT,
+       hourstamp DATETIME,
+       direction TEXT,
+       section TEXT,
+       lastmodified DATETIME DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TRIGGER trg_staff_upd UPDATE ON staff
+ BEGIN
+  UPDATE staff SET lastmodified=CURRENT_TIMESTAMP WHERE id = NEW.id;
+ END;
+CREATE TRIGGER trg_hours_upd UPDATE ON hours
+ BEGIN
+  UPDATE hours SET lastmodified=CURRENT_TIMESTAMP WHERE id = NEW.id;
+ END;
\ No newline at end of file
diff --git a/db/hourtrax.sqlite b/db/hourtrax.sqlite
new file mode 100644 (file)
index 0000000..9899025
Binary files /dev/null and b/db/hourtrax.sqlite differ
diff --git a/db/hourtrax_dev.sql b/db/hourtrax_dev.sql
new file mode 100644 (file)
index 0000000..81bac3f
--- /dev/null
@@ -0,0 +1,52 @@
+PRAGMA foreign_keys=OFF;\r
+BEGIN TRANSACTION;\r
+CREATE TABLE "staff" (\r\r
+       id TEXT primary key not null,\r\r
+       prename TEXT,\r\r
+       surname TEXT,\r\r
+       pin TEXT,\r\r
+       loginattemps integer,\r\r
+       blocked integer,\r\r
+       lastmodified DATETIME DEFAULT CURRENT_TIMESTAMP\r\r
+);\r
+INSERT INTO "staff" VALUES('a69d71b8-2e76-4d28-bf4d-475c4086dcf8','Barbara','BORNAN COLON','1234',NULL,NULL,'2018-01-29 08:13:04');\r
+INSERT INTO "staff" VALUES('fa9c1e71-07c7-4c10-a4bc-3c98bfdab8c3','Amandine','Lammens',NULL,NULL,NULL,'2018-01-28 10:52:45');\r
+INSERT INTO "staff" VALUES('4d982724-0d60-466f-bd75-b9d1b99305f1','Audrey','MAQUINISTA GRACA',NULL,NULL,NULL,'2018-01-28 10:52:45');\r
+INSERT INTO "staff" VALUES('cb791aec-47d4-4b2a-b338-68d5ac6001b7','Jennie','KENTON',NULL,NULL,NULL,'2018-01-28 10:52:45');\r
+INSERT INTO "staff" VALUES('f1a3bbb4-eb7d-485e-a902-cd0024339645','Daniela','Acito ',NULL,NULL,NULL,'2018-01-28 10:52:46');\r
+CREATE TABLE "contract" (\r\r
+       id integer primary key autoincrement not null,\r\r
+       idstaff TEXT NOT NULL,\r\r
+       startdate DATE DEFAULT CURRENT_DATE,\r\r
+       weekhours REAL DEFAULT 40.0,\r\r
+       lastmodified DATETIME DEFAULT CURRENT_TIMESTAMP\r\r
+);\r
+INSERT INTO "contract" VALUES(1,'a69d71b8-2e76-4d28-bf4d-475c4086dcf8','2015-01-27',40.0,'2018-01-28 10:52:46');\r
+INSERT INTO "contract" VALUES(2,'fa9c1e71-07c7-4c10-a4bc-3c98bfdab8c3','2013-04-01',40.0,'2018-01-28 10:52:46');\r
+INSERT INTO "contract" VALUES(3,'4d982724-0d60-466f-bd75-b9d1b99305f1','2015-05-01',40.0,'2018-01-28 10:52:46');\r
+INSERT INTO "contract" VALUES(4,'cb791aec-47d4-4b2a-b338-68d5ac6001b7','2013-11-25',40.0,'2018-01-28 10:52:46');\r
+INSERT INTO "contract" VALUES(5,'f1a3bbb4-eb7d-485e-a902-cd0024339645','2016-03-07',40.0,'2018-01-28 10:52:47');\r
+CREATE TABLE "hours" (\r\r
+       id integer primary key autoincrement not null,\r\r
+       idstaff TEXT,\r\r
+       stamp_in DATETIME,\r\r
+       stamp_out DATETIME,\r\r
+       direction TEXT,\r\r
+       section TEXT,\r\r
+       lastmodified DATETIME DEFAULT CURRENT_TIMESTAMP\r\r
+);\r
+INSERT INTO "hours" VALUES(4,'a69d71b8-2e76-4d28-bf4d-475c4086dcf8','2018-01-01 08:00:00','2018-01-01 17:00:00',NULL,NULL,'2018-01-29 09:24:52');\r
+INSERT INTO "hours" VALUES(14,'a69d71b8-2e76-4d28-bf4d-475c4086dcf8','2018-01-02 08:00:00','2018-01-02 17:00:00',NULL,NULL,'2018-01-29 09:44:10');\r
+INSERT INTO "hours" VALUES(15,'a69d71b8-2e76-4d28-bf4d-475c4086dcf8','2018-01-07 08:00:00','2018-01-07 16:30:00',NULL,NULL,'2018-01-29 09:44:40');\r
+DELETE FROM sqlite_sequence;\r
+INSERT INTO "sqlite_sequence" VALUES('contract',5);\r
+INSERT INTO "sqlite_sequence" VALUES('hours',15);\r
+CREATE TRIGGER trg_staff_upd UPDATE ON staff\r\r
+ BEGIN\r\r
+  UPDATE staff SET lastmodified=CURRENT_TIMESTAMP WHERE id = NEW.id;\r\r
+ END;\r
+CREATE TRIGGER trg_contract_upd UPDATE ON contract\r\r
+ BEGIN\r\r
+  UPDATE contract SET lastmodified=CURRENT_TIMESTAMP WHERE id = NEW.id;\r\r
+ END;\r
+COMMIT;\r
diff --git a/icons/Add_New.svg b/icons/Add_New.svg
new file mode 100644 (file)
index 0000000..f2d89ac
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?> \r<svg version="1.2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" \r      x="0px" y="0px" width="50px" height="50px" viewBox="0 0 52.33 52.33"> \r<path d="M19.833,0L32.5,0 32.5,19.833999 52.334,19.833999 52.334,32.500999 32.5,32.500999 32.5,52.333 19.833,52.333 19.833,32.500999 0,32.500999 0,19.833999 19.833,19.833999z"/> \r</svg>
\ No newline at end of file
diff --git a/icons/Sand_Clock__03.svg b/icons/Sand_Clock__03.svg
new file mode 100644 (file)
index 0000000..f5fe8f0
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?> \r<svg version="1.2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" \r      x="0px" y="0px" width="50px" height="50px" viewBox="0 0 14.78 21.33"> \r<path d="M7.1938303,17.066999L9.2158589,17.859412 11.191,18.103077 11.191,18.931999 3.1980002,18.931999 3.1980002,18.103077 5.3671691,17.812599z M7.2161794,11.2656L2.6861783,14.0768 2.6861783,19.3706 11.67838,19.3706 11.67838,14.2383z M3.426,4.6199997L11.316,4.6199997 11.316,6.7935736 7.2563896,9.3129997 3.426,6.7818542z M2.8736798,1.9636199L2.8736798,7.25388 7.2604291,10.1055 11.86588,7.25 11.86588,1.9636199z M0,0L14.78,0 14.78,1.9636199 12.790387,1.9636199 12.790387,7.7617502 12.779987,7.7696202 12.796987,7.80283 8.1549654,10.6836 12.602886,13.5684 12.602886,19.3706 14.78,19.3706 14.78,21.334001 0,21.334001 0,19.3706 1.760392,19.3706 1.760392,13.5612 1.7721022,13.5535 1.7486719,13.5203 6.335983,10.6759 1.9478934,7.7513399 1.9478934,1.9636199 0,1.9636199z"/> \r</svg>
\ No newline at end of file
diff --git a/icons/Stop_Clock.svg b/icons/Stop_Clock.svg
new file mode 100644 (file)
index 0000000..ab6cfbc
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?> \r<svg version="1.2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" \r      x="0px" y="0px" width="50px" height="50px" viewBox="0 0 54.09 64"> \r<path d="M27.01135,34.797C29.658478,34.797 31.806999,36.945377 31.806999,39.593949 31.806999,42.23962 29.658478,44.387998 27.01135,44.387998 26.184092,44.387998 25.405671,44.178195 24.726353,43.808978L24.68371,43.784446 15.832217,48.526999 14.663001,46.649397 22.339024,40.666025 22.31443,40.55954C22.250549,40.247596 22.217,39.924658 22.217,39.593949 22.217,36.945377 24.364122,34.797 27.01135,34.797z M27.01146,21.938999C17.262583,21.938999 9.3590002,29.844053 9.3589997,39.593998 9.3590002,49.341244 17.262583,57.244999 27.01146,57.244999 36.760237,57.244999 44.664,49.341244 44.664,39.593998 44.664,29.844053 36.760237,21.938999 27.01146,21.938999z M27.011558,15.183998C40.491858,15.183998 51.42,26.112107 51.42,39.593998 51.42,53.072886 40.491858,63.999998 27.011558,63.999998 13.531358,63.999998 2.6029997,53.072886 2.6029997,39.593998 2.6029997,26.112107 13.531358,15.183998 27.011558,15.183998z M6.0752745,12.689999L11.013999,17.614599 4.9399252,23.710999 0,18.7878z M48.017965,12.616999L54.092001,18.713409 49.153337,23.637999 43.078001,17.541489z M19.674001,0L34.568001,0 34.568001,8.645998 31.879,8.645998 31.879,12.712999 22.216999,12.712999 22.216999,8.645998 19.674001,8.645998z"/> \r</svg>
\ No newline at end of file
diff --git a/icons/splash.png b/icons/splash.png
new file mode 100644 (file)
index 0000000..c392eae
Binary files /dev/null and b/icons/splash.png differ
diff --git a/icons/splash.xcf b/icons/splash.xcf
new file mode 100644 (file)
index 0000000..909ac0e
Binary files /dev/null and b/icons/splash.xcf differ
diff --git a/icons/test.png b/icons/test.png
new file mode 100644 (file)
index 0000000..22f7345
Binary files /dev/null and b/icons/test.png differ
diff --git a/new_install/fileto_change.txt b/new_install/fileto_change.txt
new file mode 100644 (file)
index 0000000..a21b917
--- /dev/null
@@ -0,0 +1,27 @@
+cd / && tar czvf /home/dks/hourtrax_syssource.tar.gz \
+/etc/ssh/sshd_config \
+/etc/systemd/system/autologin@.service \
+/etc/sudoers \
+/etc/modules \
+/boot/config.txt \
+/var/spool/cron/crontabs/dks \
+/etc/openvpn/DKS-VPN-dks-hourtrax.conf \
+/etc/modprobe.d/raspi-blacklist.conf \
+/usr/share/plymouth/themes/pix/splash.png \
+/etc/lightdm/lightdm.conf \
+/etc/xdg/openbox/menu.xml \
+/etc/xdg/lxsession/LXDE-pi/sshpwd.sh \
+/etc/xdg/lxsession/LXDE-pi/autostart \
+/etc/xdg/lxsession/LXDE/autostart
+cd / && tar czvf /home/dks/hourtrax_source.tar.gz \
+/home/dks/bin \
+/home/dks/tmp \
+/home/dks/share \
+/home/dks/web \
+/home/dks/data \
+/home/dks/splash.png \
+/home/dks/perl5 \
+/home/dks/.config/lxsession/LXDE-pi/autostart \
+/home/dks/.profile
+
+sudo apt-get install cryptsetup openvpn
\ No newline at end of file
diff --git a/new_install/history.txt b/new_install/history.txt
new file mode 100644 (file)
index 0000000..767cb6a
--- /dev/null
@@ -0,0 +1,292 @@
+    1  cd /etc/xdg/autostart/
+    2  ls
+    3  cat pprompt.desktop 
+    4  cat /etc/xdg/lxsession/LXDE-pi/sshpwd.sh 
+    5  vim.tiny /etc/xdg/lxsession/LXDE-pi/sshpwd.sh 
+    6  cat /etc/xdg/lxsession/LXDE-pi/sshpwd.sh 
+    7  ls
+    8  cat lxpolkit.desktop 
+    9  cd ..
+   10  ls
+   11  cd openbox/
+   12  ls
+   13  cat autostart 
+   14  cd LXDE/
+   15  ls
+   16  cat menu.xml 
+   17  cd ..
+   18  cd ..
+   19  ls
+   20  cd autostart/
+   21  ls
+   22  cat lxkeymap.desktop 
+   23  cat /etc/xdg/lxsession/LXDE-pi/autokey.sh
+   24  ps ax
+   25  ps ax
+   26  kill -9 745
+   27  ps ax
+   28  kill -9 744
+   29  ps ax
+   30  ls
+   31  cat lxpolkit.desktop 
+   32  ls
+   33  cd ..
+   34  ls
+   35  cd lxpanel/
+   36  ls
+   37  cat launchtaskbar.cfg 
+   38  ls
+   39  cd default/panels/
+   40  ls
+   41  cat panel 
+   42  cd
+   43  exit
+   44  systemctl daemon-reload
+   45  exit
+   46  systemctl daemon-reload
+   47  systemctl start autologin@tty1
+   48  sudo deluser -remove-home pi
+   49  ps ax
+   50  systemctl stop autologin@tty1
+   51  sudo deluser -remove-home pi
+   52  cd /home/
+   53  ls
+   54  shutdown -r now
+   55  cd /etc/xdg/
+   56  ls
+   57  cd lxsession/
+   58  ls
+   59  cd LXDE-pi/
+   60  ls
+   61  ls -l
+   62  cta autostart
+   63  cat autostart
+   64  vim.tiny  autostart
+   65  cd ..
+   66  cd LXDE
+   67  ls
+   68  cat autostart 
+   69  cd ..
+   70  exit
+   71  cd /etc/xdg/openbox/
+   72  ls
+   73  cat autostart 
+   74  cat lxde-pi-rc.xml 
+   75  cd /etc/lightdm/
+   76  ls
+   77  vim.tiny lightdm.conf
+   78  grep xserver-command lightdm.conf 
+   79  sed /etc/lightdm/lightdm.conf -e 's/#xserver-command=X/xserver-command=X -nocursor/'
+   80  sed /etc/lightdm/lightdm.conf -i -e 's/#xserver-command=X/xserver-command=X -nocursor/'
+   81  exit
+   82  cd /etc/lightdm/
+   83  ls
+   84  cat pi-greeter.conf 
+   85  kill -9 1704
+   86  ls
+   87  cat lightdm.conf 
+   88  cat lightdm.conf cd ..
+   89  cd ..
+   90  ls
+   91  cd X11/
+   92  grep -Rn Xorg
+   93  cd ..
+   94  cd xdg/
+   95  grep -Rn Xorg
+   96  cd ..
+   97  find . -name xsetroot
+   98  find . -name "xinitrc"
+   99  cat X11/xinit/xinitrc 
+  100  cat /etc/X11/Xsession
+  101  ps ax
+  102  cd /usr/sbin/
+  103  sl
+  104  ls
+  105  cat pi-greeter 
+  106  ls
+  107  pi-greeter --help
+  108  ls
+  109  cd /etc/xdg/systemd/
+  110  ls
+  111  ls 
+  112  ls -l
+  113  cat user
+  114  cd user
+  115  ls
+  116  ls
+  117  cd ..
+  118  shutdown -r now
+  119  cd /var/log/
+  120  ls
+  121  ls -ltr
+  122  cat auth.log 
+  123  ls
+  124  cd lightdm/
+  125  ls
+  126  ls -l
+  127  cat lightdm.log
+  128  cat x-0.log
+  129  cd
+  130  cd /etc/lightdm/
+  131  ls
+  132  vim.tiny lightdm.conf 
+  133  shutdown -r now
+  134  cd /etc/lightdm/
+  135  vim.tiny lightdm.conf 
+  136  ps ax
+  137  export DISPLAY="0.0"
+  138  startx
+  139  exit
+  140  ps ax
+  141  apt-cache search chromium
+  142  apt-get install chromium-browser
+  143  chromium-browser -kiosk
+  144  chromium-browser -kiosk "http://www.dks.lu"
+  145  exit
+  146  startlxde-pi
+  147  export DISPLAY=0.0
+  148  startlxde-pi
+  149  export DISPLAY=:.0
+  150  startlxde-pi
+  151  export DISPLAY=:0.0
+  152  startlxde-pi
+  153  cat /usr/share/X11/xorg.conf.d/99-fbturbo.conf
+  154  fdisk /dev/sda
+  155  fdisk -l
+  156  fdisk /dev/sda
+  157  fdisk -l
+  158  fdisk /dev/sda
+  159  fdisk -l
+  160  dd bs=4K if=/dev/random of=/dev/sda1
+  161  dd bs=4K if=/dev/urandom of=/dev/sda1
+  162  fdisk -l
+  163  cryptsetup -yuh sha256 -caes-xts-plain -s 256 luksFormat/dev/sda1
+  164  cryptsetup -yvh sha256 -caes-xts-plain -s 256 luksFormat/dev/sda1
+  165  cryptsetup -yvh sha256 -caes-xts-plain -s 256 luksFormat /dev/sda1
+  166  cryptsetup luksOpen /dev/sda1 2fcc2458-0278-11e8-8829-33f2eb7606e8
+  167  mkfs.ext3 /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8
+  168  id dks
+  169  mount -o uid=1001,gid=1001 /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8 /mnt
+  170  mount -t ext3 -o uid=1001,gid=1001 /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8 /mnt
+  171  mount /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8 /mnt
+  172  chmod -R dks:dks /mnt
+  173  chown -R dks:dks /mnt
+  174  cryptsetup luksClose /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8
+  175  ls
+  176  ls
+  177  cd /mnt/
+  178  ls
+  179  mkdir testdata
+  180  ls
+  181  umount /mnt
+  182  cd
+  183  umount /mnt
+  184  cryptsetup luksClose /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8
+  185  mount /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8 /mnt
+  186  cryptsetup luksOpen /dev/sda1 secret
+  187  mkfs.ext3 /dev/mapper/secret
+  188  mount /dev/mapper/secret /mnt
+  189  cd /mnt/
+  190  ls
+  191  umount /mnt
+  192  cd
+  193  umount /mnt
+  194  cryptsetup close luksClose /dev/mapper/secret
+  195  cryptsetup luksClose /dev/mapper/secret
+  196  cryptsetup luksOpen /dev/sda1 2fcc2458-0278-11e8-8829-33f2eb7606e8
+  197  mount /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8 /mnt
+  198  cd /mnt/
+  199  ls
+  200  df -h
+  201  umount /mnt
+  202  cd
+  203  umount /mnt
+  204  mkfs.ext3 /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8
+  205  mount /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8 /mnt
+  206  cd /mnt
+  207  ls -al
+  208  cd 
+  209  cryptsetup -v my_disk_mapper
+  210  cryptsetup -v /dev/sda1
+  211  cryptsetup
+  212  cryptsetup -v 2fcc2458-0278-11e8-8829-33f2eb7606e8
+  213  mount /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8 /mnt
+  214  cd /mnt
+  215  ls
+  216  mkdir testsetup
+  217  cd ..
+  218  umount /mnt
+  219  cryptsetup luksClose /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8
+  220  cryptsetup luksOpen /dev/sda1 2fcc2458-0278-11e8-8829-33f2eb7606e8
+  221  cryptsetup -v 2fcc2458-0278-11e8-8829-33f2eb7606e8
+  222  cryptsetup --help
+  223  cryptsetup status 2fcc2458-0278-11e8-8829-33f2eb7606e8
+  224  mount /dev/mapper/2fcc2458-0278-11e8-8829-33f2eb7606e8 /mnt
+  225  cd /mnt/
+  226  ls
+  227  cd 
+  228  umount /mnt
+  229  exit
+  230  apt-get search aescrypt
+  231  apt-cache search aescrypt
+  232  apt-cache search aes
+  233  apt-cache search crypt
+  234  fdisk -l
+  235  exit
+  236  apt-get install cryptsetup
+  237  cat /etc/modules
+  238  modprobe dm_crypt
+  239  echo dm_crypt >> /etc/modules
+  240  cat /etc/modules
+  241  uuid 
+  242  uuid 
+  243  cd /etc/xdg/openbox/
+  244  ls
+  245  cat autostart 
+  246  ls
+  247  cat menu.xml 
+  248  vim.tiny  menu.xml 
+  249  cp  menu.xml menu.xml.orig
+  250  vim.tiny  menu.xml 
+  251  cd ..
+  252  ls
+  253  cd ..
+  254  sl
+  255  ls
+  256  cd lightdm/
+  257  ls
+  258  cat lightdm.conf 
+  259  cd 
+  260  cd /home/dks/
+  261  ls
+  262  ls -al
+  263  cd .config/
+  264  ls
+  265  cd openbox/
+  266  ls
+  267  cat lxde-pi-rc.xml 
+  268  ls
+  269  cd ..
+  270  ls
+  271  ls -l
+  272  cd lxsession/LXDE-pi/
+  273  ls
+  274  cat autostart 
+  275  exit
+  276  apt-cache search smbclient
+  277  cd /mnt/
+  278  ls
+  279  ls -l
+  280  mount -t cifs -ouser=kilian,pass=fb1ia1ka,rw //home-backup/mirror /mnt 
+  281  ls
+  282  cd ..
+  283  ls
+  284  cd mnt/
+  285  ls
+  286  cd
+  287  mount
+  288  umount /mnt
+  289  exit
+  290  history
+  291  history
+  292  history > history.txt
diff --git a/new_install/hourtrax.json b/new_install/hourtrax.json
new file mode 100644 (file)
index 0000000..951f3f8
--- /dev/null
@@ -0,0 +1 @@
+{"sync":{"path":null,"dbname":null,"pwd":null,"host":null,"time":null,"user":null,"sql":"select uuid as id,prename,surname,weekhours,entrydate as startdate from staff;"}}
diff --git a/new_install/hourtrax.passwd b/new_install/hourtrax.passwd
new file mode 100644 (file)
index 0000000..c4ecd8a
--- /dev/null
@@ -0,0 +1 @@
+admin=hourtrax
diff --git a/new_install/hourtrax.sqlite b/new_install/hourtrax.sqlite
new file mode 100644 (file)
index 0000000..59b7e72
Binary files /dev/null and b/new_install/hourtrax.sqlite differ
diff --git a/splash.png b/splash.png
new file mode 100644 (file)
index 0000000..c392eae
Binary files /dev/null and b/splash.png differ
diff --git a/sys/boot/config.txt b/sys/boot/config.txt
new file mode 100644 (file)
index 0000000..da872e1
--- /dev/null
@@ -0,0 +1,57 @@
+# For more options and information see
+# http://rpf.io/configtxt
+# Some settings may impact device functionality. See link above for details
+
+# uncomment if you get no picture on HDMI for a default "safe" mode
+#hdmi_safe=1
+
+# uncomment this if your display has a black border of unused pixels visible
+# and your display can output without overscan
+#disable_overscan=1
+
+# uncomment the following to adjust overscan. Use positive numbers if console
+# goes off screen, and negative if there is too much border
+#overscan_left=16
+#overscan_right=16
+#overscan_top=16
+#overscan_bottom=16
+
+# uncomment to force a console size. By default it will be display's size minus
+# overscan.
+#framebuffer_width=1280
+#framebuffer_height=720
+
+# uncomment if hdmi display is not detected and composite is being output
+#hdmi_force_hotplug=1
+
+# uncomment to force a specific HDMI mode (this will force VGA)
+#hdmi_group=1
+#hdmi_mode=1
+
+# uncomment to force a HDMI mode rather than DVI. This can make audio work in
+# DMT (computer monitor) modes
+#hdmi_drive=2
+
+# uncomment to increase signal to HDMI, if you have interference, blanking, or
+# no display
+#config_hdmi_boost=4
+
+# uncomment for composite PAL
+#sdtv_mode=2
+
+#uncomment to overclock the arm. 700 MHz is the default.
+#arm_freq=800
+
+# Uncomment some or all of these to enable the optional hardware interfaces
+#dtparam=i2c_arm=on
+#dtparam=i2s=on
+#dtparam=spi=on
+
+# Uncomment this to enable the lirc-rpi module
+#dtoverlay=lirc-rpi
+
+# Additional overlays and parameters are documented /boot/overlays/README
+
+# Enable audio (loads snd_bcm2835)
+dtparam=audio=on
+lcd_rotate=2
diff --git a/sys/etc/lightdm/lightdm.conf b/sys/etc/lightdm/lightdm.conf
new file mode 100644 (file)
index 0000000..9e8aff7
--- /dev/null
@@ -0,0 +1,165 @@
+#
+# General configuration
+#
+# start-default-seat = True to always start one seat if none are defined in the configuration
+# greeter-user = User to run greeter as
+# minimum-display-number = Minimum display number to use for X servers
+# minimum-vt = First VT to run displays on
+# lock-memory = True to prevent memory from being paged to disk
+# user-authority-in-system-dir = True if session authority should be in the system location
+# guest-account-script = Script to be run to setup guest account
+# logind-check-graphical = True to on start seats that are marked as graphical by logind
+# log-directory = Directory to log information to
+# run-directory = Directory to put running state in
+# cache-directory = Directory to cache to
+# sessions-directory = Directory to find sessions
+# remote-sessions-directory = Directory to find remote sessions
+# greeters-directory = Directory to find greeters
+# backup-logs = True to move add a .old suffix to old log files when opening new ones
+#
+[LightDM]
+#start-default-seat=true
+#greeter-user=lightdm
+#minimum-display-number=0
+#minimum-vt=7
+#lock-memory=true
+#user-authority-in-system-dir=false
+#guest-account-script=guest-account
+#logind-check-graphical=false
+#log-directory=/var/log/lightdm
+#run-directory=/var/run/lightdm
+#cache-directory=/var/cache/lightdm
+#sessions-directory=/usr/share/lightdm/sessions:/usr/share/xsessions:/usr/share/wayland-sessions
+#remote-sessions-directory=/usr/share/lightdm/remote-sessions
+#greeters-directory=/usr/share/lightdm/greeters:/usr/share/xgreeters
+#backup-logs=true
+
+#
+# Seat configuration
+#
+# Seat configuration is matched against the seat name glob in the section, for example:
+# [Seat:*] matches all seats and is applied first.
+# [Seat:seat0] matches the seat named "seat0".
+# [Seat:seat-thin-client*] matches all seats that have names that start with "seat-thin-client".
+#
+# type = Seat type (xlocal, xremote, unity)
+# pam-service = PAM service to use for login
+# pam-autologin-service = PAM service to use for autologin
+# pam-greeter-service = PAM service to use for greeters
+# xserver-command = X server command to run (can also contain arguments e.g. X -special-option)
+# xmir-command = Xmir server command to run (can also contain arguments e.g. Xmir -special-option)
+# xserver-config = Config file to pass to X server
+# xserver-layout = Layout to pass to X server
+# xserver-allow-tcp = True if TCP/IP connections are allowed to this X server
+# xserver-share = True if the X server is shared for both greeter and session
+# xserver-hostname = Hostname of X server (only for type=xremote)
+# xserver-display-number = Display number of X server (only for type=xremote)
+# xdmcp-manager = XDMCP manager to connect to (implies xserver-allow-tcp=true)
+# xdmcp-port = XDMCP UDP/IP port to communicate on
+# xdmcp-key = Authentication key to use for XDM-AUTHENTICATION-1 (stored in keys.conf)
+# unity-compositor-command = Unity compositor command to run (can also contain arguments e.g. unity-system-compositor -special-option)
+# unity-compositor-timeout = Number of seconds to wait for compositor to start
+# greeter-session = Session to load for greeter
+# greeter-hide-users = True to hide the user list
+# greeter-allow-guest = True if the greeter should show a guest login option
+# greeter-show-manual-login = True if the greeter should offer a manual login option
+# greeter-show-remote-login = True if the greeter should offer a remote login option
+# user-session = Session to load for users
+# allow-user-switching = True if allowed to switch users
+# allow-guest = True if guest login is allowed
+# guest-session = Session to load for guests (overrides user-session)
+# session-wrapper = Wrapper script to run session with
+# greeter-wrapper = Wrapper script to run greeter with
+# guest-wrapper = Wrapper script to run guest sessions with
+# display-setup-script = Script to run when starting a greeter session (runs as root)
+# display-stopped-script = Script to run after stopping the display server (runs as root)
+# greeter-setup-script = Script to run when starting a greeter (runs as root)
+# session-setup-script = Script to run when starting a user session (runs as root)
+# session-cleanup-script = Script to run when quitting a user session (runs as root)
+# autologin-guest = True to log in as guest by default
+# autologin-user = User to log in with by default (overrides autologin-guest)
+# autologin-user-timeout = Number of seconds to wait before loading default user
+# autologin-session = Session to load for automatic login (overrides user-session)
+# autologin-in-background = True if autologin session should not be immediately activated
+# exit-on-failure = True if the daemon should exit if this seat fails
+#
+[Seat:*]
+#type=xlocal
+#pam-service=lightdm
+#pam-autologin-service=lightdm-autologin
+#pam-greeter-service=lightdm-greeter
+xserver-command=X -nocursor
+#xmir-command=Xmir
+#xserver-config=
+#xserver-layout=
+#xserver-allow-tcp=false
+#xserver-share=true
+#xserver-hostname=
+#xserver-display-number=
+#xdmcp-manager=
+#xdmcp-port=177
+#xdmcp-key=
+#unity-compositor-command=unity-system-compositor
+#unity-compositor-timeout=60
+greeter-session=pi-greeter
+greeter-hide-users=false
+#greeter-allow-guest=true
+#greeter-show-manual-login=false
+#greeter-show-remote-login=true
+#user-session=default
+#allow-user-switching=true
+#allow-guest=true
+#guest-session=
+#session-wrapper=lightdm-session
+#greeter-wrapper=
+#guest-wrapper=
+#display-setup-script=
+#display-stopped-script=
+#greeter-setup-script=
+#session-setup-script=
+#session-cleanup-script=
+#autologin-guest=false
+autologin-user=dks
+#autologin-user-timeout=0
+#autologin-in-background=false
+#autologin-session=
+#exit-on-failure=false
+
+#
+# XDMCP Server configuration
+#
+# enabled = True if XDMCP connections should be allowed
+# port = UDP/IP port to listen for connections on
+# listen-address = Host/address to listen for XDMCP connections (use all addresses if not present)
+# key = Authentication key to use for XDM-AUTHENTICATION-1 or blank to not use authentication (stored in keys.conf)
+# hostname = Hostname to report to XDMCP clients (defaults to system hostname if unset)
+#
+# The authentication key is a 56 bit DES key specified in hex as 0xnnnnnnnnnnnnnn.  Alternatively
+# it can be a word and the first 7 characters are used as the key.
+#
+[XDMCPServer]
+#enabled=false
+#port=177
+#listen-address=
+#key=
+#hostname=
+
+#
+# VNC Server configuration
+#
+# enabled = True if VNC connections should be allowed
+# command = Command to run Xvnc server with
+# port = TCP/IP port to listen for connections on
+# listen-address = Host/address to listen for VNC connections (use all addresses if not present)
+# width = Width of display to use
+# height = Height of display to use
+# depth = Color depth of display to use
+#
+[VNCServer]
+#enabled=false
+#command=Xvnc
+#port=5900
+#listen-address=
+#width=1024
+#height=768
+#depth=8
diff --git a/sys/etc/modprobe.d/raspi-blacklist.conf b/sys/etc/modprobe.d/raspi-blacklist.conf
new file mode 100644 (file)
index 0000000..6ba6d45
--- /dev/null
@@ -0,0 +1,4 @@
+#bt
+blacklist btbcm
+blacklist hci_uart 
+
diff --git a/sys/etc/modules b/sys/etc/modules
new file mode 100644 (file)
index 0000000..77aa6bb
--- /dev/null
@@ -0,0 +1,7 @@
+# /etc/modules: kernel modules to load at boot time.
+#
+# This file contains the names of kernel modules that should be loaded
+# at boot time, one per line. Lines beginning with "#" are ignored.
+
+i2c-dev
+dm_crypt
diff --git a/sys/etc/openvpn/DKS-VPN-dks-hourtrax.conf b/sys/etc/openvpn/DKS-VPN-dks-hourtrax.conf
new file mode 100644 (file)
index 0000000..8cd495f
--- /dev/null
@@ -0,0 +1,153 @@
+client
+dev tun
+proto udp
+remote aran.sysaki.com 1098
+remote aran.dks.lu 1098
+
+remote-random
+resolv-retry infinite
+nobind
+persist-key
+persist-tun
+mute-replay-warnings
+<ca>
+-----BEGIN CERTIFICATE-----
+MIIDHjCCAgagAwIBAgIJAK61AhpxApOUMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV
+BAMMBGFyYW4wHhcNMTcxMjE4MTM0MTIyWhcNMjcxMjE2MTM0MTIyWjAPMQ0wCwYD
+VQQDDARhcmFuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1TydPk4X
+8YyPjXI0htiMsM9C5jP0iH/Hgl7w+t0NL6fvcJhwy8BntJ/FRlS/GFoszmH+0zSt
+pVeymgEwSQ2SrZixSkln2XhbTIH/NUEHosdllECJgj83W0Kpbtms8Cs6/FfhvMB4
+4o/GnN/mkmA9kslguStksV2vQh98Os9rw5gEKM5qFYO5FYUZIIrcsLgyzztPpw4H
+vhrAzxuuu+zg9cOGAihlWV+CSlQJDZyC9hH9xal8UsTlYn9HFqVjr39k7OJg0WC6
+gyjQ92nzqEw49Jkb3NMAarF0zPcP25WV97Aq7EDZV/G8LZbg4p+xCrkz2iUJttu1
+CZjhS9uNsvIAYQIDAQABo30wezAdBgNVHQ4EFgQUqVEvl+MVxdWqNx36igfxqjrJ
+f9QwPwYDVR0jBDgwNoAUqVEvl+MVxdWqNx36igfxqjrJf9ShE6QRMA8xDTALBgNV
+BAMMBGFyYW6CCQCutQIacQKTlDAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjAN
+BgkqhkiG9w0BAQsFAAOCAQEAysh4aGsnv4CebGvzZ9mZlmK5o4JTj4bqRw8sFe4h
+wW/bZ0ggMnnvNZF1VJJgO/UQQq2gIiOw7zLrlSmPHIhRPkQspemk6uU0m2h6K9rn
+AnX3qeVP5dXbZsqr1jI1/aCzxSW5hu6WUfR3Q+u1pGm9HePhrRJj0djW8Kd7UO+s
+PfpIN9gbfLoLnAfCJIc3BccBoD5IqU4s7vleekQGCJHloT5S3MefiIUg7d8leW4V
+8PiCohkgHwMOZy3ACy1ZPuT92x+7JCaNV56X7iT3GZhkyQ4tSKu3UpMFkz8obYKw
+8RrNsXXBRWgnuakkMY6wHJJZBPJn+qDrWbO7nxVTOqY22g==
+-----END CERTIFICATE-----
+</ca>
+<cert>
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            76:24:55:b4:57:ee:a0:c0:7b:e2:b5:a0:5c:3d:80:3d
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=aran
+        Validity
+            Not Before: Feb  6 07:54:45 2018 GMT
+            Not After : Feb  4 07:54:45 2028 GMT
+        Subject: CN=dks-hourtrax
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:b1:67:a4:6b:28:de:50:03:da:b0:48:19:5d:28:
+                    f4:b7:43:68:2c:6d:41:bc:b8:76:ff:b7:93:2f:93:
+                    f5:d1:fc:90:27:6b:1b:f2:84:0d:5e:dc:be:1f:77:
+                    77:bb:66:34:43:ac:7e:35:13:df:90:54:31:e9:67:
+                    44:76:a7:e1:70:61:10:bc:3d:84:0d:75:8d:2c:b3:
+                    06:d0:5a:c0:20:2f:0b:e2:15:5e:3f:38:61:ed:fa:
+                    d8:8e:03:cd:c9:07:94:25:eb:ab:59:4a:7e:92:10:
+                    5d:1a:ee:de:31:35:14:a4:52:f2:79:5c:11:6e:e8:
+                    ef:6c:57:69:a1:94:74:96:b7:e0:dc:ba:a5:b3:b4:
+                    33:65:79:b6:e7:de:aa:1a:d2:c3:9a:7f:6d:73:35:
+                    82:a4:fb:1e:13:2b:c7:96:5c:d9:d9:23:1b:6a:80:
+                    8d:17:22:69:09:88:85:03:be:6e:4b:11:d2:05:a9:
+                    51:7d:bf:a0:a4:e1:55:cb:27:10:4d:3b:fd:b9:cc:
+                    a5:0c:5a:de:6e:95:5c:e9:4e:53:70:df:8f:06:1b:
+                    56:67:46:91:08:39:f4:bb:74:ae:18:90:39:1b:b1:
+                    51:64:5c:bf:58:fb:18:90:0a:ec:ac:68:98:2d:54:
+                    cc:a1:1c:b4:9d:be:6d:ee:16:8a:ed:9a:56:2a:11:
+                    6e:37
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                FE:5D:3F:2A:41:EE:A5:C2:50:D0:9B:CF:89:EB:25:9C:61:3A:67:FF
+            X509v3 Authority Key Identifier: 
+                keyid:A9:51:2F:97:E3:15:C5:D5:AA:37:1D:FA:8A:07:F1:AA:3A:C9:7F:D4
+                DirName:/CN=aran
+                serial:AE:B5:02:1A:71:02:93:94
+
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+            X509v3 Key Usage: 
+                Digital Signature
+    Signature Algorithm: sha256WithRSAEncryption
+         49:3d:9c:8e:10:37:3e:ba:6d:ac:2f:6e:65:60:01:ed:01:70:
+         49:b5:16:af:29:3b:68:ca:85:58:9f:88:ff:14:5e:ae:03:0d:
+         65:99:9d:0e:6b:66:98:ad:f1:55:9c:75:f1:c7:fb:e6:61:68:
+         d6:69:03:48:4f:08:18:d5:ae:60:55:18:70:b4:ab:63:05:b5:
+         54:c7:f7:d0:8b:86:4b:34:3f:50:5c:6f:be:c1:5a:1b:22:cc:
+         24:59:76:e0:8c:c6:32:37:76:ab:bc:2d:63:27:be:2e:a6:5b:
+         86:90:1d:a5:4d:a6:9a:17:ed:57:76:f7:c7:65:f0:2b:29:84:
+         a8:f3:35:5c:66:4d:f2:38:4f:79:df:b4:c1:07:66:3f:87:d0:
+         13:fc:5b:3b:ea:da:db:ab:32:2e:72:f3:84:be:0d:e9:7e:c6:
+         16:22:a9:b7:28:f5:cf:89:51:11:51:9d:bb:ac:fc:1e:fb:85:
+         27:31:74:bd:6d:64:1b:d5:d6:d8:31:ff:ee:3f:9c:17:04:6c:
+         a3:3a:64:3b:22:88:78:75:3e:37:9d:b1:8b:a2:e8:7a:6f:0f:
+         af:9b:2c:a7:0a:dc:af:4a:f3:e5:3b:6e:97:c4:cf:2e:0e:64:
+         71:2e:c8:51:32:90:9a:53:95:be:a9:d1:bd:ea:cb:9b:0a:9d:
+         03:5b:04:85
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgIQdiRVtFfuoMB74rWgXD2APTANBgkqhkiG9w0BAQsFADAP
+MQ0wCwYDVQQDDARhcmFuMB4XDTE4MDIwNjA3NTQ0NVoXDTI4MDIwNDA3NTQ0NVow
+FzEVMBMGA1UEAwwMZGtzLWhvdXJ0cmF4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAsWekayjeUAPasEgZXSj0t0NoLG1BvLh2/7eTL5P10fyQJ2sb8oQN
+Xty+H3d3u2Y0Q6x+NRPfkFQx6WdEdqfhcGEQvD2EDXWNLLMG0FrAIC8L4hVePzhh
+7frYjgPNyQeUJeurWUp+khBdGu7eMTUUpFLyeVwRbujvbFdpoZR0lrfg3Lqls7Qz
+ZXm2596qGtLDmn9tczWCpPseEyvHllzZ2SMbaoCNFyJpCYiFA75uSxHSBalRfb+g
+pOFVyycQTTv9ucylDFrebpVc6U5TcN+PBhtWZ0aRCDn0u3SuGJA5G7FRZFy/WPsY
+kArsrGiYLVTMoRy0nb5t7haK7ZpWKhFuNwIDAQABo4GQMIGNMAkGA1UdEwQCMAAw
+HQYDVR0OBBYEFP5dPypB7qXCUNCbz4nrJZxhOmf/MD8GA1UdIwQ4MDaAFKlRL5fj
+FcXVqjcd+ooH8ao6yX/UoROkETAPMQ0wCwYDVQQDDARhcmFuggkArrUCGnECk5Qw
+EwYDVR0lBAwwCgYIKwYBBQUHAwIwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBCwUA
+A4IBAQBJPZyOEDc+um2sL25lYAHtAXBJtRavKTtoyoVYn4j/FF6uAw1lmZ0Oa2aY
+rfFVnHXxx/vmYWjWaQNITwgY1a5gVRhwtKtjBbVUx/fQi4ZLND9QXG++wVobIswk
+WXbgjMYyN3arvC1jJ74upluGkB2lTaaaF+1XdvfHZfArKYSo8zVcZk3yOE9537TB
+B2Y/h9AT/Fs76trbqzIucvOEvg3pfsYWIqm3KPXPiVERUZ27rPwe+4UnMXS9bWQb
+1dbYMf/uP5wXBGyjOmQ7Ioh4dT43nbGLouh6bw+vmyynCtyvSvPlO26XxM8uDmRx
+LshRMpCaU5W+qdG96subCp0DWwSF
+-----END CERTIFICATE-----
+</cert>
+<key>
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCxZ6RrKN5QA9qw
+SBldKPS3Q2gsbUG8uHb/t5Mvk/XR/JAnaxvyhA1e3L4fd3e7ZjRDrH41E9+QVDHp
+Z0R2p+FwYRC8PYQNdY0sswbQWsAgLwviFV4/OGHt+tiOA83JB5Ql66tZSn6SEF0a
+7t4xNRSkUvJ5XBFu6O9sV2mhlHSWt+DcuqWztDNlebbn3qoa0sOaf21zNYKk+x4T
+K8eWXNnZIxtqgI0XImkJiIUDvm5LEdIFqVF9v6Ck4VXLJxBNO/25zKUMWt5ulVzp
+TlNw348GG1ZnRpEIOfS7dK4YkDkbsVFkXL9Y+xiQCuysaJgtVMyhHLSdvm3uFort
+mlYqEW43AgMBAAECggEAdIXkwtX3H4tIitzwe/Y4tl65/+RLd0+aKeBD9s4yehhv
+/tQlaSc/OP24iyaHKDm7Pm///ZDuGPiUSAAiQeWG969ptJqbL9P0RFXatePFqd38
+6iSq4M+B0zQeJTluYwptTnL6+w0pEXtaDCS1IJEC2+P3i6rjcHvjNsA7i3vdEFji
+Prze6oZ/UoJIGn97V+qePCmcY1raD5SUeHz/tgB1qY3zq9TdjS953pijJKlXgXVM
+pI/WhovKKYFmnbTVsY9WaMXvJz3MTrCosk9LIDdVOIei7m1GI+vQoGS9I3YhFogs
+8qNweEJLLF6wi3tXrur5ZYTIqBYHtrE88RsnfWDqAQKBgQDfdOVTrzoCGVcNPLTK
+nj7qpEfOgJS/1dSRgcH2MGKqPl+fPbqoCXEWKQicAbuVRWKKzvjHon+IvMn1ymgF
+u31uePVtF0adHJP389d5VeeeO7Tl3t50an+k+cPTpRudmpfpUaH+SE7BpXv8V9B+
+eES92r5zhr2xEuVewBIPoyr8twKBgQDLPc3jdsjZpIYheYsP1yDyuFgi8cau7mqw
+4v/KmcyuOAIHlrZB6V+XkNpL0U0gWOdrH4pgEdTKhtu/lycAKNS4AJkJLqE3dQQx
+c3LAdtlQ9NuHoY/SxP2wcZlFEeQsGUpj4azg/bibGh/RNsy5vi6EW8hfrTwvtvfq
+cruGN66agQKBgEwj7bxdGbv7XHEzPTtJPpD/V0RjBcx0FRFbkHbNt+Dgjf6Zrw9w
+4Cq34qod4QgU82Xu7lA/64rxITPyOw2w/CV3a9E3PCVuxnCXktVSUzDUkWg+T7iF
+TWcuf+6O9OD3+0lSOouFoehT9fJfDbj6TBoQ/hIpWFuM38EwWzce1xfzAoGAN9b1
+OGesa0+uoofYUzPSE53eaUtwQSO0IIFdsfZrq/orZJZd4OITp3re3zHUNOz4OBBk
+XlzH0BUZTxxiVMLjHuLbKRCsrqXxzvxfLM36iymbHzqeX1RMNywe5kEyJYOVUrfw
+XaiYDdUxpLOfr/C2qxrkhJT+EkX8+2cmaovl5wECgYEAmO7l3iVoEET9HrxuvinL
+OC600K0iIpNNAIWSywGa4V04WBXrGNMx3SAkxEdQblrSIE1dPxsJnq+clvSyqeo5
+PLi7mSs/XpE7HffjYMhtyzv+UKUCfvbRsjIDH2/Ka6WZZalBw4I0aKcWYvvqACly
+FX4rIxtOEaNSC9J07tCgN1A=
+-----END PRIVATE KEY-----
+</key>
+comp-lzo
+verb 3
+mute 20
+cipher AES-256-CBC
diff --git a/sys/etc/ssh/sshd_config b/sys/etc/ssh/sshd_config
new file mode 100644 (file)
index 0000000..1a565b6
--- /dev/null
@@ -0,0 +1,123 @@
+#      $OpenBSD: sshd_config,v 1.100 2016/08/15 12:32:04 naddy Exp $
+
+# This is the sshd server system-wide configuration file.  See
+# sshd_config(5) for more information.
+
+# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin
+
+# The strategy used for options in the default sshd_config shipped with
+# OpenSSH is to specify options with their default value where
+# possible, but leave them commented.  Uncommented options override the
+# default value.
+
+Port 3587
+#AddressFamily any
+#ListenAddress 0.0.0.0
+#ListenAddress ::
+
+#HostKey /etc/ssh/ssh_host_rsa_key
+#HostKey /etc/ssh/ssh_host_ecdsa_key
+#HostKey /etc/ssh/ssh_host_ed25519_key
+
+# Ciphers and keying
+#RekeyLimit default none
+
+# Logging
+#SyslogFacility AUTH
+#LogLevel INFO
+
+# Authentication:
+
+#LoginGraceTime 2m
+#PermitRootLogin prohibit-password
+#StrictModes yes
+#MaxAuthTries 6
+#MaxSessions 10
+
+#PubkeyAuthentication yes
+
+# Expect .ssh/authorized_keys2 to be disregarded by default in future.
+#AuthorizedKeysFile    .ssh/authorized_keys .ssh/authorized_keys2
+
+#AuthorizedPrincipalsFile none
+
+#AuthorizedKeysCommand none
+#AuthorizedKeysCommandUser nobody
+
+# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
+#HostbasedAuthentication no
+# Change to yes if you don't trust ~/.ssh/known_hosts for
+# HostbasedAuthentication
+#IgnoreUserKnownHosts no
+# Don't read the user's ~/.rhosts and ~/.shosts files
+#IgnoreRhosts yes
+
+# To disable tunneled clear text passwords, change to no here!
+#PasswordAuthentication yes
+#PermitEmptyPasswords no
+
+# Change to yes to enable challenge-response passwords (beware issues with
+# some PAM modules and threads)
+ChallengeResponseAuthentication no
+
+# Kerberos options
+#KerberosAuthentication no
+#KerberosOrLocalPasswd yes
+#KerberosTicketCleanup yes
+#KerberosGetAFSToken no
+
+# GSSAPI options
+#GSSAPIAuthentication no
+#GSSAPICleanupCredentials yes
+#GSSAPIStrictAcceptorCheck yes
+#GSSAPIKeyExchange no
+
+# Set this to 'yes' to enable PAM authentication, account processing,
+# and session processing. If this is enabled, PAM authentication will
+# be allowed through the ChallengeResponseAuthentication and
+# PasswordAuthentication.  Depending on your PAM configuration,
+# PAM authentication via ChallengeResponseAuthentication may bypass
+# the setting of "PermitRootLogin without-password".
+# If you just want the PAM account and session checks to run without
+# PAM authentication, then enable this but set PasswordAuthentication
+# and ChallengeResponseAuthentication to 'no'.
+UsePAM yes
+
+#AllowAgentForwarding yes
+#AllowTcpForwarding yes
+#GatewayPorts no
+X11Forwarding yes
+#X11DisplayOffset 10
+#X11UseLocalhost yes
+#PermitTTY yes
+PrintMotd no
+#PrintLastLog yes
+#TCPKeepAlive yes
+#UseLogin no
+#UsePrivilegeSeparation sandbox
+#PermitUserEnvironment no
+#Compression delayed
+#ClientAliveInterval 0
+#ClientAliveCountMax 3
+#UseDNS no
+#PidFile /var/run/sshd.pid
+#MaxStartups 10:30:100
+#PermitTunnel no
+#ChrootDirectory none
+#VersionAddendum none
+
+# no default banner path
+#Banner none
+
+# Allow client to pass locale environment variables
+AcceptEnv LANG LC_*
+
+# override default of no subsystems
+Subsystem      sftp    /usr/lib/openssh/sftp-server
+
+# Example of overriding settings on a per-user basis
+#Match User anoncvs
+#      X11Forwarding no
+#      AllowTcpForwarding no
+#      PermitTTY no
+#      ForceCommand cvs server
diff --git a/sys/etc/sudoers b/sys/etc/sudoers
new file mode 100644 (file)
index 0000000..6b5b7e4
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# This file MUST be edited with the 'visudo' command as root.
+#
+# Please consider adding local content in /etc/sudoers.d/ instead of
+# directly modifying this file.
+#
+# See the man page for details on how to write a sudoers file.
+#
+Defaults       env_reset
+Defaults       mail_badpass
+Defaults       secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+
+# Host alias specification
+
+# User alias specification
+
+# Cmnd alias specification
+
+# User privilege specification
+root   ALL=(ALL:ALL) ALL
+
+# Allow members of group sudo to execute any command
+%sudo  ALL=(ALL:ALL) NOPASSWD:ALL
+
+# See sudoers(5) for more information on "#include" directives:
+
+#includedir /etc/sudoers.d
diff --git a/sys/etc/systemd/system/autologin@.service b/sys/etc/systemd/system/autologin@.service
new file mode 100644 (file)
index 0000000..79276af
--- /dev/null
@@ -0,0 +1,47 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Getty on %I
+Documentation=man:agetty(8) man:systemd-getty-generator(8)
+Documentation=http://0pointer.de/blog/projects/serial-console.html
+After=systemd-user-sessions.service plymouth-quit-wait.service
+After=rc-local.service
+
+# If additional gettys are spawned during boot then we should make
+# sure that this is synchronized before getty.target, even though
+# getty.target didn't actually pull it in.
+Before=getty.target
+IgnoreOnIsolate=yes
+
+# On systems without virtual consoles, don't start any getty. Note
+# that serial gettys are covered by serial-getty@.service, not this
+# unit.
+ConditionPathExists=/dev/tty0
+
+[Service]
+# the VT is cleared by TTYVTDisallocate
+ExecStart=-/sbin/agetty --autologin dks --noclear %I $TERM
+Type=idle
+Restart=always
+RestartSec=0
+UtmpIdentifier=%I
+TTYPath=/dev/%I
+TTYReset=yes
+TTYVHangup=yes
+TTYVTDisallocate=yes
+KillMode=process
+IgnoreSIGPIPE=no
+SendSIGHUP=yes
+
+# Unset locale for the console getty since the console has problems
+# displaying some internationalized messages.
+Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION=
+
+[Install]
+WantedBy=getty.target
+DefaultInstance=tty1
diff --git a/sys/etc/xdg/lxsession/LXDE-pi/autostart b/sys/etc/xdg/lxsession/LXDE-pi/autostart
new file mode 100644 (file)
index 0000000..ebef229
--- /dev/null
@@ -0,0 +1,6 @@
+#@lxpanel --profile LXDE-pi
+#@pcmanfm --desktop --profile LXDE-pi
+#@xscreensaver -no-splash
+#@point-rpi
+@/home/dks/bin/mountdrives.pl -m
+
diff --git a/sys/etc/xdg/lxsession/LXDE-pi/sshpwd.sh b/sys/etc/xdg/lxsession/LXDE-pi/sshpwd.sh
new file mode 100644 (file)
index 0000000..cd4e4f1
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+#export TEXTDOMAIN=pprompt
+
+#. gettext.sh
+
+#if [ -e /run/sshwarn ] ; then
+#      zenity --warning --no-wrap --text="$(gettext "SSH is enabled and the default password for the 'pi' user has not been changed.\nThis is a security risk - please login as the 'pi' user and run Raspberry Pi Configuration to set a new password.")"
+#fi
+
+
diff --git a/sys/etc/xdg/lxsession/LXDE/autostart b/sys/etc/xdg/lxsession/LXDE/autostart
new file mode 100644 (file)
index 0000000..0575b76
--- /dev/null
@@ -0,0 +1,11 @@
+
+#@lxpanel --profile LXDE
+#@pcmanfm --desktop --profile LXDE
+#screen saver and screen blank
+@xset s off
+@xset -dpms
+@xset s noblank
+#@xscreensaver -no-splash
+
+@xmodmap -e "pointer = 1 10 9 8 7 6 5 4 3 2"
+
diff --git a/sys/etc/xdg/openbox/menu.xml b/sys/etc/xdg/openbox/menu.xml
new file mode 100644 (file)
index 0000000..9f2f01f
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<openbox_menu xmlns="http://openbox.org/"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://openbox.org/
+                file:///usr/share/openbox/menu.xsd">
+
+<menu id="root-menu" label="Openbox 3">
+</menu>
+
+</openbox_menu>
diff --git a/sys/usr/share/plymouth/themes/pix/splash.png b/sys/usr/share/plymouth/themes/pix/splash.png
new file mode 100644 (file)
index 0000000..c392eae
Binary files /dev/null and b/sys/usr/share/plymouth/themes/pix/splash.png differ
diff --git a/sys/var/spool/cron/crontabs/dks b/sys/var/spool/cron/crontabs/dks
new file mode 100644 (file)
index 0000000..4d78dc9
--- /dev/null
@@ -0,0 +1,28 @@
+# Edit this file to introduce tasks to be run by cron.
+# 
+# Each task to run has to be defined through a single line
+# indicating with different fields when the task will be run
+# and what command to run for the task
+# 
+# To define the time you can provide concrete values for
+# minute (m), hour (h), day of month (dom), month (mon),
+# and day of week (dow) or use '*' in these fields (for 'any').# 
+# Notice that tasks will be started based on the cron's system
+# daemon's notion of time and timezones.
+# 
+# Output of the crontab jobs (including errors) is sent through
+# email to the user the crontab file belongs to (unless redirected).
+# 
+# For example, you can run a backup of all your user accounts
+# at 5 a.m every week with:
+# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
+# 
+# For more information see the manual pages of crontab(5) and cron(8)
+# 
+# m h  dom mon dow   command
+HOME=/home/dks
+PATH=/home/dks/bin:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin
+MAILTO=
+SHELL=/bin/bash
+0 1 * * * /home/dks/bin/syncdb.pl
+
diff --git a/web/clock.html b/web/clock.html
new file mode 100644 (file)
index 0000000..f4c27ce
--- /dev/null
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>DKS Hourtrax</title>
+<meta http-equiv="cache-control" content="max-age=0" />
+  <meta http-equiv="cache-control" content="no-cache" />
+  <meta http-equiv="expires" content="0" />
+  <meta http-equiv="expires" content="Tue, 01 Jan 1970 1:00:00 GMT" />
+  <meta http-equiv="pragma" content="no-cache" />
+<link href="css/clock.css" rel="stylesheet" type="text/css">
+</head>
+<body>
+<header>
+       <h1 style="float: left;"><span style="color: #52638e">DKS</span> HourTrax &nbsp;</h1> 
+       <button id="reload" style="top: -20px; height: 50px; width: 100px;" onclick="location.href='clock.html';">Logout</button>&nbsp;
+       <div id="mnuviews" style="float: left;">
+               <button id="mnubtnnewpincode" style="top: -20px; height: 50px; width: 100px;" onclick="login.gotoNewLogin(null);">Changer PIN</button>&nbsp;
+               <button id="mnubtnstamp" style="top: -20px; height: 50px; width: 100px;" onclick="track.loadtrackscreen();">Pointer</button>&nbsp;
+       </div>
+       
+</header>
+<div id="scruserlist" style="float: left; text-align: center;">
+               
+       </div>
+       <div id="scruserpin">
+               <div class="usergreeting" id="usergreeting1">Bonjour Prename,</div>
+               <div class="pincode"><input type="password" value="" id="pincode" readonly="1"/></div>
+               <div id="pinmessage"></div>
+               <div id="keypad">
+               <button class="btnkeypad" onclick="login.setPin('1');">1</button>
+               <button class="btnkeypad" onclick="login.setPin('2');">2</button> 
+               <button class="btnkeypad" onclick="login.setPin('3');">3</button> 
+               <button class="btnkeypad" onclick="login.setPin('4');">4</button> 
+               <button class="btnkeypad" onclick="login.setPin('5');">5</button>
+               <button class="btnkeypad" onclick="login.setPin('6');">6</button>
+               <button class="btnkeypad" onclick="login.setPin('7');">7</button>
+               <button class="btnkeypad" onclick="login.setPin('8');">8</button>
+               <button class="btnkeypad" onclick="login.setPin('9');">9</button>
+               <button class="btnkeypad" onclick="login.setPin('COR');" style="color: red;">COR</button>
+               <button class="btnkeypad" onclick="login.setPin('0');">0</button>
+               <button class="btnkeypad" onclick="login.setPin('OK');" style="color: green;">OK</button>
+               </div>
+       </div>
+       <div id="scrnewuserpin">
+               <div class="usergreeting" id="usergreeting2">Bonjour Prename,</div>
+               <div class="pincode"><input type="password" value="" id="newpincode1" readonly="1" placeholder="nouveau pin..."/><input type="password" value="" id="newpincode2"  readonly="1" placeholder="confirmez pin.."/></div>
+               <div id="pinmessage2"></div>
+               <div id="keypad">
+               <button class="btnkeypad" onclick="login.setPin('1');">1</button>
+               <button class="btnkeypad" onclick="login.setPin('2');">2</button> 
+               <button class="btnkeypad" onclick="login.setPin('3');">3</button> 
+               <button class="btnkeypad" onclick="login.setPin('4');">4</button> 
+               <button class="btnkeypad" onclick="login.setPin('5');">5</button>
+               <button class="btnkeypad" onclick="login.setPin('6');">6</button>
+               <button class="btnkeypad" onclick="login.setPin('7');">7</button>
+               <button class="btnkeypad" onclick="login.setPin('8');">8</button>
+               <button class="btnkeypad" onclick="login.setPin('9');">9</button>
+               <button class="btnkeypad" onclick="login.setPin('COR');" style="color: red;">COR</button>
+               <button class="btnkeypad" onclick="login.setPin('0');">0</button>
+               <button class="btnkeypad" onclick="login.setPin('OK');" style="color: green;">OK</button>
+               </div>
+       </div>
+       <div id="scrtimetracker">
+               <div class="usergreeting" id="usergreeting3">PrĂ©nom Nom</div>
+               <div class="usergreeting"><strong>Total heures mois courrant:</strong>&nbsp;<span id="monthhours">00:00 h</span></div>
+               <div id="timetracker">
+                       <button class="btntrack" id="btntrackin" onclick="track.setTrack('in');">EntrĂ©e<br/><span id="lasttrackin"></span></button><button class="btntrack" id="btntrackout" onclick="track.setTrack('out');">Sortie<br/><span id="lasttrackout"></span></button>
+               </div>
+       </div>
+       <div id="scrloader" style="text-align: center;">
+               
+               <progress style=" margin: auto; margin-top: 200px; width: 300px;" min="0" max="100"></progress>
+               
+       </div>
+       <div id="scrstatus" style="text-align: center;">
+               
+               <div id="statusmsg" style=" margin: auto; margin-top: 100px; width: 600px; font-size: 20px;" ></div>
+               
+       </div>
+       
+
+<script src="js/jquery-3.2.1.min.js"></script>
+<!-- <script src="js/database.js"></script> -->
+<script src="js/login.js"></script>
+<script src="js/track.js"></script>
+<script src="js/app.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/web/css/admin.css b/web/css/admin.css
new file mode 100644 (file)
index 0000000..1487d61
--- /dev/null
@@ -0,0 +1,240 @@
+       * {
+               font-family: sans-sherif, Arial, Helvetica !important;
+       }
+       body,html {
+               margin: 0px;
+       }
+header {
+               overflow: hidden;
+               position: fixed; /* Set the navbar to fixed position */
+       top: 0; /* Position the navbar at the top of the page */
+       width: 100%; /* Full width */
+               height: 60px;
+               padding-left: 5px;
+               background: linear-gradient(to right, purple, gray);
+}
+
+header > h1 {
+       color: yellow;
+       margin-top: 10px;
+       float: left;
+       text-shadow: 0px -1px 0px rgba(0,0,0,.5);
+}
+div.headerlinks {
+       margin-left: 10px;
+       margin-top: 5px;
+       float: left;
+}
+button {
+       /* display: inline-block;
+        */
+        text-decoration: none;
+       color: #fff;
+       font-weight: bold;
+       background-color: #538fbe;
+       /* padding: 20px 70px;
+       font-size: 24px; */
+       border: 1px solid #2d6898;
+       background-image: linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -o-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -moz-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -webkit-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -ms-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       
+       background-image: -webkit-gradient(
+               linear,
+               left bottom,
+               left top,
+               color-stop(0, rgb(73,132,180)),
+               color-stop(1, rgb(97,155,203))
+       );
+       -webkit-border-radius: 5px;
+       -moz-border-radius: 5px;
+       border-radius: 5px;
+       text-shadow: 0px -1px 0px rgba(0,0,0,.5);
+       /* -webkit-box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       -moz-box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5); */
+       /* -webkit-transition: all .1s ease-in-out;
+       -moz-transition: all .2s ease-in-out;
+       transition: all .2s ease-in-out;
+       -webkit-transform: rotateX(20deg); */
+}
+
+
+button {
+       margin-left: 3px;
+       height: 30px;
+}
+
+div.headerlinks button {
+       height: 50px;
+}
+
+
+section.main {
+       width: 1024px;
+       margin: auto;
+       margin-top: 70px;
+       
+}
+table {
+       margin-top: 10px;
+       width: 100%;
+       border: 1px solid silver;
+       border-collapse: collapse;
+}
+table thead {
+       background-color: gray;
+}
+
+table thead th {
+       padding: 5px 10px;
+       border: 1px solid silver;
+}
+
+table tbody tr:nth-child(even){
+       background-color: rgba(128,0,128,0.3);
+       
+}
+
+table tbody tr:hover {
+       background-color: #79bcff;
+}
+
+table tbody td {
+       border: 1px solid silver;
+       padding: 5px 10px;
+}
+input[type=number]::-webkit-inner-spin-button, 
+input[type=number]::-webkit-outer-spin-button { 
+  -webkit-appearance: none; 
+  margin: 0; 
+}
+
+
+
+input, select {
+       border: 1px solid silver;
+       -webkit-border-radius: 5px;
+       -moz-border-radius: 5px;
+       border-radius: 5px;
+       padding: 5px 10px;
+}
+
+input:disabled {
+       border: 0;
+       color: #000000;
+       background: transparent !important;
+}
+
+.page {
+       display: none;
+}
+
+input[type=number] {
+       text-align: right;
+}
+
+
+
+tbody > tr.selected {
+       background-color: #0080ff;
+}
+
+.panel {
+       border: 1px solid silver;
+       width: 100%;
+       display: block;
+       border: 1px solid silver;
+       padding: 5px;
+       -webkit-border-radius: 5px;
+       -moz-border-radius: 5px;
+       border-radius: 5px;
+       margin: 4px;
+}
+
+.panel-head {
+       margin: 0px;
+       padding: 3px;
+       height: 20px;
+       
+}
+
+h4 {
+       margin: 0px;
+       padding: 2px;
+}
+
+.tabset > input[type="radio"] {
+  position: absolute;
+  left: -200vw;
+}
+
+.tabset .tab-panel {
+  display: none;
+}
+
+.tabset > input:first-child:checked ~ .tab-panels > .tab-panel:first-child,
+.tabset > input:nth-child(3):checked ~ .tab-panels > .tab-panel:nth-child(2),
+.tabset > input:nth-child(5):checked ~ .tab-panels > .tab-panel:nth-child(3),
+.tabset > input:nth-child(7):checked ~ .tab-panels > .tab-panel:nth-child(4),
+.tabset > input:nth-child(9):checked ~ .tab-panels > .tab-panel:nth-child(5),
+.tabset > input:nth-child(11):checked ~ .tab-panels > .tab-panel:nth-child(6) {
+  display: block;
+}
+
+.tabset > label {
+  position: relative;
+  display: inline-block;
+  padding: 15px 15px 15px;
+  border: 1px solid transparent;
+  border-bottom: 0;
+  cursor: pointer;
+  font-weight: 600;
+}
+
+/* .tabset > label::after {
+  position: absolute;
+  left: 15px;
+  bottom: 10px;
+  width: 22px;
+  height: 4px;
+  background: #8d8d8d;
+} */
+
+.tabset > label:hover,
+.tabset > input:focus + label {
+  color: #06c;
+}
+
+.tabset > label:hover::after,
+.tabset > input:focus + label::after,
+.tabset > input:checked + label::after {
+  background: #06c;
+} 
+
+.tabset > input:checked + label {
+  border-color: #ccc;
+  border-bottom: 1px solid #fff;
+  margin-bottom: -1px;
+}
+
+.tab-panel {
+  padding: 30px 0;
+  border-top: 1px solid #ccc;
+}
+
+label.formlabel {
+       width: 120px;
+}
+
+div.row {
+       margin-top: 3px;
+       margin-bottom: 3px;
+}
+
+section.sysconfig {
+       display: none;
+}
+
diff --git a/web/css/clock.css b/web/css/clock.css
new file mode 100644 (file)
index 0000000..86cd02a
--- /dev/null
@@ -0,0 +1,236 @@
+       
+       body,html {
+               overflow: hidden;
+               margin: 0px;
+               
+       }
+       div {
+               margin: auto;
+       }
+       header {
+               height: 60px;
+               padding-left: 5px;
+               background: linear-gradient(to right, purple, gray);
+       }
+       h1 {
+               padding-top: 5px;
+               margin-top: 0px;
+               color: yellow;
+       }
+       button {
+       display: inline-block;
+       text-decoration: none;
+       color: #fff;
+       font-weight: bold;
+       background-color: #538fbe;
+       /* padding: 20px 70px;
+       font-size: 24px; */
+       border: 1px solid #2d6898;
+       background-image: linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -o-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -moz-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -webkit-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       background-image: -ms-linear-gradient(bottom, rgb(73,132,180) 0%, rgb(97,155,203) 100%);
+       
+       background-image: -webkit-gradient(
+               linear,
+               left bottom,
+               left top,
+               color-stop(0, rgb(73,132,180)),
+               color-stop(1, rgb(97,155,203))
+       );
+       -webkit-border-radius: 5px;
+       -moz-border-radius: 5px;
+       border-radius: 5px;
+       text-shadow: 0px -1px 0px rgba(0,0,0,.5);
+       -webkit-box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       -moz-box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       box-shadow: 0px 6px 0px #2b638f, 0px 3px 15px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       -webkit-transition: all .1s ease-in-out;
+       -moz-transition: all .2s ease-in-out;
+       transition: all .2s ease-in-out;
+       -webkit-transform: rotateX(20deg);
+}
+
+button:hover {
+       background-image: linear-gradient(bottom, rgb(79,142,191) 0%, rgb(102,166,214) 100%);
+       background-image: -o-linear-gradient(bottom, rgb(79,142,191) 0%, rgb(102,166,214) 100%);
+       background-image: -moz-linear-gradient(bottom, rgb(79,142,191) 0%, rgb(102,166,214) 100%);
+       background-image: -webkit-linear-gradient(bottom, rgb(79,142,191) 0%, rgb(102,166,214) 100%);
+       background-image: -ms-linear-gradient(bottom, rgb(79,142,191) 0%, rgb(102,166,214) 100%);
+       
+       /* background-image: -webkit-gradient(
+               linear,
+               left bottom,
+               left top,
+               color-stop(0, rgb(79,142,191)),
+               color-stop(1, rgb(102,166,214))
+       ); */
+}
+
+button:active {
+-webkit-box-shadow: 0px 2px 0px #2b638f, 0px 1px 6px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+-moz-box-shadow: 0px 2px 0px #2b638f, 0px 1px 6px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+box-shadow: 0px 2px 0px #2b638f, 0px 1px 6px rgba(0,0,0,.4), inset 0px 1px 0px rgba(255,255,255,.3), inset 0px 0px 3px rgba(255,255,255,.5);
+       background-image: linear-gradient(bottom, rgb(88,154,204) 0%, rgb(90,150,199) 100%);
+       background-image: -o-linear-gradient(bottom, rgb(88,154,204) 0%, rgb(90,150,199) 100%);
+       background-image: -moz-linear-gradient(bottom, rgb(88,154,204) 0%, rgb(90,150,199) 100%);
+       background-image: -webkit-linear-gradient(bottom, rgb(88,154,204) 0%, rgb(90,150,199) 100%);
+       background-image: -ms-linear-gradient(bottom, rgb(88,154,204) 0%, rgb(90,150,199) 100%);
+       
+       background-image: -webkit-gradient(
+               linear,
+               left bottom,
+               left top,
+               color-stop(0, rgb(88,154,204)),
+               color-stop(1, rgb(90,150,199))
+       );
+    -webkit-transform: translate(0, 4px) rotateX(20deg);  
+    -moz-transform: translate(0, 4px);  
+    transform: translate(0, 4px);  
+}
+
+button:disabled {
+       background-image: none;
+       background-color: #f4f4f4;
+       color: #000;
+       font-weight: bold;
+       box-shadow: none;
+       -webkit-transform: none;
+}
+       /* button {
+               background-color: lightgrey;
+               border: 1px solid silver;
+               outline: none;
+       } */
+       button.user {
+               height: 90px;
+               width: 90px;
+               margin: 10px;
+                white-space: nowrap;
+                overflow: hidden;
+       }
+       button.btnkeypad {
+               height: 70px;
+               width: 70px;
+               margin: 5px;
+               font-weight: bold;
+               font-size: 20px;
+                white-space: nowrap;
+                overflow: hidden;
+       }
+       button.btntrack {
+               background-color: 
+               height: 150px;
+               width: 150px;
+               margin: 15px;
+               font-weight: bold;
+               font-size: 30px;
+                white-space: nowrap;
+                overflow: hidden;
+       }
+       #scrloader {
+               display: none;
+               margin: auto;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #scruserlist {
+               display: none;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #scruserpin {
+               display: none;
+               margin: auto;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #scrnewuserpin {
+               display: none;
+               margin: auto;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #scrtimetracker{
+               display: none;
+               margin: auto;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #scrstatus{
+               display: none;
+               margin: auto;
+               border: 1px solid silver;
+               max-width: 800px;
+               width: 800px;
+               height: 430px;
+               max-height: 430px;
+               overflow: hidden;
+       }
+       #keypad {
+               border: 1px solid silver;
+               width: 270px;
+               margin: auto;
+       }
+       #timetracker{
+               border: 1px solid silver;
+               width: 400px;
+               margin: auto;
+               margin-top: 20px;
+               text-align: center;
+       }
+       div.pincode {
+               text-align: center;
+       }
+       #pincode,#newpincode1,#newpincode2 {
+               min-height: 30px;
+               border: 1px solid silver;
+               width: 270px;
+               margin: auto;
+               text-align: center;
+               font-weight: bold;
+               font-size: 10px;
+       }
+       #newpincode1,#newpincode2{
+               width: 135px;
+       }
+       .usergreeting {
+               text-align: center;
+       }
+       #pinmessage, #pinmessage2 {
+               height: 30px;
+               display: block;
+               color: red;
+               font-weight: bold;
+               text-align: center;
+       }
+       #lasttrackin,#lasttrackout {
+               font-size: 12px; 
+               color: #000;
+               font-weight: bold;
+       }
+       
+       
+/* screenheight: 480;*/
+/* screenwidth: 80px;*/
\ No newline at end of file
diff --git a/web/index.html b/web/index.html
new file mode 100644 (file)
index 0000000..85b7f22
--- /dev/null
@@ -0,0 +1,249 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Hourtrax - Admin</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<meta http-equiv="cache-control" content="max-age=0" />
+<meta http-equiv="cache-control" content="no-cache" />
+<meta http-equiv="expires" content="0" />
+<meta http-equiv="expires" content="Tue, 01 Jan 1970 1:00:00 GMT" />
+<meta http-equiv="pragma" content="no-cache" />
+<link href="css/admin.css" rel="stylesheet" type="text/css">
+</head>
+<body>
+       <header>
+               <h1>Hourtrax</h1>
+               <div class="headerlinks">
+                       <button onclick="admin.loadpage('page_track');">Pointages</button>
+                       <button onclick="admin.loadpage('page_staff');">EmployĂ©(e)s</button>
+
+
+                       <button onclick="admin.loadpage('page_sysconfig');">Configuration</button>
+               </div>
+       </header>
+       <section class="main">
+               <div id="page_track" class="page">
+
+                       <div id="pagenav_track" class="pagenav">
+                               <span id="tracklabel">Pointages:</span> <select id="select_table"
+                                       onchange="admin.change_tracktable();">
+                                       <option value="trackincomplete">incomplet</option>
+                                       <option value="trackbyday">par jour</option>
+                                       <option value="trackbystaff">par employĂ©(e)</option>
+                               </select> <input type="date" class="trackbyday" id="cmb_trackdate"
+                                       onchange="admin.change_tracktable();" /> <input type="month"
+                                       class="trackbystaff" id="cmb_trackmonth"
+                                       onchange="admin.change_tracktable();" /> <select
+                                       class="trackbystaff" id="select_staff"
+                                       onchange="admin.change_tracktable();">
+                                       <option value="employe">Prename Surname</option>
+                               </select>
+                       </div>
+                       <table id="table_trackbyday" style="display: none;">
+                               <thead>
+                                       <tr>
+                                               <th>Nom</th>
+                                               <th>EntrĂ©e</th>
+                                               <th>Sortie</th>
+                                       </tr>
+
+                               </thead>
+                               <tbody id="table_trackbyday_tbody">
+                               </tbody>
+                       </table>
+                       <table id="table_trackbystaff" style="display: none;">
+                               <thead>
+                                       <tr>
+                                               <th>Date</th>
+                                               <th>EntrĂ©e</th>
+                                               <th>Sortie</th>
+                                       </tr>
+
+                               </thead>
+                               <tbody id="table_trackbystaff_tbody">
+                               </tbody>
+                       </table>
+                       <table id="table_trackincomplete" style="display: none;">
+                               <thead>
+                                       <tr>
+                                               <th>Date</th>
+                                               <th>Nom</th>
+                                               <th>EntrĂ©e</th>
+                                               <th>Sortie</th>
+                                       </tr>
+
+                               </thead>
+                               <tbody id="table_trackincomplete_tbody">
+                               </tbody>
+                       </table>
+               </div>
+               <div id="page_staff" class="page">
+                       <div class="pagenav">
+                               <button onclick="admin.addstaff();">Ajouter EmployĂ©(e)</button>
+                       </div>
+                       <table id="table_staff">
+                               <thead>
+                                       <tr>
+                                               <th>Nom</th>
+                                               <th>PrĂ©nom</th>
+                                               <th>date debut</th>
+                                               <th>H/(Semaine)</th>
+                                               <th>bloquĂ©</th>
+                                               <th>inactive</th>
+                                               <th>Code PIN</th>
+                                               <th>&nbsp;</th>
+                                       </tr>
+
+                               </thead>
+                               <tbody id="table_staff_tbody">
+                               </tbody>
+                       </table>
+               </div>
+               <div id="page_sysconfig" class="page">
+                       <div  class="pagenav">
+                               <span id="tracklabel">Configuration:</span> <select id="select_sysconfig"
+                                       onchange="sysconfig.showsection();">
+                                       <option value="sysexport">Export des donnĂ©es</option>
+                                       <option value="sysaccess">Accès admin</option>
+                                       <!--<option value="syspause">Pause obligatoire</option>-->
+                                       <option value="syssync">Sychronisation</option>
+                                       <!--<option value="sysbackup">Backup</option>-->
+                                       <option value="syssystem">WLAN & Hostname</option>
+                               </select>
+                               <button onclick="sysconfig.shutdown();">Shutdown</button>
+                               <button onclick="sysconfig.restart();">Restart</button>
+                       </div>
+                       <hr/>
+                       <section id="sysexport" class="sysconfig" style="display: block;">
+                                       <h4>Export des donnĂ©es comme fichier CSV:</h4>
+                                                       <div class="row">
+                                                       <select id="cmb_exportmonth"/>
+                                                       </select>
+                                                       
+                                                       <button onclick="sysconfig.exportdata();">Exporter</button>
+                                                       </div>
+                               </section>
+                       <section id="syspause" class="sysconfig">
+                                                       <h4>pause obligatoire</h4>
+                                                       <div class="row">
+                                                               max longueur pause: <input type="number"
+                                                                       id="sysconfig_pausetime" style="width: 40px;"> minutes
+                                                       </div>
+                                                       <div class="row">
+                                                               pause de <input type="time" id="sysconfig_pausestarttime"
+                                                                       style="width: 70px"> Ă  <input type="time"
+                                                                       id="sysconfig_pauseendtime" style="width: 70px">
+                                                       </div>
+                                                       <div class="row">
+                                                                       <input type="checkbox" name="radiopausecond"
+                                                                               id="sysconfig_alwaysremovepause"> dĂ©duire toujours
+                                                               </div>
+                                                               <div class="row">
+                                                                       <input type="checkbox" name="radiopausecond"
+                                                                               id="sysconfig_onlywhennottracked"> si pointĂ© dans la
+                                                                       periode marquĂ©, dĂ©duire ce qui manque
+                                                               </div>
+                                                               <button onclick="sysconfig.savepause();">Sauvegarder</button>
+                                                       
+                       </section>
+                       <section id="syssync" class="sysconfig">                                
+                                                               <h4>Synchronisation base de donnĂ©e</h4>
+                                                               <div class="row">
+                                                                       DB-Type:<select id="sysconfig_syncdbtype">
+                                                                               <option value="sqlite">SQLite</option>
+                                                                       </select> <label>Host:</label><input type="text" id="sysconfig_synchost">
+                                                                       <label>Dossier PartagĂ©:</label><input type="text"
+                                                                               id="sysconfig_syncpath">
+                                                               </div>
+
+                                                               <div class="row">
+                                                                       Utilisateur: <input type="text" id="sysconfig_syncuser">&nbsp;Mot
+                                                                       de passe: <input type="password" id="sysconfig_syncpwd" />
+                                                               </div>
+                                                               <div class="row">
+                                                                       Synchronisation quotidienne Ă  <input type="time"
+                                                                               id="sysconfig_synctime" />
+                                                               </div>
+                                                               <div class="row">
+                                                                       <span style="font-weight: bold;">Base de donnĂ©e</span>
+                                                               </div>
+                                                               <div class="row">
+                                                                       Nom: <input type="text" id="sysconfig_syncdbname">
+                                                               </div>
+                                                               <div class="row">
+                                                                       <span style="font-weight: bold;">Champs:</span>
+                                                               </div>
+                                                               
+                                                               <div class="row">
+                                                                       SQL:
+                                                                       <textarea id="sysconfig_syncsql" style="width: 100%; height: 200px;">
+                                                                               
+                                                                       </textarea>
+                                                                       
+                                                               </div>
+                                                               <div class="row">
+                                                               <button onclick="sysconfig.setSyncData();">Sauvegarder</button>
+                                                               &nbsp;
+                                                               <button onclick="">Synchroniser</button>&nbsp;
+                                                               <button onclick="">Test Connection</button>
+                                                               </div>
+                                                       </section>
+                                       <section id="sysbackup" class="sysconfig">
+                                                       <h4>Backup</h4>
+                                                       <div>
+                                                       <select id="sysconfig_backuptype"><option>Samba
+                                                                       / Windows Share</option></select> Host: <input type="text"
+                                                               id="sysconfig_backuphost"> Dossier partagĂ©: <input
+                                                               type="text" id="sysconfig_backuppath">
+                                               </div>
+                                               <div>
+                                                       Utilisateur: <input type="text" id="sysconfig_backupuser">&nbsp;Mot
+                                                       de passe: <input type="password" id="sysconfig_backupPwd" />
+                                               </div>
+                                               <div>
+                                                       Backup quotidien Ă  <input type="time" id="sysconfig_backuptime" />
+                                               </div>
+                                               <div>
+                                                       Max nombre de backups Ă  garder: <input type="number" id="sysconfig_backupmaxnum" />
+                                               </div>
+                                               <button onclick="">Test Connection</button><button onclick="">Backup Maintenant</button><button onclick="">Voir les backups</button>
+                                       </section>
+                                       <section id="sysaccess" class="sysconfig">
+                                               <h4>Accès Admin</h4>
+                                               <div class="row">
+                                                       <label>Login:</label><input type="text" id="sysconfig_user" placeholder="login"> 
+                                               </div>
+                                               <div class="row">
+                                                       <label>Mot de passe:</label>
+                                                       <input type="password" id="sysconfig_pwd1" placeholder="mot de passe.." />
+                                                       <input type="password" id="sysconfig_pwd2" placeholder="confirmer mot de passe" />
+                                               </div>
+                                               <button onclick="sysconfig.setSystemUser();">Sauvegarder</button>
+       
+                                       </section>
+                                       <section id="syssystem" class="sysconfig">
+
+                                               <div>
+                                                       Hostname:<input type="text" id="sysconfig_hostname">
+                                               </div>
+                                               <div>
+                                                       Wifi SSID:<select id="sysconfig_wlanssid"></select>
+                                                       Mot de passe: <input type="password" id="sysconfig_wlanpwd" />
+                                               </div>
+                                               <button onclick="sysconfig.setNetworkConfig();">Sauvegarder</button>
+                                               <div class="row"><h3>RĂ©seau actuelle:</h3></div>
+                                               <div id="sysconfig_netstatus" class="row">
+                                               
+                                               </div>
+                                       </section>
+                                       
+                               </div>
+
+       </section>
+
+       <script src="js/admin.js"></script>
+       <script src="js/request.js"></script>
+       <script src="js/sysconfig.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/web/js/admin.js b/web/js/admin.js
new file mode 100644 (file)
index 0000000..5afc8ed
--- /dev/null
@@ -0,0 +1,249 @@
+var admin = {
+               lastrow: null,
+               lastpage: null,
+               lasttracktable: null,
+               currentrowdata: null,
+               loadpage: function (pagename){
+                       //disable all first
+                       admin.lastrow = null;
+                       document.getElementById(admin.lastpage).style.display = 'none';
+                       if (pagename == 'page_track'){
+                               document.getElementById("select_table").style.display = 'inline';
+                               
+                               document.getElementById("page_track").style.display = 'block';
+                               admin.change_tracktable();
+                               
+                       } else if (pagename == 'page_staff'){
+                               document.getElementById("page_staff").style.display = 'block';
+                               admin.loadtable_staff();
+                       }else if (pagename == 'page_exportdata'){
+                               document.getElementById("page_exportdata").style.display = 'block';
+                       } else if (pagename == 'page_sysconfig'){
+                               sysconfig.showsection(sysconfig.lastsection);
+                               document.getElementById("page_sysconfig").style.display = 'block';
+                               
+                       }
+                       admin.lastpage=pagename;
+               },
+               change_tracktable: function(){
+                       document.getElementById("cmb_trackdate").style.display = 'none';
+                       document.getElementById("cmb_trackmonth").style.display = 'none';
+                       document.getElementById("select_staff").style.display = 'none';
+                       if (admin.lasttracktable != null){
+                               document.getElementById(admin.lasttracktable).style.display = 'none';
+                       }
+                       var tablesel = document.getElementById("select_table").value;
+                       //console.log(tablesel);
+                       document.getElementById('table_' + tablesel).style.display = 'block';
+                       admin.lasttracktable = 'table_' + tablesel;
+                       if (tablesel == "trackbyday"){
+                               document.getElementById('cmb_trackdate').style.display = 'inline';
+                               admin.loadtable_trackbyday();
+                       }else if (tablesel == "trackbystaff"){
+                               document.getElementById("cmb_trackmonth").style.display = 'inline';
+                               document.getElementById("select_staff").style.display = 'inline';
+                               admin.loadtable_trackbystaff();
+                       } else {
+                               admin.loadtable_trackincomplete();
+                       }
+               },
+               loadtable_staff: function(){
+                       admin.lastrow = null;
+                       document.getElementById("table_staff_tbody").innerHTML="";
+                       var xq = {"type":"querysorted","sql":"select st.id,st.prename,st.surname,st.pin,st.disabled,st.blocked, co.startdate, co.weekhours from staff st left join contract co on (st.id=co.idstaff) order by st.surname,st.prename,st.id;"};
+                       var tbldata = req.reqdata('POST','sqlite/query',xq,null);
+                       var ntbldata = ''; 
+                       for (var i in tbldata.sqldata){
+                               var row = '<tr data-id="'+tbldata.sqldata[i].id+'" onclick="admin.setRowEditable(this);" >';
+                               //row += '<td><input type="text" value="'+ tbldata.sqldata[i].id+'" onfocusout="admin.changeData(this);" style="width: 50px;" class="textright" placeholder="Id" id="staff_id_'+ i+'"  disabled="1"/></td>';
+                               row += '<td><input type="text" value="'+ tbldata.sqldata[i].surname+'" onfocusout="admin.changeData(this);" placeholder="Nom" id="staff_surname_'+ i+'"  disabled="1"/></td>';
+                               row += '<td><input type="text" value="'+ tbldata.sqldata[i].prename+'" onfocusout="admin.changeData(this);" placeholder="PrĂ©nom" id="staff_prename_'+ i+'"  disabled="1"/></td>';
+                               row += '<td><input type="date" value="'+ tbldata.sqldata[i].startdate+'" onfocusout="admin.changeData(this);" style="width: 150px;" id="contract_startdate_'+ i+'"  disabled="1"/></td>';
+                               row += '<td><input type="number" value="'+ tbldata.sqldata[i].weekhours+'" onfocusout="admin.changeData(this);" style="width: 40px;" class="textright" id="contract_weekhours_'+ i+'"  disabled="1"/>h</td>';
+                               row += '<td><input type="checkbox" value="true" onchange="admin.changeData(this);" id="staff_blocked_'+ i+'" '+((tbldata.sqldata[i].blocked == true)?'checked="1"':'')+'  disabled="1"/></td>';
+                               row += '<td><input type="checkbox" value="true" onchange="admin.changeData(this);" id="staff_disabled_'+ i+'" '+((tbldata.sqldata[i].disabled == true)?'checked="1"':'')+'  disabled="1"/></td>';
+                               row += '<td><input type="password" ' + ((tbldata.sqldata[i].pin!=null)?'value="'+ tbldata.sqldata[i].pin +'"':"")+' onfocusout="admin.changeData(this);" placeholder="Code Pin" id="staff_pin_'+ i+'"  disabled="1"/></td>';
+                               row += '<td><button id="staff_delete" onclick="admin.deleteRow(\'staff\',\''+ tbldata.sqldata[i].id +'\');">Supprimer</button></td>';
+                               row += '</tr>';
+                               ntbldata += row;
+                       }
+                       document.getElementById("table_staff_tbody").innerHTML=ntbldata;
+               },
+               loadtable_trackbyday: function(){
+                       admin.lastrow = null;
+                       document.getElementById("table_trackbyday_tbody").innerHTML="";
+                       var trackdate = document.getElementById("cmb_trackdate").value;
+                       //console.log("trackdate" + trackdate);
+                       var xq = {"type":"querysorted","sql":"select hr.id,co.idstaff,st.prename,st.surname,hr.stamp_in, hr.stamp_out from staff st left join contract co on (st.id=co.idstaff) left join (select id,idstaff,stamp_in,stamp_out from hours where date(stamp_in) = date('"+trackdate +"')) hr on (st.id=hr.idstaff) where st.disabled is null and co.startdate <= date('"+ trackdate +"') order by st.surname,st.prename,co.idstaff;"};
+                       var tbldata = req.reqdata('POST','sqlite/query',xq,null);
+                       var ntbldata = ''; 
+                       for (var i in tbldata.sqldata){
+                               var row='<tr data-id="'+tbldata.sqldata[i].id+'" data-idstaff="'+tbldata.sqldata[i].idstaff+'" onclick="admin.setRowEditable(this);">';
+                               row += '<td>'+tbldata.sqldata[i].surname+' '+tbldata.sqldata[i].prename+'</td>';
+                               row += '<td><input type="time" id="hours_stampin_'+ i +'" value="'+((tbldata.sqldata[i].stamp_out != null)?tbldata.sqldata[i].stamp_in.substring(11,16):'')+'" onfocusout="admin.changeData(this);" disabled="1"/></td>';
+                               row += '<td><input type="time" id="hours_stampout_'+ i +'" value="'+((tbldata.sqldata[i].stamp_out != null)?tbldata.sqldata[i].stamp_out.substring(11,16):'')+'" onfocusout="admin.changeData(this);" disabled="1"/></td>';
+                               row +='</tr>';
+                               ntbldata += row;
+                       }
+                       document.getElementById("table_trackbyday_tbody").innerHTML=ntbldata;
+               },
+               loadtable_trackbystaff: function(){
+                       admin.lastrow = null;
+                       document.getElementById("table_trackbystaff_tbody").innerHTML="";
+                       var trackdate = document.getElementById("cmb_trackmonth").value;
+                       var staffid = document.getElementById("select_staff").value;
+                       var bdate = new Date(trackdate + "-01");
+                       var edate = new Date(bdate.getFullYear(),bdate.getMonth()+1,1);
+                       var sqldays = new Array();
+                   var cdate2 = new Date();
+                   for (var cdate2=bdate;cdate2<=edate;cdate2.setDate(cdate2.getDate() +1)){
+                       sqldays.push ("SELECT date('" + cdate2.toISOString().substring(0,10) +"') as daydate");
+                   }
+                   var  sqlret = sqldays.join(' UNION ALL ');
+                   
+                       var sql = "select md.daydate,hr.id,hr.stamp_in,hr.stamp_out,hr.idstaff from  (" + sqlret +") md left join (select * from hours where idstaff='"+staffid+"' and date(stamp_in) between date('"+trackdate+"-01') and date('"+trackdate+"-01','+1 month','-1 day')) hr on (date(hr.stamp_in)= md.daydate);";
+                       var xq = {"type":"querysorted","sql":sql};
+                       var tbldata = req.reqdata('POST','sqlite/query',xq,null);
+                       var ntbldata = ''; 
+                       
+//                     var cdate = new Date(bdate);
+//                     
+//                     while (cdate <= enddate){
+//                             console.log("cdate:" + cdate.toISOString());
+//                             cdate.setDate(cdate.getDate() + 1);
+//                     } 
+                       for (var i in tbldata.sqldata){
+                               
+                               var row='<tr data-id="'+((tbldata.sqldata[i].id != null)?tbldata.sqldata[i].id:'')+'" data-idstaff="'+staffid+'" onclick="admin.setRowEditable(this);">';
+                               row += '<td><input type="date" id="hours_stampdate_'+ i +'" value="'+tbldata.sqldata[i].daydate+'" disabled="1" readonly="1"/></td>';
+                               row += '<td><input type="time" id="hours_stampin_'+ i+'" value="'+((tbldata.sqldata[i].stamp_in != null)?tbldata.sqldata[i].stamp_in.substring(11,16):"")+'" onfocusout="admin.changeData(this);" disabled="1"/></td>';
+                               row += '<td><input type="time" id="hours_stampout_'+ i +'" value="'+((tbldata.sqldata[i].stamp_out != null)?tbldata.sqldata[i].stamp_out.substring(11,16):'')+'" onfocusout="admin.changeData(this);" disabled="1"/></td>';
+                               row +='</tr>';
+                               ntbldata += row;
+                       }
+                       document.getElementById("table_trackbystaff_tbody").innerHTML=ntbldata;
+               },
+               loadtable_trackincomplete: function(){
+                       admin.lastrow = null;
+                       document.getElementById("table_trackincomplete_tbody").innerHTML="";
+                       var xq = {"type":"querysorted","sql":"select hr.id,hr.idstaff,st.prename,st.surname,hr.stamp_in,hr.stamp_out from hours hr left join staff st on (hr.idstaff=st.id) where stamp_out is null order by hr.stamp_in,st.surname,st.prename,st.id; "};
+                       var tbldata = req.reqdata('POST','sqlite/query',xq,null);
+                       var ntbldata = ''; 
+                       for (var i in tbldata.sqldata){
+                               
+                               var row='<tr data-id="'+tbldata.sqldata[i].id+'" data-idstaff="'+tbldata.sqldata[i].idstaff+'" onclick="admin.setRowEditable(this);">';
+                               row += '<td><input type="date" id="hours_stampdate_'+ i+'" value="'+tbldata.sqldata[i].stamp_in.substring(0,10)+'" disabled="1" onfocusout="admin.changeData(this);" readonly="1"/></td>';
+                               row += '<td>'+tbldata.sqldata[i].surname+' '+tbldata.sqldata[i].prename+'</td>';
+                               row += '<td><input type="time" id="hours_stampin_'+ i+'" value="'+tbldata.sqldata[i].stamp_in.substring(11,16)+'" disabled="1"/></td>';
+                               row += '<td><input type="time" id="hours_stampout_'+ i+'" value="'+((tbldata.sqldata[i].stamp_out != null)?tbldata.sqldata[i].stamp_out.substring(11,16):'')+'" onfocusout="admin.changeData(this);" disabled="1"/></td>';
+                               row +='</tr>';
+                               ntbldata += row;
+                       }
+                       document.getElementById("table_trackincomplete_tbody").innerHTML=ntbldata;
+               },
+               deleteRow: function(table,id){
+                       
+                       var xq = {"type":"exec","sql":"delete from "+ table+" WHERE id='"+ id +"';"};
+                       var ret = req.reqdata('POST','sqlite/exec',xq,null);
+                       if (ret){
+                               admin.loadpage(admin.lastpage);
+                       }
+               },
+               addRow: function(){
+                       
+               },
+               export_data: function(){
+                       
+               },
+               save_config: function(){
+                       
+               },
+               changeData: function(field){
+                       var dbfield = field.id.split("_");
+                       
+                       var where=[];
+                       for (var i in admin.currentrowdata){
+                               where.push( i + "='" +admin.currentrowdata[i] + "'");
+                       }
+                       var sql = "";
+                       var value= "'" + field.value + "'";
+                       if (field.type == "date"){
+                               value= "date('"+ field.value+"')";
+                       } else if (field.type == "number"){
+                               value= field.value;
+                       } else if (field.type == "checkbox"){
+                               if (field.checked == true){
+                                       value= "'1'";
+                               } else {
+                                       value= "null";
+                               }
+                               
+                       }
+                       if (field.value == ''){
+                               value = "null";
+                       }
+                       if (where.length > 0 ){
+                               sql = "UPDATE " + dbfield[0] +" SET " + dbfield[1] + "=" + value + " WHERE "+ where.join(" AND ") +";";
+                       } else {
+                               sql = "INSERT INTO " + dbfield[0] + " (" + dbfield[1] + ") VALUES (" + value + ");";
+                       }
+                       console.log(sql);
+                       var xu = {"type":"exec","sql":sql};
+                       var tbldata = req.reqdata('POST','sqlite/query',xu,req.asyncNoEvent);
+                       //console.log(field.type);
+                       //console.log(field.id + " " + field.tagName +  " " + field.type);
+                       
+               },
+               
+               load_select_staff: function(selected){
+                       document.getElementById("select_staff").innerHTML='';
+                       var xq = {"type":"querysorted","sql":"select id,prename,surname from staff order by surname,prename,id"};
+                       var cmbdata = req.reqdata('POST','sqlite/query',xq,null);
+                       var tcmbdata = '';
+                       for (var i in cmbdata.sqldata){
+                               tcmbdata += '<option value="'+cmbdata.sqldata[i].id+'">'+cmbdata.sqldata[i].surname+' '+cmbdata.sqldata[i].prename+'</option>';
+                       }
+                       document.getElementById("select_staff").innerHTML=tcmbdata;
+               },
+               setRowEditable: function(row){
+                       //console.log("set Editable!");
+                       if (row == admin.lastrow){
+                               return false;
+                       }
+                       if (admin.lastrow != null){
+                               var lins = admin.lastrow.getElementsByTagName('input');
+                               for (var z=0;z < lins.length; z++){
+                                       //console.log("YUppi " + z);
+                                       document.getElementById(lins[z].id).setAttribute('disabled',true);
+                               }
+                       }
+                       admin.currentrowdata = row.dataset;
+                       var ins = row.getElementsByTagName('input');
+                       for (var z=0;z < ins.length; z++){
+                               //console.log("Yeah " + z);
+                               if (document.getElementById(ins[z].id).getAttribute('readonly') == null){
+                                       document.getElementById(ins[z].id).removeAttribute('disabled');
+                               }
+                               
+                       }
+                       
+                       ////console.log(row.dataset);
+                       admin.lastrow = row;
+                       return true;
+               }
+       }
+window.onload = function() {
+         //console.log('window - onload');
+         admin.lastpage = 'page_track';
+         admin.loadpage('page_track');
+         var cdate = new Date();
+         //console.log(cdate.toISOString().substring(0,10));
+         document.getElementById("cmb_trackdate").value = cdate.toISOString().substring(0,10);
+         document.getElementById("cmb_trackmonth").value = cdate.toISOString().substring(0,7);
+         admin.load_select_staff(null);
+         var nav = navigator.userAgent
+         
+         if (nav.indexOf("Chrome/65") == -1){
+                 location.href='/supported.html';
+         }
+};
\ No newline at end of file
diff --git a/web/js/app.js b/web/js/app.js
new file mode 100644 (file)
index 0000000..b326e98
--- /dev/null
@@ -0,0 +1,99 @@
+var app={
+               interval: null,
+               intervaltime: 60,
+               currentscreen: 'scrloader',
+               showscreen: function screen(screenid){
+                       //console.log("old screen:" + app.currentscreen);
+                       document.getElementById(app.currentscreen).style.display = 'none';
+                       document.getElementById(screenid).style.display = 'block';
+                       app.currentscreen = screenid;
+                       //console.log("new screen:" + app.currentscreen);
+               },
+               checkIdle: function(){
+                       if (app.intervaltime < 0){
+                               location.href = "clock.html";
+                       } else {
+                               app.intervaltime = app.intervaltime - 1; 
+                       }
+               },
+               startCheckIdle: function(){
+                       window.setInterval(app.checkIdle(),1000);
+                       
+               },
+               stopCheckIdle: function(){
+                       window.clearInterval(app.interval);
+               },
+               database: 'hourtrax',
+               reqdata: function(method,url,data,callback){
+                       var host = '';
+                       var ret = null;
+                       var rdata = null;
+                       var async = true;
+                       if (callback == null){
+                               async = false;
+                       }
+                       if (location.protocol === 'file:'){
+                               host = 'http://localhost:6060';
+                       }
+                       var request = new XMLHttpRequest();
+                       if (typeof data == 'object'){
+                               var xdata = [];
+                               for (var i in data){
+                                        var value = '';
+                                        if (typeof(data[i]) == 'object'){
+                                                value = encodeURICOmponent(JSON.stringify(data[i]));
+                                        } else {
+                                                value = encodeURIComponent(data[i]);
+                                        }
+                                        xdata.push(i + "=" + value);
+                               }
+                               rdata = xdata.join("&");
+                       }else {
+                               rdata = data;
+                       }
+                       //console.log("Data to send: " + decodeURIComponent(rdata));
+                       var sendurl = host + '/' + url;
+                       if (method.toUpperCase() == 'GET'){
+                               sendurl = sendurl + '?' + rdata;
+                       }
+                       //console.log("sending URL: " + method + " => " +sendurl);
+                       request.open(method.toUpperCase(), sendurl, false);
+                       request.onload = function(){
+                               if (request.status >= 200 && request.status <= 400){
+                                       //console.log("Status returned: " + request.status + "resp:" + request.getResponseHeader("Content-Type"));
+                                       if (request.getResponseHeader("Content-Type").indexOf('application/json') == 0){
+                                               var xparse = JSON.parse(request.responseText);
+                                               ret = xparse.result;
+                                       }else {
+                                               ret = request.responseText;
+                                       }
+                                       ////console.log("data returned: " + request.responseText);
+                                       if (callback){
+                                               callback(ret);
+                                       }
+                                       
+                               } else {
+                                       //console.log("ServerERROR: " + request.status + "\n" + request.responseText);
+                                       alert("ServerERROR:" + request.status + "\n" + request.responseText);
+                               }
+                       };
+                       request.onerror = function(){
+                               //console.log("ERROR: connection ERROR\n" + url);
+                               alert("Connection ERROR!\n" + url);
+                       };
+                       if (method.toUpperCase() == 'POST'){
+                               request.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8');
+                               request.send(rdata);
+                       } else {
+                               //request.withCredentials = true;
+                               request.send();
+                       }
+                       return ret;
+               }
+}
+
+window.onload = function() {
+         //console.log('window - onload');
+         login.gotoUsers();
+};
+
diff --git a/web/js/database.js b/web/js/database.js
new file mode 100644 (file)
index 0000000..e4f904f
--- /dev/null
@@ -0,0 +1,71 @@
+var appdb = {
+               dbfile: null,
+               url: null,
+               dbquery: function(sQuery){
+                       var type='querysorted';
+                       var result= {sqldata:[]};
+                       //console.log(this.url + 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery));
+                       //dump(this.url + 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery) + "\n");
+                       $.ajax({
+                               encoding:"UTF-8",
+                               method: "POST",
+                               url:this.url + '/query', 
+                               data: 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery),
+                               crossDomain: true,
+                               success: function (data){
+                                       //dump(data + "\n");
+                                               result=data.result;
+                                       },
+                                       error: function(data){
+                                               alert("Error:" + JSON.stringify(data));
+                                               console.log("Error:" + JSON.stringify(data));
+                                       },
+                               async:false
+                       });
+                       return result;
+               },
+               dbqueryarray: function(sQuery){
+                       var type='queryarray';
+                       var result= {sqldata:[]};
+                       //console.log(this.url + 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery));
+                       //alert(this.url + 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery) + "\n");
+                       $.ajax({
+                               encoding:"UTF-8",
+                               method: "POST",
+                               url:this.url + '/query', 
+                               data: 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery),
+                               crossDomain: true,
+                               success: function (data){
+                                               result=data.result;
+                                       },
+                               error: function(data){
+                                       alert("Error:" + JSON.stringify(data));
+                                       console.log("Error:" + JSON.stringify(data));
+                               },
+                               async:false
+                       });
+                       return result;
+               },
+               dbexec: function(sQuery){
+                       var type='exec';
+                       var result= {sqldata:[]};
+                       //dump(this.url + 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery) + "\n");
+                       //console.log(this.url + '&db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery) );
+                       $.ajax({
+                               encoding:"UTF-8",
+                               method: "POST",
+                               url:this.url + '/exec', 
+                               data: 'db=' + this.dbfile + '&type=' + type + '&sql=' +encodeURIComponent(sQuery),
+                               crossDomain: true,
+                               success: function (data){
+                                               result=data.result;
+                                       },
+                                       error: function(data){
+                                               alert("Error:" + JSON.stringify(data));
+                                               console.log("Error:" + JSON.stringify(data));
+                                       },
+                               async:false
+                       });
+                       return result;
+               }
+}
\ No newline at end of file
diff --git a/web/js/jquery-3.2.1.min.js b/web/js/jquery-3.2.1.min.js
new file mode 100644 (file)
index 0000000..644d35e
--- /dev/null
@@ -0,0 +1,4 @@
+/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=Array.isArray(d)))?(e?(e=!1,f=c&&Array.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,N,e),g(f,c,O,e)):(f++,j.call(a,g(f,c,N,e),g(f,c,O,e),g(f,c,N,c.notifyWith))):(d!==N&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S),
+a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},U=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function V(){this.expando=r.expando+V.uid++}V.uid=1,V.prototype={cache:function(a){var b=a[this.expando];return b||(b={},U(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){Array.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(L)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var W=new V,X=new V,Y=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Z=/[A-Z]/g;function $(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:Y.test(a)?JSON.parse(a):a)}function _(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Z,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=$(c)}catch(e){}X.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return X.hasData(a)||W.hasData(a)},data:function(a,b,c){return X.access(a,b,c)},removeData:function(a,b){X.remove(a,b)},_data:function(a,b,c){return W.access(a,b,c)},_removeData:function(a,b){W.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=X.get(f),1===f.nodeType&&!W.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),_(f,d,e[d])));W.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){X.set(this,a)}):T(this,function(b){var c;if(f&&void 0===b){if(c=X.get(f,a),void 0!==c)return c;if(c=_(f,a),void 0!==c)return c}else this.each(function(){X.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=W.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var aa=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ba=new RegExp("^(?:([+-])=|)("+aa+")([a-z%]*)$","i"),ca=["Top","Right","Bottom","Left"],da=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},ea=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function fa(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&ba.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var ga={};function ha(a){var b,c=a.ownerDocument,d=a.nodeName,e=ga[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),ga[d]=e,e)}function ia(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=W.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&da(d)&&(e[f]=ha(d))):"none"!==c&&(e[f]="none",W.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ia(this,!0)},hide:function(){return ia(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){da(this)?r(this).show():r(this).hide()})}});var ja=/^(?:checkbox|radio)$/i,ka=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c<d;c++)W.set(a[c],"globalEval",!b||W.get(b[c],"globalEval"))}var pa=/<|&#?\w+;/;function qa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(pa.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ka.exec(f)||["",""])[1].toLowerCase(),i=ma[h]||ma._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g,h=[],i=b.delegateCount,j=a.target;if(i&&j.nodeType&&!("click"===a.type&&a.button>=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c<i;c++)d=b[c],e=d.selector+" ",void 0===g[e]&&(g[e]=d.needsContext?r(e,this).index(j)>-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i<b.length&&h.push({elem:j,handlers:b.slice(i)}),h},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==xa()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===xa()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&B(this,"input"))return this.click(),!1},_default:function(a){return B(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?va:wa,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:wa,isPropagationStopped:wa,isImmediatePropagationStopped:wa,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=va,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=va,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=va,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&sa.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&ta.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return ya(this,a,b,c,d)},one:function(a,b,c,d){return ya(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=wa),this.each(function(){r.event.remove(this,a,c,b)})}});var za=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/<script|<style|<link/i,Ba=/checked\s*(?:[^=]|=\s*.checked.)/i,Ca=/^true\/(.*)/,Da=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}X.hasData(a)&&(h=X.access(a),i=r.extend({},h),X.set(b,i))}}function Ia(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ja.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ja(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,na(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Ga),l=0;l<i;l++)j=h[l],la.test(j.type||"")&&!W.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Da,""),k))}return a}function Ka(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(na(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&oa(na(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(za,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d<e;d++)Ia(f[d],g[d]);if(b)if(c)for(f=f||na(a),g=g||na(h),d=0,e=f.length;d<e;d++)Ha(f[d],g[d]);else Ha(a,h);return g=na(h,"script"),g.length>0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(na(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ja(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(na(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var La=/^margin/,Ma=new RegExp("^("+aa+")(?!px)[a-z%]+$","i"),Na=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",ra.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,ra.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Oa(a,b,c){var d,e,f,g,h=a.style;return c=c||Na(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&Ma.test(g)&&La.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Pa(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Qa=/^(none|table(?!-c[ea]).+)/,Ra=/^--/,Sa={position:"absolute",visibility:"hidden",display:"block"},Ta={letterSpacing:"0",fontWeight:"400"},Ua=["Webkit","Moz","ms"],Va=d.createElement("div").style;function Wa(a){if(a in Va)return a;var b=a[0].toUpperCase()+a.slice(1),c=Ua.length;while(c--)if(a=Ua[c]+b,a in Va)return a}function Xa(a){var b=r.cssProps[a];return b||(b=r.cssProps[a]=Wa(a)||a),b}function Ya(a,b,c){var d=ba.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Za(a,b,c,d,e){var f,g=0;for(f=c===(d?"border":"content")?4:"width"===b?1:0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+ca[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+ca[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+ca[f]+"Width",!0,e))):(g+=r.css(a,"padding"+ca[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+ca[f]+"Width",!0,e)));return g}function $a(a,b,c){var d,e=Na(a),f=Oa(a,b,e),g="border-box"===r.css(a,"boxSizing",!1,e);return Ma.test(f)?f:(d=g&&(o.boxSizingReliable()||f===a.style[b]),"auto"===f&&(f=a["offset"+b[0].toUpperCase()+b.slice(1)]),f=parseFloat(f)||0,f+Za(a,b,c||(g?"border":"content"),d,e)+"px")}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Oa(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=Ra.test(b),j=a.style;return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:j[b]:(f=typeof c,"string"===f&&(e=ba.exec(c))&&e[1]&&(c=fa(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(j[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i?j.setProperty(b,c):j[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b),i=Ra.test(b);return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Oa(a,b,d)),"normal"===e&&b in Ta&&(e=Ta[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Qa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?$a(a,b,d):ea(a,Sa,function(){return $a(a,b,d)})},set:function(a,c,d){var e,f=d&&Na(a),g=d&&Za(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=ba.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Ya(a,c,g)}}}),r.cssHooks.marginLeft=Pa(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Oa(a,"marginLeft"))||a.getBoundingClientRect().left-ea(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+ca[d]+b]=f[d]||f[d-2]||f[0];return e}},La.test(a)||(r.cssHooks[a+b].set=Ya)}),r.fn.extend({css:function(a,b){return T(this,function(a,b,c){var d,e,f={},g=0;if(Array.isArray(b)){for(d=Na(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f<g;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&da(a),q=W.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],cb.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=W.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ia([a],!0),j=a.style.display||j,k=r.css(a,"display"),ia([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=W.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ia([a],!0),m.done(function(){p||ia([a]),W.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=hb(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],Array.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=kb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=ab||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;g<i;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),f<1&&i?c:(i||h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:ab||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;c<d;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);f<g;f++)if(d=kb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,hb,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j}r.Animation=r.extend(kb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return fa(c.elem,a,ba.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(L);for(var c,d=0,e=a.length;d<e;d++)c=a[d],kb.tweeners[c]=kb.tweeners[c]||[],kb.tweeners[c].unshift(b)},prefilters:[ib],prefilter:function(a,b){b?kb.prefilters.unshift(a):kb.prefilters.push(a)}}),r.speed=function(a,b,c){var d=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off?d.duration=0:"number"!=typeof d.duration&&(d.duration in r.fx.speeds?d.duration=r.fx.speeds[d.duration]:d.duration=r.fx.speeds._default),null!=d.queue&&d.queue!==!0||(d.queue="fx"),d.old=d.complete,d.complete=function(){r.isFunction(d.old)&&d.old.call(this),d.queue&&r.dequeue(this,d.queue)},d},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(da).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=kb(this,r.extend({},a),f);(e||W.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=W.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&db.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=W.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;b<g;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gb(b,!0),a,d,e)}}),r.each({slideDown:gb("show"),slideUp:gb("hide"),slideToggle:gb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(ab=r.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||r.fx.stop(),ab=void 0},r.fx.timer=function(a){r.timers.push(a),r.fx.start()},r.fx.interval=13,r.fx.start=function(){bb||(bb=!0,eb())},r.fx.stop=function(){bb=null},r.fx.speeds={slow:600,fast:200,_default:400},r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var lb,mb=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return T(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),
+null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!B(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("<script>").prop({charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&f("error"===a.type?404:200,a.type)}),d.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Tb=[],Ub=/(=)\?(?=&|$)|\?\?/;r.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Tb.pop()||r.expando+"_"+ub++;return this[a]=!0,a}}),r.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Ub.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ub.test(b.data)&&"data");if(h||"jsonp"===b.dataTypes[0])return e=b.jsonpCallback=r.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Ub,"$1"+e):b.jsonp!==!1&&(b.url+=(vb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||r.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?r(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Tb.push(e)),g&&r.isFunction(f)&&f(g[0]),g=f=void 0}),"script"}),o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=C.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=qa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.fn.load=function(a,b,c){var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=pb(a.slice(h)),a=a.slice(0,h)),r.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&r.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?r("<div>").append(r.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},r.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){r.fn[b]=function(a){return this.on(b,a)}}),r.expr.pseudos.animated=function(a){return r.grep(r.timers,function(b){return a===b.elem}).length},r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),b=f.ownerDocument,c=b.documentElement,e=b.defaultView,{top:d.top+e.pageYOffset-c.clientTop,left:d.left+e.pageXOffset-c.clientLeft}):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),B(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||ra})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return T(this,function(a,d,e){var f;return r.isWindow(a)?f=a:9===a.nodeType&&(f=a.defaultView),void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Pa(o.pixelPosition,function(a,c){if(c)return c=Oa(a,b),Ma.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return T(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.holdReady=function(a){a?r.readyWait++:r.ready(!0)},r.isArray=Array.isArray,r.parseJSON=JSON.parse,r.nodeName=B,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var Vb=a.jQuery,Wb=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=Wb),b&&a.jQuery===r&&(a.jQuery=Vb),r},b||(a.jQuery=a.$=r),r});
diff --git a/web/js/login.js b/web/js/login.js
new file mode 100644 (file)
index 0000000..d5ab186
--- /dev/null
@@ -0,0 +1,148 @@
+var login = {
+               userid: null,
+               pinfield: null,
+               gotoLogin: function (btn){
+                       document.getElementById("reload").style.visibility = 'visible';
+                       document.getElementById("mnuviews").style.visibility = 'hidden';
+                       login.pinfield = 'pincode';
+                       console.log(typeof(btn));
+                       console.log(btn);
+                       app.intervaltime = 60;
+                       
+                       if (typeof(btn) == "object"){
+                               if (btn.sqldata){
+                                       document.getElementById("pinmessage").innerHTML = '';
+                                       document.getElementById('pincode').value = '';
+                                       document.getElementById("usergreeting1").innerHTML ="Bonjour, " + document.getElementById("btn_user_" + login.userid).getAttribute('data-prename') + " entrez votre code pin:";
+                               } else {
+                                       login.userid= btn.getAttribute('data-id');
+                                       document.getElementById("usergreeting1").innerHTML="Bonjour, " + btn.getAttribute('data-prename')  + "! Entrez votre code pin:";
+                                       document.getElementById("pinmessage").innerHTML = '';
+                                       app.startCheckIdle();
+                               }
+                               
+                       } else if ((typeof(btn) == "string") && (btn == 'notpasswd')) {
+                               document.getElementById("usergreeting1").innerHTML ="Bonjour, " + document.getElementById("btn_user_" + login.userid).getAttribute('data-prename') + " entrez votre code pin:";
+                               
+                       }
+                       app.showscreen('scruserpin');   
+               },
+               gotoNewLogin: function (btn){
+                       document.getElementById("reload").style.visibility = 'visible';
+                       document.getElementById("mnuviews").style.visibility = 'hidden';
+                       console.log("New Login!");
+                       console.log(btn);
+                       app.intervaltime = 60;
+                       if (btn != null){
+                               login.userid= btn.getAttribute('data-id');
+                               document.getElementById("usergreeting2").innerHTML="Bonjour, " + btn.getAttribute('data-prename')  + "! Entrez votre nouveau code pin:";
+                               app.startCheckIdle();
+                       }
+                       
+                       login.pinfield = "newpincode1";
+                       document.getElementById("newpincode1").value = "";
+                       document.getElementById("newpincode2").value = "";
+                       document.getElementById("pinmessage2").innerHTML = '';
+                       app.showscreen('scrnewuserpin');
+                       
+               },
+               gotoUsers: function(){
+                       var sqlq = "select id,prename,surname,case when pin is null then 'nopin' else null end as haspin, case when blocked is not null then 'blocked' else null end as isblocked from staff st left join (select idstaff,max(startdate) as startdate from contract where startdate <= CURRENT_DATE group by idstaff) co on (st.id=co.idstaff);";
+                       app.reqdata('POST','sqlite/query',{"type": "querysorted","sql":sqlq},login.loadUsers);
+                       
+               },
+               setPin:function (key){
+                       app.intervaltime = 60;
+                       if (key == 'COR'){
+                               document.getElementById('pincode').value = '';
+                               document.getElementById('newpincode1').value = '';
+                               document.getElementById('newpincode2').value = '';
+                               if (login.pinfield == "newpincode2"){
+                                       login.pinfield = "newpincode1";
+                               }
+                               document.getElementById("pinmessage").innerHTML = '';
+                               document.getElementById("pinmessage2").innerHTML = '';
+                       } else if (key == 'OK'){
+                               if (login.pinfield == 'pincode'){
+                                       app.showscreen('scrloader');
+                                       var xa = {"type":"querysorted","sql":"select id from staff where id='"+login.userid+"' and pin='"+document.getElementById("pincode").value+"';"};
+                                               app.reqdata('POST','sqlite/query',xa,login.getAccess);
+                               } else if (login.pinfield == 'newpincode1'){
+                                       login.pinfield = 'newpincode2';
+                               } else if (login.pinfield == 'newpincode2'){
+                                       var pin1 = document.getElementById("newpincode1").value;
+                                       var pin2 = document.getElementById("newpincode2").value;
+                                       if ((pin1.length < 4) || (pin2.length < 4)){
+                                               document.getElementById("pinmessage2").innerHTML = 'minimum 4 nombre requis!';
+                                       } 
+                                       else if (pin1 != pin2){
+                                               document.getElementById("pinmessage2").innerHTML = 'les codes pin sont pas identiques!';
+                                       } else {
+                                               app.showscreen('scrloader');
+                                               var xa = {"type":"exec","sql":"UPDATE staff SET pin='"+ pin1 +"' WHERE id='"+login.userid+"';"};
+                                                       app.reqdata('POST','sqlite/exec',xa,login.gotoLogin);
+                                       }
+                               }
+                               
+                       } else {
+                               //console.log("Add number " + key);
+                               var cobj = document.getElementById(login.pinfield);
+                               var cpin = cobj.value;
+                               
+                               cobj.value = cpin + key;
+                       }
+               },
+               getAccess: function(data){
+                       app.intervaltime = 60;
+                       console.log(data);
+                       console.log('getAccess');
+                       if ((data.sqldata) && (data.sqldata[0])){
+                               var xa = {"type":"exec","sql":"update staff set loginattemps=null where id='"+login.userid+"';"};
+                               var ret = app.reqdata('POST','sqlite/exec',xa,null);
+                               console.log("Access OK!")
+                               console.log(ret);
+                               if (ret){
+                                       var xq = {"type":"querysorted","sql":"select last_id, case when current_stamp_in is not null then strftime('%d.%m.%Y<br/>%H:%M',current_stamp_in) else null end as current_stamp_in, case when current_stamp_out is not null then strftime('%d.%m.%Y<br/>%H:%M',current_stamp_out) else  null end as current_stamp_out,cast((daytrack  / 3600.0) as integer) || ':' || case when cast(((daytrack % 3600.0) / 60.0) as integer) < 10 then '0'  else '' end || cast(((daytrack % 3600.0) / 60.0) as integer) as monthtrack from (select max(id) as last_id, case when date(max(stamp_in)) = CURRENT_DATE then max(stamp_in) else null end as current_stamp_in,case when date(max(stamp_out)) = CURRENT_DATE then max(stamp_out) else null end as current_stamp_out,sum(case when stamp_out is not null then cast(strftime('%s',stamp_out) as integer) - cast(strftime('%s',stamp_in) as integer) else 0 end) as daytrack from hours where idstaff='"+ login.userid+"' and stamp_in between date('now','start of month') and date('now','start of month','+1 month','-1 day'));"};
+                                       app.reqdata('POST','sqlite/query',xq,track.loadtrackscreen);
+                               }
+                       } else {
+                               var xa = {"type":"exec","sql":"update staff set loginattemps=(select case when max(loginattemps) is null then 1 else loginattemps+1 end as newattemps from staff where id='"+login.userid+"') where id='"+login.userid+'";'};
+                               app.reqdata('POST','sqlite/exec',xa,null);
+                               document.getElementById("pinmessage").innerHTML = 'code pin pas correcte!';
+                               login.gotoLogin('notpassed');
+                       }
+                       
+                       //console.log(data);
+//                     if(login.curpincode == '1234'){
+//                             
+//                     } else {
+//                             login.curpincode = '';
+//                             login.pinenc = '';
+//                             document.getElementById("pincode").innerHTML = login.pinenc;
+//                             document.getElementById("pinmessage").innerHTML = 'Code pas correcte';
+//                     }
+               },
+               loadUsers: function(data){
+                       document.getElementById("reload").style.visibility = 'hidden';
+                       document.getElementById("mnuviews").style.visibility = 'hidden';
+                       var ulist = document.getElementById("scruserlist");
+                       ulist.innerHTML = '';
+                       console.log('loadUsers');
+                       //console.log("TEST" + data);
+                       if (data != null){
+                               var btns = '';
+                               for (var i in data.sqldata){
+                                       console.log(data.sqldata[i]);
+                                       var disabled = ";"
+                                       if (data.sqldata[i].isblocked != null){
+                                               diabled='disabled="1"';
+                                       }
+                                       btns += '<button class="user" onclick="login.goto'+((data.sqldata[i].haspin == null)?"":"New")+'Login(this);" id="btn_user_'+data.sqldata[i].id+'" data-id="'+ data.sqldata[i].id+'" data-prename="'+data.sqldata[i].prename+'" data-surname="'+data.sqldata[i].surname+'" data-haspin="'+data.sqldata[i].haspin+'" '+ disabled +'>'+ data.sqldata[i].prename+'<br/>'+ data.sqldata[i].surname+'</button>';
+                               }
+                               ulist.innerHTML = btns;
+                       }
+                       app.stopCheckIdle();
+                       app.showscreen('scruserlist');
+               }
+}
+
diff --git a/web/js/request.js b/web/js/request.js
new file mode 100644 (file)
index 0000000..119ba84
--- /dev/null
@@ -0,0 +1,105 @@
+var req = {
+               reqdata: function(method,url,data,callback){
+                       var host = '';
+                       var ret = null;
+                       var rdata = null;
+                       var async = true;
+                       if (callback == null){
+                               async = false;
+                       }
+                       if (location.protocol === 'file:'){
+                               host = 'http://localhost:6060';
+                       }
+                       else {
+                               host = location.origin;
+                       }
+                       var request = new XMLHttpRequest();
+                       if (typeof data == 'object'){
+                               var xdata = [];
+                               for (var i in data){
+                                        var value = '';
+                                        if (typeof(data[i]) == 'object'){
+                                                value = encodeURIComponent(JSON.stringify(data[i]));
+                                        } else {
+                                                value = encodeURIComponent(data[i]);
+                                        }
+                                        xdata.push(i + "=" + value);
+                               }
+                               rdata = xdata.join("&");
+                       }else {
+                               rdata = data;
+                       }
+                       //console.log("Data to send: " + decodeURIComponent(rdata));
+                       var sendurl = host + '/' + url;
+                       if (method.toUpperCase() == 'GET'){
+                               sendurl = sendurl + '?' + rdata;
+                       }
+                       //console.log("sending URL: " + method + " => " +sendurl);
+                       request.open(method.toUpperCase(), sendurl, false);
+                       request.onload = function(){
+                               if (request.status >= 200 && request.status <= 400){
+                                       //console.log("Status returned: " + request.status + "resp:" + request.getResponseHeader("Content-Type"));
+                                       if (request.getResponseHeader("Content-Type").indexOf('application/json') == 0){
+                                               var xparse = JSON.parse(request.responseText);
+                                               ret = xparse.result;
+                                       }else if (request.getResponseHeader("Content-Type").indexOf('application/vnd.ms-excel') == 0){
+                                               var filename = "";
+                                       var disposition = request.getResponseHeader('Content-Disposition');
+                                       if (disposition && disposition.indexOf('attachment') !== -1) {
+                                           var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
+                                           var matches = filenameRegex.exec(disposition);
+                                           if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
+                                       }
+                                       var type = request.getResponseHeader('Content-Type');
+                                       var blob = new Blob([this.response], { type: type });
+                                       var URL = window.URL;
+                                       var downloadUrl = URL.createObjectURL(blob);
+                                       if (filename) {
+                                               var a = document.createElement("a");
+                                               if (typeof a.download === 'undefined') {
+                                                       window.location = downloadUrl;
+                                               } else {
+                                                       a.href = downloadUrl;
+                                                       a.download = filename;
+                                                       document.body.appendChild(a);
+                                                       a.click();
+                                               }
+                                       } else {
+                                               window.location = downloadUrl;
+                                       }
+                                       //setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100);
+                                       }
+                                       else {
+                                               ret = request.responseText;
+                                               console.log(ret);
+                                       }
+                                       ////console.log("data returned: " + request.responseText);
+                                       if (callback){
+                                               callback(ret);
+                                       }
+                                       
+                               } else {
+                                       //console.log("ServerERROR: " + request.status + "\n" + request.responseText);
+                                       alert("ServerERROR:" + request.status + "\n" + request.responseText);
+                               }
+                       };
+                       request.onerror = function(){
+                               //console.log("ERROR: connection ERROR\n" + url);
+                               alert("Connection ERROR!\n" + url);
+                       };
+                       if (method.toUpperCase() == 'POST'){
+                               request.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8');
+                               request.send(rdata);
+                       } else {
+                               //request.withCredentials = true;
+                               request.send();
+                       }
+                       return ret;
+               },
+               asyncNoEvent: function(data){
+                       console.log("query done");
+                       console.log(data);
+                       console.log("done");
+               }
+               
+}
\ No newline at end of file
diff --git a/web/js/sysconfig.js b/web/js/sysconfig.js
new file mode 100644 (file)
index 0000000..999c7ce
--- /dev/null
@@ -0,0 +1,149 @@
+var sysconfig={
+               lastsection: null,
+               showsection: function(){
+                       var sectionsel = document.getElementById("select_sysconfig").value;
+                       if (sysconfig.lastsection != null){
+                               document.getElementById(sysconfig.lastsection).style.display = 'none';
+                               document.getElementById(sectionsel).style.display = 'block';
+                               sysconfig.lastsection = sectionsel;
+                       } else {
+                               document.getElementById("sysexport").style.display = 'block';
+                               sysconfig.lastsection = "sysexport";
+                       }
+                       console.log(sysconfig.lastsection);
+                       if (sysconfig.lastsection == 'sysaccess'){
+                               req.reqdata("GET",'app/getaccess','',sysconfig.getSystemUser);
+                       } else if (sysconfig.lastsection == 'syssync'){
+                               req.reqdata("GET",'app/preferences/hourtrax','',sysconfig.getSyncData);
+                       } else if (sysconfig.lastsection == 'sysexport'){
+                               var xa = {"type":"querysorted","sql":"select distinct strftime('%Y-%m',stamp_in) as exportmonth from hours order by stamp_in DESC;"};
+                               req.reqdata("POST",'sqlite/query',xa,sysconfig.setExportConfig);
+                       } else if (sysconfig.lastsection == 'syssystem' ){
+                               req.reqdata("GET",'system/getconfig','',sysconfig.getSystemConfig);
+                       }
+                       
+               },
+               setExportConfig: function(data){
+                       console.log("setExportConfig");
+                       console.log(data);
+                       document.getElementById('cmb_exportmonth').innerHTML = "";
+                       var expm=[];
+                       if (data.sqldata){
+                               for (var m in data.sqldata){
+                                       expm.push('<option value="'+data.sqldata[m].exportmonth+'">'+data.sqldata[m].exportmonth+'</option>');
+                               }
+                       }
+                       document.getElementById('cmb_exportmonth').innerHTML = expm.join('');
+               },
+               getSyncData: function(data){
+                       console.log("getSyncData");
+                       console.log(data);
+                       var section = document.getElementById("syssync");
+                       var elements = section.querySelectorAll('input');
+                       if ((data) && (data.sync)){
+                               elements.forEach( function (index, value) {
+                                         var ident = index.id.substring(14);
+                                         if (data.sync[ident] != null){
+                                                 index.value=data.sync[ident];
+                                         }
+                               });
+                       }
+                       elements = section.querySelectorAll('textarea');
+                       elements.forEach( function (index, value) {
+                                 var ident = index.id.substring(14);
+                                 if (data.sync[ident] != null){
+                                         index.innerHTML=data.sync[ident];
+                                 }
+                       });
+               },
+               setSyncData: function(){
+                       var section = document.getElementById("syssync");
+                       var elements = section.querySelectorAll('input');
+                       var syncdata={ "sync":{}};
+                       elements.forEach( function (index, value) {
+                                 var ident = index.id.substring(14);
+                                 syncdata.sync[ident] = ((index.value)?index.value:null);
+                       });
+                       elements = section.querySelectorAll('textarea');
+                       elements.forEach( function (index, value) {
+                                 var ident = index.id.substring(14);
+                                 syncdata.sync[ident] = ((index.value)?index.value:null);
+                       });
+                       console.log(syncdata);
+                       req.reqdata("POST",'app/preferences/hourtrax',syncdata,req.asyncNoEvent);
+               },
+               getSystemUser: function(data){
+                       console.log(data);
+                       document.getElementById("sysconfig_user").value = data.login;
+               },
+               setSystemUser: function(){
+                       var pwd1 = document.getElementById("sysconfig_pwd1").value;
+                       var pwd2 = document.getElementById("sysconfig_pwd2").value;
+                       var user = document.getElementById("sysconfig_user").value;
+                       if (user == ""){
+                               alert("Nom du utilisateur requis!");
+                               return;
+                       }
+                       if (pwd1 != pwd2){
+                               alert("les mot de passe ne sont pas identiques");
+                               return;
+                       }
+                       if (pwd1.length < 8){
+                               alert("le mot de passe doit avoir au moin 8 charactères!");
+                               return;
+                       }
+                       var xa = {"login":user,"passwd":pwd1};
+                       req.reqdata('POST','app/setaccess',xa,req.asyncNoEvent);
+               },
+               setNetworkConfig: function(){
+                       var hostname  = document.getElementById("sysconfig_hostname").value;
+                       var wlanssid  = document.getElementById("sysconfig_wlanssid").value;
+                       var wlanpwd  = document.getElementById("sysconfig_wlanpwd").value;
+                       
+                       var xa = {"hostname":{"hostname":hostname},"wlan":{"ssid":wlanssid,"passphrase":wlanpwd}};
+                       req.reqdata('POST','system/setconfig',xa,sysconfig.showsection);
+               },
+               getSystemConfig: function(data){
+                       console.log(data);
+                       var ssids = data.wlan.networks;
+               
+                       var currentssid = '';
+                       if (data.wlan.ssid){
+                               currentssid = data.wlan.ssid;
+                       }
+                       //gethostname
+                       document.getElementById("sysconfig_hostname").value = data.hostname;
+                       document.getElementById("sysconfig_wlanssid").innerHTML  ="";
+                       var strssids ="";
+                       for (var i in ssids){
+                               strssids += '<option value="'+ ssids[i] +'">'+ ssids[i] + '</option>'; 
+                       }
+                       document.getElementById("sysconfig_wlanssid").innerHTML = strssids;
+                       document.getElementById("sysconfig_wlanssid").value = currentssid;
+                       document.getElementById("sysconfig_wlanpwd").value = "";
+                       document.getElementById("sysconfig_netstatus").innerHTML ="";
+                       var netstatus = "";
+                       for (var e in data.activenet){
+                               if (e != "lo"){
+                                       netstatus += '<strong>'+ e +'</strong>:' + data.activenet[e]+ '<br/>';
+                               }
+                       }
+                       document.getElementById("sysconfig_netstatus").innerHTML = netstatus;
+               },
+               
+               exportdata: function(){
+                       var monthdate = document.getElementById('cmb_exportmonth').value;
+                       
+                       
+                       var xa = {"type":"CSV","sql":"select st.surname || ' ' ||st.prename as name, hr.stamp_in as entry, hr.stamp_out as exit from hours hr left join staff st on (st.id=hr.idstaff) where hr.stamp_in between date('" + monthdate+"-01') and date('" + monthdate+"-01','+1 month','-1 day') and hr.stamp_out between date('" + monthdate+"-01') and date('" + monthdate+"-01','+1 month','-1 day');","filename":"export_" +monthdate+".csv"}
+               
+                       req.reqdata("POST",'sqlite/export',xa,null);
+                       
+               },
+               shutdown: function(){
+                       req.reqdata("GET",'system/shutdown','',req.asyncNoEvent);
+               },
+               restart:function(){
+                       req.reqdata("GET",'system/restart','',req.asyncNoEvent);
+               }
+}
\ No newline at end of file
diff --git a/web/js/track.js b/web/js/track.js
new file mode 100644 (file)
index 0000000..22a72dc
--- /dev/null
@@ -0,0 +1,57 @@
+var track = {
+               trackid: null,
+               setTrack: function(type){
+                       app.showscreen('scrloader');
+                       console.log(type);
+                       var tracksql = "";
+                       if (type == 'in'){
+                               tracksql = "insert into hours (idstaff,stamp_in) VALUES ('"+ login.userid+"',CURRENT_TIMESTAMP);";
+                       } else if (type == 'out'){
+                               tracksql = "update hours set stamp_out=CURRENT_TIMESTAMP where idstaff='"+ login.userid+"' and id='"+track.trackid+"';";
+                       }
+                       var xt = {"type":"exec","sql":tracksql};
+                       app.reqdata('POST','sqlite/exec',xt,track.loadsuccessscreen);
+               },
+               loadtrackscreen: function(data){
+                       
+                       console.log('loadtrackscreen');
+                       document.getElementById("mnuviews").style.visibility = 'visible';
+                       console.log(data);
+                       if (data) {
+                               var btn=document.getElementById("btn_user_" + login.userid); 
+                               document.getElementById("usergreeting3").innerHTML = btn.getAttribute("data-prename") + " " + btn.getAttribute("data-surname"); 
+                               document.getElementById("monthhours").innerHTML = data.sqldata[0].monthtrack + "h";
+                               document.getElementById("btntrackin").setAttribute("disabled",false);
+                               document.getElementById("btntrackout").setAttribute("disabled",false);
+                               document.getElementById("lasttrackin").innerHTML = '&nbsp;<br/>&nbsp;';
+                               document.getElementById("lasttrackout").innerHTML = '&nbsp;<br/>&nbsp;';
+                               track.trackid = data.sqldata[0].last_id;
+                               if (data.sqldata[0].current_stamp_in != null){
+                                       document.getElementById("lasttrackin").innerHTML = data.sqldata[0].current_stamp_in;
+                               }
+                               if (data.sqldata[0].current_stamp_out != null){
+                                       document.getElementById("lasttrackout").innerHTML = data.sqldata[0].current_stamp_out;
+                               }
+                               if ((data.sqldata[0].current_stamp_in == null) && (data.sqldata[0].current_stamp_out == null)){
+                                       document.getElementById("btntrackin").removeAttribute("disabled");
+                               } else if ((data.sqldata[0].current_stamp_in != null) && (data.sqldata[0].current_stamp_out == null)){
+                                       document.getElementById("btntrackout").removeAttribute("disabled");
+                               } else if ((data.sqldata[0].current_stamp_in != null) && (data.sqldata[0].current_stamp_out != null)) {
+                                       if (data.sqldata[0].current_stamp_in > data.sqldata[0].current_stamp_out){
+                                               document.getElementById("btntrackout").removeAttribute("disabled");
+                                       } else {
+                                               document.getElementById("btntrackin").removeAttribute("disabled");
+                                       }
+                               }
+                       }
+                       //document.getElementById("usergreeting3").value = ""
+                       app.showscreen('scrtimetracker');
+               },
+               loadsuccessscreen: function(data){
+                       console.log(data);
+                       document.getElementById("statusmsg").innerHTML = "L'heure est enregistrĂ©";
+                       app.showscreen('scrstatus');
+                       track.trackid = null;
+                       setTimeout("location.href='clock.html'",3000);
+               }
+}
\ No newline at end of file
diff --git a/web/supported.html b/web/supported.html
new file mode 100644 (file)
index 0000000..4fc8a27
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Hourtrax - Supported Browsers</title>
+</head>
+<body>
+       <h1>Only Google Chrome Browser is supported!</h1>
+       <p> minimum Version: 65.*<br/>Dowmload Google Chrome here: <br/><a  href="https://www.google.com/chrome/"><img src="https://www.google.com/chrome/assets/common/images/chrome_logo_2x.png" style="border: 1px solid silver" alt="Download Google Chrome"></a></p>
+       
+</body>
+</html>
\ No newline at end of file
diff --git a/web/test.html b/web/test.html
new file mode 100644 (file)
index 0000000..a758cc5
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test</title>
+</head>
+<body>
+       <div id="data"></div>
+       <progress max="100" min="0">
+<script type="text/javascript">
+window.onload = function() {
+         getlocation();
+};
+
+function getlocation(){
+       var test = "location.hash =" + location.hash + "<br/>";
+       test += "location.host = " + location.host + "<br/>";
+       test += "location.hostname = " + location.hostname + "<br/>";
+       test += "location.href = " + location.href + "<br/>";
+       test += "location.origin = " + location.origin + "<br/>";
+       test += "location.pathname = " + location.pathname + "<br/>";
+       test += "location.port = " + location.port + "<br/>";
+       test += "location.protocol = " + location.protocol + "<br/>";
+       test += "location.search = " + location.search + "<br/>";
+       document.getElementById("data").innerHTML = test;
+}
+</script>
+</body>
+</html>
\ No newline at end of file