From: Kilian Saffran Date: Mon, 19 Apr 2021 14:04:21 +0000 (+0200) Subject: v20210419 X-Git-Url: http://cloud.dks.lu/git/?a=commitdiff_plain;h=HEAD;p=hourtrax.git v20210419 --- diff --git a/.hourtrax/hourtrax.sql b/.hourtrax/hourtrax.sql index 6a6c2af..4009253 100644 --- a/.hourtrax/hourtrax.sql +++ b/.hourtrax/hourtrax.sql @@ -1,61 +1,61 @@ -CREATE TABLE staff ( - id TEXT NOT NULL, - staffnumber TEXT, - prename TEXT, - surname TEXT, - pin TEXT, - rfid TEXT, - isblocked BOOLEAN, - isdisabled BOOLEAN, - isdeleted BOOLEAN, - modified DATETIME DEFAULT (datetime('now','localtime')), - created DATETIME DEFAULT (datetime('now','localtime')), - PRIMARY KEY (id) -); -CREATE TABLE timetracks ( - id text not null, - id_staff TEXT, - daydate date, - stamp_in DATETIME, - stamp_out DATETIME, - tracktype TEXT, - modified DATETIME DEFAULT (datetime('now','localtime')), - created DATETIME DEFAULT (datetime('now','localtime')), - primary key (id) -); -CREATE TABLE fingerprints ( - id_staff TEXT, - fingerhash TEXT, - fingertype integer, - modified DATETIME DEFAULT (datetime('now','localtime')), - created DATETIME DEFAULT (datetime('now','localtime')), - primary key (id_staff,fingerhash) -); -CREATE TABLE staffworktimes ( - id TEXT not null, - id_staff TEXT, - daydate date, - starttime1 TEXT, - endtime1 TEXT, - starttime2 TEXT, - endtime2 TEXT, - modified DATETIME DEFAULT (datetime('now','localtime')), - created DATETIME DEFAULT (datetime('now','localtime')), - primary key (id) -); -CREATE TRIGGER trg_staff_upd UPDATE ON staff - BEGIN - UPDATE staff SET modified=datetime('now','localtime') WHERE id = NEW.id; - END; -CREATE TRIGGER trg_timetracks_upd UPDATE ON staff - BEGIN - UPDATE timetracks SET modified=datetime('now','localtime') WHERE id = NEW.id; - END; -CREATE TRIGGER trg_staffworktimes_upd UPDATE ON staff - BEGIN - UPDATE staffworktimes SET modified=datetime('now','localtime') WHERE id = NEW.id; - END; - CREATE TRIGGER trg_fingerprints_upd UPDATE ON staff - BEGIN - UPDATE staffworktimes SET modified=datetime('now','localtime') WHERE id_staff = NEW.id_staff and fingertype= NEW.fingertype; - END; +CREATE TABLE staff ( + id TEXT NOT NULL, + staffnumber TEXT, + prename TEXT, + surname TEXT, + pin TEXT, + rfid TEXT, + isblocked BOOLEAN, + isdisabled BOOLEAN, + isdeleted BOOLEAN, + modified DATETIME DEFAULT (datetime('now','localtime')), + created DATETIME DEFAULT (datetime('now','localtime')), + PRIMARY KEY (id) +); +CREATE TABLE timetracks ( + id text not null, + id_staff TEXT, + daydate date, + stamp_in DATETIME, + stamp_out DATETIME, + tracktype TEXT, + modified DATETIME DEFAULT (datetime('now','localtime')), + created DATETIME DEFAULT (datetime('now','localtime')), + primary key (id) +); +CREATE TABLE fingerprints ( + id_staff TEXT, + fingerhash TEXT, + fingertype integer, + modified DATETIME DEFAULT (datetime('now','localtime')), + created DATETIME DEFAULT (datetime('now','localtime')), + primary key (id_staff,fingerhash) +); +CREATE TABLE staffworktimes ( + id TEXT not null, + id_staff TEXT, + daydate date, + starttime1 TEXT, + endtime1 TEXT, + starttime2 TEXT, + endtime2 TEXT, + modified DATETIME DEFAULT (datetime('now','localtime')), + created DATETIME DEFAULT (datetime('now','localtime')), + primary key (id) +); +CREATE TRIGGER trg_staff_upd UPDATE ON staff + BEGIN + UPDATE staff SET modified=datetime('now','localtime') WHERE id = NEW.id; + END; +CREATE TRIGGER trg_timetracks_upd UPDATE ON staff + BEGIN + UPDATE timetracks SET modified=datetime('now','localtime') WHERE id = NEW.id; + END; +CREATE TRIGGER trg_staffworktimes_upd UPDATE ON staff + BEGIN + UPDATE staffworktimes SET modified=datetime('now','localtime') WHERE id = NEW.id; + END; + CREATE TRIGGER trg_fingerprints_upd UPDATE ON staff + BEGIN + UPDATE staffworktimes SET modified=datetime('now','localtime') WHERE id_staff = NEW.id_staff and fingertype= NEW.fingertype; + END; diff --git a/.hourtrax/potsync.conf b/.hourtrax/potsync.conf index 7267af7..276fce0 100644 --- a/.hourtrax/potsync.conf +++ b/.hourtrax/potsync.conf @@ -1,6 +1,6 @@ -USERAGENT=POT Hourtrax -POTAPIKEY=6b9c80d2-9153-11eb-8558-efa3f9982293 -POTAPIURL=http://app.pot.lan/potapp/api.php -POTSCHEMA=alicehartmann -POTLASTSYNCFROMSERVER= -POTLASTSYNCTOSERVER= +USERAGENT=POT Hourtrax +POTAPIKEY=6b9c80d2-9153-11eb-8558-efa3f9982293 +POTAPIURL=http://app.pot.lan/potapp/api.php +POTSCHEMA=alicehartmann +POTLASTSYNCFROMSERVER= +POTLASTSYNCTOSERVER= diff --git a/.vscode/sftp.json b/.vscode/sftp.json index 8d5f6a8..198bff8 100644 --- a/.vscode/sftp.json +++ b/.vscode/sftp.json @@ -1,11 +1,11 @@ - -{ - "name": "htx-test", - "scheme": "sftp", - "context": "bin/", - "host": "dhart-pot02", - "username": "pot", - "password": "sai4seip", - "port":3587, - "remotePath": "/home/pot/bin/" -} + +{ + "name": "htx-test", + "scheme": "sftp", + "context": "bin/", + "host": "dhart-hourtrax01", + "username": "pot", + "password": "sai4seip", + "port":3587, + "remotePath": "/home/pot/bin/" +} diff --git a/bin/CGI/api/db.cgi b/bin/CGI/api/db.cgi new file mode 100644 index 0000000..6a6e177 --- /dev/null +++ b/bin/CGI/api/db.cgi @@ -0,0 +1,126 @@ +#!/Users/kilian/perl5/perlbrew/perls/perl-5.24.1/bin/perl +use strict; +use FindBin qw($Bin); +# use lib ('CGI/api/lib/perl5'); +# use lib ('CGI/api/lib'); +use lib ($Bin.'/CGI/api/lib/perl5'); +use lib ($Bin.'/CGI/api/lib'); +use CGI; +use CGI::Cookie; +# use CGI::Carp qw/fatalsToBrowser/; +use File::Basename; +use JSON::PP; +#use UUID::Tiny ':std'; +use dksconfig qw/$sitecfg/; +use dksdb; + +# use session; +#use sendemail; +my $cgi = new CGI(); +my $scriptpath = $cgi->url(-absolute => 1); +my $p = (); +my @params = $cgi->param(); +foreach my $pe (@params){ + $p->{$pe} = $cgi->param($pe); +} +my $html->{result} = (); +# $p->{sid} = $cgi->cookie($sitecfg->{cookiename}); +# my $se = session->new(); +# my $sess = $se->getsession($p->{sid}); +print $cgi->header(-type=>"application/json", -charset => "utf-8"); +my $dbredirect = {}; +if ($sitecfg->{dbtype} eq "SQLite"){ + if (exists($p->{db})){ + $dbredirect->{dsn} = "DBI:SQLite:dbname=".$sitecfg->{datapath}.'/'.$p->{db}.'.sqlite'; + } +} +$html->{conn} = $dbredirect; +# if ($sess == undef){ +# $html->{error} = "No Authorisation"; +# print JSON::PP::encode_json($html); +# exit(0); +# } +# $html->{p} = $p; +# $html->{sess} =$sess; +#my $datapath = $ENV{"DOCUMENT_ROOT"}.dirname(dirname($scriptpath)).'/data/'; +if (($cgi->request_method() eq "GET") || ($cgi->request_method() eq "POST")){ + + # my @params = $cgi->param(); + # foreach my $pp (@params){ + # $p->{$pp} = $cgi->param($pp); + # } + my $db = dksdb->new($dbredirect); + if (exists($p->{get})){ + my $sql = "select * from vw_".$p->{get}; + if (exists($p->{fields}) ){ + $sql = "select ".$p->{fields}." from vw_".$p->{get}; + } + if (exists($p->{filter})){ + $sql .= " WHERE ".$p->{filter}.";"; + } + $html->{result}->{sqldata} = $db->query($sql); + } + elsif (exists($p->{set})){ + my $type = "ins"; + foreach my $x (keys(%{$p})){ + if ($x =~ /^ident_/ ){ + if ($p->{$x} ne ""){ + $type = "upd"; + } else { + my $uuid = create_uuid(UUID_TIME); + my $uuid_str=uuid_to_string($uuid); + $p->{$x} = $uuid_str; + } + + } + } + + my $x = $p; + delete $x->{sid}; + delete $x->{set}; + my @sql = (); + + if ($type eq "ins"){ + + @sql = $db->create_ddl_insert($x); + }else { + @sql = $db->create_ddl_update($x); + } + if (scalar(@sql) > 0 ){ + my $rid = $db->query($sql[0]); + if (keys(%{$rid}) > 0 ){ + $html->{result} = $rid->{0}; + } + } + } + elsif (exists($p->{del})){ + my $x = $p; + delete $x->{sid}; + delete $x->{del}; + my @sql = $db->create_ddl_delete($x); + if (scalar(@sql) > 0 ){ + my $rid = $db->exec($sql[0]); + if (keys(%{$rid}) > 0 ){ + $html->{result} = $rid->{0}; + } + } + } + elsif (exists($p->{sql})){ + if (exists($p->{key})){ + $html->{result} = $db->querybykey($p->{key},$p->{sql}); + } + if ((lc($p->{sql}) =~ /^insert/) || (lc($p->{sql}) =~ /^update/) || (lc($p->{sql}) =~ /^delete/)){ + $html->{result} = $db->exec($p->{sql}); + } + else { + $html->{result} = $db->query($p->{sql}); + } + } + elsif (exists($p->{ddl})){ + $html->{result} = $db->exec($p->{sql}); + } +} +print JSON::PP::encode_json($html); +# for my $e ( keys %ENV ) { +# print "$e: $ENV{$e}
"; +# } \ No newline at end of file diff --git a/bin/CGI/api/fingerprint.cgi b/bin/CGI/api/fingerprint.cgi new file mode 100644 index 0000000..0ade403 --- /dev/null +++ b/bin/CGI/api/fingerprint.cgi @@ -0,0 +1,92 @@ +#!/Users/kilian/perl5/perlbrew/perls/perl-5.24.1/bin/perl +use strict; +use FindBin qw($Bin); +# use lib ('CGI/api/lib/perl5'); +# use lib ('CGI/api/lib'); +use lib ($Bin.'/CGI/api/lib/perl5'); +use lib ($Bin.'/CGI/api/lib'); +use CGI; +use CGI::Cookie; +# use CGI::Carp qw/fatalsToBrowser/; +use File::Basename; +use JSON::PP; + +use dksconfig qw/$sitecfg/; +use dksdb; +my $cgi = new CGI(); +my $scriptpath = $cgi->url(-absolute => 1); +my $p = (); +my @params = $cgi->param(); +foreach my $pe (@params){ + $p->{$pe} = $cgi->param($pe); +} +my $html->{result} = (); +# $p->{sid} = $cgi->cookie($sitecfg->{cookiename}); +# my $se = session->new(); +# my $sess = $se->getsession($p->{sid}); +if (exists($p->{fn}) ){ + if (exists($p->{id_user})){ + if ($p->{fn} eq "new"){ + my $cmd ='/usr/bin/python2 '.dirname($0).'/tools/fingerledon.py'; + my $res = `$cmd`; + chomp($res); + if ($res eq ''){ + $cmd ='/usr/bin/python2 '.dirname($0).'/tools/fingerenroll.py'; + my $res = `$cmd`; + } + + + + } elsif ($p->{fn} eq "check"){ + my $cmd ='sudo /usr/bin/python '.dirname($0).'/tools/fingersearch.py'; + my $res = `$cmd`; + chomp($res); + if ($res =~ /POS:.*HASH:/){ + my ($pos,$hash) = $res =~ m/.*POS:(\d+);HASH:(.+)\s*$/; + $html->{result}->{pos} = $pos; + $html->{result}->{hash} = $hash; + if (length($pos) > 0 && length($hash) > 0){ + my $sql = "select id,fingerpos,fingerhash from staff where id=".$p->{id_user}." and fingerpos=".$pos." and fingerhash='".$hash."';"; + my $db = dksdb->new(); + $html->{result}->{sql} = $sql; + my @rdb = $db->query($sql); + if (scalar(@rdb) > 0 ){ + $html->{result}->{status} ="OK"; + $html->{result}->{db} = \@rdb; + $cmd ='/usr/bin/python2 '.dirname($0).'/tools/fingerledoff.py'; + $res = `$cmd`; + } + } + + } else { + $html->{result}->{status} = $res; + } + } + } + elsif($p->{fn} eq "unload"){ + my $cmd = 'ps ax | grep "tools/finger" | awk \'{ print \$ }\''; + my $res = `$cmd`; + } + elsif($p->{fn} eq "ledon"){ + my $cmd ='/usr/bin/python2 '.dirname($0).'/tools/fingerledon.py'; + my $res = `$cmd`; + chomp($res); + if ($res eq ""){ + $html->{result}->{status} = "ok"; + } else { + $html->{result}->{status} = "error"; + } + } + elsif($p->{fn} eq "ledoff"){ + my $cmd ='/usr/bin/python2 '.dirname($0).'/tools/fingerledoff.py'; + my $res = `$cmd`; + chomp($res); + if ($res eq ""){ + $html->{result}->{status} = "ok"; + } else { + $html->{result}->{status} = "error"; + } + } +} +print $cgi->header(-type=>"application/json", -charset => "utf-8"); +print JSON::PP::encode_json($html); \ No newline at end of file diff --git a/bin/CGI/api/lib/dksconfig.pm b/bin/CGI/api/lib/dksconfig.pm new file mode 100644 index 0000000..e35669d --- /dev/null +++ b/bin/CGI/api/lib/dksconfig.pm @@ -0,0 +1,42 @@ +package dksconfig; + +use strict; +# use lib ('./lib/perl5'); +# use lib ('./lib'); +# use lib ('./'); +use File::Basename; +use Exporter 'import'; +our @EXPORT_OK = qw($sitecfg); +my $cfgpath=$ENV{HOME}.'/.hourtrax'; +if ($^O eq "MSWin32"){ + $cfgpath = $ENV{APPDATA}.'/hourtrax'; +} elsif ($^O eq "darwin"){ + $cfgpath = $ENV{HOME}.'/Library/Application Support/hourtrax'; +} +$cfgpath =~ s/\\/\//g; +our $sitecfg ={ + #cookiename => undef, + datapath => $cfgpath, + dbtype => 'SQLite', #'PgPP' or ScLite + dsn => 'DBI:SQLite:dbname='.$cfgpath.'/hourtrax.sqlite', + dbuser => '', + dbpassword => '', + basepath => '/', + staticpath => 'htdocs/', + #dsn => 'DBI:PgPP:dbname=fldlu_db;host=DKS-LAPTOP.fritz.box', + #dsn => 'DBI:PgPP:dbname=fldlu_db;host=sql629.your-server.de', + #dbuser => 'fldlu_user', + #dbpassword => 'Tm6G1bjQSudiEBAY', + page => 'index.tt', + pagename => 'index', + # basepath => substr((exists($ENV{"SCRIPT_FILENAME"})?dirname($ENV{"SCRIPT_FILENAME"}):dirname($0)),length($ENV{"DOCUMENT_ROOT"})), + # datapath => substr((exists($ENV{"SCRIPT_FILENAME"})?dirname($ENV{"SCRIPT_FILENAME"}):dirname($0)),length($ENV{"DOCUMENT_ROOT"})).'/data/', + # docroot => $ENV{"DOCUMENT_ROOT"}, + # registration_enabled => '1', + # default_group => 'users', + # sitename => 'FLD Member Area', + # season => '2019-2020', + # season_id => 2 +}; + +1; \ No newline at end of file diff --git a/bin/CGI/api/lib/dksdb.pm b/bin/CGI/api/lib/dksdb.pm new file mode 100644 index 0000000..fda73ef --- /dev/null +++ b/bin/CGI/api/lib/dksdb.pm @@ -0,0 +1,417 @@ +package dksdb; + +use strict; +use lib ('./lib/perl5'); +use lib ('./lib'); +use lib ('./'); +#BEGIN { $ENV{DBI_PUREPERL} = 2 } +use DBI; +use File::Basename; + +use Digest::SHA::PurePerl qw(sha256_hex); +use DBD::PgPP; +use DBD::SQLite; +use URI::Encode qw(uri_encode uri_decode); +use Encode; +use dksconfig qw($sitecfg); +use Text::Unidecode; + + +sub new { + my $class = shift; + my $param = shift; + my $self = bless {}, $class; + foreach my $p (keys %{$param}){ + $self->{$p} = $param->{$p}; + } + + return $self; +} + +sub securetext(){ + my $self = shift; + my $text = shift; + $text =~ s/'/''/g; + return $text; +} + +sub query(){ + my $self = shift; + my $stat = shift; + # my $vw_info = shift; + my @retdata = (); + my $dbh = DBI->connect(((exists($self->{dsn}))?$self->{dsn}:$sitecfg->{dsn}),$sitecfg->{dbuser},$sitecfg->{dbpassword},{PrintError=>0,RaiseError=>0,AutoCommit=>1}) or return \@retdata;#$retdata[0] = "dbquery Connection Error!".$!; + $stat = encode("utf8", $stat); + # open FILE,">>tmp/sql.log"; + # print FILE "$stat\n"; + # close FILE; + my $sth = $dbh->prepare($stat) or return \@retdata;#$retdata[0] = "dbquery".$dbh->errstr. "- SQL: ".$stat;; + + + $sth->execute() or return \@retdata; #"dbquery: ".$sth->errstr; + + while(my $data = $sth->fetchrow_hashref()){ + my $row = (); + foreach my $k (keys %{$data}){ + $row->{$k} = decode("utf-8",$data->{$k}); + } + push @retdata,$row; + } + + $sth->finish(); + $dbh->disconnect(); + + return \@retdata; +} + +sub querybykey(){ + my $self = shift; + my $key = shift; + my $stat = shift; + #my $retempty = shift; + my @retdata =(); + my $dbh = DBI->connect(((exists($self->{dsn}))?$self->{dsn}:$sitecfg->{dsn}),$sitecfg->{dbuser},$sitecfg->{dbpassword},{PrintError=>0,RaiseError=>0,AutoCommit=>1}) or return \@retdata;#$retdata[0] = "dbquery Connection Error!".$!; + # $stat = encode("utf8", $stat); + + # open FILE,">>sql.log"; + # print FILE "$stat\n"; + # close FILE; + my $sth = $dbh->prepare($stat) or return \@retdata;#$retdata[0] = "dbquery: ".$stat; + $sth->execute() or return \@retdata;#$retata[0] = "dbquery: ".$stat; + while(my $data = $sth->fetchrow_hashref()) + { + if (exists $data->{$key}){ + my $row = (); + foreach my $k (keys %{$data}){ + $row->{$k} = decode("utf-8",$data->{$k}); + } + push @retdata,$row; + } + } + # if (keys(%{$retdata}) == 0){ + # $retdata =(); + # } + $sth->finish(); + $dbh->disconnect(); + return \@retdata; +} + +# sub dbquerysorted(){ +# my $self = shift; +# my $stat = shift; +# # my $vw_info = shift; +# my $retdata; +# my $dbh = DBI->connect(((exists($self->{dsn}))?$self->{dsn}:$sitecfg->{dsn}),$sitecfg->{dbuser},$sitecfg->{dbpassword},{PrintError=>0,RaiseError=>0,AutoCommit=>1}) or return $retdata->{error} = "dbquery Connection Error!".$!; +# # $stat = encode("utf8", $stat); +# # open FILE,">>tmp/sql.log"; +# # print FILE "\n==\n$stat\n==\n"; +# # close FILE; +# my $sth = $dbh->prepare($stat) or return $retdata->{error} = "dbquerysorted ".$dbh->errstr. "- SQL: ".$stat;; + + +# $sth->execute() or return $retdata->{error} = "dbquerysorted: ".$sth->errstr; +# my $count = 0; + +# while(my $data = $sth->fetchrow_hashref()) +# { +# #$retdata->{$count} = $data; +# my $row = (); +# foreach my $k (keys %{$data}){ +# $row->{$k} = decode("utf-8",$data->{$k}); +# } +# push @retdata,$row; +# } + +# # my $qstruct = (); +# # my $num_fields = $sth->{NUM_OF_FIELDS}; + +# # for ( my $i=0; $i< $num_fields; $i++ ) { +# # $qstruct->{$i}->{name} = $sth->{NAME}->[$i]; +# # #$qstruct->{$i}->{type} = $sth->{COMMENT}->[$i]; +# # #$qstruct->{$i}->{precision} = $sth->{PRECISION}->[$i]; +# # } + +# $sth->finish(); +# $dbh->disconnect(); + +# return $retdata; +# } + +sub exec(){ + my $self = shift; + my $stat = shift; + my $retdata; + my $dbh = DBI->connect(((exists($self->{dsn}))?$self->{dsn}:$sitecfg->{dsn}),$sitecfg->{dbuser},$sitecfg->{dbpassword},{PrintError=>0,RaiseError=>0,AutoCommit=>1}) or return $retdata->{error} = "exec Connection Error!".$!; + # $stat = decode("UTF-8", $stat); + # open FILE,">>tmp/sql.log"; + # print FILE "\n==\n$stat\n==\n"; + # close FILE; + my $sth = $dbh->prepare($stat) or return $retdata->{error} = "exec ".$dbh->errstr. "- SQL: ".$stat; + $retdata->{success} = $dbh->do($stat) or return $retdata->{error} = "dbexec ".$dbh->errstr. "- SQL: ".$stat; + $dbh->disconnect(); + return $retdata; +} + +# sub dbqueryarray(){ +# my $self = shift; +# my $stat = shift; +# my @retdata = (); +# my $dbh = DBI->connect(((exists($self->{dsn}))?$self->{dsn}:$sitecfg->{dsn}),$sitecfg->{dbuser},$sitecfg->{dbpassword},{PrintError=>0,RaiseError=>0,AutoCommit=>1}) or return ({"error" => "dbqueryarray Connection Error!".$!}); +# #$stat = encode("utf8", $stat); +# #open FILE,">>/tmp/sql.log"; +# #print "$stat\n"; +# # close FILE; +# my $sth = $dbh->prepare($stat); + +# $sth->execute() or print "dbqueryarray: ".$sth->errstr; +# my $count = 0; + +# while(my $data = $sth->fetchrow_hashref()) +# { +# my $row = (); +# foreach my $k (keys %{$data}){ +# $row->{$k} = decode("utf-8",$data->{$k}); +# } +# push @retdata,$row; +# } + +# $sth->finish(); +# $dbh->disconnect(); +# #%retdata = sort {$a <=> $b} keys %retdata; +# return \@retdata; +# } + + +sub create_ddl_insert(){ + my $self = shift; + my $data = shift; + my $fields = (); + my @ddl = (); + + foreach my $f (keys(%{$data})){ + if (($f =~ /\_/) && ($f !~ /^ident_/)){ + my $t = substr($f,0,index($f,"_")); + my $c = substr($f,length($t)+1); + #my ($t,$c) = $f =~ m/(.+)\_(.+)/; + $fields->{$t}->{$c} = $data->{$f}; + } elsif ($f =~ /^ident_/){ + my $f2 = $f; + $f2 =~ s/^ident_//; + + my $t = substr($f2,0,index($f2,"_")); + my $c = substr($f2,length($t)+1); + $fields->{$t}->{$c} = $data->{$f}; + } + + } + + foreach my $tb (keys(%{$fields})){ + my @sqlcol = (); + my @sqlval = (); + foreach my $c (keys(%{$fields->{$tb}})){ + my $v = $fields->{$tb}->{$c}; + $v =~ s/'/''/g; + push (@sqlcol,$c); + if ($v eq ''){ + $v = 'null'; + } else { + $v = "'".$v."'"; + } + push (@sqlval,$v); + } + push(@ddl,"INSERT INTO ".$tb." (".join(",",@sqlcol).") VALUES (".join(",",@sqlval).") returning id;"); + } + return @ddl; +} + +sub create_ddl_insert_json(){ + my $self = shift; + my $schema = shift; + my $table = shift; + my $columns = shift; + my $data = shift; + my @ddl = (); + my @sqlcol = (); + my @sqlval = (); + foreach my $c (keys(%{$data})){ + #if (exists($columns->{$c})){ + push (@sqlcol,'"'.$c.'"'); + my $v = $data->{$c}; + + if ($v eq ''){ + $v = 'null'; + }elsif ($v =~ /^data:.+;base64,/){ + $v =~ s/'/''/g; + $v = "'".$v."'"; + } + else { + $v= uri_decode($v); + $v =~ s/'/''/g; + if ($columns->{$c}->{data_type} eq "ARRAY"){ + if (ref($data->{$c}) eq "ARRAY"){ + $v = "{\"".join("\",\"",@{$data->{$c}})."\"}"; + } + else { + $v = 'null'; + } + $v =~ s/""/null/g; + }elsif ($columns->{$c}->{data_type} =~ /^timestamp/ ){ + + }elsif($columns->{$c}->{data_type} eq "date"){ + + }elsif($columns->{$c}->{data_type} eq "time"){ + + } + $v = "'".$v."'"; + } + push (@sqlval,$v); + #} + } + return "INSERT INTO ".$schema.".\"".$table."\" (".join(",",@sqlcol).") VALUES (".join(",",@sqlval).");"; +} + +sub create_ddl_update(){ + my $self = shift; + my $data = shift; + my $fields = (); + my @ddl = (); + foreach my $f (keys(%{$data})){ + if ($f =~ /^ident_/){ + my $fx = substr($f,6); + my $t = substr($fx,0,index($fx,"_")); + my $c = substr($fx,length($t)+1); + #my ($t,$c) = $f =~ m/^ident_(.+)\_([a-z0-9|\_]+)/; + $fields->{$t}->{cond}->{$c} = $data->{$f}; + } elsif ( ($f !~ /^ident/) && ($f =~ /.+\_.+/) ){ + my $t = substr($f,0,index($f,"_")); + my $c = substr($f,length($t)+1); + #my ($t,$c) = $f =~ m/^(.+)\_([a-z0-9|\_]+)/; + $fields->{$t}->{fields}->{$c} = $data->{$f}; + } + } + foreach my $tb (keys(%{$fields})){ + my @sqlupd = (); + my @sqlcond = (); + foreach my $c (keys(%{$fields->{$tb}->{fields}})){ + + my $v = $fields->{$tb}->{fields}->{$c}; + $v =~ s/'/''/g; + + if ($c =~ /-/){ + my @jp = split('-',$c); + if ($v eq ''){ + $v = 'null'; + } else { + $v = '"'.$v.'"'; + } + $c = 'jsonb_set(to_jsonb('.$jp[0].'),\'{"'.$jp[1].'"}\',\''.$v.'\')::json'; + push (@sqlupd,$jp[0]."=".$c); + }else { + if ($v eq ''){ + $v = 'null'; + } else { + $v = "'".$v."'"; + } + push (@sqlupd,$c."=".$v); + } + + } + foreach my $c (keys(%{$fields->{$tb}->{cond}})){ + my $v = $fields->{$tb}->{cond}->{$c}; + $v =~ s/'/''/g; + if ($v eq ''){ + $v = 'null'; + } else { + $v = "'".$v."'"; + } + push (@sqlcond,$c."=".$v); + } + push(@ddl,"UPDATE ".$tb." SET ".join(",",@sqlupd)." WHERE ".join(" AND ",@sqlcond).";"); + } + + return @ddl; +} + +sub create_cnt_statement(){ + my $self = shift; + my $data = shift; + my $fields = (); + my @ddl = (); + foreach my $f (keys(%{$data})){ + if ($f =~ /^ident_/){ + my $fx = substr($f,6); + my $t = substr($fx,0,index($fx,"_")); + my $c = substr($fx,length($t)+1); + #my ($t,$c) = $f =~ m/^ident_(.+)\_([a-z0-9|\_]+)/; + $fields->{$t}->{cond}->{$c} = $data->{$f}; + } + } + foreach my $tb (keys(%{$fields})){ + my @sqlcond = (); + foreach my $c (keys(%{$fields->{$tb}->{cond}})){ + my $v = $fields->{$tb}->{cond}->{$c}; + $v =~ s/'/''/g; + if ($v eq ''){ + $v = 'null'; + } else { + $v = "'".$v."'"; + } + push (@sqlcond,$c."=".$v); + } + push(@ddl,"SELECT count(*) as cnt from ".$tb." WHERE ".join(" AND ",@sqlcond).";"); + } + # open FILE,">>tmp/sql.log"; + # print FILE "\n==\n".join("\n",@ddl)."\n==\n"; + # close FILE; + return @ddl; +} + +sub create_ddl_delete(){ + my $self = shift; + my $data = shift; + my $fields = (); + my @ddl = (); + my @refcols = (); + my $refdata = (); + foreach my $f (keys(%{$data})){ + if ($f =~ /^ident_/){ + my ($t,$c) = $f =~ m/ident_(.+)\_(.+)/; + + $fields->{$t}->{cond}->{$c} = $data->{$f}; + push(@refcols,"'".$c.'_'.$t."'"); + $refdata->{$c.'_'.$t} = $data->{$f}; + } + } + +# my $ref = $self->dbquerysorted("select TABLE_NAME,COLUMN_NAME from information_schema.KEY_COLUMN_USAGE where COLUMN_NAME in (".join(",",@refcols).") and CONSTRAINT_SCHEMA='".$self->{dbname}."';"); +# foreach my $r (keys(%{$ref})){ +# my $refv = $refdata->{$ref->{$r}->{COLUMN_NAME}}; +# if ($refv eq ''){ +# $refv = ' is null'; +# } else { +# $refv =~ s/'/''/g; +# $refv = "='".$refv."'"; +# } +# push(@ddl,"DELETE from ".$ref->{$r}->{TABLE_NAME}." where ".$ref->{$r}->{COLUMN_NAME}.$refv.";"); +# } + foreach my $tb (keys(%{$fields})){ + my @sqlcond = (); + foreach my $c (keys(%{$fields->{$tb}->{cond}})){ + my $v = $fields->{$tb}->{cond}->{$c}; + $v =~ s/'/''/g; + push (@sqlcond,$c."='".$v."'"); + } + push(@ddl,"DELETE FROM ".$tb." WHERE ".join(" AND ",@sqlcond).";"); + } + return @ddl; +} + +sub textunidecode(){ + my $self = shift; + my $text = shift; + $text = lc(unidecode(decode("utf-8",$text))); + $text =~ s/^[a-z0-9]//g; + return $text; +} + +1; diff --git a/bin/CGI/api/lib/sendemail.pm b/bin/CGI/api/lib/sendemail.pm new file mode 100644 index 0000000..58ed8fb --- /dev/null +++ b/bin/CGI/api/lib/sendemail.pm @@ -0,0 +1,130 @@ +package sendemail; + +use strict; +use lib ('./lib/perl5'); +use lib ('./lib'); +use lib ('./'); +use Data::Dumper; +use File::Basename qw/dirname basename/; +use dksdb; + +sub new { + my $class = shift; + my $self = bless {}, $class; + $self->{server} = "mail.your-server.de"; + $self->{port} = "587"; + $self->{user} = 'kilian.saffran@fld.lu'; + $self->{password} = "Y6cWvXR6D2"; + $self->{from} = 'webmaster@fld.lu'; + return $self; +} + +sub sendemail(){ + my $self = shift; + my $template = shift; + my $iduser = shift; + my $sendto = shift; + my $data = shift; + my $attach = shift; + my $body = ""; + my $subject = ""; + my $maildata = (); + my $db = dksdb->new(); + my $send = -1; + my $tmpl = $db->query("select *,ml.mailtemplate from mailtemplates mt join maillayouts ml on (mt.id_maillayout=ml.id) where templatename='".$template."';"); + if (keys(%{$tmpl}) > 0){ + $tmpl = $tmpl->{0}; + } + # open (LOG,">>tmp/sendmail.log"); + # print LOG $ENV{SCRIPT_FILENAME}; + # print LOG "SEND EMAIL:".Dumper($data)."\n"; + # close(LOG); + my $datasql = $tmpl->{'emaildatasql'}; + $data->{id} = $iduser; + foreach my $key (keys(%{$data})){ + my $srch = '%%'.lc($key).'%%'; + my $repl = $data->{$key}; + $datasql =~ s/$srch/$repl/g; + } + # open (LOG,">>tmp/sendmail.log"); + # print LOG "TEMPLATE DATA:".$datasql."\n"; + # close(LOG); + $maildata = $db->query($datasql); + + $body = $tmpl->{'emailtext'}; + $subject = $tmpl->{'mailsubject'}; + foreach my $key (keys(%{$maildata->{0}})){ + $data->{$key} = $maildata->{0}->{$key}; + } + foreach my $key (keys(%{$data})){ + my $srch = '%%'.lc($key).'%%'; + my $repl = $data->{$key}; + $body =~ s/$srch/$repl/g; + $subject =~ s/$srch/$repl/g; + } + my $bodytmpl = $tmpl->{mailtemplate}; + $bodytmpl =~ s/%%BODYCONTENT%%/$body/; + my $siteurl = $ENV{'REQUEST_SCHEME'}.'://'.$ENV{"HTTP_HOST"}; + $bodytmpl =~ s/%%siteurl%%/$siteurl/g; + $bodytmpl =~ s/%%SITEURL%%/$siteurl/g; + $bodytmpl =~ s/\r//g; + #$bodytmpl =~ s/"/\\\"/g; + #PROD REPLACE all not replaced DATA + #$bodytmpl =~ s/%%\w+%%//g; + #$sendto = 'ksaffran@dks.lu'; + # open (LOG,">>tmp/sendmail.log"); + # print LOG "SUBJECT:".$subject."\n"; + # print LOG "BODY TEXT:".$bodytmpl."\n"; + # close(LOG); + if (($bodytmpl ne "") && ($subject ne "") && ($sendto =~ /.+\@.+\..+/)){ + + + my $binsemail = dirname($ENV{'SCRIPT_FILENAME'}).'/sendEmail'; + my $f = dirname($ENV{SCRIPT_FILENAME}).'/tmp/mailbody_'.$sendto.'.txt'; + $f =~ s/\@/_/g; + if (! -e $binsemail){ + $binsemail = dirname($ENV{'SCRIPT_FILENAME'}).'/api/sendEmail'; + $f = dirname($ENV{SCRIPT_FILENAME}).'/api/tmp/mailbody_'.$sendto.'.txt'; + $f =~ s/\@/_/g; + if (! -e $binsemail){ + return 256; + } + } + + my $cmd= 'perl "'.$binsemail.'" -f '.$tmpl->{mailfrom}.' '; + $cmd .= ' -s "'.$self->{server}.':'.$self->{port}.'" -xu "'.$self->{user}.'" -xp "'.$self->{password}.'" -q '; + $cmd .= '-o tls=auto '; + $cmd .= '-o message-content-type=html '; + $cmd .= '-o message-charset=ISO-8859-1 '; + $cmd .= '-o message-file='.$f.' '; + $cmd .= '-t "'.$sendto.'" '; + $cmd .= '-u "'.$subject.'" '; + # open (LOG,">>sendmail.log"); + # print LOG $cmd."\n"; + # # print LOG "BODY TEXT:".$bodytmpl."\n"; + # close(LOG); + open(EML,">".$f); + print EML $bodytmpl; + close(EML); + # $cmd .= '-m "'.$bodytmpl.'" '; + if ($attach != undef){ + $cmd .= " -a"; + + foreach my $a (@{$attach}){ + $cmd .= " ".$a." "; + } + } + # open (LOG,">>tmp/sendmail.log"); + # print LOG "SEND EMAIL CMD:".$cmd."\n"; + # close(LOG); + # $cmd =~ s/'/''/g; + $send = system($cmd); + # open (LOG,">>tmp/sendmail.log"); + # print LOG "CMD RETURN NUM:".$send."\n"; + # close(LOG); + unlink($f); + } + return $send; +} + +1; \ No newline at end of file diff --git a/bin/CGI/api/lib/session.pm b/bin/CGI/api/lib/session.pm new file mode 100644 index 0000000..44ab6a1 --- /dev/null +++ b/bin/CGI/api/lib/session.pm @@ -0,0 +1,232 @@ +package session; + +use strict; +use lib ('./lib/perl5'); +use lib ('./lib'); +use lib ('./'); +use File::Basename; +use Digest::SHA qw(sha256_hex); + +use dksdb; +use sendemail; +# use Data::Dumper; + +sub new { + my $class = shift; + my $self = bless {}, $class; + $self->{db} = dksdb->new(); + return $self; +} + +sub checklogin(){ + my $self = shift; + my $login = shift; + my $password = shift; + # open FILE,">>tmp/sql.log"; + # print FILE "pwd: $password\n"; + # close(FILE); + my $pwd = sha256_hex($password); + my $ret->{messagetype} ='w3-red'; + # my $newsid = undef; + $login = lc($login); + $login =~ s/^\s+//; + $login =~ s/\s+$//; + + $ret->{message} = "Passwuert oder Login onbekannt!"; + $ret->{messagetype} = "w3-red"; + $ret->{sid} = undef; + my $user = $self->{db}->dbquerysorted("select id from users where lower(username)=lower('".$self->{db}->securetext($login)."') and userpassword = '".$pwd."' and \"blocked\" is null;"); + # open FILE,">>tmp/sql.log"; + # print FILE "select id from users where username= '".$self->{db}->securetext($login)."' and userpassword = '".$pwd."' and \"blocked\" is null;\n"; + # close FILE; + if (keys(%{$user}) > 0){ + $ret->{sid} = $self->randomstring(40); + $self->{db}->dbexec("DELETE FROM sessions where id_user=".$user->{0}->{id}." and remote_addr='".$ENV{REMOTE_ADDR}."' and user_agent='".$ENV{HTTP_USER_AGENT}."';"); + my $r = $self->{db}->dbexec("INSERT INTO sessions (id_user,idsession,remote_addr,user_agent) VALUES (".$user->{0}->{id}.", '".$ret->{sid}."', '".$ENV{REMOTE_ADDR}."', '".$ENV{HTTP_USER_AGENT}."');"); + } + return $ret; +} + +sub savepassword(){ + my $self = shift; + my $iduser = shift; + my $newpwd = shift; + my $pwd = sha256_hex($newpwd); + $self->{db}->dbexec("UPDATE users SET userpassword = '".$pwd."' WHERE id=".$iduser.";"); + return 1; +} + +sub passwordforgotten(){ + my $self = shift; + my $email = shift; + my $ret->{messagetype} ='w3-red'; + $ret->{message} = "Onbekannt E-mail!"; + my $sql = "select id,userpassword from users where username='".$self->{db}->securetext($email)."';"; + my $ex = $self->{db}->dbquerysorted($sql); + if (keys(%{$ex}) > 0){ + my $newpwd = $self->randomstring(12); + my $pwd = sha256_hex($newpwd); + $self->{db}->dbexec("UPDATE users SET userpassword = '".$pwd."' WHERE id=".$ex->{0}->{id}.";"); + my $data->{newpassword} = $newpwd; + my $eml = sendemail->new(); + my $mret = $eml->sendemail('user_forgotpasswd',$ex->{0}->{id},$email,$data,undef); + if ($mret != 0){ + $ret->{messagetype} ='w3-red'; + $ret->{message} = "Den Moment ass et leider nët méglech d'Passwuert autmatesch zreckzesetzen,
wend dech w.e.g. via E-Mail un webmaster\@fld.lu!"; + return $ret; + } + $ret->{message} = "Mir hun dir eng E-Mail, matt engem neien Passwuert gescheckt!"; + $ret->{messagetype} = "w3-green"; + } + return $ret; +} + +sub registeruser(){ + my $self = shift; + my $data = shift; + my $ret->{messagetype} ='w3-red'; + $ret->{message} = "Een Fehler ass passéiert, probéier et spéier nach eemol!"; + $ret->{page} = "message.tt"; + if (!exists($data->{license}) || !exists($data->{regcode}) || !exists($data->{email}) || !exists($data->{terms})){ + $ret->{message} = "W.e.g. All Felder ausfëllen!"; + $ret->{page} = "register.tt"; + return $ret; + } + foreach my $d (%{$data}){ + $data->{$d} = $self->{db}->securetext($data->{$d}); + } + + my $user = $self->{db}->dbquerysorted("select id from users where username='".$data->{email}."';"); + if (keys(%{$user}) > 0){ + $ret->{page} = "register.tt"; + $ret->{message} = "Een Benotzer matt der selwechter E-Mail existéiert schon!"; + return $ret; + } + my $license = $self->{db}->dbquerysorted("select us.id as id_user,lic.license,mb.id as id_member,us.username,us.vcode,us.regcode from members mb join licenses lic on (lic.id_member=mb.id) join users us on (mb.id_user=us.id) where us.regcode='".$data->{regcode}."' and lic.license='".$data->{license}."' limit 1"); + if (keys(%{$license}) == 0){ + $ret->{message} = "Falsch Lizenz-Nummer oder falschen Régistréierungs-Code!"; + return $ret; + } + my $regcode = $license->{0}->{regcode}; + my $newcode = $self->randomstring(6); + #my $usergroup = $self->{db}->dbquerysorted("select id from usergroups where usergroup ='avocat';"); + my $newuserid = $self->{db}->dbquerysorted("UPDATE users set username='".$data->{email}."',vcode='".$newcode."' where id=".$license->{0}->{id_user}." ;"); + my $maildata->{vcode} = $newcode; + my $eml = sendemail->new(); + my $mret = $eml->sendemail('user_verification',$license->{0}->{id_user},$data->{email},$maildata,undef); + if ($mret == 0){ + $ret->{message} = "Merci,
Mir hun dir elo eng E-Mail gescheckt, mattengem Code fir deng E-Mail ze verifizéieren!
Gëff desen Code w.e.g. an daat Feld hei drënner an!
Bei Problemer wend dech w.e.g. via E-Mail un webmaster\@fld.lu"; + $ret->{messagetype} = "w3-green"; + $ret->{page} = "validationcode.tt"; + } else { + $self->{db}->dbexec("UPDATE users set username='".$data->{email}."',vcode=null where id=".$license->{0}->{id_user}." ;"); + $ret->{message} = "Aus iergend engem Grond konnten mir dir keng E-Mail un '".$data->{email}."' schecken! Falls dess E-Mail-Address net existéiert, versich et nach eng Kéier matt enger E-Mail-Address, déi existéiert!
Bei Problemer wend dech w.e.g. via E-Mail un webmaster\@fld.lu"; + $ret->{messagetype} = "w3-red"; + $ret->{page} = "register.tt"; + } + #$self->{db}->dbexec("insert into appaccess (id_user) values (".$newuserid->{0}->{id}.");"); + # $ret->{messagetype} = "w3-green"; + + return $ret; +} + +sub validateaccount(){ + my $self = shift; + my $data = shift; + foreach my $d (%{$data}){ + $data->{$d} = $self->{db}->securetext($data->{$d}); + } + + my $ret->{messagetype} ='w3-red'; + my $vcodedata = $self->{db}->dbquerysorted("select id,vcode,username from users where vcode='".$data->{vcode}."';"); + if (keys(%{$vcodedata}) == 0){ + $ret->{message} = "Benotzer onbekannt oder Code falsch!"; + $ret->{page} = "validationcode.tt"; + } + my $newpwd = $self->randomstring(12); + my $pwd = sha256_hex($newpwd); + my $maildata->{password} = $newpwd; + my $eml = sendemail->new(); + my $newuserid = $self->{db}->dbquerysorted("UPDATE users set userpassword='".$pwd."',vcode=null,regcode=null where id=".$vcodedata->{0}->{id}." returning id,username;"); + my $mret = $eml->sendemail('user_registration',$vcodedata->{0}->{id},$vcodedata->{0}->{username},$maildata,undef); + if ($mret == 0){ + $ret->{message} = "Merci,
Mir hun dir elo eng E-Mail gescheckt, matt all deenen néidegen Donnéeen fir dech anzeloggen!
Bei Problemer wend dech w.e.g. via E-Mail un webmaster\@fld.lu"; + $ret->{messagetype} = "w3-green"; + $ret->{page} = "message.tt"; + } else { + $ret->{message} = "Aus iergend engem Grond konnten mir dir keng E-Mail un '".$newuserid->{0}->{username}."' schecken! Falls dess E-Mail-Address net existéiert, versich et nach eng Kéier matt enger E-Mail-Address, déi existéiert!
Bei Problemer wend dech w.e.g. via E-Mail un webmaster\@fld.lu"; + $ret->{page} = "message.tt"; + } + return $ret; +} + +sub getsession($){ + my $self = shift; + my $sid = shift; + my $sql ="select se.idsession,us.id,us.username,string_agg(distinct(aug.usergroup),',') as usergroups from sessions se +join users us on (us.id=se.id_user) +left join useringroups uig on (us.id=uig.id_user) +left join usergroups aug on (aug.id=uig.id_group) +where se.idsession= '".$self->{db}->securetext($sid)."' +and se.remote_addr= '".$ENV{REMOTE_ADDR}."' +and se.user_agent='".$ENV{HTTP_USER_AGENT}."' and us.blocked is null group by se.id,us.id;"; + my $res= $self->{db}->dbquerysorted($sql); + my $ret = undef; + if (keys(%{$res}) > 0){ + return $res->{0}; + } + return $ret; +} + +sub deletesession(){ + my $self = shift; + my $sid = shift; + $self->{db}->dbexec("DELETE FROM sessions where idsession='".$self->{db}->securetext($sid)."';"); +} + +sub randomstring(){ + my $self = shift; + my $num = shift; + my @alphanumeric = ('a'..'z', 'A'..'Z', 0..9); + my $randstring = join '', map $alphanumeric[rand @alphanumeric], 0..$num; + return $randstring; +} + + +# sub deleteprofile(){ +# my $self = shift; +# my $data = shift; +# my $ret->{message} = "mot de passe ou profile inconnue!"; +# $ret->{messagetype} = "danger"; +# if ($data->{id_user} eq ''){ +# $ret->{sid} = undef; +# return $ret; +# } +# my $pwd = sha256_hex($data->{password}); +# my $user = $self->{db}->dbquerysorted("select id from users where id= '".$data->{id_user}."' and userpassword = '".$pwd."';"); +# if (keys(%{$user}) > 0){ +# $self->admindeleteuser($data->{id_user}); +# my $ret->{'message'} = "Votre profile a été supprimé!"; +# $ret->{'messagetype'} = "info"; +# $ret->{sid} = undef; +# } +# return $ret; + +# } + +# sub admindeleteuser(){ +# my $self = shift; +# my $id_user = shift; +# my @dl = ("DELETE FROM public.useringroups WHERE id_uset=".$id_user.";", +# "DELETE FROM public.userclients WHERE id_user=".$id_user.";", +# "DELETE FROM public.appaccess WHERE id_user=".$id_user.";", +# "DELETE FROM public.modulepreferences WHERE id_user=".$id_user.";",, +# "DELETE FROM public.sessions WHERE id_user=".$id_user.";", +# "delete from users where id=".$id_user.";"); +# foreach my $s (@dl){ +# $self->{db}->dbexec($s); +# } +# return 1; +# } + +1; \ No newline at end of file diff --git a/bin/CGI/api/service.cgi b/bin/CGI/api/service.cgi new file mode 100644 index 0000000..1e019e6 --- /dev/null +++ b/bin/CGI/api/service.cgi @@ -0,0 +1,208 @@ +#!/Users/kilian/perl5/perlbrew/perls/perl-5.24.1/bin/perl +use strict; +use FindBin qw($Bin $RealBin); +# use lib ('CGI/api/lib/perl5'); +# use lib ('CGI/api/lib'); +use lib ($Bin.'/CGI/api/lib/perl5'); +use lib ($Bin.'/CGI/api/lib'); +use CGI; +use CGI::Cookie; +# use CGI::Carp qw/fatalsToBrowser/; +use File::Basename; +use JSON::PP; +use dksconfig qw/$sitecfg/; +use dksdb; +use Digest::SHA::PurePerl qw(sha256_hex); + +# use session; +#use sendemail; +my $cgi = new CGI(); +my $scriptpath = $cgi->url(-absolute => 1); +my $p = (); +my @params = $cgi->param(); +foreach my $pe (@params){ + $p->{$pe} = $cgi->param($pe); +} +my $html->{result} = (); +# $p->{sid} = $cgi->cookie($sitecfg->{cookiename}); +# my $se = session->new(); +# my $sess = $se->getsession($p->{sid}); +print $cgi->header(-type=>"application/json", -charset => "utf-8"); + +if (exists($p->{fn})){ + if ($p->{fn} eq "info"){ + $html->{result} = &appinfo($p); + } + if ($$p->{fn} eq "preferences"){ + $html->{result} = &preferences($p); + } + if ($p->{fn} eq "setaccess"){ + $html->{result} = &setWebLogin($p); + } + if ($p->{fn} eq "getaccess"){ + $html->{result} = &getWebLogin($p); + } + if (($p->{fn} eq "stop") || ($p->{fn} eq "unload")){ + exit(0); + } +} +print JSON::PP::encode_json($html); + +sub preferences(){ + + my $req =shift; + my $name = &getAppName(); + my $pref= (); + my $appcfgpath = &getappconfigpath(); + if (exists($req->{page})){ + $appcfgpath =~ s/\\/\//g; + if (-e $appcfgpath.'/'.$req->{page}.'.json'){ + open(PREF,$appcfgpath.'/'.$req->{page}.'.json'); + my $strpref = ""; + while (my $l = ){ + $strpref .= $l; + } + close(PREF); + $pref=JSON::PP::decode_json($strpref); + } + my @kk = keys(%{$p}); + my $setcron = ''; + #print "keys.".Dumper(@kk)."\n"; + if (scalar(@kk) > 0){ + foreach my $px (@kk){ + my $newpref = JSON::PP::decode_json($req->{$px}); + if ($p eq 'sync'){ + $setcron = $p; + } + #if (exists($pref->{result}->{$p})){ + $pref->{$p} = $newpref; + #} + } + open(PREF,">".$appcfgpath.'/'.$req->{page}.'.json'); + print PREF JSON::PP::encode_json($pref); + close(PREF); + + } + if ($setcron ne ''){ + if ($setcron eq 'sync'){ + &setcron(dirname($0).'/syncdb.pl',$pref->{sync}->{time}); + } + } + } + +} + +sub appinfo(){ + my $req = shift; + my $ret = (); + + my $name = basename($0); + $name =~ s/srv\.pl$//; + $name =~ s/srv\.exe$//; + $ret->{OS} = $^O; + $ret->{app} = $name; + $ret->{appcfgpath} = &getappconfigpath(); + if ($^O eq "MSWin32"){ + $ret->{home} = $ENV{USERPROFILE}; + $ret->{user} = $ENV{USERNAME}; + $$ret->{hostname} = $ENV{COMPUTERNAME}; + $ret->{arch} = $ENV{PROCESSOR_ARCHITEW6432}; + $ret->{appcfgpath} =~ s/\\/\//g; + $ret->{home} =~ s/\\/\//g; + } else { + $ret->{home} = $ENV{HOME}; + $ret->{user} = $ENV{USER}; + $ret->{hostname} = `hostname -s`; + chomp($ret->{hostname}); + $ret->{arch} = `uname -m`; + chomp($ret->{arch}); + } + if (! -e $ret->{appcfgpath}){ + make_path($ret->{appcfgpath}); + } + if (-e $ret->{appcfgpath}.'/'.$name.'.json'){ + open(LCFG,$ret->{appcfgpath}.'/'.$name.'.json'); + my $strprofile = ""; + while (my $l = ){ + $strprofile .= $l; + } + close(LCFG); + if ($strprofile ne ""){ + $ret->{appconfig} = JSON::PP::decode_json($strprofile); + } + } + if (!exists($ret->{appconfig})){ + $ret->{appconfig} = undef; + } + return $ret; +} + +sub getWebLogin(){ + + my $req = shift; + my $ret = (); + + my $apppath = &getappconfigpath(); + my $loginname= ""; + #print "read:". $apppath.'/'.&getAppName().'.passwd'."\n"; + if (-e $apppath.'/'.&getAppName().'.passwd'){ + open(AUTH,$apppath.'/'.&getAppName().'.passwd'); + while (my $l = ){ + chomp($l); + if ($l eq ""){ next;} + ($loginname) = $l =~ m/^(\w+)\=.*/; + } + close(AUTH); + } + $ret->{login} = $loginname; + return $ret; +} + +sub setWebLogin(){ + my $req = shift; + my $ret = -1; + my $apppath = &getappconfigpath(); + if (exists($req->{login}) && exists($req->{passwd})){ + open(AUTH,">".$apppath.'/'.&getAppName().'.passwd'); + print AUTH $req->{login}.'='.$req->{passwd}; + close(AUTH); + $ret = 0; + } + return $ret; +} + +sub getappconfigpath(){ + my $datadir = dirname($RealBin).'/data'; + return $datadir; +} + +sub getAppName(){ + my $name = basename($0); + $name =~ s/srv\.pl$//; + $name =~ s/srv\.exe$//; + return lc($name); +} + +sub setcron(){ + 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}); +} diff --git a/bin/CGI/api/system.cgi b/bin/CGI/api/system.cgi new file mode 100644 index 0000000..5d0d463 --- /dev/null +++ b/bin/CGI/api/system.cgi @@ -0,0 +1,167 @@ +#!/Users/kilian/perl5/perlbrew/perls/perl-5.24.1/bin/perl +use strict; +use FindBin qw($Bin); +# use lib ('CGI/api/lib/perl5'); +# use lib ('CGI/api/lib'); +use lib ($Bin.'/CGI/api/lib/perl5'); +use lib ($Bin.'/CGI/api/lib'); +use CGI; +use CGI::Cookie; +# use CGI::Carp qw/fatalsToBrowser/; +use File::Basename; +use JSON::PP; +use dksconfig qw/$sitecfg/; +use dksdb; + +# use session; +#use sendemail; +my $cgi = new CGI(); +my $scriptpath = $cgi->url(-absolute => 1); +my $p = (); +my @params = $cgi->param(); +foreach my $pe (@params){ + $p->{$pe} = $cgi->param($pe); +} +my $html->{result} = undef; +# $p->{sid} = $cgi->cookie($sitecfg->{cookiename}); +# my $se = session->new(); +# my $sess = $se->getsession($p->{sid}); +print $cgi->header(-type=>"application/json", -charset => "utf-8"); +if (exists($p->{fn})) { + if ($p->{fn} eq "getconfig"){ + $html->{result} = &getconfig($p); + } elsif ($p->{fn} eq "setconfig") { + $html->{result} = &setconfig($p); + } elsif ($p->{fn} eq "shutdown"){ + print "Shutdown launched!\n"; + system('sudo shutdown -h now'); + $html->{result} = "shutdown launched"; + } elsif ($p->{fn} eq "restart"){ + print "Restart launched!\n"; + system('sudo shutdown -r now'); + $html->{result} = "restart launched"; + } +} +print JSON::PP::encode_json($html); + +sub getconfig(){ + + my $req = shift; + my $ret = (); + my @sections = ('lan','wlan','hostname','activenet'); + if (exists($req->{section})){ + @sections = split(',',$req->{section}); + } + foreach my $s (@sections){ + if ($s eq 'wlan') { + $ret->{$s} = &getwifinetworks(); + } elsif ($s eq 'hostname') { + $ret->{$s} = &gethostname(); + } elsif ($s eq 'activenet'){ + $ret->{$s} = &getactivenet(); + } + } + return $ret; +} + +sub setconfig(){ + my $req = shift; + my $ret = (); + my @sections = ('wlan','hostname'); + if (exists($req->{section})){ + @sections = split(',',$req->{section}); + } + foreach my $k (@sections){ + my $values = JSON::PP::decode_json($req->{$k}); + #print Dumper($values); + if ($k eq 'wlan'){ + $ret->{$k} = &setwifinetwork($values); + } + if ($k eq 'hostname'){ + $ret->{$k} = &sethostname($values); + } + } + return $ret; +} + +#weblogin + +sub getactivenet(){ + 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 $hostname = `hostname -s`; + chomp($hostname); + return $hostname; +} + +sub sethostname(){ + 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 $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 $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; +} diff --git a/bin/CGI/api/tools/fingerdelete.py b/bin/CGI/api/tools/fingerdelete.py new file mode 100644 index 0000000..cfcdf29 --- /dev/null +++ b/bin/CGI/api/tools/fingerdelete.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +PyFingerprint +Copyright (C) 2015 Bastian Raschke +All rights reserved. + +""" +import sys +from pyfingerprint2.pyfingerprint import PyFingerprint +if len(sys.argv) == 1: + print ('Error: No position!'); + exit(1) + +## Deletes a finger from sensor +## + + +## Tries to initialize the sensor +try: + f = PyFingerprint('/dev/ttyUSB0', 57600, 0xFFFFFFFF, 0x00000000) + + if ( f.verifyPassword() == False ): + raise ValueError('The given fingerprint sensor password is wrong!') + +except Exception as e: + print('The fingerprint sensor could not be initialized!') + print('Exception message: ' + str(e)) + exit(1) + +## Gets some sensor information +#print('Currently used templates: ' + str(f.getTemplateCount()) +'/'+ str(f.getStorageCapacity())) + +## Tries to delete the template of the finger +try: + positionNumber = int(sys.argv[1]) + + if ( f.deleteTemplate(positionNumber) == True ): + print('Template ' + str(positionNumber) +' deleted!') + +except Exception as e: + print('Operation failed!') + print('Exception message: ' + str(e)) + exit(1) diff --git a/bin/CGI/api/tools/fingerenroll.py b/bin/CGI/api/tools/fingerenroll.py new file mode 100644 index 0000000..8319dc8 --- /dev/null +++ b/bin/CGI/api/tools/fingerenroll.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +PyFingerprint +Copyright (C) 2015 Bastian Raschke +All rights reserved. + +""" + +import time +from pyfingerprint2.pyfingerprint import PyFingerprint + + +## Enrolls new finger +## + +## Tries to initialize the sensor +try: + f = PyFingerprint('/dev/ttyUSB0', 57600, 0xFFFFFFFF, 0x00000000) + + if ( f.verifyPassword() == False ): + raise ValueError('The given fingerprint sensor password is wrong!') + +except Exception as e: + print('The fingerprint sensor could not be initialized!') + print('Exception message: ' + str(e)) + exit(1) + +## Gets some sensor information +print('Currently used templates: ' + str(f.getTemplateCount()) +'/'+ str(f.getStorageCapacity())) + +## Tries to enroll new finger +try: + print('Waiting for finger...') + + ## Wait that finger is read + while ( f.readImage() == False ): + pass + + ## Converts read image to characteristics and stores it in charbuffer 1 + f.convertImage(0x01) + + ## Checks if finger is already enrolled + result = f.searchTemplate() + positionNumber = result[0] + + if ( positionNumber >= 0 ): + print('Template already exists at position #' + str(positionNumber)) + exit(0) + + print('Remove finger...') + time.sleep(2) + + print('Waiting for same finger again...') + + ## Wait that finger is read again + while ( f.readImage() == False ): + pass + + ## Converts read image to characteristics and stores it in charbuffer 2 + f.convertImage(0x02) + + ## Compares the charbuffers + if ( f.compareCharacteristics() == 0 ): + raise Exception('Fingers do not match') + + ## Creates a template + f.createTemplate() + + ## Saves template at new position number + positionNumber = f.storeTemplate() + print('Finger enrolled successfully!') + print('New template position #' + str(positionNumber)) + +except Exception as e: + print('Operation failed!') + print('Exception message: ' + str(e)) + exit(1) diff --git a/bin/CGI/api/tools/fingerledoff.py b/bin/CGI/api/tools/fingerledoff.py new file mode 100644 index 0000000..1eec425 --- /dev/null +++ b/bin/CGI/api/tools/fingerledoff.py @@ -0,0 +1,27 @@ +""" +PyFingerprint +Copyright (C) 2015 Bastian Raschke +All rights reserved. + +""" + +import time +from pyfingerprint2.pyfingerprint import PyFingerprint + + +## Enrolls new finger +## + +## Tries to initialize the sensor +try: + f = PyFingerprint('/dev/ttyUSB0', 57600, 0xFFFFFFFF, 0x00000000) + + if ( f.verifyPassword() == False ): + raise ValueError('The given fingerprint sensor password is wrong!') + +except Exception as e: + print('The fingerprint sensor could not be initialized!') + print('Exception message: ' + str(e)) + exit(1) + +f.setLEDoff() diff --git a/bin/CGI/api/tools/fingerledon.py b/bin/CGI/api/tools/fingerledon.py new file mode 100644 index 0000000..8a08f7f --- /dev/null +++ b/bin/CGI/api/tools/fingerledon.py @@ -0,0 +1,27 @@ +""" +PyFingerprint +Copyright (C) 2015 Bastian Raschke +All rights reserved. + +""" + +import time +from pyfingerprint2.pyfingerprint import PyFingerprint + + +## Enrolls new finger +## + +## Tries to initialize the sensor +try: + f = PyFingerprint('/dev/ttyUSB0', 57600, 0xFFFFFFFF, 0x00000000) + + if ( f.verifyPassword() == False ): + raise ValueError('The given fingerprint sensor password is wrong!') + +except Exception as e: + print('The fingerprint sensor could not be initialized!') + print('Exception message: ' + str(e)) + exit(1) + +f.setLEDon() diff --git a/bin/CGI/api/tools/fingersearch.py b/bin/CGI/api/tools/fingersearch.py new file mode 100644 index 0000000..be98fce --- /dev/null +++ b/bin/CGI/api/tools/fingersearch.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +PyFingerprint +Copyright (C) 2015 Bastian Raschke +All rights reserved. + +""" + +import hashlib +from pyfingerprint2.pyfingerprint import PyFingerprint + + +## Search for a finger +## + +## Tries to initialize the sensor +try: + f = PyFingerprint('/dev/ttyUSB0', 57600, 0xFFFFFFFF, 0x00000000) + + if ( f.verifyPassword() == False ): + raise ValueError('The given fingerprint sensor password is wrong!') + +except Exception as e: + print('The fingerprint sensor could not be initialized!') + print('Exception message: ' + str(e)) + exit(1) + +## Gets some sensor information +print('Currently used templates: ' + str(f.getTemplateCount()) +'/'+ str(f.getStorageCapacity())) + +## Tries to search the finger and calculate hash +try: + print('Waiting for finger...') + + ## Wait that finger is read + while ( f.readImage() == False ): + pass + + ## Converts read image to characteristics and stores it in charbuffer 1 + f.convertImage(0x01) + + ## Searchs template + result = f.searchTemplate() + + positionNumber = result[0] + accuracyScore = result[1] + + if ( positionNumber == -1 ): + print('No match found!') + exit(0) + else: + print('Found template at position #' + str(positionNumber)) + print('The accuracy score is: ' + str(accuracyScore)) + ## Loads the found template to charbuffer 1 + f.loadTemplate(positionNumber, 0x01) + + ## Downloads the characteristics of template loaded in charbuffer 1 + characterics = str(f.downloadCharacteristics(0x01)).encode('utf-8') + + ## Hashes characteristics of template + #print('SHA-2 hash of template: ' + hashlib.sha256(characterics).hexdigest()) + print('POS:' + str(positionNumber) + ';HASH:' + hashlib.sha256(characterics).hexdigest()) + +except Exception as e: + print('Operation failed!') + print('Exception message: ' + str(e)) + exit(1) diff --git a/bin/CGI/api/tools/pyfingerprint2/__init__.py b/bin/CGI/api/tools/pyfingerprint2/__init__.py new file mode 100644 index 0000000..4882220 --- /dev/null +++ b/bin/CGI/api/tools/pyfingerprint2/__init__.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +PyFingerprint +Copyright (C) 2015 Bastian Raschke +All rights reserved. + +""" + +__version__ = '1.5' diff --git a/bin/CGI/api/tools/pyfingerprint2/__init__.pyc b/bin/CGI/api/tools/pyfingerprint2/__init__.pyc new file mode 100644 index 0000000..04b4849 Binary files /dev/null and b/bin/CGI/api/tools/pyfingerprint2/__init__.pyc differ diff --git a/bin/CGI/api/tools/pyfingerprint2/pyfingerprint.py b/bin/CGI/api/tools/pyfingerprint2/pyfingerprint.py new file mode 100644 index 0000000..f5e8d19 --- /dev/null +++ b/bin/CGI/api/tools/pyfingerprint2/pyfingerprint.py @@ -0,0 +1,1358 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +PyFingerprint +Copyright (C) 2015 Bastian Raschke +All rights reserved. + +""" + +import os +import serial +from PIL import Image +import struct + + +## Baotou start byte +FINGERPRINT_STARTCODE = 0xEF01 + +## Packet identification +## + +FINGERPRINT_COMMANDPACKET = 0x01 + +FINGERPRINT_ACKPACKET = 0x07 +FINGERPRINT_DATAPACKET = 0x02 +FINGERPRINT_ENDDATAPACKET = 0x08 + +## Instruction codes +## + +FINGERPRINT_VERIFYPASSWORD = 0x13 +FINGERPRINT_SETPASSWORD = 0x12 +FINGERPRINT_SETADDRESS = 0x15 +FINGERPRINT_SETSYSTEMPARAMETER = 0x0E +FINGERPRINT_GETSYSTEMPARAMETERS = 0x0F +FINGERPRINT_TEMPLATEINDEX = 0x1F +FINGERPRINT_TEMPLATECOUNT = 0x1D + +FINGERPRINT_READIMAGE = 0x01 + +## Note: The documentation mean upload to host computer. +FINGERPRINT_DOWNLOADIMAGE = 0x0A + +FINGERPRINT_CONVERTIMAGE = 0x02 + +FINGERPRINT_CREATETEMPLATE = 0x05 +FINGERPRINT_STORETEMPLATE = 0x06 +FINGERPRINT_SEARCHTEMPLATE = 0x04 +FINGERPRINT_LOADTEMPLATE = 0x07 +FINGERPRINT_DELETETEMPLATE = 0x0C + +FINGERPRINT_CLEARDATABASE = 0x0D +FINGERPRINT_GENERATERANDOMNUMBER = 0x14 +FINGERPRINT_COMPARECHARACTERISTICS = 0x03 + +## Note: The documentation mean download from host computer. +FINGERPRINT_UPLOADCHARACTERISTICS = 0x09 + +## Note: The documentation mean upload to host computer. +FINGERPRINT_DOWNLOADCHARACTERISTICS = 0x08 +## LED +FINGERPRINT_LEDON = 0x50 +FINGERPRINT_LEDOFF = 0x51 +## Packet reply confirmations +## + +FINGERPRINT_OK = 0x00 +FINGERPRINT_ERROR_COMMUNICATION = 0x01 + +FINGERPRINT_ERROR_WRONGPASSWORD = 0x13 + +FINGERPRINT_ERROR_INVALIDREGISTER = 0x1A + +FINGERPRINT_ERROR_NOFINGER = 0x02 +FINGERPRINT_ERROR_READIMAGE = 0x03 + +FINGERPRINT_ERROR_MESSYIMAGE = 0x06 +FINGERPRINT_ERROR_FEWFEATUREPOINTS = 0x07 +FINGERPRINT_ERROR_INVALIDIMAGE = 0x15 + +FINGERPRINT_ERROR_CHARACTERISTICSMISMATCH = 0x0A + +FINGERPRINT_ERROR_INVALIDPOSITION = 0x0B +FINGERPRINT_ERROR_FLASH = 0x18 + +FINGERPRINT_ERROR_NOTEMPLATEFOUND = 0x09 + +FINGERPRINT_ERROR_LOADTEMPLATE = 0x0C + +FINGERPRINT_ERROR_DELETETEMPLATE = 0x10 + +FINGERPRINT_ERROR_CLEARDATABASE = 0x11 + +FINGERPRINT_ERROR_NOTMATCHING = 0x08 + +FINGERPRINT_ERROR_DOWNLOADIMAGE = 0x0F +FINGERPRINT_ERROR_DOWNLOADCHARACTERISTICS = 0x0D + +## Unknown error codes +## + +FINGERPRINT_ADDRCODE = 0x20 +FINGERPRINT_PASSVERIFY = 0x21 + +FINGERPRINT_PACKETRESPONSEFAIL = 0x0E + +FINGERPRINT_ERROR_TIMEOUT = 0xFF +FINGERPRINT_ERROR_BADPACKET = 0xFE + + +class PyFingerprint(object): + """ + A python written library for the ZhianTec ZFM-20 fingerprint sensor. + + @attribute integer(4 bytes) __address + Address to connect to sensor. + + @attribute integer(4 bytes) __password + Password to connect to sensor. + + @attribute Serial __serial + UART serial connection via PySerial. + """ + __address = None + __password = None + __serial = None + + def __init__(self, port = '/dev/ttyUSB0', baudRate = 57600, address = 0xFFFFFFFF, password = 0x00000000): + """ + Constructor + + @param string port + @param integer baudRate + @param integer(4 bytes) address + @param integer(4 bytes) password + """ + + if ( os.path.exists(port) == False ): + raise ValueError('The fingerprint sensor port "' + port + '" was not found!') + + if ( baudRate < 9600 or baudRate > 115200 or baudRate % 9600 != 0 ): + raise ValueError('The given baudrate is invalid!') + + if ( address < 0x00000000 or address > 0xFFFFFFFF ): + raise ValueError('The given address is invalid!') + + if ( password < 0x00000000 or password > 0xFFFFFFFF ): + raise ValueError('The given password is invalid!') + + self.__address = address + self.__password = password + + ## Initialize PySerial connection + self.__serial = serial.Serial(port = port, baudrate = baudRate, bytesize = serial.EIGHTBITS, timeout = 2) + + if ( self.__serial.isOpen() == True ): + self.__serial.close() + + self.__serial.open() + + def __del__(self): + """ + Destructor + + """ + + ## Close connection if still established + if ( self.__serial is not None and self.__serial.isOpen() == True ): + self.__serial.close() + + def __rightShift(self, n, x): + """ + Shift a byte. + + @param integer n + @param integer x + @return integer + """ + + return (n >> x & 0xFF) + + def __leftShift(self, n, x): + """ + Shift a byte. + + @param integer n + @param integer x + @return integer + """ + + return (n << x) + + def __bitAtPosition(self, n, p): + """ + Get the bit of n at position p. + + @param integer n + @param integer p + @return integer + """ + + ## A bitshift 2 ^ p + twoP = 1 << p + + ## Binary AND composition (on both positions must be a 1) + ## This can only happen at position p + result = n & twoP + return int(result > 0) + + def __byteToString(self, byte): + """ + Converts a byte to string. + + @param byte byte + @return string + """ + + return struct.pack('@B', byte) + + def __stringToByte(self, string): + """ + Convert one "string" byte (like '0xFF') to real integer byte (0xFF). + + @param string string + @return byte + """ + + return struct.unpack('@B', string)[0] + + def __writePacket(self, packetType, packetPayload): + """ + Send a packet to fingerprint sensor. + + @param integer(1 byte) packetType + @param tuple packetPayload + + @return void + """ + + ## Write header (one byte at once) + self.__serial.write(self.__byteToString(self.__rightShift(FINGERPRINT_STARTCODE, 8))) + self.__serial.write(self.__byteToString(self.__rightShift(FINGERPRINT_STARTCODE, 0))) + + self.__serial.write(self.__byteToString(self.__rightShift(self.__address, 24))) + self.__serial.write(self.__byteToString(self.__rightShift(self.__address, 16))) + self.__serial.write(self.__byteToString(self.__rightShift(self.__address, 8))) + self.__serial.write(self.__byteToString(self.__rightShift(self.__address, 0))) + + self.__serial.write(self.__byteToString(packetType)) + + ## The packet length = package payload (n bytes) + checksum (2 bytes) + packetLength = len(packetPayload) + 2 + + self.__serial.write(self.__byteToString(self.__rightShift(packetLength, 8))) + self.__serial.write(self.__byteToString(self.__rightShift(packetLength, 0))) + + ## The packet checksum = packet type (1 byte) + packet length (2 bytes) + payload (n bytes) + packetChecksum = packetType + self.__rightShift(packetLength, 8) + self.__rightShift(packetLength, 0) + + ## Write payload + for i in range(0, len(packetPayload)): + self.__serial.write(self.__byteToString(packetPayload[i])) + packetChecksum += packetPayload[i] + + ## Write checksum (2 bytes) + self.__serial.write(self.__byteToString(self.__rightShift(packetChecksum, 8))) + self.__serial.write(self.__byteToString(self.__rightShift(packetChecksum, 0))) + + def __readPacket(self): + """ + Receive a packet from fingerprint sensor. + + Return a tuple that contain the following information: + 0: integer(1 byte) The packet type. + 1: integer(n bytes) The packet payload. + + @return tuple + """ + + receivedPacketData = [] + i = 0 + + while ( True ): + + ## Read one byte + receivedFragment = self.__serial.read() + + if ( len(receivedFragment) != 0 ): + receivedFragment = self.__stringToByte(receivedFragment) + ## print 'Received packet fragment = ' + hex(receivedFragment) + + ## Insert byte if packet seems valid + receivedPacketData.insert(i, receivedFragment) + i += 1 + + ## Packet could be complete (the minimal packet size is 12 bytes) + if ( i >= 12 ): + + ## Check the packet header + if ( receivedPacketData[0] != self.__rightShift(FINGERPRINT_STARTCODE, 8) or receivedPacketData[1] != self.__rightShift(FINGERPRINT_STARTCODE, 0) ): + raise Exception('The received packet do not begin with a valid header!') + + ## Calculate packet payload length (combine the 2 length bytes) + packetPayloadLength = self.__leftShift(receivedPacketData[7], 8) + packetPayloadLength = packetPayloadLength | self.__leftShift(receivedPacketData[8], 0) + + ## Check if the packet is still fully received + ## Condition: index counter < packet payload length + packet frame + if ( i < packetPayloadLength + 9 ): + continue + + ## At this point the packet should be fully received + + packetType = receivedPacketData[6] + + ## Calculate checksum: + ## checksum = packet type (1 byte) + packet length (2 bytes) + packet payload (n bytes) + packetChecksum = packetType + receivedPacketData[7] + receivedPacketData[8] + + packetPayload = [] + + ## Collect package payload (ignore the last 2 checksum bytes) + for j in range(9, 9 + packetPayloadLength - 2): + packetPayload.append(receivedPacketData[j]) + packetChecksum += receivedPacketData[j] + + ## Calculate full checksum of the 2 separate checksum bytes + receivedChecksum = self.__leftShift(receivedPacketData[i - 2], 8) + receivedChecksum = receivedChecksum | self.__leftShift(receivedPacketData[i - 1], 0) + + if ( receivedChecksum != packetChecksum ): + raise Exception('The received packet is corrupted (the checksum is wrong)!') + + return (packetType, packetPayload) + + def verifyPassword(self): + """ + Verify password of the fingerprint sensor. + + @return boolean + """ + + packetPayload = ( + FINGERPRINT_VERIFYPASSWORD, + self.__rightShift(self.__password, 24), + self.__rightShift(self.__password, 16), + self.__rightShift(self.__password, 8), + self.__rightShift(self.__password, 0), + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Sensor password is correct + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return True + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ADDRCODE ): + raise Exception('The address is wrong') + + ## DEBUG: Sensor password is wrong + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_WRONGPASSWORD ): + return False + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def setPassword(self, newPassword): + """ + Set the password of the sensor. + + @param integer(4 bytes) newPassword + @return boolean + """ + + ## Validate the password (maximum 4 bytes) + if ( newPassword < 0x00000000 or newPassword > 0xFFFFFFFF ): + raise ValueError('The given password is invalid!') + + packetPayload = ( + FINGERPRINT_SETPASSWORD, + self.__rightShift(newPassword, 24), + self.__rightShift(newPassword, 16), + self.__rightShift(newPassword, 8), + self.__rightShift(newPassword, 0), + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Password set was successful + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + self.__password = newPassword + return True + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def setAddress(self, newAddress): + """ + Set the module address of the sensor. + + @param integer(4 bytes) newAddress + @return boolean + """ + + ## Validate the address (maximum 4 bytes) + if ( newAddress < 0x00000000 or newAddress > 0xFFFFFFFF ): + raise ValueError('The given address is invalid!') + + packetPayload = ( + FINGERPRINT_SETADDRESS, + self.__rightShift(newAddress, 24), + self.__rightShift(newAddress, 16), + self.__rightShift(newAddress, 8), + self.__rightShift(newAddress, 0), + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Address set was successful + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + self.__address = newAddress + return True + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def setSystemParameter(self, parameterNumber, parameterValue): + """ + Set a system parameter of the sensor. + + @param integer(1 byte) parameterNumber + @param integer(1 byte) parameterValue + @return boolean + """ + + ## Validate the baudrate parameter + if ( parameterNumber == 4 ): + + if ( parameterValue < 1 or parameterValue > 12 ): + raise ValueError('The given baudrate parameter is invalid!') + + ## Validate the security level parameter + elif ( parameterNumber == 5 ): + + if ( parameterValue < 1 or parameterValue > 5 ): + raise ValueError('The given security level parameter is invalid!') + + ## Validate the package length parameter + elif ( parameterNumber == 6 ): + + if ( parameterValue < 0 or parameterValue > 3 ): + raise ValueError('The given package length parameter is invalid!') + + ## The parameter number is not valid + else: + raise ValueError('The given parameter number is invalid!') + + packetPayload = ( + FINGERPRINT_SETSYSTEMPARAMETER, + parameterNumber, + parameterValue, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Parameter set was successful + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return True + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_INVALIDREGISTER ): + raise Exception('Invalid register number') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + + def getSystemParameters(self): + """ + Get all available system information of the sensor. + + Return a tuple that contain the following information: + 0: integer(2 bytes) The status register. + 1: integer(2 bytes) The system id. + 2: integer(2 bytes) The storage capacity. + 3: integer(2 bytes) The security level. + 4: integer(4 bytes) The sensor address. + 5: integer(2 bytes) The packet length. + 6: integer(2 bytes) The baudrate. + + @return tuple + """ + + packetPayload = ( + FINGERPRINT_GETSYSTEMPARAMETERS, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Read successfully + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + + statusRegister = self.__leftShift(receivedPacketPayload[1], 8) | self.__leftShift(receivedPacketPayload[2], 0) + systemID = self.__leftShift(receivedPacketPayload[3], 8) | self.__leftShift(receivedPacketPayload[4], 0) + storageCapacity = self.__leftShift(receivedPacketPayload[5], 8) | self.__leftShift(receivedPacketPayload[6], 0) + securityLevel = self.__leftShift(receivedPacketPayload[7], 8) | self.__leftShift(receivedPacketPayload[8], 0) + deviceAddress = ((receivedPacketPayload[9] << 8 | receivedPacketPayload[10]) << 8 | receivedPacketPayload[11]) << 8 | receivedPacketPayload[12] ## TODO + packetLength = self.__leftShift(receivedPacketPayload[13], 8) | self.__leftShift(receivedPacketPayload[14], 0) + baudRate = self.__leftShift(receivedPacketPayload[15], 8) | self.__leftShift(receivedPacketPayload[16], 0) + + return (statusRegister, systemID, storageCapacity, securityLevel, deviceAddress, packetLength, baudRate) + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def getTemplateIndex(self, page): + """ + Get a list of the template positions with usage indicator. + + @param integer(1 byte) page + @return list + """ + + if ( page < 0 or page > 3 ): + raise ValueError('The given index page is invalid!') + + packetPayload = ( + FINGERPRINT_TEMPLATEINDEX, + page, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Read index table successfully + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + + templateIndex = [] + + ## Contain the table page bytes (skip the first status byte) + pageElements = receivedPacketPayload[1:] + + for pageElement in pageElements: + ## Test every bit (bit = template position is used indicator) of a table page element + for p in range(0, 7 + 1): + positionIsUsed = (self.__bitAtPosition(pageElement, p) == 1) + templateIndex.append(positionIsUsed) + + return templateIndex + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def getTemplateCount(self): + """ + Get the number of stored templates. + + @return integer(2 bytes) + """ + + packetPayload = ( + FINGERPRINT_TEMPLATECOUNT, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Read successfully + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + templateCount = self.__leftShift(receivedPacketPayload[1], 8) + templateCount = templateCount | self.__leftShift(receivedPacketPayload[2], 0) + return templateCount + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def readImage(self): + """ + Read the image of a finger and stores it in ImageBuffer. + + @return boolean + """ + + packetPayload = ( + FINGERPRINT_READIMAGE, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Image read successful + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return True + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + ## DEBUG: No finger found + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_NOFINGER ): + return False + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_READIMAGE ): + raise Exception('Could not read image') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + ## TODO: + ## Implementation of uploadImage() + def setLEDoff(self): + packetPayload = ( + FINGERPRINT_LEDOFF, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + + ## Get first reply packet + receivedPacket = self.__readPacket() + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return True + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + def setLEDon(self): + packetPayload = ( + FINGERPRINT_LEDON, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + + ## Get first reply packet + receivedPacket = self.__readPacket() + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return True + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + def downloadImage(self, imageDestination): + """ + Download the image of a finger to host computer. + + @param string imageDestination + @return void + """ + + destinationDirectory = os.path.dirname(imageDestination) + + if ( os.access(destinationDirectory, os.W_OK) == False ): + raise ValueError('The given destination directory "' + destinationDirectory + '" is not writable!') + + packetPayload = ( + FINGERPRINT_DOWNLOADIMAGE, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + + ## Get first reply packet + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: The sensor will sent follow-up packets + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + pass + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_DOWNLOADIMAGE ): + raise Exception('Could not download image') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + ## Initialize image library + resultImage = Image.new('L', (256, 288), 'white') + pixels = resultImage.load() + + ## Y coordinate of current pixel + line = 0 + + ## Get follow-up data packets until the last data packet is received + while ( receivedPacketType != FINGERPRINT_ENDDATAPACKET ): + + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_DATAPACKET and receivedPacketType != FINGERPRINT_ENDDATAPACKET ): + raise Exception('The received packet is no data packet!') + + ## X coordinate of current pixel + x = 0 + + for i in range(0, len(receivedPacketPayload)): + + ## Thanks to Danylo Esterman for the "multiple with 17" improvement: + + ## Draw left 4 Bits one byte of package + pixels[x, line] = (receivedPacketPayload[i] >> 4) * 17 + x = x + 1 + + ## Draw right 4 Bits one byte of package + pixels[x, line] = (receivedPacketPayload[i] & 0b00001111) * 17 + x = x + 1 + + line = line + 1 + + resultImage.save(imageDestination) + + def convertImage(self, charBufferNumber = 0x01): + """ + Convert the image in ImageBuffer to finger characteristics and store in CharBuffer1 or CharBuffer2. + + @param integer(1 byte) charBufferNumber + @return boolean + """ + + if ( charBufferNumber != 0x01 and charBufferNumber != 0x02 ): + raise ValueError('The given charbuffer number is invalid!') + + packetPayload = ( + FINGERPRINT_CONVERTIMAGE, + charBufferNumber, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Image converted + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return True + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_MESSYIMAGE ): + raise Exception('The image is too messy') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_FEWFEATUREPOINTS ): + raise Exception('The image contains too few feature points') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_INVALIDIMAGE ): + raise Exception('The image is invalid') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def createTemplate(self): + """ + Combine the characteristics which are stored in CharBuffer1 and CharBuffer2 to a template. + The created template will be stored again in CharBuffer1 and CharBuffer2 as the same. + + @return boolean + """ + + packetPayload = ( + FINGERPRINT_CREATETEMPLATE, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Template created successful + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return True + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + ## DEBUG: The characteristics not matching + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_CHARACTERISTICSMISMATCH ): + return False + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def storeTemplate(self, positionNumber = -1, charBufferNumber = 0x01): + """ + Save a template from the specified CharBuffer to the given position number. + + @param integer(2 bytes) positionNumber + @param integer(1 byte) charBufferNumber + @return integer + """ + + ## Find a free index + if ( positionNumber == -1 ): + for page in range(0, 4): + ## Free index found? + if ( positionNumber >= 0 ): + break + + templateIndex = self.getTemplateIndex(page) + + for i in range(0, len(templateIndex)): + ## Index not used? + if ( templateIndex[i] == False ): + positionNumber = (len(templateIndex) * page) + i + break + + if ( positionNumber < 0x0000 or positionNumber >= self.getStorageCapacity() ): + raise ValueError('The given position number is invalid!') + + if ( charBufferNumber != 0x01 and charBufferNumber != 0x02 ): + raise ValueError('The given charbuffer number is invalid!') + + packetPayload = ( + FINGERPRINT_STORETEMPLATE, + charBufferNumber, + self.__rightShift(positionNumber, 8), + self.__rightShift(positionNumber, 0), + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Template stored successful + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return positionNumber + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_INVALIDPOSITION ): + raise Exception('Could not store template in that position') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_FLASH ): + raise Exception('Error writing to flash') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def searchTemplate(self): + """ + Search the finger characteristics in CharBuffer in database. + + Return a tuple that contain the following information: + 0: integer(2 bytes) The position number of found template. + 1: integer(2 bytes) The accuracy score of found template. + + @return tuple + """ + + ## CharBuffer1 and CharBuffer2 are the same in this case + charBufferNumber = 0x01 + + ## Begin search at index 0 + positionStart = 0x0000 + templatesCount = self.getStorageCapacity() + + packetPayload = ( + FINGERPRINT_SEARCHTEMPLATE, + charBufferNumber, + self.__rightShift(positionStart, 8), + self.__rightShift(positionStart, 0), + self.__rightShift(templatesCount, 8), + self.__rightShift(templatesCount, 0), + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Found template + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + + positionNumber = self.__leftShift(receivedPacketPayload[1], 8) + positionNumber = positionNumber | self.__leftShift(receivedPacketPayload[2], 0) + + accuracyScore = self.__leftShift(receivedPacketPayload[3], 8) + accuracyScore = accuracyScore | self.__leftShift(receivedPacketPayload[4], 0) + + return (positionNumber, accuracyScore) + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + ## DEBUG: Did not found a matching template + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_NOTEMPLATEFOUND ): + return (-1, -1) + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def loadTemplate(self, positionNumber, charBufferNumber = 0x01): + """ + Load an existing template specified by position number to specified CharBuffer. + + @param integer(2 bytes) positionNumber + @param integer(1 byte) charBufferNumber + @return boolean + """ + + if ( positionNumber < 0x0000 or positionNumber >= self.getStorageCapacity() ): + raise ValueError('The given position number is invalid!') + + if ( charBufferNumber != 0x01 and charBufferNumber != 0x02 ): + raise ValueError('The given charbuffer number is invalid!') + + packetPayload = ( + FINGERPRINT_LOADTEMPLATE, + charBufferNumber, + self.__rightShift(positionNumber, 8), + self.__rightShift(positionNumber, 0), + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Template loaded successful + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return True + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_LOADTEMPLATE ): + raise Exception('The template could not be read') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_INVALIDPOSITION ): + raise Exception('Could not load template from that position') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def deleteTemplate(self, positionNumber, count = 1): + """ + Delete templates from fingerprint database. Per default one. + + @param integer(2 bytes) positionNumber + @param integer(2 bytes) count + @return boolean + """ + + capacity = self.getStorageCapacity() + + if ( positionNumber < 0x0000 or positionNumber >= capacity ): + raise ValueError('The given position number is invalid!') + + if ( count < 0x0000 or count > capacity - positionNumber ): + raise ValueError('The given count is invalid!') + + packetPayload = ( + FINGERPRINT_DELETETEMPLATE, + self.__rightShift(positionNumber, 8), + self.__rightShift(positionNumber, 0), + self.__rightShift(count, 8), + self.__rightShift(count, 0), + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Template deleted successful + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return True + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_INVALIDPOSITION ): + raise Exception('Invalid position') + + ## DEBUG: Could not delete template + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_DELETETEMPLATE ): + return False + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def clearDatabase(self): + """ + Clear the complete template database. + + @return boolean + """ + + packetPayload = ( + FINGERPRINT_CLEARDATABASE, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Database cleared successful + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + return True + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + ## DEBUG: Could not clear database + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_CLEARDATABASE ): + return False + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def compareCharacteristics(self): + """ + Compare the finger characteristics of CharBuffer1 with CharBuffer2 and return the accuracy score. + + @return integer(2 bytes) + """ + + packetPayload = ( + FINGERPRINT_COMPARECHARACTERISTICS, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: Comparison successful + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + accuracyScore = self.__leftShift(receivedPacketPayload[1], 8) + accuracyScore = accuracyScore | self.__leftShift(receivedPacketPayload[2], 0) + return accuracyScore + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + ## DEBUG: The characteristics do not matching + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_NOTMATCHING ): + return 0 + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + def uploadCharacteristics(self, charBufferNumber = 0x01, characteristicsData = [0]): + """ + Upload finger characteristics to CharBuffer1 or CharBuffer2. + + @author: David Gilson + + @param integer(1 byte) charBufferNumber + @param integer(list) characteristicsData + + @return boolean + Return true if everything is right. + """ + + if ( charBufferNumber != 0x01 and charBufferNumber != 0x02 ): + raise ValueError('The given charbuffer number is invalid!') + + if ( characteristicsData == [0] ): + raise ValueError('The characteristics data is required!') + + maxPacketSize = self.getMaxPacketSize() + + ## Upload command + + packetPayload = ( + FINGERPRINT_UPLOADCHARACTERISTICS, + charBufferNumber + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + + ## Get first reply packet + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: The sensor will sent follow-up packets + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + pass + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + elif ( receivedPacketPayload[0] == FINGERPRINT_PACKETRESPONSEFAIL ): + raise Exception('Could not upload characteristics') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + ## Upload data packets + packetNbr = len(characteristicsData) / maxPacketSize + + if ( packetNbr <= 1 ): + self.__writePacket(FINGERPRINT_ENDDATAPACKET, characteristicsData) + else: + i = 1 + while ( i < packetNbr ): + lfrom = (i-1) * maxPacketSize + lto = lfrom + maxPacketSize + self.__writePacket(FINGERPRINT_DATAPACKET, characteristicsData[lfrom:lto]) + i += 1 + + lfrom = (i-1) * maxPacketSize + lto = lfrom + maxPacketSize + self.__writePacket(FINGERPRINT_ENDDATAPACKET, characteristicsData[lfrom:lto]) + + ## Verify uploaded characteristics + characterics = self.downloadCharacteristics(charBufferNumber) + return (characterics == characteristicsData) + + def getMaxPacketSize(self): + """ + Get the maximum allowed size of packet by sensor. + + @author: David Gilson + + @return int + Return the max size. Default 32 bytes. + """ + + packetMaxSizeType = self.getSystemParameters()[5] + + if (packetMaxSizeType == 1): + return 64 + elif (packetMaxSizeType == 2): + return 128 + elif (packetMaxSizeType == 3): + return 256 + else: + return 32 + + def getStorageCapacity(self): + """ + Get the sensor storage capacity. + + @return int + The storage capacity. + """ + + return self.getSystemParameters()[2] + + def generateRandomNumber(self): + """ + Generate a random 32-bit decimal number. + + @author: Philipp Meisberger + + @return int + The generated random number + """ + packetPayload = ( + FINGERPRINT_GENERATERANDOMNUMBER, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + pass + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + number = 0 + number = number | self.__leftShift(receivedPacketPayload[1], 24) + number = number | self.__leftShift(receivedPacketPayload[2], 16) + number = number | self.__leftShift(receivedPacketPayload[3], 8) + number = number | self.__leftShift(receivedPacketPayload[4], 0) + return number + + def downloadCharacteristics(self, charBufferNumber = 0x01): + """ + Download the finger characteristics of CharBuffer1 or CharBuffer2. + + @param integer(1 byte) charBufferNumber + + @return list + Return a list that contains 512 integer(1 byte) elements of the characteristic. + """ + + if ( charBufferNumber != 0x01 and charBufferNumber != 0x02 ): + raise ValueError('The given charbuffer number is invalid!') + + packetPayload = ( + FINGERPRINT_DOWNLOADCHARACTERISTICS, + charBufferNumber, + ) + + self.__writePacket(FINGERPRINT_COMMANDPACKET, packetPayload) + + ## Get first reply packet + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_ACKPACKET ): + raise Exception('The received packet is no ack packet!') + + ## DEBUG: The sensor will sent follow-up packets + if ( receivedPacketPayload[0] == FINGERPRINT_OK ): + pass + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_COMMUNICATION ): + raise Exception('Communication error') + + elif ( receivedPacketPayload[0] == FINGERPRINT_ERROR_DOWNLOADCHARACTERISTICS ): + raise Exception('Could not download characteristics') + + else: + raise Exception('Unknown error '+ hex(receivedPacketPayload[0])) + + completePayload = [] + + ## Get follow-up data packets until the last data packet is received + while ( receivedPacketType != FINGERPRINT_ENDDATAPACKET ): + + receivedPacket = self.__readPacket() + + receivedPacketType = receivedPacket[0] + receivedPacketPayload = receivedPacket[1] + + if ( receivedPacketType != FINGERPRINT_DATAPACKET and receivedPacketType != FINGERPRINT_ENDDATAPACKET ): + raise Exception('The received packet is no data packet!') + + for i in range(0, len(receivedPacketPayload)): + completePayload.append(receivedPacketPayload[i]) + + return completePayload diff --git a/bin/CGI/api/tools/pyfingerprint2/pyfingerprint.pyc b/bin/CGI/api/tools/pyfingerprint2/pyfingerprint.pyc new file mode 100644 index 0000000..081a906 Binary files /dev/null and b/bin/CGI/api/tools/pyfingerprint2/pyfingerprint.pyc differ diff --git a/bin/CGI/index.cgi b/bin/CGI/index.cgi new file mode 100644 index 0000000..542d7bf --- /dev/null +++ b/bin/CGI/index.cgi @@ -0,0 +1,199 @@ +#!C:\Strawberry\perl\bin\perl.exe + +use strict; +use FindBin qw($Bin); +# use lib ('CGI/api/lib/perl5'); +# use lib ('CGI/api/lib'); +use lib ($Bin.'/CGI/api/lib/perl5'); +use lib ($Bin.'/CGI/api/lib'); +# use lib ('./api/lib/perl5'); +# use lib ('./api/lib'); +use File::Basename qw/dirname basename/; +use Template; +# use Template::Constants qw( :debug ); +use CGI; +#use CGI::Carp qw(fatalsToBrowser); +use CGI::Cookie; +use Data::Dumper; +use JSON::PP; + +use dksconfig qw/$sitecfg/; +# use session; + +my $skl = "skeleton/index.tt"; +my $cgi = new CGI(); +my $p=(); +my $cookie; +my $vars = $sitecfg; + +#$vars->{filepath} = substr($cgi->url({-absolute=>1}),length($vars->{basepath})+1); +$vars->{filepath} = substr($ENV{PATH_INFO},1); +$vars->{baseurl} = $cgi->url({-base=>1}).$vars->{basepath}; + +if ($vars->{basepath} eq "/"){ + $vars->{siteurl} = $cgi->url({-base=>1}); +}else { + $vars->{siteurl} = $cgi->url({-base=>1}).dirname($vars->{basepath}); + $vars->{docroot} = $vars->{docroot}.dirname($vars->{basepath}); + #$vars->{sitepath} = dirname($vars->{basepath}); +} + + +if ($vars->{filepath} ne ""){ + $vars->{suffix} = substr($vars->{filepath},rindex($vars->{filepath},'.')); + $vars->{page} = $vars->{filepath}; + $vars->{page} =~ s/html$/tt/; +} +$vars->{abspath} = ""; + +# my $sess = (); +# my $se = session->new(); +# $p->{sid} = $cgi->cookie($vars->{cookiename}); +if ($cgi->request_method() eq "GET"){ + my @params = $cgi->param(); + foreach my $pp (@params){ + $p->{$pp} = $cgi->param($pp); + } + +} + +# if ($cgi->request_method() eq "POST"){ + +# my @params = $cgi->param(); +# foreach my $pp (@params){ +# $p->{$pp} = $cgi->param($pp); +# } +# # $vars->{hasposts} = $p; +# # if (exists($p->{'btnlogin'})){ +# # my $ret = $se->checklogin($p->{login},$p->{password}); +# # if ($ret->{sid} ne ""){ +# # $p->{sid} = $ret->{sid}; +# # $cookie = CGI::Cookie->new(-name=>$vars->{cookiename},-value=>$p->{sid},-httponly => 1); +# # }else { +# # $vars->{message} = $ret->{message}; +# # $vars->{messagetype} = $ret->{messagetype}; +# # $vars->{page} = "message.tt"; +# # } +# # } +# # if (exists($p->{'btnregister'})){ + +# # my $ret = $se->registeruser($p); + +# # $vars->{message} = $ret->{message}; +# # $vars->{messagetype} = $ret->{messagetype}; +# # $vars->{page} = $ret->{page}; +# # } +# # if (exists($p->{'btnforgotpassword'})){ +# # my $ret = $se->passwordforgotten($p->{email}); +# # $vars->{message} = $ret->{message}; +# # $vars->{messagetype} = $ret->{messagetype}; +# # $vars->{page} = "message.tt"; +# # } +# # if (exists($p->{'btnvalidateemail'})){ +# # my $ret = $se->validateaccount($p); +# # $vars->{message} = $ret->{message}; +# # $vars->{messagetype} = $ret->{messagetype}; +# # $vars->{page} = "message.tt"; +# # } +# # if (exists($p->{'btnresendcode'})){ +# # my $ret = $se->resendcode($p->{email}); +# # $vars->{message} = $ret->{message}; +# # $vars->{messagetype} = $ret->{messagetype}; +# # $vars->{page} = "message.tt"; +# # } + +# # if (exists($p->{logout})){ +# # $se->deletesession($p->{sid}); +# # $p->{sid} = ""; +# # $cookie = CGI::Cookie->new(-name=>$vars->{cookiename},-value=>"",-httponly => 1); +# # } +# # if (exists($p->{btndeleteprofile})){ +# # my $ret = $se->deleteprofile($p->{deleteprofile}); +# # $vars->{message} = $ret->{message}; +# # $vars->{messagetype} = $ret->{messagetype}; +# # $vars->{page} = "message.tt"; +# # if (exists($ret->{sid})){ +# # $p->{sid} = ""; +# # } +# # $cookie = CGI::Cookie->new(-name=>$vars->{cookiename},-value=>"",-httponly => 1); +# # } +# } + +# # if ($p->{sid} ne ""){ +# # $sess = $se->getsession($p->{sid}); +# # } + + +# #$vars->{beforex} = $vars->{page}; +# if (!exists($sess->{id}) || (!exists($p->{sid})) || $p->{sid} eq ""){ +# $skl = "skeleton/index.tt"; +# } +# # if ($vars->{page} eq "deleteprofile.tt") { +# # $skl = "skeleton/login.tt"; +# # } +# # my ($appname) = $ENV{REQUEST_URI} =~ /.*\/module\/(\w+)\/.*/; +# if ($p->{sid} ne ""){ +# $vars->{session} = $sess; +# } +# #SESSION - End +# # #BEGIN - Browser Blocking +# # if (($ENV{HTTP_USER_AGENT} !~ /Chrome/) || ($ENV{HTTP_USER_AGENT} =~ /Edge/) || ($ENV{HTTP_USER_AGENT} =~ /Firefox/)){ +# # $skl = "skeleton/browser.tt"; +# # } +# # #END - Browser Blocking +my $ctype = 'text/html'; +if ($vars->{suffix} eq ".js"){ + $ctype= "text/javascript"; +} elsif ($vars->{suffix} eq ".css"){ + $ctype = "text/css"; +} +#print $cgi->header(-type=>$ctype, -charset=>"utf-8",-cookie => $cookie); +print $cgi->header(-type=>$ctype, -charset=>"utf-8"); + +# print dirname($ENV{"SCRIPT_FILENAME"}); + +my $template = Template->new({INCLUDE_PATH => [$Bin.'/CGI/tmpl']}); +#} + +my @lv = split(/\//,$vars->{filepath}); +my $absnum = scalar(@lv)-1; + +for (my $i=0;$i<$absnum;$i++){ + $vars->{abspath} .= "../"; +} +if ($vars->{page} =~ /^app/){ + my @spl = split("/",$vars->{page}); + $vars->{app} = $spl[1]; +} +# $vars->{page} = $vars->{page}; +if ($vars->{page} =~ /module/){ + $vars->{module} = basename(dirname($vars->{page})); +} +$vars->{pagename} = basename($vars->{page}); +$vars->{pagename} =~ s/\.tt$//; +# my ($appname) = $ENV{REQUEST_URI} =~ /.*\/apps\/(\w+)\/.*/; +#$vars->{requri} = $ENV{REQUEST_URI}; + +#BEGIN - iFrame - Modules +#$skl ne "skeleton/login.tt" && +if ($vars->{page} =~ /module/){ + $skl = "skeleton/module.tt"; + if ($vars->{page} !~ /\.tt$/) { + $skl = "skeleton/file.tt"; + } + $vars->{params}= $p; +}elsif ($vars->{page} =~ /^app/){ + $skl = "skeleton/app.tt"; + if ($vars->{page} !~ /\.tt$/) { + $skl = "skeleton/file.tt"; + } + $vars->{params}= $p; +} + +#END - iFrame - Modules + +$template->process($skl,$vars) || die "Template process failed: ", $template->error(), "\n"; +#print '/*
'.Dumper($vars)."
*/";
+
+
+
diff --git a/bin/CGI/tmpl/app/hourtrax/index.tt b/bin/CGI/tmpl/app/hourtrax/index.tt
new file mode 100644
index 0000000..5cba12e
--- /dev/null
+++ b/bin/CGI/tmpl/app/hourtrax/index.tt
@@ -0,0 +1,59 @@
+[% USE date %]
+[% vstamp=date.format(date.now, '%d%m%Y%H%M%S') %]
+
+
+
+
+
+
+  
+  
+
+Invoice Journal
+
+
+
+
+
+
+
+ +
+
+ +
+ + +
+ +
+ + +
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/bin/CGI/tmpl/app/hourtrax/module/members/index.js b/bin/CGI/tmpl/app/hourtrax/module/members/index.js new file mode 100644 index 0000000..d474d6a --- /dev/null +++ b/bin/CGI/tmpl/app/hourtrax/module/members/index.js @@ -0,0 +1,97 @@ +var tbl=null; + + +function initpage(){ + //console.log(mpref.cfg); + //console.log("APP:" + parent.app); + flatpickr(".timefield",{altInput: true, + altFormat: "H:i", + allowInput: true, + enableTime: true, + noCalendar: true, + dateFormat: "H:i", + time_24hr: true, + "locale": "fr", + }); + flatpickr(".datefield",{altInput: true, + altFormat: "d.m.Y", + dateFormat: "Y-m-d", + allowInput: true, + "locale": "fr", + }); + tbl = new Tabulator("#tbl_staff", { + headerFilterPlaceholder:"filtre...", + height: "95vh", + layout:"fitColumns", + selectable:1, + locale:"fr", + columns: [ + {title:"Nom", field:"surname",headerFilter:"input"}, + {title:"Prénom", field:"prename",headerFilter:"input"}, + {title:"Blocké", field:"blocked",formatter:"tickCross", + formatterParams:{allowEmpty:true,allowTruthy:true,tickElement:'X'}}, + {title:"Invisible", field:"disabled",formatter:"tickCross", + formatterParams:{allowEmpty:true,allowTruthy:true,tickElement:'X'}}, + ] + }); + gettbldata(); + return false; +} + +function gettbldata(){ + sql = "SELECT id, prename, surname, blocked, disabled FROM staff;"; + req.reqdata("POST","db.cgi",{ "sql":sql},loadtbldata); + return false; +} + +function loadtbldata(data){ + console.log(data); + if (data){ + tbl.setData(data); + } + return false; +} + +function edit(){ + var udata = tbl.getSelectedData(); + if (udata[0]){ + console.log(udata); + req.reqdata("POST","db.cgi",{"get":"staff","filter":"id=" + udata[0].id},loadstaffdata); + } + return false; +} + +function loadstaffdata(data){ + if (data){ + fillformbydataclass('staff',data[0],false); + document.getElementById('dlgstaff').style.display='block'; + } +} + +function add(){ + cleanform('staff'); + document.getElementById('dlgstaff').style.display='block'; +} + +function remove(){ + var udata = tbl.getSelectedData(); + if (udata[0]){ + fillformbydataclass('staffremove',udata[0],false); + document.getElementById('dlgstaffremove').style.display='block'; + } +} + +function staffremove(ident){ + req.reqdata("POST","db.cgi",{"del":"1","ident_staff_id":document.getElementById("ident_" + ident + "_id")},afterstaffremove); + +} + +function afterstaffremove(){ + gettbldata(); + formsaved(); + document.getElementById('dlgstaffremove').style.display='none'; +} + +function afterstaffsaved(){ + document.getElementById('dlgstaff').style.display='none'; +} \ No newline at end of file diff --git a/bin/CGI/tmpl/app/hourtrax/module/members/index.tt b/bin/CGI/tmpl/app/hourtrax/module/members/index.tt new file mode 100644 index 0000000..fabb851 --- /dev/null +++ b/bin/CGI/tmpl/app/hourtrax/module/members/index.tt @@ -0,0 +1,61 @@ +[% PROCESS macro/fields.tt %] +
+
+ + + + +
+
+
+
+
+
+ × +

Employé(e)

+
+
+ +
+ [% fieldhidden("id","staff","ident",'') %] + [% fieldeditbox("surname","staff","Nom",'w3-half','','') %] + [% fieldeditbox("prename","staff","Prénom",'w3-half','','') %] + [% fieldpasswordbox("pin","staff","Code Pin",'w3-third','','') %] + [% fieldcheckbox("disabled","staff","invisible",'w3-third','','') %] + [% fieldcheckbox("blocked","staff","bloqué",'w3-third','','') %] +
+ [% fieldtimebox("dailyhours","staff","heures par jour",'w3-third','','') %] + [% fieldtimebox("pausehours","staff","pause à deduire",'w3-third','','') %] +
+
+
+
+ + + [% formdlgsavebutton('users','sauvegarder','afterstaffsaved') %] +
+
+
+
+
+
+ × +

Supprimer Employé(e)?

+
+
+ +
+ [% fieldhidden("id","staffremove","ident",'') %] + [% fieldeditbox("surname","staffremove","Nom",'w3-half','readonly','') %] + [% fieldeditbox("prename","staffremove","Prénom",'w3-half','readonly','') %] +
+
+
+ + + +
+
+
\ No newline at end of file diff --git a/bin/CGI/tmpl/app/hourtrax/module/settings/index.js b/bin/CGI/tmpl/app/hourtrax/module/settings/index.js new file mode 100644 index 0000000..e69de29 diff --git a/bin/CGI/tmpl/app/hourtrax/module/settings/index.tt b/bin/CGI/tmpl/app/hourtrax/module/settings/index.tt new file mode 100644 index 0000000..a8c7a27 --- /dev/null +++ b/bin/CGI/tmpl/app/hourtrax/module/settings/index.tt @@ -0,0 +1,34 @@ +[% PROCESS macro/fields.tt %] +
+
+

Accès Admin

+
+
+ [% fieldeditbox("name","login",'Login','','') %] + [% fieldpasswordbox("password1","login",'Mot de passe','','') %] + [% fieldpasswordbox("password2","login",'Répérer mot de passe','','') %] +
+
+
+ +
+
+
+

Réseau

+
+
+ [% fieldeditbox("hostname","system",'Hostname','','readonly','') %] + [% fieldselectbox("wlanssid","system",'Wifi SSID','','','') %] + [% fieldpasswordbox ("wlanpassword","system",'Wifi Mot de passe','','','') %] +
+
+
+ +
+ +
+ +
+ + + \ No newline at end of file diff --git a/bin/CGI/tmpl/app/hourtrax/module/timetrack/index.js b/bin/CGI/tmpl/app/hourtrax/module/timetrack/index.js new file mode 100644 index 0000000..10301b9 --- /dev/null +++ b/bin/CGI/tmpl/app/hourtrax/module/timetrack/index.js @@ -0,0 +1,90 @@ +var tbl=null; + +var tbltypes = [{"value":"trackbyday","label":"pointage par jour"}, +{"value":"trackbystaff","label":"pointage par employé(e) et mois"},{"value":"trackincomplete","label":"pointages incomplètes"}]; + +curtbl = "trackbyday"; +function initpage(){ + console.log(mpref.cfg); + console.log("APP:" + parent.app); + flatpickr(".timefield",{altInput: true, + altFormat: "H:M", + dateFormat: "H:M", + allowInput: true, + "locale": "fr", + }); + flatpickr(".datefield",{altInput: true, + altFormat: "d.m.Y H:M", + dateFormat: "Y-m-d H:M", + allowInput: true, + "locale": "fr", + }); + tbl = new Tabulator("#tbl_clocks", { + headerFilterPlaceholder:"filtre...", + height: "95vh", + layout:"fitColumns", + selectable:1, + locale:"fr", + // responsiveLayout:"collapse", + groupBy:["staffname"], + groupStartOpen:[true, false], + groupHeader:[ + function(value, count, data){ //generate header contents for gender groups + return value; + }, + function(value, count, data){ //generate header contents for color groups + return value; + }, + ], + columns: [ + {title:"Mois", field:"dspdaydate",formatter:"datetime", formatterParams:{ + inputFormat:"DD.MM.YYYY", + outputFormat:"MMM YYYY", + invalidPlaceholder:""},headerFilter:"input"}, + {title:"Date", field:"dspdaydate",align: "right",formatter:"datetime", formatterParams:{ + inputFormat:"DD.MM.YYYY", + outputFormat:"DD.MM.YYYY", + invalidPlaceholder:""},headerFilter:"input"}, + {title:"Nom", field:"staffname",headerFilter:"input"}, + {title:"Entrée",align: "right",field:"entry"}, + {title:"Sortie",align: "right",field:"leave"}, + {title:"Incomplète",field:"incomplete",formatter:"tickCross", + formatterParams:{allowEmpty:true,allowTruthy:true,tickElement:'X'}}, + {title:"Total", field:"hoursworked",align: "right",formatter:"money",sorter:"number", + formatterParams:{decimal:",",thousand:".",symbol:"",symbolAfter:"h",precision:2}, + bottomCalc:"sum", bottomCalcParams:{precision:2},bottomCalcFormatterParams:{decimal:",",thousand:".",symbol:"",symbolAfter:"h",precision:2}} + ] + }); + gettbldata(); + +} + +function gettbldata(){ + sql = "select hrx.id,hrx.id_staff, coalesce(upper(hrx.surname),'') || ' ' || coalesce(hrx.prename,'') as staffname,date(hrx.stamp_in) as daydate,strftime(\"%d.%m.%Y\",date(hrx.stamp_in)) as dspdaydate,strftime(\"%H:%M\",hrx.stamp_in) as entry,strftime(\"%H:%M\",hrx.stamp_out) as leave,printf(\"%.2f\",CAST ((julianday(hrx.stamp_out) - julianday(hrx.stamp_in)) * 24 as REAL)) as hoursworked,hrx.incomplete from (SELECT hr.id, hr.id_staff, st.prename,st.surname, hr.stamp_in, case when hr.stamp_out is null then date(hr.stamp_in,'+1 day') || ' 00:00:00' else hr.stamp_out end as stamp_out,case when hr.stamp_out is null then true else null end as incomplete FROM hours hr join staff st on (hr.id_staff=st.id)) hrx order by hrx.stamp_in,hrx.id_staff;"; + req.reqdata("POST","db.cgi",{ "sql":sql},loadtbldata); + +} + +function loadtbldata(data){ + console.log(data); + if (data){ + tbl.setData(data); + } +} + +function edit(){ + var udata = tbl.getSelectedData(); + if (udata[0]){ + console.log(udata); + + } + +} + +function add(){ + +} + +function remove(){ + +} \ No newline at end of file diff --git a/bin/CGI/tmpl/app/hourtrax/module/timetrack/index.tt b/bin/CGI/tmpl/app/hourtrax/module/timetrack/index.tt new file mode 100644 index 0000000..138a8d4 --- /dev/null +++ b/bin/CGI/tmpl/app/hourtrax/module/timetrack/index.tt @@ -0,0 +1,9 @@ +
+
+ + + + +
+
+
\ No newline at end of file diff --git a/bin/CGI/tmpl/app/timeclock/index.js b/bin/CGI/tmpl/app/timeclock/index.js new file mode 100644 index 0000000..1f9310f --- /dev/null +++ b/bin/CGI/tmpl/app/timeclock/index.js @@ -0,0 +1,295 @@ +var currentuser= null; +var currentpanel = null; +var currentpinfield = null; +var lasttrack = null; +var interval = null; +var intervaltime = 60; + +function initpage(){ + loadusers(); +} + +function loadpanel(pnlid){ + var pnls = document.querySelectorAll(".clockpanel"); + + for (var i in pnls){ + //var pnl = pnls[i]; + if (pnls[i].id){ + //console.log(pnls[i].id); + pnls[i].style.display = 'none'; + } + // + } + currentpanel = pnlid; + console.log("Panel: " + currentpanel + " Users" + JSON.stringify(currentuser)); + //console.log(currentuser); + if ((pnlid == 'users') || (pnlid == null)){ + loadusers(); + } + else if (pnlid == 'userpin'){ + loaduserpin(); + } + else if (pnlid == 'newuserpin'){ + loadnewuserpin(); + } + else if (pnlid == 'timetracker'){ + console.log("before timetracker"); + loadpnltimetracker(); + } + else if (pnlid == 'status'){ + loadstatus(); + } + return false; +} + +function displaypanel(){ + intervaltime = 60; + if ((currentpanel ) && (currentpanel != 'users')) { + startCheckIdle(); + } else { + stopCheckIdle(); + } + console.log("Current Panel:" + currentpanel); + if (currentpanel){ + document.getElementById("pnl_" + currentpanel).style.display = 'block'; + } + + return false; +} + +function loadusers(){ + req.reqdata("POST","db.cgi",{"sql":"select id,prename,surname from staff where disabled is null and blocked is null;"},displayusers); + document.getElementById("sidebtn_pnlusers").style.display = 'none'; + document.getElementById("sidebtn_pnlchangepin").style.display = 'none'; + document.getElementById("sidebtn_pnluserpin").style.display = 'none'; + currentuser=null; + displaypanel(); + return false; +} + +function displayusers(data){ + document.getElementById('userlist').innerHTML=""; + //console.log(data); + + if (data){ + // if (data.length > 20){ + // document.getElementById('btnscrollup').style.display = 'inline'; + // document.getElementById('btnscrolldown').style.display = 'inline'; + // } else { + // document.getElementById('btnscrollup').style.display = 'none'; + // document.getElementById('btnscrolldown').style.display = 'none'; + // } + var ulist = ""; + for (var i in data){ + ulist += ''; + } + document.getElementById('userlist').innerHTML=ulist; + } +} + +function loaduserpin(){ + document.getElementById("pincode").value=''; + currentpinfield="pincode"; + document.getElementById("usergreeting1").innerHTML = 'Bonjour, '+ currentuser.prename + ' ' + currentuser.surname + ''; + document.getElementById("sidebtn_pnlusers").style.display = 'block'; + document.getElementById("sidebtn_pnlchangepin").style.display = 'none'; + document.getElementById("sidebtn_pnluserpin").style.display = 'none'; + displaypanel(); + return false; +} + +function loadnewuserpin(){ + document.getElementById("newpincode1").value=''; + document.getElementById("newpincode2").value=''; + document.getElementById("usergreeting2").innerHTML = 'Bonjour, '+ currentuser.prename + ' ' + currentuser.surname + ''; + currentpinfield="newpincode1"; + document.getElementById("sidebtn_pnlusers").style.display = 'block'; + document.getElementById("sidebtn_pnlchangepin").style.display = 'none'; + document.getElementById("sidebtn_pnluserpin").style.display = 'none'; + displaypanel(); + return false; +} + +function loadpnltimetracker(){ + console.log("load time tracker"); + document.getElementById("trackusername").innerHTML="" + currentuser.prename + " " + currentuser.surname + ""; + document.getElementById("sidebtn_pnlusers").style.display = 'block'; + document.getElementById("sidebtn_pnlchangepin").style.display = 'block'; + document.getElementById("sidebtn_pnluserpin").style.display = 'none'; + displaypanel(); + return false; +} + +function loadloader(){ + document.getElementById("sidebtn_pnlusers").style.display = 'block'; + document.getElementById("sidebtn_pnlchangepin").style.display = 'none'; + document.getElementById("sidebtn_pnluserpin").style.display = 'none'; + displaypanel(); + return false; +} + +function loadstatus(){ + document.getElementById("sidebtn_pnlusers").style.display = 'block'; + document.getElementById("sidebtn_pnlchangepin").style.display = 'none'; + document.getElementById("sidebtn_pnluserpin").style.display = 'none'; + displaypanel(); + return false; +} + +function setuser(id){ + var sqlq = "select id,prename,surname,pin,blocked,disabled from staff where id="+id+";"; + req.reqdata("POST","db.cgi",{"sql":sqlq},loaduser); + return false; +} + +function loaduser(data){ + if (data){ + currentuser = data[0]; + console.log(currentuser); + console.log("after cuser"); + if ((currentuser.pin == "" || currentuser.pin == null)){ + loadpanel('newuserpin'); + } else { + loadpanel('userpin'); + } + }else { + currentuser = null; + } + return false; +} + +function checkuserpin(){ + var sqlq = "select pin,blocked,disabled from staff where id="+currentuser.id+";"; + req.reqdata("POST","db.cgi",{"sql":sqlq},loadtimetracker); + return false; +} + + + +function loadtimetracker(data){ + var userpin= document.getElementById("pincode").value; + if (data){ + console.log(userpin + "<=>" + data[0].pin); + if (userpin==data[0].pin){ + req.reqdata("POST","db.cgi",{"sql":"select id,strftime(\"%d.%m.%Y\",date(stamp_in)) as daydate,strftime(\"%H:%M\",stamp_in) as stamp_in,strftime(\"%H:%M\",stamp_out) as stamp_out,date(stamp_in) as cmpdaydate from hours where id_staff="+ currentuser.id+" order by date(stamp_in) desc,stamp_in desc,stamp_out desc LIMIT 1;"},loadtrackdata); + loadpanel("timetracker"); + } else { + document.getElementById("pincode").value = ""; + intervaltime=60; + document.getElementById("usergreeting1").innerHTML = 'Code PIN pas correcte!'; + } + } + return false; +} + +function loadtrackdata(data){ + console.log(data); + var cdate = new Date().toISOString().substring(0,10); + document.getElementById("btntrackin").disabled = true; + document.getElementById("btntrackout").disabled = true; + //console.log(cdate + "<=>" + data[0].cmpdaydate); + if (data && data[0] && data.length > 0){ + lasttrack=data[0]; + if (data[0].cmpdaydate && (data[0].cmpdaydate == cdate)){ + console.log("IN:" + data[0].stamp_in); + console.log("out:" + data[0].stamp_out); + if ((data[0].stamp_in != null) && (data[0].stamp_out != null)){ + document.getElementById("btntrackin").disabled = false; + } else { + document.getElementById("btntrackout").disabled = false; + } + } else { + document.getElementById("btntrackin").disabled = false; + } + // + document.getElementById("lasttrack").innerHTML = "Dernier Pointage:
" + ((data[0].daydate)?data[0].daydate:"") + " "+ ((data[0].stamp_in)?" Entrée:" + data[0].stamp_in:"N/A") + " Sortie: " +((data[0].stamp_out)?data[0].stamp_out:"N/A") + ""; + } else { + lasttrack = null; + document.getElementById("btntrackin").disabled = false; + document.getElementById("lasttrack").innerHTML = ""; + } + + return false; +} + +function setnewuserpin(){ + var pin1 = document.getElementById("newpincode1").value; + var pin2 = document.getElementById("newpincode2").value; + console.log(pin1 + "<=>" + pin2); + if (pin1.length >= 4 && pin2.length == 0){ + currentpinfield = "newpincode2"; + return false; + } + if (pin1.length < 4 || pin2.length < 4){ + clearUserPin(); + document.getElementById("usergreeting2").innerHTML = 'Entrez un code pin de min 4 nombres!'; + return false; + } else if (pin1 != pin2){ + clearUserPin(); + document.getElementById("usergreeting2").innerHTML = 'Les codes ne sont pas identiques!'; + return false; + } + req.reqdata("POST","db.cgi",{"sql":"update staff set pin='" + pin1 + "' where id=" + currentuser.id + ";"},afterpincodeupdate); + return false; +} + +function afterpincodeupdate(data){ + setuser(currentuser.id); + return false; +} + +function setPinValue(key){ + intervaltime = 60; + var cobj = document.getElementById(currentpinfield); + var cpin = cobj.value; + cobj.value = cpin + key; + return false; +} + +function clearUserPin(){ + intervaltime = 60; + document.getElementById("pincode").value = ""; + document.getElementById("newpincode1").value = ""; + document.getElementById("newpincode2").value = ""; + return false; +} + +function setTrack(direction){ + var sql = ""; + //intervaltime = 60; + if (direction == 'in'){ + sql = "INSERT INTO hours (id_staff, stamp_in) VALUES ("+currentuser.id+",CURRENT_TIMESTAMP);"; + } else if (direction == 'out'){ + sql = "UPDATE hours SET stamp_out=CURRENT_TIMESTAMP where id_staff='"+ currentuser.id+"' and id='"+lasttrack.id+"';"; + } + //console.log(sql); + req.reqdata("POST","db.cgi",{"sql":sql},showstatus); +} + +function showstatus(data){ + //console.log(data); + if (data && data.success) { document.getElementById("statusmsg").innerHTML = "Merci, le pointage a été enregistré!"} + loadpanel("status"); + setTimeout("loadpanel('users');",3000); +} + +function checkIdle(){ + //console.log("check idle"); + if (intervaltime < 0){ + //console.log("change view"); + location.href = location.href; + } else { + + intervaltime = intervaltime - 1; + //console.log(intervaltime); + } +} +function startCheckIdle(){ + interval = window.setInterval("checkIdle()",1000); + +} +function stopCheckIdle(){ + window.clearInterval(interval); +} + + diff --git a/bin/CGI/tmpl/app/timeclock/index.tt b/bin/CGI/tmpl/app/timeclock/index.tt new file mode 100644 index 0000000..7f4203f --- /dev/null +++ b/bin/CGI/tmpl/app/timeclock/index.tt @@ -0,0 +1,117 @@ +[% USE date %] +[% vstamp=date.format(date.now, '%d%m%Y%H%M%S') %] + + + + + + + + + + +Time Clock + + +
+ + + + +
+
+
+
+
+
+
+ +
+
+
Bonjour Prename,
+
+
+
+ + + + + + + + + + + + +
+
+
+ + + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/bin/CGI/tmpl/block/snackbar.tt b/bin/CGI/tmpl/block/snackbar.tt new file mode 100644 index 0000000..464a162 --- /dev/null +++ b/bin/CGI/tmpl/block/snackbar.tt @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/bin/CGI/tmpl/macro/fields.tt b/bin/CGI/tmpl/macro/fields.tt new file mode 100644 index 0000000..63c16fe --- /dev/null +++ b/bin/CGI/tmpl/macro/fields.tt @@ -0,0 +1,123 @@ +[% MACRO fieldhidden(column,table,ident,value) BLOCK -%] + +[% END -%] +[% MACRO fieldeditbox(column,table,title,size,state,value,plhold) BLOCK -%] +
+ + 0 %][% state %][% END %]/> + +
+[% END -%] +[% MACRO fieldfile(column,table,title,size,state,value) BLOCK -%] +
+ + 0 %][% state %][% END %]/> + +
+[% END -%] +[% MACRO fieldpasswordbox(column,table,title,size,state,value) BLOCK -%] +
+ + 0 %][% state %][% END %]/> + +
+[% END -%] +[% MACRO fieldtagbox(column,table,title,size,state,value) BLOCK -%] +
+ + 0 %][% state %][% END %]/> + +
+[% END -%] +[% MACRO fieldcheckbox(column,table,title,size,state,value) BLOCK -%] +
+
+ 0 %][% state %][% END %]> + +
+[% END -%] + +[% MACRO fieldemailbox(column,table,title,size,state,value) BLOCK -%] +
+ + 0 %][% state %][% END %]/> +
+[% END -%] + +[% MACRO fieldselectbox(column,table,title,size,state,value) BLOCK -%] + [% IF state.length > 0 %] + [% fieldeditbox(column,table,title,size,state,value) %] + [% ELSE %] +
+ + + +
+ [% END %] +[% END -%] +[% MACRO fieldmultiselectbox(column,table,title,size,state,value) BLOCK -%] + [% IF state.length > 0 %] + [% fieldeditbox(column,table,title,size,state,value) %] + [% ELSE %] +
+ + + +
+ [% END %] +[% END -%] +[% MACRO fielddatebox(column,table,title,size,state,value) BLOCK -%] +
+ + 0 %][% state %][% END %]]/> + +
+[% END -%] +[% MACRO fieldtimebox(column,table,title,size,state,value) BLOCK -%] +
+ + 0 %][% state %][% END %]]/> + +
+[% END -%] +[% MACRO fieldtextarea(column,table,title,size,state,height,value) BLOCK -%] +
+ + +
+[% END -%] +[% MACRO fieldrichtextarea(column,table,title,size,state,height,value) BLOCK -%] +
+ + +
+[% END -%] + +[% MACRO formsavebutton(formname,btnname) BLOCK -%] +
+ +
+[% END -%] +[% MACRO formsavetextfilebutton(formname,btnname) BLOCK -%] +
+ +
+[% END -%] +[% MACRO formdlgsavebutton(formname,btnname,clbk) BLOCK -%] + +[% END -%] +[% MACRO formsavefilebutton(formname,btnname,container) BLOCK -%] +[% IF container.length > 0 %] +
+ [% END %] + +[% IF container.length > 0 %] +
+[% END %] +[% END -%] \ No newline at end of file diff --git a/bin/CGI/tmpl/skeleton/app.tt b/bin/CGI/tmpl/skeleton/app.tt new file mode 100644 index 0000000..e5e9f5f --- /dev/null +++ b/bin/CGI/tmpl/skeleton/app.tt @@ -0,0 +1,3 @@ +[% #USE DBI %] +[% #USE dksdb = DBI(dsn, dbuser, dbpassword) %] +[% INCLUDE $page %] \ No newline at end of file diff --git a/bin/CGI/tmpl/skeleton/file.tt b/bin/CGI/tmpl/skeleton/file.tt new file mode 100644 index 0000000..e5e9f5f --- /dev/null +++ b/bin/CGI/tmpl/skeleton/file.tt @@ -0,0 +1,3 @@ +[% #USE DBI %] +[% #USE dksdb = DBI(dsn, dbuser, dbpassword) %] +[% INCLUDE $page %] \ No newline at end of file diff --git a/bin/CGI/tmpl/skeleton/index.tt b/bin/CGI/tmpl/skeleton/index.tt new file mode 100644 index 0000000..b4df386 --- /dev/null +++ b/bin/CGI/tmpl/skeleton/index.tt @@ -0,0 +1,36 @@ +[% USE date %] +[% #USE env = EnvHash %] +[% vstamp=date.format(date.now, '%d%m%Y%H%M%S') %] + + + + + + + + + +Hourtrax + + +
+
+ +
Hourtrax
+ + +
+
+
+
+ +
+
+
+
+ + + + + + diff --git a/bin/CGI/tmpl/skeleton/module.tt b/bin/CGI/tmpl/skeleton/module.tt new file mode 100644 index 0000000..f5bd93d --- /dev/null +++ b/bin/CGI/tmpl/skeleton/module.tt @@ -0,0 +1,61 @@ +[% USE Dumper %] + +[% #USE dksdb = DBI(dsn, dbuser, dbpassword) %] +[% #USE date %] +[% vstamp=date.format(date.now, '%d%m%Y') %] + + + + + + + + + [% adminname %] - [% module %] - [% pagename %] + + + + + + + + + + + [% #INCLUDE "module/$module/css.tt" %] + + + + +
+ [% #IF session.id %] + [% INCLUDE $page %] +
+ + + + + + + + + + + [% #INCLUDE "module/$module/javascript.tt" %] + + + [% #ELSE %] + + [% #END %] + [% INCLUDE block/snackbar.tt %] + + + \ No newline at end of file diff --git a/bin/htdocs/css/admin.css b/bin/htdocs/css/admin.css new file mode 100644 index 0000000..78685c1 --- /dev/null +++ b/bin/htdocs/css/admin.css @@ -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/bin/htdocs/css/clock.css b/bin/htdocs/css/clock.css new file mode 100644 index 0000000..79ba471 --- /dev/null +++ b/bin/htdocs/css/clock.css @@ -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/bin/htdocs/css/clock2.css b/bin/htdocs/css/clock2.css new file mode 100644 index 0000000..9886d0c --- /dev/null +++ b/bin/htdocs/css/clock2.css @@ -0,0 +1,5 @@ +.btnkeypad { + height: 75px; + font-weight: bold; + font-size: 30px; +} \ No newline at end of file diff --git a/bin/htdocs/css/w3pro.css b/bin/htdocs/css/w3pro.css new file mode 100644 index 0000000..3013489 --- /dev/null +++ b/bin/htdocs/css/w3pro.css @@ -0,0 +1,378 @@ +/* W3PRO.CSS 4.13 June 2019 by Jan Egil and Borge Refsnes */ +html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit} +/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */ +html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0} +article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item} +audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline} +audio:not([controls]){display:none;height:0}[hidden],template{display:none} +a{background-color:transparent}a:active,a:hover{outline-width:0} +abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted} +b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000} +small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} +sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none} +code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible} +button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold} +button,input{overflow:visible}button,select{text-transform:none} +button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button} +button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0} +button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText} +fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em} +legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto} +[type=checkbox],[type=radio]{padding:0} +[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto} +[type=search]{-webkit-appearance:textfield;outline-offset:-2px} +[type=search]::-webkit-search-decoration{-webkit-appearance:none} +::-webkit-file-upload-button{-webkit-appearance:button;font:inherit} +/* End extract */ +html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden} +h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}.w3-serif{font-family:serif} +h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin: 0}.w3-wide{letter-spacing:4px} +hr{border:0;border-top:1px solid #eee;margin:20px 0} +.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit} +.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc} +.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1} +.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1} +.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center} +.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top} +.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px} +.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap} +.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)} +.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} +.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none} +.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none} +.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%} +.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none} +.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block} +.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s} +.w3-input{padding:8px;display:block;border:1px solid #ccc;width:100%;background-color: #e8f0fe; } +.w3-select{padding:9px 0; display:block;width:100%;border:1px solid #ccc;background-color: #e8f0fe;} +.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer} +.w3-dropdown-hover:hover .w3-dropdown-content{display:block; } +.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000} +.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000} +.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1} +.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px} +.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto} +.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%} +.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%} +.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px} +.w3-main,#main{transition:margin-left .4s} +.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)} +.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px} +.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto} +.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0} +.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left} +.w3-bar .w3-button{white-space:normal} +.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0} +.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%} +.w3-responsive{display:block;overflow-x:auto} +.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before, +.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both} +.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%} +.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%} +.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%} +.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%} +@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%} +.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%} +.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}} +@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%} +.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%} +.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}} +.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px} +.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px} +.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell} +.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom} +.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important} +@media (max-width:1205px){.w3-auto{max-width:95%}} +@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px} +.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative} +.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center} +.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}} +@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}} +@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}} +@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}} +@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}} +.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0} +.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2} +.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0} +.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0} +.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)} +.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)} +.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)} +.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)} +.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)} +.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none} +.w3-display-position{position:absolute} +.w3-circle{border-radius:50%} +.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px} +.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px} +.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px} +.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px} +.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word} +.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%} +.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16)} +.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)} +.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}} +.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}} +.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}} +.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}} +.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}} +.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}} +.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}} +.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}} +.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important} +.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1} +.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75} +.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)} +.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)} +.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)} +.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important} +.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important} +.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important} +.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important} +.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important} +.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important} +.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important} +.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important} +.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important} +.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important} +.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important} +.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important} +.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important} +.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important} +.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important} +.w3-left{float:left!important}.w3-right{float:right!important} +.w3-button:hover{color:#000!important;background-color:#ccc!important} +.w3-transparent,.w3-hover-none:hover{background-color:transparent!important} +.w3-hover-none:hover{box-shadow:none!important} +/* DEFAULT COLORS */ +.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important} +.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important} +.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important} +.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important} +.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important} +.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important} +.w3-blue-grey,.w3-hover-blue-grey:hover{color:#fff!important;background-color:#607d8b!important} +.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important} +.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important} +.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important} +.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important} +.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important} +.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important} +.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important} +.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important} +.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important} +.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important} +.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important} +.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important} +.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important} +.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important} +.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important} +.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important} +.w3-grey,.w3-hover-grey:hover{color:#000!important;background-color:#9e9e9e!important} +.w3-light-grey,.w3-hover-light-grey:hover{color:#000!important;background-color:#f1f1f1!important} +.w3-dark-grey,.w3-hover-dark-grey:hover{color:#fff!important;background-color:#616161!important} +.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffe7e7!important}.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#e7ffe7!important} +.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffd7!important}.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#e7ffff!important} +.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important} +.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important} +.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important} +.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important} +.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important} +.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important} +.w3-text-blue-grey,.w3-hover-text-blue-grey:hover{color:#607d8b!important} +.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important} +.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important} +.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important} +.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important} +.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important} +.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important} +.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important} +.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important} +.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important} +.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important} +.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important} +.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important} +.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important} +.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important} +.w3-text-white,.w3-hover-text-white:hover{color:#fff!important} +.w3-text-black,.w3-hover-text-black:hover{color:#000!important} +.w3-text-grey,.w3-hover-text-grey:hover{color:#757575!important} +.w3-text-light-grey,.w3-hover-text-light-grey:hover{color:#f1f1f1!important} +.w3-text-dark-grey,.w3-hover-text-dark-grey:hover{color:#3a3a3a!important} +.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important} +.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important} +.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important} +.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important} +.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important} +.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important} +.w3-border-blue-grey,.w3-hover-blue-grey:hover{border-color:#607d8b!important} +.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important} +.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important} +.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important} +.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important} +.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important} +.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important} +.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important} +.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important} +.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important} +.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important} +.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important} +.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important} +.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important} +.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important} +.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important} +.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important} +.w3-border-grey,.w3-hover-border-grey:hover{border-color:#9e9e9e!important} +.w3-border-light-grey,.w3-hover-border-light-grey:hover{border-color:#f1f1f1!important} +.w3-border-dark-grey,.w3-hover-border-dark-grey:hover{border-color:#616161!important} +.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important} +.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffd7!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important} +/* DEFAULT THEME */ +.w3-theme-l5 {color:#000 !important; background-color:#f6f8fc !important} +.w3-theme-l4 {color:#000 !important; background-color:#e1e9f6 !important} +.w3-theme-l3 {color:#000 !important; background-color:#c3d3ed !important} +.w3-theme-l2 {color:#000 !important; background-color:#a5bee4 !important} +.w3-theme-l1 {color:#fff !important; background-color:#88a8db !important} +.w3-theme-d1 {color:#fff !important; background-color:#5180cb !important} +.w3-theme-d2 {color:#fff !important; background-color:#3a6fc3 !important} +.w3-theme-d3 {color:#fff !important; background-color:#3361aa !important} +.w3-theme-d4 {color:#fff !important; background-color:#2c5392 !important} +.w3-theme-d5 {color:#fff !important; background-color:#24457a !important} + +.w3-theme-light {color:#000 !important; background-color:#f6f8fc !important} +.w3-theme-dark {color:#fff !important; background-color:#24457a !important} +.w3-theme-action {color:#fff !important; background-color:#24457a !important} + +.w3-theme {color:#fff !important; background-color:#6a92d3 !important} +.w3-text-theme {color:#6a92d3 !important} +.w3-border-theme {border-color:#6a92d3 !important} + +.w3-hover-theme:hover {color:#fff !important; background-color:#6a92d3 !important} +.w3-hover-text-theme:hover {color:#6a92d3 !important} +.w3-hover-border-theme:hover {border-color:#6a92d3 !important} + +.w3-label { color: rgb(153, 150, 150);} +#main {margin-left: 210px;} +@media (max-width:768px){ + #sidebar { display: none;} + #main { margin-left: 0px;} +} + +.w3-select { + display: block; + font-size: 16px; + font-family: sans-serif; + font-weight: normal; + color: #444; + line-height: 1.3; + padding: .6em 1.4em .5em .8em; + width: 100%; + max-width: 100%; + box-sizing: border-box; + margin: 0; + border-bottom: 1px solid #aaa; + box-shadow: 0 1px 0 1px rgba(0,0,0,.04); + /* border-radius: .5em; */ + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + background-color: #e8f0fe; + background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23000%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E'), + linear-gradient(to bottom, #e8f0fe 0%,#e8f0fe 100%); + background-repeat: no-repeat, repeat; + background-position: right .7em top 50%, 0 0; + background-size: .65em auto, 100%; +} +.w3-select::-ms-expand { + display: none; +} +.w3-select:hover { + border-color: #888; +} +.w3-select:focus { + border-color: #aaa; + box-shadow: 0 0 1px 1px #6a92d3; + box-shadow: 0 0 0 1px -moz-mac-focusring; + color: #222; + outline: none; +} + + +.w3-select option { + font-weight:normal; +} + +.w3-table { + table-layout: fixed; +} + +.w3-text-line-through { text-decoration: line-through; } + +#snackbar { + visibility: hidden; + min-width: 250px; + margin-left: -125px; + background-color: #333; + color: #fff; + text-align: center; + border-radius: 2px; + padding: 16px; + position: fixed; + z-index: 1; + left: 50%; + bottom: 30px; + font-size: 17px; +} + +#snackbar.show { + visibility: visible; + -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s; + animation: fadein 0.5s, fadeout 0.5s 2.5s; +} + +@-webkit-keyframes fadein { + from {bottom: 0; opacity: 0;} + to {bottom: 30px; opacity: 1;} +} + +@keyframes fadein { + from {bottom: 0; opacity: 0;} + to {bottom: 30px; opacity: 1;} +} + +@-webkit-keyframes fadeout { + from {bottom: 30px; opacity: 1;} + to {bottom: 0; opacity: 0;} +} + +@keyframes fadeout { + from {bottom: 30px; opacity: 1;} + to {bottom: 0; opacity: 0;} +} + +.tabulator-header-filter > input { + background-color: #e8f0fe; + border: 1px solid #ccc; + font-weight: normal; +} + +.w3-readonly { + pointer-events:none; + padding:8px;display:block;border:0px;width:100%;background-color: #fff; +} + +.right-side-bg { + background: url("../img/bg1.jpg"); + background-size: cover; + min-height: 100vh; +} + +/* .mceContentBody { + background: #e8f0fe; + color:#000; +} */ + +/* .tabulator-row-even { + background-color: #757575; +} */ \ No newline at end of file diff --git a/bin/htdocs/img/dks_1000.png b/bin/htdocs/img/dks_1000.png new file mode 100644 index 0000000..87b44eb Binary files /dev/null and b/bin/htdocs/img/dks_1000.png differ diff --git a/bin/htdocs/img/hourtrax.png b/bin/htdocs/img/hourtrax.png new file mode 100644 index 0000000..1399862 Binary files /dev/null and b/bin/htdocs/img/hourtrax.png differ diff --git a/bin/htdocs/img/hourtrax.svg b/bin/htdocs/img/hourtrax.svg new file mode 100644 index 0000000..05f00e2 --- /dev/null +++ b/bin/htdocs/img/hourtrax.svg @@ -0,0 +1,90 @@ + + + + + + + image/svg+xml + + + + + + + Hourtrax + + + + + + + + + + + + + + + diff --git a/bin/htdocs/img/icons/Adobe_Acrobat.svg b/bin/htdocs/img/icons/Adobe_Acrobat.svg new file mode 100644 index 0000000..43a58e1 --- /dev/null +++ b/bin/htdocs/img/icons/Adobe_Acrobat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bin/htdocs/img/icons/Adobe_PDF_Export.svg b/bin/htdocs/img/icons/Adobe_PDF_Export.svg new file mode 100644 index 0000000..49169f9 --- /dev/null +++ b/bin/htdocs/img/icons/Adobe_PDF_Export.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bin/htdocs/img/icons/Agreement_01.svg b/bin/htdocs/img/icons/Agreement_01.svg new file mode 100644 index 0000000..27a8a06 --- /dev/null +++ b/bin/htdocs/img/icons/Agreement_01.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bin/htdocs/img/icons/Bill.svg b/bin/htdocs/img/icons/Bill.svg new file mode 100644 index 0000000..b891cd2 --- /dev/null +++ b/bin/htdocs/img/icons/Bill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bin/htdocs/img/icons/Document_Save.svg b/bin/htdocs/img/icons/Document_Save.svg new file mode 100644 index 0000000..7262b20 --- /dev/null +++ b/bin/htdocs/img/icons/Document_Save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bin/htdocs/img/icons/Save.svg b/bin/htdocs/img/icons/Save.svg new file mode 100644 index 0000000..ce6bfa3 --- /dev/null +++ b/bin/htdocs/img/icons/Save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bin/htdocs/img/icons/address.svg b/bin/htdocs/img/icons/address.svg new file mode 100644 index 0000000..4d51035 --- /dev/null +++ b/bin/htdocs/img/icons/address.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/address_white.svg b/bin/htdocs/img/icons/address_white.svg new file mode 100644 index 0000000..a5de7d7 --- /dev/null +++ b/bin/htdocs/img/icons/address_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/apps.svg b/bin/htdocs/img/icons/apps.svg new file mode 100644 index 0000000..eef3c30 --- /dev/null +++ b/bin/htdocs/img/icons/apps.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/apps_white.svg b/bin/htdocs/img/icons/apps_white.svg new file mode 100644 index 0000000..f37f596 --- /dev/null +++ b/bin/htdocs/img/icons/apps_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/archive.svg b/bin/htdocs/img/icons/archive.svg new file mode 100644 index 0000000..0e614f8 --- /dev/null +++ b/bin/htdocs/img/icons/archive.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/archive_white.svg b/bin/htdocs/img/icons/archive_white.svg new file mode 100644 index 0000000..1dcbdf5 --- /dev/null +++ b/bin/htdocs/img/icons/archive_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/calendar.svg b/bin/htdocs/img/icons/calendar.svg new file mode 100644 index 0000000..6b51e03 --- /dev/null +++ b/bin/htdocs/img/icons/calendar.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/calendar_white.svg b/bin/htdocs/img/icons/calendar_white.svg new file mode 100644 index 0000000..c738ec5 --- /dev/null +++ b/bin/htdocs/img/icons/calendar_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/clocktime.svg b/bin/htdocs/img/icons/clocktime.svg new file mode 100644 index 0000000..7a94f5b --- /dev/null +++ b/bin/htdocs/img/icons/clocktime.svg @@ -0,0 +1,18 @@ + + + + + + diff --git a/bin/htdocs/img/icons/clocktime_white.svg b/bin/htdocs/img/icons/clocktime_white.svg new file mode 100644 index 0000000..be92d7a --- /dev/null +++ b/bin/htdocs/img/icons/clocktime_white.svg @@ -0,0 +1,14 @@ + + + + diff --git a/bin/htdocs/img/icons/club.svg b/bin/htdocs/img/icons/club.svg new file mode 100644 index 0000000..8a941e3 --- /dev/null +++ b/bin/htdocs/img/icons/club.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/club_white.svg b/bin/htdocs/img/icons/club_white.svg new file mode 100644 index 0000000..5325dc0 --- /dev/null +++ b/bin/htdocs/img/icons/club_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/clubs.svg b/bin/htdocs/img/icons/clubs.svg new file mode 100644 index 0000000..0a7d497 --- /dev/null +++ b/bin/htdocs/img/icons/clubs.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/clubs_white.svg b/bin/htdocs/img/icons/clubs_white.svg new file mode 100644 index 0000000..7184d28 --- /dev/null +++ b/bin/htdocs/img/icons/clubs_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/code.svg b/bin/htdocs/img/icons/code.svg new file mode 100644 index 0000000..c3b4f28 --- /dev/null +++ b/bin/htdocs/img/icons/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bin/htdocs/img/icons/cube.svg b/bin/htdocs/img/icons/cube.svg new file mode 100644 index 0000000..31b3129 --- /dev/null +++ b/bin/htdocs/img/icons/cube.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/cube_white.svg b/bin/htdocs/img/icons/cube_white.svg new file mode 100644 index 0000000..9a78edf --- /dev/null +++ b/bin/htdocs/img/icons/cube_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/cubelight.svg b/bin/htdocs/img/icons/cubelight.svg new file mode 100644 index 0000000..ff786d4 --- /dev/null +++ b/bin/htdocs/img/icons/cubelight.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/cubelight_white.svg b/bin/htdocs/img/icons/cubelight_white.svg new file mode 100644 index 0000000..c7965b6 --- /dev/null +++ b/bin/htdocs/img/icons/cubelight_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/dashboard.svg b/bin/htdocs/img/icons/dashboard.svg new file mode 100644 index 0000000..c5f36cc --- /dev/null +++ b/bin/htdocs/img/icons/dashboard.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/dashboard_white.svg b/bin/htdocs/img/icons/dashboard_white.svg new file mode 100644 index 0000000..c6a7dda --- /dev/null +++ b/bin/htdocs/img/icons/dashboard_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/download.svg b/bin/htdocs/img/icons/download.svg new file mode 100644 index 0000000..eb20956 --- /dev/null +++ b/bin/htdocs/img/icons/download.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/download_white.svg b/bin/htdocs/img/icons/download_white.svg new file mode 100644 index 0000000..100cebf --- /dev/null +++ b/bin/htdocs/img/icons/download_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/duplicate.svg b/bin/htdocs/img/icons/duplicate.svg new file mode 100644 index 0000000..cebaad8 --- /dev/null +++ b/bin/htdocs/img/icons/duplicate.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/duplicate_white.svg b/bin/htdocs/img/icons/duplicate_white.svg new file mode 100644 index 0000000..a643c74 --- /dev/null +++ b/bin/htdocs/img/icons/duplicate_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/edit.svg b/bin/htdocs/img/icons/edit.svg new file mode 100644 index 0000000..c0bb9ac --- /dev/null +++ b/bin/htdocs/img/icons/edit.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/edit_white.svg b/bin/htdocs/img/icons/edit_white.svg new file mode 100644 index 0000000..bbbe89f --- /dev/null +++ b/bin/htdocs/img/icons/edit_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/file.svg b/bin/htdocs/img/icons/file.svg new file mode 100644 index 0000000..6df51bd --- /dev/null +++ b/bin/htdocs/img/icons/file.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/file/dir.png b/bin/htdocs/img/icons/file/dir.png new file mode 100644 index 0000000..a2e3c0f Binary files /dev/null and b/bin/htdocs/img/icons/file/dir.png differ diff --git a/bin/htdocs/img/icons/file/doc.png b/bin/htdocs/img/icons/file/doc.png new file mode 100644 index 0000000..8e43190 Binary files /dev/null and b/bin/htdocs/img/icons/file/doc.png differ diff --git a/bin/htdocs/img/icons/file/docx.png b/bin/htdocs/img/icons/file/docx.png new file mode 100644 index 0000000..8e43190 Binary files /dev/null and b/bin/htdocs/img/icons/file/docx.png differ diff --git a/bin/htdocs/img/icons/file/file.png b/bin/htdocs/img/icons/file/file.png new file mode 100644 index 0000000..c981a83 Binary files /dev/null and b/bin/htdocs/img/icons/file/file.png differ diff --git a/bin/htdocs/img/icons/file/jpg.png b/bin/htdocs/img/icons/file/jpg.png new file mode 100644 index 0000000..3f2999c Binary files /dev/null and b/bin/htdocs/img/icons/file/jpg.png differ diff --git a/bin/htdocs/img/icons/file/pdf.png b/bin/htdocs/img/icons/file/pdf.png new file mode 100644 index 0000000..8614b6e Binary files /dev/null and b/bin/htdocs/img/icons/file/pdf.png differ diff --git a/bin/htdocs/img/icons/file/png.png b/bin/htdocs/img/icons/file/png.png new file mode 100644 index 0000000..3f2999c Binary files /dev/null and b/bin/htdocs/img/icons/file/png.png differ diff --git a/bin/htdocs/img/icons/file/txt.png b/bin/htdocs/img/icons/file/txt.png new file mode 100644 index 0000000..2e41cee Binary files /dev/null and b/bin/htdocs/img/icons/file/txt.png differ diff --git a/bin/htdocs/img/icons/file/xls.png b/bin/htdocs/img/icons/file/xls.png new file mode 100644 index 0000000..ceabc7d Binary files /dev/null and b/bin/htdocs/img/icons/file/xls.png differ diff --git a/bin/htdocs/img/icons/file/xlsx.png b/bin/htdocs/img/icons/file/xlsx.png new file mode 100644 index 0000000..ceabc7d Binary files /dev/null and b/bin/htdocs/img/icons/file/xlsx.png differ diff --git a/bin/htdocs/img/icons/file_white.svg b/bin/htdocs/img/icons/file_white.svg new file mode 100644 index 0000000..3e04bda --- /dev/null +++ b/bin/htdocs/img/icons/file_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/folder.svg b/bin/htdocs/img/icons/folder.svg new file mode 100644 index 0000000..f1f6e4a --- /dev/null +++ b/bin/htdocs/img/icons/folder.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/folder_white.svg b/bin/htdocs/img/icons/folder_white.svg new file mode 100644 index 0000000..25baa23 --- /dev/null +++ b/bin/htdocs/img/icons/folder_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/globe.svg b/bin/htdocs/img/icons/globe.svg new file mode 100644 index 0000000..9af64dc --- /dev/null +++ b/bin/htdocs/img/icons/globe.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/globe_white.svg b/bin/htdocs/img/icons/globe_white.svg new file mode 100644 index 0000000..f0c2dea --- /dev/null +++ b/bin/htdocs/img/icons/globe_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/inbox.svg b/bin/htdocs/img/icons/inbox.svg new file mode 100644 index 0000000..034e942 --- /dev/null +++ b/bin/htdocs/img/icons/inbox.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/inbox_white.svg b/bin/htdocs/img/icons/inbox_white.svg new file mode 100644 index 0000000..1d98ae9 --- /dev/null +++ b/bin/htdocs/img/icons/inbox_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/library.svg b/bin/htdocs/img/icons/library.svg new file mode 100644 index 0000000..0a917f3 --- /dev/null +++ b/bin/htdocs/img/icons/library.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/library_white.svg b/bin/htdocs/img/icons/library_white.svg new file mode 100644 index 0000000..3928fdd --- /dev/null +++ b/bin/htdocs/img/icons/library_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/license.svg b/bin/htdocs/img/icons/license.svg new file mode 100644 index 0000000..54f0fec --- /dev/null +++ b/bin/htdocs/img/icons/license.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/license_white.svg b/bin/htdocs/img/icons/license_white.svg new file mode 100644 index 0000000..2563f36 --- /dev/null +++ b/bin/htdocs/img/icons/license_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/list.svg b/bin/htdocs/img/icons/list.svg new file mode 100644 index 0000000..ea3f0e2 --- /dev/null +++ b/bin/htdocs/img/icons/list.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/list_white.svg b/bin/htdocs/img/icons/list_white.svg new file mode 100644 index 0000000..b57b752 --- /dev/null +++ b/bin/htdocs/img/icons/list_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/logout.svg b/bin/htdocs/img/icons/logout.svg new file mode 100644 index 0000000..da64de0 --- /dev/null +++ b/bin/htdocs/img/icons/logout.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/logout_white.svg b/bin/htdocs/img/icons/logout_white.svg new file mode 100644 index 0000000..3feb1b7 --- /dev/null +++ b/bin/htdocs/img/icons/logout_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/menu.svg b/bin/htdocs/img/icons/menu.svg new file mode 100644 index 0000000..1a247ab --- /dev/null +++ b/bin/htdocs/img/icons/menu.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/menu_white.svg b/bin/htdocs/img/icons/menu_white.svg new file mode 100644 index 0000000..222cc94 --- /dev/null +++ b/bin/htdocs/img/icons/menu_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/newspaper.svg b/bin/htdocs/img/icons/newspaper.svg new file mode 100644 index 0000000..420b45a --- /dev/null +++ b/bin/htdocs/img/icons/newspaper.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/newspaper_white.svg b/bin/htdocs/img/icons/newspaper_white.svg new file mode 100644 index 0000000..472b389 --- /dev/null +++ b/bin/htdocs/img/icons/newspaper_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/numberlist.svg b/bin/htdocs/img/icons/numberlist.svg new file mode 100644 index 0000000..33ac90c --- /dev/null +++ b/bin/htdocs/img/icons/numberlist.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/numberlist_white.svg b/bin/htdocs/img/icons/numberlist_white.svg new file mode 100644 index 0000000..e98d4aa --- /dev/null +++ b/bin/htdocs/img/icons/numberlist_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/package.svg b/bin/htdocs/img/icons/package.svg new file mode 100644 index 0000000..0b6a94d --- /dev/null +++ b/bin/htdocs/img/icons/package.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/package_white.svg b/bin/htdocs/img/icons/package_white.svg new file mode 100644 index 0000000..9a40a7b --- /dev/null +++ b/bin/htdocs/img/icons/package_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/pictures.svg b/bin/htdocs/img/icons/pictures.svg new file mode 100644 index 0000000..97bb690 --- /dev/null +++ b/bin/htdocs/img/icons/pictures.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bin/htdocs/img/icons/pictures_white.svg b/bin/htdocs/img/icons/pictures_white.svg new file mode 100644 index 0000000..9142d7f --- /dev/null +++ b/bin/htdocs/img/icons/pictures_white.svg @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/bin/htdocs/img/icons/plus.svg b/bin/htdocs/img/icons/plus.svg new file mode 100644 index 0000000..08e85b5 --- /dev/null +++ b/bin/htdocs/img/icons/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/plus_white.svg b/bin/htdocs/img/icons/plus_white.svg new file mode 100644 index 0000000..22714fe --- /dev/null +++ b/bin/htdocs/img/icons/plus_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/remove.svg b/bin/htdocs/img/icons/remove.svg new file mode 100644 index 0000000..e459477 --- /dev/null +++ b/bin/htdocs/img/icons/remove.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/remove_white.svg b/bin/htdocs/img/icons/remove_white.svg new file mode 100644 index 0000000..77e856e --- /dev/null +++ b/bin/htdocs/img/icons/remove_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/squares.svg b/bin/htdocs/img/icons/squares.svg new file mode 100644 index 0000000..8e084a0 --- /dev/null +++ b/bin/htdocs/img/icons/squares.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/squares_white.svg b/bin/htdocs/img/icons/squares_white.svg new file mode 100644 index 0000000..489f802 --- /dev/null +++ b/bin/htdocs/img/icons/squares_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/target.svg b/bin/htdocs/img/icons/target.svg new file mode 100644 index 0000000..72b5eb0 --- /dev/null +++ b/bin/htdocs/img/icons/target.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/target_white.svg b/bin/htdocs/img/icons/target_white.svg new file mode 100644 index 0000000..b02c6c4 --- /dev/null +++ b/bin/htdocs/img/icons/target_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/user.svg b/bin/htdocs/img/icons/user.svg new file mode 100644 index 0000000..37b4e3d --- /dev/null +++ b/bin/htdocs/img/icons/user.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/img/icons/user_white.svg b/bin/htdocs/img/icons/user_white.svg new file mode 100644 index 0000000..78d94ca --- /dev/null +++ b/bin/htdocs/img/icons/user_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/htdocs/js/admin.js b/bin/htdocs/js/admin.js new file mode 100644 index 0000000..e43b9d9 --- /dev/null +++ b/bin/htdocs/js/admin.js @@ -0,0 +1,78 @@ +var admin = { + current_dataset: {}, + loadapp: function(appident){ + console.log(appident); + console.log(location.origin + '/app/' + appident + '/index.html'); + location.href=location.origin + '/app/' + appident + '/index.html'; + }, + loadpage: function(modulepage,modulename){ + console.log(admin.current_dataset); + console.log("Load module:" + modulepage); + if (modulename){ + document.getElementById("modulename").innerHTML = modulename; + } + var pm = []; + for (var i in admin.current_dataset){ + pm.push(i + "=" + encodeURIComponent(admin.current_dataset[i])); + } + if (pm.length > 0){ + modulepage = modulepage + "?" + pm.join("&"); + } + console.log(modulepage); + document.getElementById("moduleframe").setAttribute('src',modulepage); + }, + sidebarclick: function(modulepage,modulename){ + admin.ladpage(modulepage,modulename); + }, + logout: function(){ + req.reqdata("POST",location.href,{"logout":"1"},admin.reloadpage); + + }, + reloadpage(page){ + location.href=location.href; + + }, + getdatasets: function(){ + req.reqdata("POST","db.cgi",{"db":app,"type":"array","sql":"select * from datastores;"},admin.loaddatasets); + }, + loaddatasets: function(data){ + console.log(data); + var gdt = document.getElementById('globaldatasets'); + gdt.innerHTML = ''; + if (data && data.sqldata){ + var opts = ';' + for (var i in data.sqldata){ + opts += ''; + } + gdt.innerHTML = opts; + } + if (admin.current_dataset.db){ + + }else { + admin.current_dataset["db"]=gdt.value; + } + console.log(admin.current_dataset); + } +} + +function reload_page(){ + location.href=location.href; +} + +function closeSidebar(){ + document.getElementById("sidebar").style.display = "none"; + document.getElementById("main").style.margin = "0 0 0 0"; + document.getElementById("modulename").style.setProperty("margin-left","0px"); +} + +function openSidebar(){ + document.getElementById("sidebar").style.display = "block"; + document.getElementById("main").style.setProperty("margin-left","210px"); + document.getElementById("modulename").style.setProperty("margin-left","150px"); +} + +document.addEventListener("DOMContentLoaded", function() { + if (app && app != ""){ + //initpage() + } +}); \ No newline at end of file diff --git a/bin/htdocs/js/admin_ht.js b/bin/htdocs/js/admin_ht.js new file mode 100644 index 0000000..751b0e4 --- /dev/null +++ b/bin/htdocs/js/admin_ht.js @@ -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 = ''; + //row += ''; + row += ''; + row += ''; + row += ''; + row += 'h'; + row += ''; + row += ''; + row += ''; + row += ''; + row += ''; + 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=''; + row += ''+tbldata.sqldata[i].surname+' '+tbldata.sqldata[i].prename+''; + row += ''; + row += ''; + row +=''; + 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=''; + row += ''; + row += ''; + row += ''; + row +=''; + 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=''; + row += ''; + row += ''+tbldata.sqldata[i].surname+' '+tbldata.sqldata[i].prename+''; + row += ''; + row += ''; + row +=''; + 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 += ''; + } + 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'; + }*/s +};s \ No newline at end of file diff --git a/bin/htdocs/js/fieldsave.js b/bin/htdocs/js/fieldsave.js new file mode 100644 index 0000000..bf317a0 --- /dev/null +++ b/bin/htdocs/js/fieldsave.js @@ -0,0 +1,48 @@ +var fsaveelements = document.querySelectorAll("input.fieldsave"); +for (var i = 0; i < fsaveelements.length; i++) { + fsaveelements[i].addEventListener('blur', function (event) { + savefield(event.currentTarget.id); + }, false); +} + +function savefield(obj){ + // console.log("save"); + var field = {"ident":obj.id}; + //var obj = document.getElementById(objid); + var fieldname = obj.getAttribute('name'); + var xsp = fieldname.split("_"); + // console.log(xsp); + // console.log(xsp[0] + "_id"); + var identfield = document.getElementById("id"); + field["ident_" + xsp[0] + "_id"] = identfield.value; + if (obj.tagName == 'TEXTAREA'){ + field[obj.getAttribute('name')] = obj.innerHTML; + } else if (obj.tagName == "SELECT"){ + field[obj.getAttribute('name')] = obj.value; + } else if ((obj.getAttribute('type') == "checkbox")){ + if ($("#" +objid).prop('checked')){ + field[obj.getAttribute('name')] = obj.value; + } else { + field[obj.getAttribute('name')] = ""; + } + + } else if (obj.getAttribute('type') == "file") { + if (obj.value != ""){ + alert("file save TODO!") + return false; + } + } else { + field[obj.getAttribute('name')] = obj.value; + } + + field["fn"] = "savefield"; + // console.log(field); + + req.reqdata("POST","index.cgi",field,fieldsaved); + return false; +} + +function fieldsaved(data){ + console.log("field saved"); + // console.log(data); +} \ No newline at end of file diff --git a/bin/htdocs/js/formsave.js b/bin/htdocs/js/formsave.js new file mode 100644 index 0000000..cd98192 --- /dev/null +++ b/bin/htdocs/js/formsave.js @@ -0,0 +1,193 @@ +function saveform(frmid,aftercallback){ + var flds=getformcontent(frmid,null); + flds["fn"] ="saveform"; + + //console.log(flds); + if (aftercallback){ + req.reqdata("POST","index.cgi",flds,aftercallback); + formsaved({}); + } + else { + req.reqdata("POST","index.cgi",flds,formsaved); + } + return false; +} + +function formsaved(data){ + var sb = document.getElementById("snackbar"); + sb.className="show w3-green"; + sb.innerHTML = 'Les données ont été sauvegarder!'; + setTimeout(function(){ sb.className = sb.className.replace("show w3-green", ""); }, 3000); + return false; +} + +function showsnackbar(xclass,xmessage){ + var sb = document.getElementById("snackbar"); + sb.className="show " + xclass; + sb.innerHTML = xmessage; + setTimeout(function(){ sb.className = sb.className.replace(sb.className, ""); }, 3000); + return false; +} + +function getformcontent(frmid,dataflds){ + var frm = document.getElementById("frm_" + frmid); + var flds = []; + if (dataflds){ + flds = dataflds; + } + + for (var i = 0; i < frm.elements.length; i++) { + var field = frm.elements[i]; + //console.log("field:" + field.id + " Name:" + field.getAttribute("name")); + if (field.tagName == "INPUT" || field.tagName == "SELECT" || field.tagName == "TEXTAREA"){ + if (field.classList.contains("tagedit")){ + var fvalue=field.value.trim(); + var ndata = null; + if (fvalue != ""){ + ndata = fvalue.split(","); + } + + flds[field.getAttribute("name")] = ndata; + }else if (field.tagName == "TEXTAREA" ){ + if (field.classList.contains("richeditarea")){ + flds[field.getAttribute("name")] = tinymce.get(field.id).getContent(); + } else { + flds[field.getAttribute("name")] = field.innerHTML; + } + + }else if (field.type == "checkbox" ){ + if (field.checked){ + flds[field.getAttribute("name")] = "1"; + } else { + flds[field.getAttribute("name")] = ""; + } + + } + else { + if (field.tagName == "SELECT" && field.multiple == true){ + var opts = field.selectedOptions; + var vals = []; + for (var o in opts){ + if (opts[o].value){ + vals.push(opts[o].value); + } + } + if (vals.length > 0) { + flds[field.getAttribute("name")] = vals; + } else { + flds[field.getAttribute("name")] = ""; + } + + } else { + flds[field.getAttribute("name")] = field.value; + } + + } + + } + } + return flds; +} + +function cleanform(frmname){ + //console.log("Clean Form: " + frmname); + var frm = document.getElementById("frm_" + frmname); + + for (var f in frm){ + //console.log(frm[f].id); + if (frm[f] && frm[f].id){ + if (frm[f].tagName == 'INPUT'){ + //console.log("is INPUT" + frm[f].id + " type:" + frm[f].type + " class:" + frm[f].classList); + if (frm[f].type == "checkbox"){ + frm[f].checked = false; + } else if (frm[f].classList.contains("datefield")){ + if (frm[f]._flatpickr){ frm[f]._flatpickr.clear(); } + } else if (frm[f].classList.contains("choices__input")){ + if (choice[frmname][frm[f].id]){ + choice[frmname][frm[f].id].removeActiveItems(); + } + } else { + frm[f].value = ""; + } + } + if (frm[f].tagName == 'SELECT'){ + //console.log("is INPUT" + frm[f].id + " multiple:" + frm[f].multiple + " class:" + frm[f].classList); + if (frm[f].multiple == true){ + if (frm[f].classList.contains("choices__input")){ + choice[frmname][frm[f].id].removeActiveItems(); + } + } else { + frm[f].value = ""; + } + + } + if (frm[f].tagName == 'TEXTAREA'){ + //console.log("is INPUT" + frm[f].id + " class:" + frm[f].classList); + if (frm[f].classList.contains("richeditarea")){ + tinymce.get(frm[f].id).setContent(""); + } else { + frm[f].innerHTML = ""; + } + } + } + } + return false; +} + +function fillformbydataclass(dataclass,data,readonly = false){ + //console.log(data); + var frm = document.querySelectorAll('.data_'+ dataclass); + if (data){ + for (var f in frm){ + console.log(frm[f].id + "=>" + data[frm[f].id]); + if (data[frm[f].id]){ + console.log(frm[f].id + " => " + data[frm[f].id]); + if (readonly){ frm[f].readonly = true;} + if (frm[f].tagName == 'INPUT'){ + if (frm[f].type == "checkbox"){ + if (data[frm[f].id] == "1"){ + frm[f].checked = true; + } else { + frm[f].checked = false; + } + } else if (frm[f].classList.contains("datefield")){ + frm[f]._flatpickr.setDate(data[frm[f].id]); + } else if (frm[f].classList.contains("choices__input")){ + if ((data[frm[f].id] != null) && (data[frm[f].id] != '[""]')){ + choice[dataclass][frm[f].id].setValue(JSON.parse(data[frm[f].id])); + } + } else { + frm[f].value=data[frm[f].id]; + } + } + if (frm[f].tagName == 'SELECT'){ + if (frm[f].classList.contains("choices__input")){ + if (frm[f].multiple == true){ + //console.log(data[frm[f].id]); + choice[dataclass][frm[f].id].setChoiceByValue(JSON.parse(data[frm[f].id])); + }else { + choice[dataclass][frm[f].id].setChoiceByValue(data[frm[f].id]); + } + } else { + frm[f].value=data[frm[f].id]; + } + + } + } + + } + } +} + +function fillselectlist(obj,data,vidcol,vvalcol){ + var sellist = []; + obj.clearStore(); + sellist.push({value:"",label:""}); + if (data){ + for (var i in data){ + sellist.push({value:data[i][vidcol],label:data[i][vvalcol]}); + } + } + obj.setChoices(sellist, 'value', 'label', true); + return false; +} \ No newline at end of file diff --git a/bin/htdocs/js/moduleglobal.js b/bin/htdocs/js/moduleglobal.js new file mode 100644 index 0000000..1855b24 --- /dev/null +++ b/bin/htdocs/js/moduleglobal.js @@ -0,0 +1,24 @@ +document.addEventListener("DOMContentLoaded", function() { + console.log( "Iframe "+ location.pathname.substring(location.pathname.lastIndexOf("/")) +" ready!" ); + mpref.loadconfig(); + initpage(); +}); + +var mpref ={ + cfg: null, + getSearchParams: function (k){ + //alert(location.href); + var p={}; + console.log("params =>" + location.search); + location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(s,k,v){p[k]=v}); + return k?p[k]:p; + }, + loadconfig: function(){ + this.cfg = this.getSearchParams(); + //var page = location.pathname.substring(location.pathname.lastIndexOf("/")); + //page = page.replace(/\.html/,''); + //apppref.getpreference(page); + //appdb.dbfile = this.cfg.dbfile; + //appdb.url = decodeURIComponent(this.cfg.serviceurl) + 'sqlite/' + decodeURIComponent(this.cfg.dbfile); + } +} diff --git a/bin/htdocs/js/request.js b/bin/htdocs/js/request.js new file mode 100644 index 0000000..a303a86 --- /dev/null +++ b/bin/htdocs/js/request.js @@ -0,0 +1,149 @@ +var api = location.origin+'/api/'; +// if (location.pathname.indexOf('modules') > 0){ +// api = location.origin + location.pathname.substring(0, location.pathname.indexOf('module')) + 'api/'; +// } +console.log(api); +var req = { + multipartform: function(url,frmdata,callback=null){ + var ret = null; + var rdata = null; + var async = false; + if (callback){ + async=true; + } + + var request = new XMLHttpRequest(); + + console.log(frmdata); + var sendurl = api + url; + console.log("Multipart sending URL: " + "POST" + " => " +sendurl); + request.open("POST", sendurl, true); + 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; + //console.log(ret); + } + else { + ret = request.responseText; + } + if (async){ + callback(ret); + } + } else { + alert("ServerERROR:" + request.status + "\n" + request.responseText); + } + }; + request.onerror = function(){ + alert("Connection ERROR!\n" + url); + }; + + request.setRequestHeader('Content-Type','multipart/form-data'); + request.send(frmdata); + return ret; + }, + reqdata: function(method,url,data,callback=null){ + + var ret = null; + var rdata = null; + var async = false; + if (callback){ + async=true; + } + + 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(rdata); + //console.log("Data to send: " + decodeURIComponent(rdata)); + var sendurl = api + url; + if (method.toUpperCase() == 'GET'){ + sendurl = sendurl + '?' + rdata; + } + console.log("sending URL: " + method + " => " +sendurl + '?' + rdata); + request.open(method.toUpperCase(), sendurl, true); + 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){ + //console.log(xparse); + var xparse = JSON.parse(request.responseText); + ret = xparse.result; + //console.log(ret); + } + // 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("TEXT returned:" + ret); + } + ////console.log("data returned: " + request.responseText); + if (async){ + 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/bin/htdocs/js/sysconfig.js b/bin/htdocs/js/sysconfig.js new file mode 100644 index 0000000..4dd7b82 --- /dev/null +++ b/bin/htdocs/js/sysconfig.js @@ -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(''); + } + } + 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 += ''; + } + 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 += ''+ e +':' + data.activenet[e]+ '
'; + } + } + 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/bin/htdocs/vendor/choices/base.css b/bin/htdocs/vendor/choices/base.css new file mode 100644 index 0000000..2d090bd --- /dev/null +++ b/bin/htdocs/vendor/choices/base.css @@ -0,0 +1,191 @@ +/*============================================= += Generic styling = +=============================================*/ +* { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +*, +*:before, +*:after { + box-sizing: border-box; +} + +html, +body { + position: relative; + margin: 0; + width: 100%; + height: 100%; +} + +body { + font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; + font-size: 16px; + line-height: 1.4; + color: #FFFFFF; + background-color: #333; + overflow-x: hidden; +} + +label { + display: block; + margin-bottom: 8px; + font-size: 14px; + font-weight: 500; + cursor: pointer; +} + +p { + margin-top: 0; +} + +hr { + display: block; + margin: 30px 0; + border: 0; + border-bottom: 1px solid #eaeaea; + height: 1px; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin-top: 0; + margin-bottom: 12px; + font-weight: 400; + line-height: 1.2; +} + +a, +a:visited, +a:focus { + color: #FFFFFF; + text-decoration: none; + font-weight: 600; +} + +.form-control { + display: block; + width: 100%; + background-color: #f9f9f9; + padding: 12px; + border: 1px solid #ddd; + border-radius: 2.5px; + font-size: 14px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + margin-bottom: 24px; +} + +h1, +.h1 { + font-size: 32px; +} + +h2, +.h2 { + font-size: 24px; +} + +h3, +.h3 { + font-size: 20px; +} + +h4, +.h4 { + font-size: 18px; +} + +h5, +.h5 { + font-size: 16px; +} + +h6, +.h6 { + font-size: 14px; +} + +p { + margin-bottom: 8px; +} + +label + p { + margin-top: -4px; +} + +.container { + display: block; + margin: auto; + max-width: 40em; + padding: 48px; +} + +@media (max-width: 620px) { + .container { + padding: 0; + } +} + +.section { + background-color: #FFFFFF; + padding: 24px; + color: #333; +} + +.section a, +.section a:visited, +.section a:focus { + color: #00bcd4; +} + +.logo { + display: block; + margin-bottom: 12px; +} + +.logo__img { + width: 100%; + height: auto; + display: inline-block; + max-width: 100%; + vertical-align: top; + padding: 6px 0; +} + +.visible-ie { + display: none; +} + +.push-bottom { + margin-bottom: 24px; +} + +.zero-bottom { + margin-bottom: 0; +} + +.zero-top { + margin-top: 0; +} + +.text-center { + text-align: center; +} + +.is-hidden { + display: none; +} + +[data-test-hook] { + margin-bottom: 24px; +} + +/*===== End of Section comment block ======*/ diff --git a/bin/htdocs/vendor/choices/base.min.css b/bin/htdocs/vendor/choices/base.min.css new file mode 100644 index 0000000..7992056 --- /dev/null +++ b/bin/htdocs/vendor/choices/base.min.css @@ -0,0 +1 @@ +*{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,:after,:before{box-sizing:border-box}body,html{position:relative;margin:0;width:100%;height:100%}body{font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;font-size:16px;line-height:1.4;color:#fff;background-color:#333;overflow-x:hidden}hr,label{display:block}label,p{margin-bottom:8px}label{font-size:14px;font-weight:500;cursor:pointer}p{margin-top:0}hr{margin:30px 0;border:0;border-bottom:1px solid #eaeaea;height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:12px;font-weight:400;line-height:1.2}a,a:focus,a:visited{color:#fff;text-decoration:none;font-weight:600}.form-control{display:block;width:100%;background-color:#f9f9f9;padding:12px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-bottom:24px}.h1,h1{font-size:32px}.h2,h2{font-size:24px}.h3,h3{font-size:20px}.h4,h4{font-size:18px}.h5,h5{font-size:16px}.h6,h6{font-size:14px}label+p{margin-top:-4px}.container{display:block;margin:auto;max-width:40em;padding:48px}@media (max-width:620px){.container{padding:0}}.section{background-color:#fff;padding:24px;color:#333}.section a,.section a:focus,.section a:visited{color:#00bcd4}.logo{display:block;margin-bottom:12px}.logo__img{width:100%;height:auto;display:inline-block;max-width:100%;vertical-align:top;padding:6px 0}.visible-ie{display:none}.push-bottom{margin-bottom:24px}.zero-bottom{margin-bottom:0}.zero-top{margin-top:0}.text-center{text-align:center}.is-hidden{display:none}[data-test-hook]{margin-bottom:24px} \ No newline at end of file diff --git a/bin/htdocs/vendor/choices/choices.css b/bin/htdocs/vendor/choices/choices.css new file mode 100644 index 0000000..e3bc037 --- /dev/null +++ b/bin/htdocs/vendor/choices/choices.css @@ -0,0 +1,368 @@ +/*=============================== += Choices = +===============================*/ +.choices { + position: relative; + margin-bottom: 24px; + font-size: 16px; +} + +.choices:focus { + outline: none; +} + +.choices:last-child { + margin-bottom: 0; +} + +.choices.is-disabled .choices__inner, +.choices.is-disabled .choices__input { + background-color: #EAEAEA; + cursor: not-allowed; + user-select: none; +} + +.choices.is-disabled .choices__item { + cursor: not-allowed; +} + +.choices[data-type*="select-one"] { + cursor: pointer; +} + +.choices[data-type*="select-one"] .choices__inner { + padding-bottom: 7.5px; + +} + +.choices[data-type*="select-one"] .choices__input { + display: block; + width: 100%; + padding: 10px; + border-bottom: 1px solid #DDDDDD; + background-color: #FFFFFF; + margin: 0; +} + +.choices[data-type*="select-one"] .choices__button { + background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==); + padding: 0; + background-size: 8px; + position: absolute; + top: 50%; + right: 0; + margin-top: -10px; + margin-right: 25px; + height: 20px; + width: 20px; + /* border-radius: 10em; */ + opacity: .5; +} + +.choices[data-type*="select-one"] .choices__button:hover, .choices[data-type*="select-one"] .choices__button:focus { + opacity: 1; +} + +.choices[data-type*="select-one"] .choices__button:focus { + box-shadow: 0px 0px 0px 2px #00BCD4; +} + +.choices[data-type*="select-one"]:after { + content: ""; + height: 0; + width: 0; + border-style: solid; + border-color: #333333 transparent transparent transparent; + border-width: 5px; + position: absolute; + right: 11.5px; + top: 50%; + margin-top: -2.5px; + pointer-events: none; +} + +.choices[data-type*="select-one"].is-open:after { + border-color: transparent transparent #333333 transparent; + margin-top: -7.5px; +} + +.choices[data-type*="select-one"][dir="rtl"]:after { + left: 11.5px; + right: auto; +} + +.choices[data-type*="select-one"][dir="rtl"] .choices__button { + right: auto; + left: 0; + margin-left: 25px; + margin-right: 0; +} + +.choices[data-type*="select-multiple"] .choices__inner, +.choices[data-type*="text"] .choices__inner { + cursor: text; +} + +.choices[data-type*="select-multiple"] .choices__button, +.choices[data-type*="text"] .choices__button { + position: relative; + display: inline-block; + margin-top: 0; + margin-right: -4px; + margin-bottom: 0; + margin-left: 8px; + padding-left: 16px; + border-left: 1px solid #008fa1; + background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==); + background-size: 8px; + width: 8px; + line-height: 1; + opacity: .75; + border-radius: 0; +} + +.choices[data-type*="select-multiple"] .choices__button:hover, .choices[data-type*="select-multiple"] .choices__button:focus, +.choices[data-type*="text"] .choices__button:hover, +.choices[data-type*="text"] .choices__button:focus { + opacity: 1; +} + +.choices__inner { + display: inline-block; + vertical-align: top; + width: 100%; + background-color: #e8f0fe; + padding: 7.5px 7.5px 3.75px; + border: 1px solid #ccc; + /* border-radius: 2.5px; */ + /* font-size: 14px; */ + min-height: 44px; + overflow: hidden; + /* font-weight: bold; */ +} + +.is-focused .choices__inner, +.is-open .choices__inner { + border-color: #b7b7b7; +} + +.is-open .choices__inner { + /* border-radius: 2.5px 2.5px 0 0; */ +} + +.is-flipped.is-open .choices__inner { + /* border-radius: 0 0 2.5px 2.5px; */ +} + +.choices__list { + margin: 0; + padding-left: 0; + list-style: none; +} + +.choices__list--single { + display: inline-block; + padding: 4px 16px 4px 4px; + width: 100%; +} + +[dir="rtl"] .choices__list--single { + padding-right: 4px; + padding-left: 16px; +} + +.choices__list--single .choices__item { + width: 100%; +} + +.choices__list--multiple { + display: inline; +} + +.choices__list--multiple .choices__item { + display: inline-block; + vertical-align: middle; + /* border-radius: 20px; */ + padding: 4px 6px; + font-size: 16px; + height: 40px; + /* font-size: 12px; */ + /* font-weight: 500; */ + margin-right: 3.75px; + /* margin-bottom: 3.75px; */ + background-color: #607d8b; + border: 1px solid #607d8b; + color: #FFFFFF; + /* font-weightfont-weight: bold; */ + word-break: break-all; +} + +.choices__list--multiple .choices__item[data-deletable] { + padding-right: 5px; +} + +[dir="rtl"] .choices__list--multiple .choices__item { + margin-right: 0; + margin-left: 3.75px; +} + +.choices__list--multiple .choices__item.is-highlighted { + background-color: #00a5bb; + border: 1px solid #008fa1; +} + +.is-disabled .choices__list--multiple .choices__item { + background-color: #aaaaaa; + border: 1px solid #919191; +} + +.choices__list--dropdown { + display: none; + z-index: 1; + position: absolute; + width: 100%; + background-color: #FFFFFF; + border: 1px solid #DDDDDD; + top: 100%; + margin-top: -1px; + /* border-bottom-left-radius: 2.5px; */ + /* border-bottom-right-radius: 2.5px; */ + overflow: hidden; + word-break: break-all; +} + +.choices__list--dropdown.is-active { + display: block; +} + +.is-open .choices__list--dropdown { + border-color: #b7b7b7; +} + +.is-flipped .choices__list--dropdown { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: -1px; + /* border-radius: .25rem .25rem 0 0; */ +} + +.choices__list--dropdown .choices__list { + position: relative; + max-height: 300px; + overflow: auto; + -webkit-overflow-scrolling: touch; + will-change: scroll-position; +} + +.choices__list--dropdown .choices__item { + position: relative; + padding: 10px; + font-size: 14px; +} + +[dir="rtl"] .choices__list--dropdown .choices__item { + text-align: right; +} + +@media (min-width: 640px) { + .choices__list--dropdown .choices__item--selectable { + padding-right: 100px; + } + .choices__list--dropdown .choices__item--selectable:after { + content: attr(data-select-text); + font-size: 12px; + opacity: 0; + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + } + [dir="rtl"] .choices__list--dropdown .choices__item--selectable { + text-align: right; + padding-left: 100px; + padding-right: 10px; + } + [dir="rtl"] .choices__list--dropdown .choices__item--selectable:after { + right: auto; + left: 10px; + } +} + +.choices__list--dropdown .choices__item--selectable.is-highlighted { + background-color: #f2f2f2; +} + +.choices__list--dropdown .choices__item--selectable.is-highlighted:after { + opacity: .5; +} + +.choices__item { + cursor: default; +} + +.choices__item--selectable { + cursor: pointer; +} + +.choices__item--disabled { + cursor: not-allowed; + user-select: none; + opacity: .5; +} + +.choices__heading { + /* font-weight: 600; */ + font-size: 12px; + padding: 10px; + border-bottom: 1px solid #f7f7f7; + color: gray; +} + +.choices__button { + text-indent: -9999px; + -webkit-appearance: none; + appearance: none; + border: 0; + background-color: transparent; + background-repeat: no-repeat; + background-position: center; + cursor: pointer; +} + +.choices__button:focus { + outline: none; +} + +.choices__input { + display: inline-block; + vertical-align: baseline; + background-color: #f9f9f9; + font-size: 14px; + margin-bottom: 5px; + border: 0; + border-radius: 0; + max-width: 100%; + padding: 4px 0 4px 2px; +} + +.choices__input:focus { + outline: 0; +} + +[dir="rtl"] .choices__input { + padding-right: 2px; + padding-left: 0; +} + +.choices__placeholder { + opacity: .5; +} + +.choices__input.is-hidden, +.choices[data-type*="select-one"] .choices__input.is-hidden, +.choices[data-type*="select-multiple"] .choices__input.is-hidden { + display: none; +} + +/*===== End of Choices ======*/ diff --git a/bin/htdocs/vendor/choices/choices.js b/bin/htdocs/vendor/choices/choices.js new file mode 100644 index 0000000..93e8b4e --- /dev/null +++ b/bin/htdocs/vendor/choices/choices.js @@ -0,0 +1,6787 @@ +(function webpackUniversalModuleDefinition(root, factory) { + //CommonJS2 + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + //AMD + else if(typeof define === 'function' && define.amd) + define([], factory); + //CommonJS + else if(typeof exports === 'object') + exports["Choices"] = factory(); + //Window + else + root["Choices"] = factory(); +})(window, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = "/public/assets/scripts/"; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 9); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.diff = exports.cloneObject = exports.existsInArray = exports.isIE11 = exports.fetchFromObject = exports.getWindowHeight = exports.dispatchEvent = exports.sortByScore = exports.sortByAlpha = exports.calcWidthOfInput = exports.strToEl = exports.sanitise = exports.isScrolledIntoView = exports.getAdjacentEl = exports.findAncestorByAttrName = exports.wrap = exports.isElement = exports.isType = exports.getType = exports.generateId = exports.generateChars = exports.getRandomNumber = void 0; + +var _this = void 0; + +var getRandomNumber = function getRandomNumber(min, max) { + return Math.floor(Math.random() * (max - min) + min); +}; + +exports.getRandomNumber = getRandomNumber; + +var generateChars = function generateChars(length) { + var chars = ''; + + for (var i = 0; i < length; i++) { + var randomChar = getRandomNumber(0, 36); + chars += randomChar.toString(36); + } + + return chars; +}; + +exports.generateChars = generateChars; + +var generateId = function generateId(element, prefix) { + var id = element.id || element.name && "".concat(element.name, "-").concat(generateChars(2)) || generateChars(4); + id = id.replace(/(:|\.|\[|\]|,)/g, ''); + id = "".concat(prefix, "-").concat(id); + return id; +}; + +exports.generateId = generateId; + +var getType = function getType(obj) { + return Object.prototype.toString.call(obj).slice(8, -1); +}; + +exports.getType = getType; + +var isType = function isType(type, obj) { + return obj !== undefined && obj !== null && getType(obj) === type; +}; + +exports.isType = isType; + +var isElement = function isElement(element) { + return element instanceof Element; +}; + +exports.isElement = isElement; + +var wrap = function wrap(element) { + var wrapper = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document.createElement('div'); + + if (element.nextSibling) { + element.parentNode.insertBefore(wrapper, element.nextSibling); + } else { + element.parentNode.appendChild(wrapper); + } + + return wrapper.appendChild(element); +}; + +exports.wrap = wrap; + +var findAncestorByAttrName = function findAncestorByAttrName(el, attr) { + var target = el; + + while (target) { + if (target.hasAttribute(attr)) { + return target; + } + + target = target.parentElement; + } + + return null; +}; + +exports.findAncestorByAttrName = findAncestorByAttrName; + +var getAdjacentEl = function getAdjacentEl(startEl, className) { + var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1; + + if (!startEl || !className) { + return; + } + + var parent = startEl.parentNode.parentNode; + var children = Array.from(parent.querySelectorAll(className)); + var startPos = children.indexOf(startEl); + var operatorDirection = direction > 0 ? 1 : -1; + return children[startPos + operatorDirection]; +}; + +exports.getAdjacentEl = getAdjacentEl; + +var isScrolledIntoView = function isScrolledIntoView(el, parent) { + var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1; + + if (!el) { + return; + } + + var isVisible; + + if (direction > 0) { + // In view from bottom + isVisible = parent.scrollTop + parent.offsetHeight >= el.offsetTop + el.offsetHeight; + } else { + // In view from top + isVisible = el.offsetTop >= parent.scrollTop; + } + + return isVisible; +}; + +exports.isScrolledIntoView = isScrolledIntoView; + +var sanitise = function sanitise(value) { + if (!isType('String', value)) { + return value; + } + + return value.replace(/&/g, '&').replace(/>/g, '&rt;').replace(/".concat(sanitise(value), "
")); + testEl.style.position = 'absolute'; + testEl.style.padding = '0'; + testEl.style.top = '-9999px'; + testEl.style.left = '-9999px'; + testEl.style.width = 'auto'; + testEl.style.whiteSpace = 'pre'; + + if (document.body.contains(input) && window.getComputedStyle) { + var inputStyle = window.getComputedStyle(input); + + if (inputStyle) { + testEl.style.fontSize = inputStyle.fontSize; + testEl.style.fontFamily = inputStyle.fontFamily; + testEl.style.fontWeight = inputStyle.fontWeight; + testEl.style.fontStyle = inputStyle.fontStyle; + testEl.style.letterSpacing = inputStyle.letterSpacing; + testEl.style.textTransform = inputStyle.textTransform; + testEl.style.padding = inputStyle.padding; + } + } + + document.body.appendChild(testEl); + requestAnimationFrame(function () { + if (value && testEl.offsetWidth !== input.offsetWidth) { + width = testEl.offsetWidth + 4; + } + + document.body.removeChild(testEl); + callback.call(_this, "".concat(width, "px")); + }); + } else { + callback.call(_this, "".concat(width, "px")); + } +}; + +exports.calcWidthOfInput = calcWidthOfInput; + +var sortByAlpha = function sortByAlpha(a, b) { + var labelA = "".concat(a.label || a.value).toLowerCase(); + var labelB = "".concat(b.label || b.value).toLowerCase(); + + if (labelA < labelB) { + return -1; + } + + if (labelA > labelB) { + return 1; + } + + return 0; +}; + +exports.sortByAlpha = sortByAlpha; + +var sortByScore = function sortByScore(a, b) { + return a.score - b.score; +}; + +exports.sortByScore = sortByScore; + +var dispatchEvent = function dispatchEvent(element, type) { + var customArgs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; + var event = new CustomEvent(type, { + detail: customArgs, + bubbles: true, + cancelable: true + }); + return element.dispatchEvent(event); +}; + +exports.dispatchEvent = dispatchEvent; + +var getWindowHeight = function getWindowHeight() { + var body = document.body; + var html = document.documentElement; + return Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); +}; + +exports.getWindowHeight = getWindowHeight; + +var fetchFromObject = function fetchFromObject(object, path) { + var index = path.indexOf('.'); + + if (index > -1) { + return fetchFromObject(object[path.substring(0, index)], path.substr(index + 1)); + } + + return object[path]; +}; + +exports.fetchFromObject = fetchFromObject; + +var isIE11 = function isIE11() { + return !!(navigator.userAgent.match(/Trident/) && navigator.userAgent.match(/rv[ :]11/)); +}; + +exports.isIE11 = isIE11; + +var existsInArray = function existsInArray(array, value) { + var key = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'value'; + return array.some(function (item) { + if (isType('String', value)) { + return item[key] === value.trim(); + } + + return item[key] === value; + }); +}; + +exports.existsInArray = existsInArray; + +var cloneObject = function cloneObject(obj) { + return JSON.parse(JSON.stringify(obj)); +}; + +exports.cloneObject = cloneObject; + +var diff = function diff(a, b) { + var aKeys = Object.keys(a).sort(); + var bKeys = Object.keys(b).sort(); + return aKeys.filter(function (i) { + return bKeys.indexOf(i) < 0; + }); +}; + +exports.diff = diff; + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SCROLLING_SPEED = exports.KEY_CODES = exports.ACTION_TYPES = exports.EVENTS = exports.DEFAULT_CONFIG = exports.DEFAULT_CLASSNAMES = void 0; + +var _utils = __webpack_require__(0); + +var DEFAULT_CLASSNAMES = { + containerOuter: 'choices', + containerInner: 'choices__inner', + input: 'choices__input', + inputCloned: 'choices__input--cloned', + list: 'choices__list', + listItems: 'choices__list--multiple', + listSingle: 'choices__list--single', + listDropdown: 'choices__list--dropdown', + item: 'choices__item', + itemSelectable: 'choices__item--selectable', + itemDisabled: 'choices__item--disabled', + itemChoice: 'choices__item--choice', + placeholder: 'choices__placeholder', + group: 'choices__group', + groupHeading: 'choices__heading', + button: 'choices__button', + activeState: 'is-active', + focusState: 'is-focused', + openState: 'is-open', + disabledState: 'is-disabled', + highlightedState: 'is-highlighted', + hiddenState: 'is-hidden', + flippedState: 'is-flipped', + loadingState: 'is-loading', + noResults: 'has-no-results', + noChoices: 'has-no-choices' +}; +exports.DEFAULT_CLASSNAMES = DEFAULT_CLASSNAMES; +var DEFAULT_CONFIG = { + items: [], + choices: [], + silent: false, + renderChoiceLimit: -1, + maxItemCount: -1, + addItems: true, + addItemFilterFn: null, + removeItems: true, + removeItemButton: false, + editItems: false, + duplicateItemsAllowed: true, + delimiter: ',', + paste: true, + searchEnabled: true, + searchChoices: true, + searchFloor: 1, + searchResultLimit: 4, + searchFields: ['label', 'value'], + position: 'auto', + resetScrollPosition: true, + shouldSort: true, + shouldSortItems: false, + sortFn: _utils.sortByAlpha, + placeholder: true, + placeholderValue: null, + searchPlaceholderValue: null, + prependValue: null, + appendValue: null, + renderSelectedChoices: 'auto', + loadingText: 'Loading...', + noResultsText: 'No results found', + noChoicesText: 'No choices to choose from', + itemSelectText: 'Press to select', + uniqueItemText: 'Only unique values can be added', + customAddItemText: 'Only values matching specific conditions can be added', + addItemText: function addItemText(value) { + return "Press Enter to add \"".concat((0, _utils.sanitise)(value), "\""); + }, + maxItemText: function maxItemText(maxItemCount) { + return "Only ".concat(maxItemCount, " values can be added"); + }, + itemComparer: function itemComparer(choice, item) { + return choice === item; + }, + fuseOptions: { + includeScore: true + }, + callbackOnInit: null, + callbackOnCreateTemplates: null, + classNames: DEFAULT_CLASSNAMES +}; +exports.DEFAULT_CONFIG = DEFAULT_CONFIG; +var EVENTS = { + showDropdown: 'showDropdown', + hideDropdown: 'hideDropdown', + change: 'change', + choice: 'choice', + search: 'search', + addItem: 'addItem', + removeItem: 'removeItem', + highlightItem: 'highlightItem', + highlightChoice: 'highlightChoice' +}; +exports.EVENTS = EVENTS; +var ACTION_TYPES = { + ADD_CHOICE: 'ADD_CHOICE', + FILTER_CHOICES: 'FILTER_CHOICES', + ACTIVATE_CHOICES: 'ACTIVATE_CHOICES', + CLEAR_CHOICES: 'CLEAR_CHOICES', + ADD_GROUP: 'ADD_GROUP', + ADD_ITEM: 'ADD_ITEM', + REMOVE_ITEM: 'REMOVE_ITEM', + HIGHLIGHT_ITEM: 'HIGHLIGHT_ITEM', + CLEAR_ALL: 'CLEAR_ALL' +}; +exports.ACTION_TYPES = ACTION_TYPES; +var KEY_CODES = { + BACK_KEY: 46, + DELETE_KEY: 8, + ENTER_KEY: 13, + A_KEY: 65, + ESC_KEY: 27, + UP_KEY: 38, + DOWN_KEY: 40, + PAGE_UP_KEY: 33, + PAGE_DOWN_KEY: 34 +}; +exports.KEY_CODES = KEY_CODES; +var SCROLLING_SPEED = 4; +exports.SCROLLING_SPEED = SCROLLING_SPEED; + +/***/ }), +/* 2 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* WEBPACK VAR INJECTION */(function(global, module) {/* harmony import */ var _ponyfill_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7); +/* global window */ + + +var root; + +if (typeof self !== 'undefined') { + root = self; +} else if (typeof window !== 'undefined') { + root = window; +} else if (typeof global !== 'undefined') { + root = global; +} else if (true) { + root = module; +} else {} + +var result = Object(_ponyfill_js__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"])(root); +/* harmony default export */ __webpack_exports__["a"] = (result); + +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(3), __webpack_require__(14)(module))) + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || new Function("return this")(); +} catch (e) { + // This works if the window reference is available + if (typeof window === "object") g = window; +} + +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} + +module.exports = g; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _utils = __webpack_require__(0); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var WrappedElement = +/*#__PURE__*/ +function () { + function WrappedElement(_ref) { + var element = _ref.element, + classNames = _ref.classNames; + + _classCallCheck(this, WrappedElement); + + Object.assign(this, { + element: element, + classNames: classNames + }); + + if (!(0, _utils.isElement)(element)) { + throw new TypeError('Invalid element passed'); + } + + this.isDisabled = false; + } + + _createClass(WrappedElement, [{ + key: "conceal", + value: function conceal() { + // Hide passed input + this.element.classList.add(this.classNames.input); + this.element.classList.add(this.classNames.hiddenState); // Remove element from tab index + + this.element.tabIndex = '-1'; // Backup original styles if any + + var origStyle = this.element.getAttribute('style'); + + if (origStyle) { + this.element.setAttribute('data-choice-orig-style', origStyle); + } + + this.element.setAttribute('aria-hidden', 'true'); + this.element.setAttribute('data-choice', 'active'); + } + }, { + key: "reveal", + value: function reveal() { + // Reinstate passed element + this.element.classList.remove(this.classNames.input); + this.element.classList.remove(this.classNames.hiddenState); + this.element.removeAttribute('tabindex'); // Recover original styles if any + + var origStyle = this.element.getAttribute('data-choice-orig-style'); + + if (origStyle) { + this.element.removeAttribute('data-choice-orig-style'); + this.element.setAttribute('style', origStyle); + } else { + this.element.removeAttribute('style'); + } + + this.element.removeAttribute('aria-hidden'); + this.element.removeAttribute('data-choice'); // Re-assign values - this is weird, I know + + this.element.value = this.element.value; + } + }, { + key: "enable", + value: function enable() { + this.element.removeAttribute('disabled'); + this.element.disabled = false; + this.isDisabled = false; + } + }, { + key: "disable", + value: function disable() { + this.element.setAttribute('disabled', ''); + this.element.disabled = true; + this.isDisabled = true; + } + }, { + key: "triggerEvent", + value: function triggerEvent(eventType, data) { + (0, _utils.dispatchEvent)(this.element, eventType, data); + } + }, { + key: "value", + get: function get() { + return this.element.value; + } + }]); + + return WrappedElement; +}(); + +exports.default = WrappedElement; + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.TEMPLATES = void 0; + +var _classnames = _interopRequireDefault(__webpack_require__(27)); + +var _utils = __webpack_require__(0); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +var TEMPLATES = { + containerOuter: function containerOuter(globalClasses, direction, isSelectElement, isSelectOneElement, searchEnabled, passedElementType) { + var tabIndex = isSelectOneElement ? 'tabindex="0"' : ''; + var role = isSelectElement ? 'role="listbox"' : ''; + var ariaAutoComplete = ''; + + if (isSelectElement && searchEnabled) { + role = 'role="combobox"'; + ariaAutoComplete = 'aria-autocomplete="list"'; + } + + return (0, _utils.strToEl)("\n \n \n ")); + }, + containerInner: function containerInner(globalClasses) { + return (0, _utils.strToEl)("\n
\n ")); + }, + itemList: function itemList(globalClasses, isSelectOneElement) { + var _classNames; + + var localClasses = (0, _classnames.default)(globalClasses.list, (_classNames = {}, _defineProperty(_classNames, globalClasses.listSingle, isSelectOneElement), _defineProperty(_classNames, globalClasses.listItems, !isSelectOneElement), _classNames)); + return (0, _utils.strToEl)("\n
\n ")); + }, + placeholder: function placeholder(globalClasses, value) { + return (0, _utils.strToEl)("\n
\n ").concat(value, "\n
\n ")); + }, + item: function item(globalClasses, data, removeItemButton) { + var _classNames2; + + var ariaSelected = data.active ? 'aria-selected="true"' : ''; + var ariaDisabled = data.disabled ? 'aria-disabled="true"' : ''; + var localClasses = (0, _classnames.default)(globalClasses.item, (_classNames2 = {}, _defineProperty(_classNames2, globalClasses.highlightedState, data.highlighted), _defineProperty(_classNames2, globalClasses.itemSelectable, !data.highlighted), _defineProperty(_classNames2, globalClasses.placeholder, data.placeholder), _classNames2)); + + if (removeItemButton) { + var _classNames3; + + localClasses = (0, _classnames.default)(globalClasses.item, (_classNames3 = {}, _defineProperty(_classNames3, globalClasses.highlightedState, data.highlighted), _defineProperty(_classNames3, globalClasses.itemSelectable, !data.disabled), _defineProperty(_classNames3, globalClasses.placeholder, data.placeholder), _classNames3)); + return (0, _utils.strToEl)("\n \n ").concat(data.label, "\n Remove item\n \n \n ")); + } + + return (0, _utils.strToEl)("\n \n ").concat(data.label, "\n \n ")); + }, + choiceList: function choiceList(globalClasses, isSelectOneElement) { + var ariaMultiSelectable = !isSelectOneElement ? 'aria-multiselectable="true"' : ''; + return (0, _utils.strToEl)("\n \n \n ")); + }, + choiceGroup: function choiceGroup(globalClasses, data) { + var ariaDisabled = data.disabled ? 'aria-disabled="true"' : ''; + var localClasses = (0, _classnames.default)(globalClasses.group, _defineProperty({}, globalClasses.itemDisabled, data.disabled)); + return (0, _utils.strToEl)("\n \n
").concat(data.value, "
\n \n ")); + }, + choice: function choice(globalClasses, data, itemSelectText) { + var _classNames5; + + var role = data.groupId > 0 ? 'role="treeitem"' : 'role="option"'; + var localClasses = (0, _classnames.default)(globalClasses.item, globalClasses.itemChoice, (_classNames5 = {}, _defineProperty(_classNames5, globalClasses.itemDisabled, data.disabled), _defineProperty(_classNames5, globalClasses.itemSelectable, !data.disabled), _defineProperty(_classNames5, globalClasses.placeholder, data.placeholder), _classNames5)); + return (0, _utils.strToEl)("\n \n ").concat(data.label, "\n \n ")); + }, + input: function input(globalClasses) { + var localClasses = (0, _classnames.default)(globalClasses.input, globalClasses.inputCloned); + return (0, _utils.strToEl)("\n \n ")); + }, + dropdown: function dropdown(globalClasses) { + var localClasses = (0, _classnames.default)(globalClasses.list, globalClasses.listDropdown); + return (0, _utils.strToEl)("\n \n \n ")); + }, + notice: function notice(globalClasses, label) { + var _classNames6; + + var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; + var localClasses = (0, _classnames.default)(globalClasses.item, globalClasses.itemChoice, (_classNames6 = {}, _defineProperty(_classNames6, globalClasses.noResults, type === 'no-results'), _defineProperty(_classNames6, globalClasses.noChoices, type === 'no-choices'), _classNames6)); + return (0, _utils.strToEl)("\n
\n ").concat(label, "\n
\n ")); + }, + option: function option(data) { + return (0, _utils.strToEl)("\n \n ")); + } +}; +exports.TEMPLATES = TEMPLATES; +var _default = TEMPLATES; +exports.default = _default; + +/***/ }), +/* 6 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); + +// EXTERNAL MODULE: ./node_modules/lodash-es/_freeGlobal.js +var _freeGlobal = __webpack_require__(8); + +// CONCATENATED MODULE: ./node_modules/lodash-es/_root.js + + +/** Detect free variable `self`. */ +var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + +/** Used as a reference to the global object. */ +var root = _freeGlobal["a" /* default */] || freeSelf || Function('return this')(); + +/* harmony default export */ var _root = (root); + +// CONCATENATED MODULE: ./node_modules/lodash-es/_Symbol.js + + +/** Built-in value references. */ +var Symbol = _root.Symbol; + +/* harmony default export */ var _Symbol = (Symbol); + +// CONCATENATED MODULE: ./node_modules/lodash-es/_getRawTag.js + + +/** Used for built-in method references. */ +var objectProto = Object.prototype; + +/** Used to check objects for own properties. */ +var _getRawTag_hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var nativeObjectToString = objectProto.toString; + +/** Built-in value references. */ +var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined; + +/** + * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the raw `toStringTag`. + */ +function getRawTag(value) { + var isOwn = _getRawTag_hasOwnProperty.call(value, symToStringTag), + tag = value[symToStringTag]; + + try { + value[symToStringTag] = undefined; + var unmasked = true; + } catch (e) {} + + var result = nativeObjectToString.call(value); + if (unmasked) { + if (isOwn) { + value[symToStringTag] = tag; + } else { + delete value[symToStringTag]; + } + } + return result; +} + +/* harmony default export */ var _getRawTag = (getRawTag); + +// CONCATENATED MODULE: ./node_modules/lodash-es/_objectToString.js +/** Used for built-in method references. */ +var _objectToString_objectProto = Object.prototype; + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var _objectToString_nativeObjectToString = _objectToString_objectProto.toString; + +/** + * Converts `value` to a string using `Object.prototype.toString`. + * + * @private + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + */ +function objectToString(value) { + return _objectToString_nativeObjectToString.call(value); +} + +/* harmony default export */ var _objectToString = (objectToString); + +// CONCATENATED MODULE: ./node_modules/lodash-es/_baseGetTag.js + + + + +/** `Object#toString` result references. */ +var nullTag = '[object Null]', + undefinedTag = '[object Undefined]'; + +/** Built-in value references. */ +var _baseGetTag_symToStringTag = _Symbol ? _Symbol.toStringTag : undefined; + +/** + * The base implementation of `getTag` without fallbacks for buggy environments. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ +function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; + } + return (_baseGetTag_symToStringTag && _baseGetTag_symToStringTag in Object(value)) + ? _getRawTag(value) + : _objectToString(value); +} + +/* harmony default export */ var _baseGetTag = (baseGetTag); + +// CONCATENATED MODULE: ./node_modules/lodash-es/_overArg.js +/** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ +function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; +} + +/* harmony default export */ var _overArg = (overArg); + +// CONCATENATED MODULE: ./node_modules/lodash-es/_getPrototype.js + + +/** Built-in value references. */ +var getPrototype = _overArg(Object.getPrototypeOf, Object); + +/* harmony default export */ var _getPrototype = (getPrototype); + +// CONCATENATED MODULE: ./node_modules/lodash-es/isObjectLike.js +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return value != null && typeof value == 'object'; +} + +/* harmony default export */ var lodash_es_isObjectLike = (isObjectLike); + +// CONCATENATED MODULE: ./node_modules/lodash-es/isPlainObject.js + + + + +/** `Object#toString` result references. */ +var objectTag = '[object Object]'; + +/** Used for built-in method references. */ +var funcProto = Function.prototype, + isPlainObject_objectProto = Object.prototype; + +/** Used to resolve the decompiled source of functions. */ +var funcToString = funcProto.toString; + +/** Used to check objects for own properties. */ +var isPlainObject_hasOwnProperty = isPlainObject_objectProto.hasOwnProperty; + +/** Used to infer the `Object` constructor. */ +var objectCtorString = funcToString.call(Object); + +/** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @static + * @memberOf _ + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ +function isPlainObject(value) { + if (!lodash_es_isObjectLike(value) || _baseGetTag(value) != objectTag) { + return false; + } + var proto = _getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = isPlainObject_hasOwnProperty.call(proto, 'constructor') && proto.constructor; + return typeof Ctor == 'function' && Ctor instanceof Ctor && + funcToString.call(Ctor) == objectCtorString; +} + +/* harmony default export */ var lodash_es_isPlainObject = (isPlainObject); + +// EXTERNAL MODULE: ./node_modules/symbol-observable/es/index.js +var es = __webpack_require__(2); + +// CONCATENATED MODULE: ./node_modules/redux/es/createStore.js + + + +/** + * These are private action types reserved by Redux. + * For any unknown actions, you must return the current state. + * If the current state is undefined, you must return the initial state. + * Do not reference these action types directly in your code. + */ +var ActionTypes = { + INIT: '@@redux/INIT' + + /** + * Creates a Redux store that holds the state tree. + * The only way to change the data in the store is to call `dispatch()` on it. + * + * There should only be a single store in your app. To specify how different + * parts of the state tree respond to actions, you may combine several reducers + * into a single reducer function by using `combineReducers`. + * + * @param {Function} reducer A function that returns the next state tree, given + * the current state tree and the action to handle. + * + * @param {any} [preloadedState] The initial state. You may optionally specify it + * to hydrate the state from the server in universal apps, or to restore a + * previously serialized user session. + * If you use `combineReducers` to produce the root reducer function, this must be + * an object with the same shape as `combineReducers` keys. + * + * @param {Function} [enhancer] The store enhancer. You may optionally specify it + * to enhance the store with third-party capabilities such as middleware, + * time travel, persistence, etc. The only store enhancer that ships with Redux + * is `applyMiddleware()`. + * + * @returns {Store} A Redux store that lets you read the state, dispatch actions + * and subscribe to changes. + */ +};function createStore_createStore(reducer, preloadedState, enhancer) { + var _ref2; + + if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { + enhancer = preloadedState; + preloadedState = undefined; + } + + if (typeof enhancer !== 'undefined') { + if (typeof enhancer !== 'function') { + throw new Error('Expected the enhancer to be a function.'); + } + + return enhancer(createStore_createStore)(reducer, preloadedState); + } + + if (typeof reducer !== 'function') { + throw new Error('Expected the reducer to be a function.'); + } + + var currentReducer = reducer; + var currentState = preloadedState; + var currentListeners = []; + var nextListeners = currentListeners; + var isDispatching = false; + + function ensureCanMutateNextListeners() { + if (nextListeners === currentListeners) { + nextListeners = currentListeners.slice(); + } + } + + /** + * Reads the state tree managed by the store. + * + * @returns {any} The current state tree of your application. + */ + function getState() { + return currentState; + } + + /** + * Adds a change listener. It will be called any time an action is dispatched, + * and some part of the state tree may potentially have changed. You may then + * call `getState()` to read the current state tree inside the callback. + * + * You may call `dispatch()` from a change listener, with the following + * caveats: + * + * 1. The subscriptions are snapshotted just before every `dispatch()` call. + * If you subscribe or unsubscribe while the listeners are being invoked, this + * will not have any effect on the `dispatch()` that is currently in progress. + * However, the next `dispatch()` call, whether nested or not, will use a more + * recent snapshot of the subscription list. + * + * 2. The listener should not expect to see all state changes, as the state + * might have been updated multiple times during a nested `dispatch()` before + * the listener is called. It is, however, guaranteed that all subscribers + * registered before the `dispatch()` started will be called with the latest + * state by the time it exits. + * + * @param {Function} listener A callback to be invoked on every dispatch. + * @returns {Function} A function to remove this change listener. + */ + function subscribe(listener) { + if (typeof listener !== 'function') { + throw new Error('Expected listener to be a function.'); + } + + var isSubscribed = true; + + ensureCanMutateNextListeners(); + nextListeners.push(listener); + + return function unsubscribe() { + if (!isSubscribed) { + return; + } + + isSubscribed = false; + + ensureCanMutateNextListeners(); + var index = nextListeners.indexOf(listener); + nextListeners.splice(index, 1); + }; + } + + /** + * Dispatches an action. It is the only way to trigger a state change. + * + * The `reducer` function, used to create the store, will be called with the + * current state tree and the given `action`. Its return value will + * be considered the **next** state of the tree, and the change listeners + * will be notified. + * + * The base implementation only supports plain object actions. If you want to + * dispatch a Promise, an Observable, a thunk, or something else, you need to + * wrap your store creating function into the corresponding middleware. For + * example, see the documentation for the `redux-thunk` package. Even the + * middleware will eventually dispatch plain object actions using this method. + * + * @param {Object} action A plain object representing “what changed”. It is + * a good idea to keep actions serializable so you can record and replay user + * sessions, or use the time travelling `redux-devtools`. An action must have + * a `type` property which may not be `undefined`. It is a good idea to use + * string constants for action types. + * + * @returns {Object} For convenience, the same action object you dispatched. + * + * Note that, if you use a custom middleware, it may wrap `dispatch()` to + * return something else (for example, a Promise you can await). + */ + function dispatch(action) { + if (!lodash_es_isPlainObject(action)) { + throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.'); + } + + if (typeof action.type === 'undefined') { + throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?'); + } + + if (isDispatching) { + throw new Error('Reducers may not dispatch actions.'); + } + + try { + isDispatching = true; + currentState = currentReducer(currentState, action); + } finally { + isDispatching = false; + } + + var listeners = currentListeners = nextListeners; + for (var i = 0; i < listeners.length; i++) { + var listener = listeners[i]; + listener(); + } + + return action; + } + + /** + * Replaces the reducer currently used by the store to calculate the state. + * + * You might need this if your app implements code splitting and you want to + * load some of the reducers dynamically. You might also need this if you + * implement a hot reloading mechanism for Redux. + * + * @param {Function} nextReducer The reducer for the store to use instead. + * @returns {void} + */ + function replaceReducer(nextReducer) { + if (typeof nextReducer !== 'function') { + throw new Error('Expected the nextReducer to be a function.'); + } + + currentReducer = nextReducer; + dispatch({ type: ActionTypes.INIT }); + } + + /** + * Interoperability point for observable/reactive libraries. + * @returns {observable} A minimal observable of state changes. + * For more information, see the observable proposal: + * https://github.com/tc39/proposal-observable + */ + function observable() { + var _ref; + + var outerSubscribe = subscribe; + return _ref = { + /** + * The minimal observable subscription method. + * @param {Object} observer Any object that can be used as an observer. + * The observer object should have a `next` method. + * @returns {subscription} An object with an `unsubscribe` method that can + * be used to unsubscribe the observable from the store, and prevent further + * emission of values from the observable. + */ + subscribe: function subscribe(observer) { + if (typeof observer !== 'object') { + throw new TypeError('Expected the observer to be an object.'); + } + + function observeState() { + if (observer.next) { + observer.next(getState()); + } + } + + observeState(); + var unsubscribe = outerSubscribe(observeState); + return { unsubscribe: unsubscribe }; + } + }, _ref[es["a" /* default */]] = function () { + return this; + }, _ref; + } + + // When a store is created, an "INIT" action is dispatched so that every + // reducer returns their initial state. This effectively populates + // the initial state tree. + dispatch({ type: ActionTypes.INIT }); + + return _ref2 = { + dispatch: dispatch, + subscribe: subscribe, + getState: getState, + replaceReducer: replaceReducer + }, _ref2[es["a" /* default */]] = observable, _ref2; +} +// CONCATENATED MODULE: ./node_modules/redux/es/utils/warning.js +/** + * Prints a warning in the console if it exists. + * + * @param {String} message The warning message. + * @returns {void} + */ +function warning(message) { + /* eslint-disable no-console */ + if (typeof console !== 'undefined' && typeof console.error === 'function') { + console.error(message); + } + /* eslint-enable no-console */ + try { + // This error was thrown as a convenience so that if you enable + // "break on all exceptions" in your console, + // it would pause the execution at this line. + throw new Error(message); + /* eslint-disable no-empty */ + } catch (e) {} + /* eslint-enable no-empty */ +} +// CONCATENATED MODULE: ./node_modules/redux/es/combineReducers.js + + + + +function getUndefinedStateErrorMessage(key, action) { + var actionType = action && action.type; + var actionName = actionType && '"' + actionType.toString() + '"' || 'an action'; + + return 'Given action ' + actionName + ', reducer "' + key + '" returned undefined. ' + 'To ignore an action, you must explicitly return the previous state. ' + 'If you want this reducer to hold no value, you can return null instead of undefined.'; +} + +function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) { + var reducerKeys = Object.keys(reducers); + var argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer'; + + if (reducerKeys.length === 0) { + return 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.'; + } + + if (!lodash_es_isPlainObject(inputState)) { + return 'The ' + argumentName + ' has unexpected type of "' + {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + '". Expected argument to be an object with the following ' + ('keys: "' + reducerKeys.join('", "') + '"'); + } + + var unexpectedKeys = Object.keys(inputState).filter(function (key) { + return !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]; + }); + + unexpectedKeys.forEach(function (key) { + unexpectedKeyCache[key] = true; + }); + + if (unexpectedKeys.length > 0) { + return 'Unexpected ' + (unexpectedKeys.length > 1 ? 'keys' : 'key') + ' ' + ('"' + unexpectedKeys.join('", "') + '" found in ' + argumentName + '. ') + 'Expected to find one of the known reducer keys instead: ' + ('"' + reducerKeys.join('", "') + '". Unexpected keys will be ignored.'); + } +} + +function assertReducerShape(reducers) { + Object.keys(reducers).forEach(function (key) { + var reducer = reducers[key]; + var initialState = reducer(undefined, { type: ActionTypes.INIT }); + + if (typeof initialState === 'undefined') { + throw new Error('Reducer "' + key + '" returned undefined during initialization. ' + 'If the state passed to the reducer is undefined, you must ' + 'explicitly return the initial state. The initial state may ' + 'not be undefined. If you don\'t want to set a value for this reducer, ' + 'you can use null instead of undefined.'); + } + + var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.'); + if (typeof reducer(undefined, { type: type }) === 'undefined') { + throw new Error('Reducer "' + key + '" returned undefined when probed with a random type. ' + ('Don\'t try to handle ' + ActionTypes.INIT + ' or other actions in "redux/*" ') + 'namespace. They are considered private. Instead, you must return the ' + 'current state for any unknown actions, unless it is undefined, ' + 'in which case you must return the initial state, regardless of the ' + 'action type. The initial state may not be undefined, but can be null.'); + } + }); +} + +/** + * Turns an object whose values are different reducer functions, into a single + * reducer function. It will call every child reducer, and gather their results + * into a single state object, whose keys correspond to the keys of the passed + * reducer functions. + * + * @param {Object} reducers An object whose values correspond to different + * reducer functions that need to be combined into one. One handy way to obtain + * it is to use ES6 `import * as reducers` syntax. The reducers may never return + * undefined for any action. Instead, they should return their initial state + * if the state passed to them was undefined, and the current state for any + * unrecognized action. + * + * @returns {Function} A reducer function that invokes every reducer inside the + * passed object, and builds a state object with the same shape. + */ +function combineReducers(reducers) { + var reducerKeys = Object.keys(reducers); + var finalReducers = {}; + for (var i = 0; i < reducerKeys.length; i++) { + var key = reducerKeys[i]; + + if (false) {} + + if (typeof reducers[key] === 'function') { + finalReducers[key] = reducers[key]; + } + } + var finalReducerKeys = Object.keys(finalReducers); + + var unexpectedKeyCache = void 0; + if (false) {} + + var shapeAssertionError = void 0; + try { + assertReducerShape(finalReducers); + } catch (e) { + shapeAssertionError = e; + } + + return function combination() { + var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var action = arguments[1]; + + if (shapeAssertionError) { + throw shapeAssertionError; + } + + if (false) { var warningMessage; } + + var hasChanged = false; + var nextState = {}; + for (var _i = 0; _i < finalReducerKeys.length; _i++) { + var _key = finalReducerKeys[_i]; + var reducer = finalReducers[_key]; + var previousStateForKey = state[_key]; + var nextStateForKey = reducer(previousStateForKey, action); + if (typeof nextStateForKey === 'undefined') { + var errorMessage = getUndefinedStateErrorMessage(_key, action); + throw new Error(errorMessage); + } + nextState[_key] = nextStateForKey; + hasChanged = hasChanged || nextStateForKey !== previousStateForKey; + } + return hasChanged ? nextState : state; + }; +} +// CONCATENATED MODULE: ./node_modules/redux/es/bindActionCreators.js +function bindActionCreator(actionCreator, dispatch) { + return function () { + return dispatch(actionCreator.apply(undefined, arguments)); + }; +} + +/** + * Turns an object whose values are action creators, into an object with the + * same keys, but with every function wrapped into a `dispatch` call so they + * may be invoked directly. This is just a convenience method, as you can call + * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. + * + * For convenience, you can also pass a single function as the first argument, + * and get a function in return. + * + * @param {Function|Object} actionCreators An object whose values are action + * creator functions. One handy way to obtain it is to use ES6 `import * as` + * syntax. You may also pass a single function. + * + * @param {Function} dispatch The `dispatch` function available on your Redux + * store. + * + * @returns {Function|Object} The object mimicking the original object, but with + * every action creator wrapped into the `dispatch` call. If you passed a + * function as `actionCreators`, the return value will also be a single + * function. + */ +function bindActionCreators(actionCreators, dispatch) { + if (typeof actionCreators === 'function') { + return bindActionCreator(actionCreators, dispatch); + } + + if (typeof actionCreators !== 'object' || actionCreators === null) { + throw new Error('bindActionCreators expected an object or a function, instead received ' + (actionCreators === null ? 'null' : typeof actionCreators) + '. ' + 'Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?'); + } + + var keys = Object.keys(actionCreators); + var boundActionCreators = {}; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var actionCreator = actionCreators[key]; + if (typeof actionCreator === 'function') { + boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); + } + } + return boundActionCreators; +} +// CONCATENATED MODULE: ./node_modules/redux/es/compose.js +/** + * Composes single-argument functions from right to left. The rightmost + * function can take multiple arguments as it provides the signature for + * the resulting composite function. + * + * @param {...Function} funcs The functions to compose. + * @returns {Function} A function obtained by composing the argument functions + * from right to left. For example, compose(f, g, h) is identical to doing + * (...args) => f(g(h(...args))). + */ + +function compose() { + for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) { + funcs[_key] = arguments[_key]; + } + + if (funcs.length === 0) { + return function (arg) { + return arg; + }; + } + + if (funcs.length === 1) { + return funcs[0]; + } + + return funcs.reduce(function (a, b) { + return function () { + return a(b.apply(undefined, arguments)); + }; + }); +} +// CONCATENATED MODULE: ./node_modules/redux/es/applyMiddleware.js +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + + + +/** + * Creates a store enhancer that applies middleware to the dispatch method + * of the Redux store. This is handy for a variety of tasks, such as expressing + * asynchronous actions in a concise manner, or logging every action payload. + * + * See `redux-thunk` package as an example of the Redux middleware. + * + * Because middleware is potentially asynchronous, this should be the first + * store enhancer in the composition chain. + * + * Note that each middleware will be given the `dispatch` and `getState` functions + * as named arguments. + * + * @param {...Function} middlewares The middleware chain to be applied. + * @returns {Function} A store enhancer applying the middleware. + */ +function applyMiddleware() { + for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) { + middlewares[_key] = arguments[_key]; + } + + return function (createStore) { + return function (reducer, preloadedState, enhancer) { + var store = createStore(reducer, preloadedState, enhancer); + var _dispatch = store.dispatch; + var chain = []; + + var middlewareAPI = { + getState: store.getState, + dispatch: function dispatch(action) { + return _dispatch(action); + } + }; + chain = middlewares.map(function (middleware) { + return middleware(middlewareAPI); + }); + _dispatch = compose.apply(undefined, chain)(store.dispatch); + + return _extends({}, store, { + dispatch: _dispatch + }); + }; + }; +} +// CONCATENATED MODULE: ./node_modules/redux/es/index.js +/* concated harmony reexport createStore */__webpack_require__.d(__webpack_exports__, "createStore", function() { return createStore_createStore; }); +/* concated harmony reexport combineReducers */__webpack_require__.d(__webpack_exports__, "combineReducers", function() { return combineReducers; }); +/* concated harmony reexport bindActionCreators */__webpack_require__.d(__webpack_exports__, "bindActionCreators", function() { return bindActionCreators; }); +/* concated harmony reexport applyMiddleware */__webpack_require__.d(__webpack_exports__, "applyMiddleware", function() { return applyMiddleware; }); +/* concated harmony reexport compose */__webpack_require__.d(__webpack_exports__, "compose", function() { return compose; }); + + + + + + + +/* +* This is a dummy function to check if the function name has been altered by minification. +* If the function has been minified and NODE_ENV !== 'production', warn the user. +*/ +function isCrushed() {} + +if (false) {} + + + +/***/ }), +/* 7 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return symbolObservablePonyfill; }); +function symbolObservablePonyfill(root) { + var result; + var Symbol = root.Symbol; + + if (typeof Symbol === 'function') { + if (Symbol.observable) { + result = Symbol.observable; + } else { + result = Symbol('observable'); + Symbol.observable = result; + } + } else { + result = '@@observable'; + } + + return result; +}; + + +/***/ }), +/* 8 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) {/** Detect free variable `global` from Node.js. */ +var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + +/* harmony default export */ __webpack_exports__["a"] = (freeGlobal); + +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(3))) + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__(10); + + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var _fuse = _interopRequireDefault(__webpack_require__(11)); + +var _deepmerge = _interopRequireDefault(__webpack_require__(12)); + +var _store = _interopRequireDefault(__webpack_require__(13)); + +var _components = __webpack_require__(20); + +var _constants = __webpack_require__(1); + +var _templates = __webpack_require__(5); + +var _choices = __webpack_require__(28); + +var _items = __webpack_require__(29); + +var _groups = __webpack_require__(30); + +var _misc = __webpack_require__(31); + +var _general = __webpack_require__(32); + +var _utils = __webpack_require__(0); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +/** + * Choices + * @author Josh Johnson + */ +var Choices = +/*#__PURE__*/ +function () { + function Choices() { + var element = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '[data-choice]'; + var userConfig = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, Choices); + + if ((0, _utils.isType)('String', element)) { + var elements = Array.from(document.querySelectorAll(element)); // If there are multiple elements, create a new instance + // for each element besides the first one (as that already has an instance) + + if (elements.length > 1) { + return this._generateInstances(elements, userConfig); + } + } + + this.config = _deepmerge.default.all([_constants.DEFAULT_CONFIG, Choices.userDefaults, userConfig], // When merging array configs, replace with a copy of the userConfig array, + // instead of concatenating with the default array + { + arrayMerge: function arrayMerge(destinationArray, sourceArray) { + return [].concat(sourceArray); + } + }); + var invalidConfigOptions = (0, _utils.diff)(this.config, _constants.DEFAULT_CONFIG); + + if (invalidConfigOptions.length) { + console.warn('Unknown config option(s) passed', invalidConfigOptions.join(', ')); + } + + if (!['auto', 'always'].includes(this.config.renderSelectedChoices)) { + this.config.renderSelectedChoices = 'auto'; + } // Retrieve triggering element (i.e. element with 'data-choice' trigger) + + + var passedElement = (0, _utils.isType)('String', element) ? document.querySelector(element) : element; + + if (!passedElement) { + return console.error('Could not find passed element or passed element was of an invalid type'); + } + + this._isTextElement = passedElement.type === 'text'; + this._isSelectOneElement = passedElement.type === 'select-one'; + this._isSelectMultipleElement = passedElement.type === 'select-multiple'; + this._isSelectElement = this._isSelectOneElement || this._isSelectMultipleElement; + + if (this._isTextElement) { + this.passedElement = new _components.WrappedInput({ + element: passedElement, + classNames: this.config.classNames, + delimiter: this.config.delimiter + }); + } else if (this._isSelectElement) { + this.passedElement = new _components.WrappedSelect({ + element: passedElement, + classNames: this.config.classNames + }); + } + + if (!this.passedElement) { + return console.error('Passed element was of an invalid type'); + } + + if (this.config.shouldSortItems === true && this._isSelectOneElement && !this.config.silent) { + console.warn("shouldSortElements: Type of passed element is 'select-one', falling back to false."); + } + + this.initialised = false; + this._store = new _store.default(this.render); + this._initialState = {}; + this._currentState = {}; + this._prevState = {}; + this._currentValue = ''; + this._canSearch = this.config.searchEnabled; + this._isScrollingOnIe = false; + this._highlightPosition = 0; + this._wasTap = true; + this._placeholderValue = this._generatePlaceholderValue(); + this._baseId = (0, _utils.generateId)(this.passedElement.element, 'choices-'); + this._direction = this.passedElement.element.getAttribute('dir') || 'ltr'; + this._idNames = { + itemChoice: 'item-choice' + }; // Assign preset choices from passed object + + this._presetChoices = this.config.choices; // Assign preset items from passed object first + + this._presetItems = this.config.items; // Then add any values passed from attribute + + if (this.passedElement.value) { + this._presetItems = this._presetItems.concat(this.passedElement.value.split(this.config.delimiter)); + } + + this._render = this._render.bind(this); + this._onFocus = this._onFocus.bind(this); + this._onBlur = this._onBlur.bind(this); + this._onKeyUp = this._onKeyUp.bind(this); + this._onKeyDown = this._onKeyDown.bind(this); + this._onClick = this._onClick.bind(this); + this._onTouchMove = this._onTouchMove.bind(this); + this._onTouchEnd = this._onTouchEnd.bind(this); + this._onMouseDown = this._onMouseDown.bind(this); + this._onMouseOver = this._onMouseOver.bind(this); + this._onFormReset = this._onFormReset.bind(this); + this._onAKey = this._onAKey.bind(this); + this._onEnterKey = this._onEnterKey.bind(this); + this._onEscapeKey = this._onEscapeKey.bind(this); + this._onDirectionKey = this._onDirectionKey.bind(this); + this._onDeleteKey = this._onDeleteKey.bind(this); // If element has already been initialised with Choices, fail silently + + if (this.passedElement.element.getAttribute('data-choice') === 'active') { + console.warn('Trying to initialise Choices on element already initialised'); + } // Let's go + + + this.init(); + } + /* ======================================== + = Public functions = + ======================================== */ + + + _createClass(Choices, [{ + key: "init", + value: function init() { + if (this.initialised) { + return; + } + + this._createTemplates(); + + this._createElements(); + + this._createStructure(); // Set initial state (We need to clone the state because some reducers + // modify the inner objects properties in the state) 🤢 + + + this._initialState = (0, _utils.cloneObject)(this._store.state); + + this._store.subscribe(this._render); + + this._render(); + + this._addEventListeners(); + + var shouldDisable = !this.config.addItems || this.passedElement.element.hasAttribute('disabled'); + + if (shouldDisable) { + this.disable(); + } + + this.initialised = true; + var callbackOnInit = this.config.callbackOnInit; // Run callback if it is a function + + if (callbackOnInit && (0, _utils.isType)('Function', callbackOnInit)) { + callbackOnInit.call(this); + } + } + }, { + key: "destroy", + value: function destroy() { + if (!this.initialised) { + return; + } + + this._removeEventListeners(); + + this.passedElement.reveal(); + this.containerOuter.unwrap(this.passedElement.element); + + if (this._isSelectElement) { + this.passedElement.options = this._presetChoices; + } + + this.clearStore(); + this.config.templates = null; + this.initialised = false; + } + }, { + key: "enable", + value: function enable() { + if (this.passedElement.isDisabled) { + this.passedElement.enable(); + } + + if (this.containerOuter.isDisabled) { + this._addEventListeners(); + + this.input.enable(); + this.containerOuter.enable(); + } + + return this; + } + }, { + key: "disable", + value: function disable() { + if (!this.passedElement.isDisabled) { + this.passedElement.disable(); + } + + if (!this.containerOuter.isDisabled) { + this._removeEventListeners(); + + this.input.disable(); + this.containerOuter.disable(); + } + + return this; + } + }, { + key: "highlightItem", + value: function highlightItem(item) { + var runEvent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + if (!item) { + return this; + } + + var id = item.id, + _item$groupId = item.groupId, + groupId = _item$groupId === void 0 ? -1 : _item$groupId, + _item$value = item.value, + value = _item$value === void 0 ? '' : _item$value, + _item$label = item.label, + label = _item$label === void 0 ? '' : _item$label; + var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + + this._store.dispatch((0, _items.highlightItem)(id, true)); + + if (runEvent) { + this.passedElement.triggerEvent(_constants.EVENTS.highlightItem, { + id: id, + value: value, + label: label, + groupValue: group && group.value ? group.value : null + }); + } + + return this; + } + }, { + key: "unhighlightItem", + value: function unhighlightItem(item) { + if (!item) { + return this; + } + + var id = item.id, + _item$groupId2 = item.groupId, + groupId = _item$groupId2 === void 0 ? -1 : _item$groupId2, + _item$value2 = item.value, + value = _item$value2 === void 0 ? '' : _item$value2, + _item$label2 = item.label, + label = _item$label2 === void 0 ? '' : _item$label2; + var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + + this._store.dispatch((0, _items.highlightItem)(id, false)); + + this.passedElement.triggerEvent(_constants.EVENTS.highlightItem, { + id: id, + value: value, + label: label, + groupValue: group && group.value ? group.value : null + }); + return this; + } + }, { + key: "highlightAll", + value: function highlightAll() { + var _this = this; + + this._store.items.forEach(function (item) { + return _this.highlightItem(item); + }); + + return this; + } + }, { + key: "unhighlightAll", + value: function unhighlightAll() { + var _this2 = this; + + this._store.items.forEach(function (item) { + return _this2.unhighlightItem(item); + }); + + return this; + } + }, { + key: "removeActiveItemsByValue", + value: function removeActiveItemsByValue(value) { + var _this3 = this; + + this._store.activeItems.filter(function (item) { + return item.value === value; + }).forEach(function (item) { + return _this3._removeItem(item); + }); + + return this; + } + }, { + key: "removeActiveItems", + value: function removeActiveItems(excludedId) { + var _this4 = this; + + this._store.activeItems.filter(function (_ref) { + var id = _ref.id; + return id !== excludedId; + }).forEach(function (item) { + return _this4._removeItem(item); + }); + + return this; + } + }, { + key: "removeHighlightedItems", + value: function removeHighlightedItems() { + var _this5 = this; + + var runEvent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + + this._store.highlightedActiveItems.forEach(function (item) { + _this5._removeItem(item); // If this action was performed by the user + // trigger the event + + + if (runEvent) { + _this5._triggerChange(item.value); + } + }); + + return this; + } + }, { + key: "showDropdown", + value: function showDropdown(preventInputFocus) { + var _this6 = this; + + if (this.dropdown.isActive) { + return this; + } + + requestAnimationFrame(function () { + _this6.dropdown.show(); + + _this6.containerOuter.open(_this6.dropdown.distanceFromTopWindow()); + + if (!preventInputFocus && _this6._canSearch) { + _this6.input.focus(); + } + + _this6.passedElement.triggerEvent(_constants.EVENTS.showDropdown, {}); + }); + return this; + } + }, { + key: "hideDropdown", + value: function hideDropdown(preventInputBlur) { + var _this7 = this; + + if (!this.dropdown.isActive) { + return this; + } + + requestAnimationFrame(function () { + _this7.dropdown.hide(); + + _this7.containerOuter.close(); + + if (!preventInputBlur && _this7._canSearch) { + _this7.input.removeActiveDescendant(); + + _this7.input.blur(); + } + + _this7.passedElement.triggerEvent(_constants.EVENTS.hideDropdown, {}); + }); + return this; + } + }, { + key: "getValue", + value: function getValue() { + var valueOnly = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + + var values = this._store.activeItems.reduce(function (selectedItems, item) { + var itemValue = valueOnly ? item.value : item; + selectedItems.push(itemValue); + return selectedItems; + }, []); + + return this._isSelectOneElement ? values[0] : values; + } + }, { + key: "setValue", + value: function setValue(args) { + var _this8 = this; + + if (!this.initialised) { + return this; + } + + [].concat(args).forEach(function (value) { + return _this8._setChoiceOrItem(value); + }); + return this; + } + }, { + key: "setChoiceByValue", + value: function setChoiceByValue(value) { + var _this9 = this; + + if (!this.initialised || this._isTextElement) { + return this; + } // If only one value has been passed, convert to array + + + var choiceValue = (0, _utils.isType)('Array', value) ? value : [value]; // Loop through each value and + + choiceValue.forEach(function (val) { + return _this9._findAndSelectChoiceByValue(val); + }); + return this; + } + }, { + key: "setChoices", + value: function setChoices() { + var _this10 = this; + + var choices = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; + var label = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; + var replaceChoices = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + + if (!this._isSelectElement || !value) { + return this; + } // Clear choices if needed + + + if (replaceChoices) { + this.clearChoices(); + } + + this.containerOuter.removeLoadingState(); + + var addGroupsAndChoices = function addGroupsAndChoices(groupOrChoice) { + if (groupOrChoice.choices) { + _this10._addGroup({ + group: groupOrChoice, + id: groupOrChoice.id || null, + valueKey: value, + labelKey: label + }); + } else { + _this10._addChoice({ + value: groupOrChoice[value], + label: groupOrChoice[label], + isSelected: groupOrChoice.selected, + isDisabled: groupOrChoice.disabled, + customProperties: groupOrChoice.customProperties, + placeholder: groupOrChoice.placeholder + }); + } + }; + + this._setLoading(true); + + choices.forEach(addGroupsAndChoices); + + this._setLoading(false); + + return this; + } + }, { + key: "clearChoices", + value: function clearChoices() { + this._store.dispatch((0, _choices.clearChoices)()); + } + }, { + key: "clearStore", + value: function clearStore() { + this._store.dispatch((0, _misc.clearAll)()); + + return this; + } + }, { + key: "clearInput", + value: function clearInput() { + var shouldSetInputWidth = !this._isSelectOneElement; + this.input.clear(shouldSetInputWidth); + + if (!this._isTextElement && this._canSearch) { + this._isSearching = false; + + this._store.dispatch((0, _choices.activateChoices)(true)); + } + + return this; + } + }, { + key: "ajax", + value: function ajax(fn) { + var _this11 = this; + + if (!this.initialised || !this._isSelectElement || !fn) { + return this; + } + + requestAnimationFrame(function () { + return _this11._handleLoadingState(true); + }); + fn(this._ajaxCallback()); + return this; + } + /* ===== End of Public functions ====== */ + + /* ============================================= + = Private functions = + ============================================= */ + + }, { + key: "_render", + value: function _render() { + if (this._store.isLoading()) { + return; + } + + this._currentState = this._store.state; + var stateChanged = this._currentState.choices !== this._prevState.choices || this._currentState.groups !== this._prevState.groups || this._currentState.items !== this._prevState.items; + var shouldRenderChoices = this._isSelectElement; + var shouldRenderItems = this._currentState.items !== this._prevState.items; + + if (!stateChanged) { + return; + } + + if (shouldRenderChoices) { + this._renderChoices(); + } + + if (shouldRenderItems) { + this._renderItems(); + } + + this._prevState = this._currentState; + } + }, { + key: "_renderChoices", + value: function _renderChoices() { + var _this12 = this; + + var _this$_store = this._store, + activeGroups = _this$_store.activeGroups, + activeChoices = _this$_store.activeChoices; + var choiceListFragment = document.createDocumentFragment(); + this.choiceList.clear(); + + if (this.config.resetScrollPosition) { + requestAnimationFrame(function () { + return _this12.choiceList.scrollToTop(); + }); + } // If we have grouped options + + + if (activeGroups.length >= 1 && !this._isSearching) { + // If we have a placeholder choice along with groups + var activePlaceholders = activeChoices.filter(function (activeChoice) { + return activeChoice.placeholder === true && activeChoice.groupId === -1; + }); + + if (activePlaceholders.length >= 1) { + choiceListFragment = this._createChoicesFragment(activePlaceholders, choiceListFragment); + } + + choiceListFragment = this._createGroupsFragment(activeGroups, activeChoices, choiceListFragment); + } else if (activeChoices.length >= 1) { + choiceListFragment = this._createChoicesFragment(activeChoices, choiceListFragment); + } // If we have choices to show + + + if (choiceListFragment.childNodes && choiceListFragment.childNodes.length > 0) { + var activeItems = this._store.activeItems; + + var canAddItem = this._canAddItem(activeItems, this.input.value); // ...and we can select them + + + if (canAddItem.response) { + // ...append them and highlight the first choice + this.choiceList.append(choiceListFragment); + + this._highlightChoice(); + } else { + // ...otherwise show a notice + this.choiceList.append(this._getTemplate('notice', canAddItem.notice)); + } + } else { + // Otherwise show a notice + var dropdownItem; + var notice; + + if (this._isSearching) { + notice = (0, _utils.isType)('Function', this.config.noResultsText) ? this.config.noResultsText() : this.config.noResultsText; + dropdownItem = this._getTemplate('notice', notice, 'no-results'); + } else { + notice = (0, _utils.isType)('Function', this.config.noChoicesText) ? this.config.noChoicesText() : this.config.noChoicesText; + dropdownItem = this._getTemplate('notice', notice, 'no-choices'); + } + + this.choiceList.append(dropdownItem); + } + } + }, { + key: "_renderItems", + value: function _renderItems() { + var activeItems = this._store.activeItems || []; + this.itemList.clear(); // Create a fragment to store our list items + // (so we don't have to update the DOM for each item) + + var itemListFragment = this._createItemsFragment(activeItems); // If we have items to add, append them + + + if (itemListFragment.childNodes) { + this.itemList.append(itemListFragment); + } + } + }, { + key: "_createGroupsFragment", + value: function _createGroupsFragment(groups, choices, fragment) { + var _this13 = this; + + var groupFragment = fragment || document.createDocumentFragment(); + + var getGroupChoices = function getGroupChoices(group) { + return choices.filter(function (choice) { + if (_this13._isSelectOneElement) { + return choice.groupId === group.id; + } + + return choice.groupId === group.id && (_this13.config.renderSelectedChoices === 'always' || !choice.selected); + }); + }; // If sorting is enabled, filter groups + + + if (this.config.shouldSort) { + groups.sort(this.config.sortFn); + } + + groups.forEach(function (group) { + var groupChoices = getGroupChoices(group); + + if (groupChoices.length >= 1) { + var dropdownGroup = _this13._getTemplate('choiceGroup', group); + + groupFragment.appendChild(dropdownGroup); + + _this13._createChoicesFragment(groupChoices, groupFragment, true); + } + }); + return groupFragment; + } + }, { + key: "_createChoicesFragment", + value: function _createChoicesFragment(choices, fragment) { + var _this14 = this; + + var withinGroup = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + // Create a fragment to store our list items (so we don't have to update the DOM for each item) + var choicesFragment = fragment || document.createDocumentFragment(); + var _this$config = this.config, + renderSelectedChoices = _this$config.renderSelectedChoices, + searchResultLimit = _this$config.searchResultLimit, + renderChoiceLimit = _this$config.renderChoiceLimit; + var filter = this._isSearching ? _utils.sortByScore : this.config.sortFn; + + var appendChoice = function appendChoice(choice) { + var shouldRender = renderSelectedChoices === 'auto' ? _this14._isSelectOneElement || !choice.selected : true; + + if (shouldRender) { + var dropdownItem = _this14._getTemplate('choice', choice, _this14.config.itemSelectText); + + choicesFragment.appendChild(dropdownItem); + } + }; + + var rendererableChoices = choices; + + if (renderSelectedChoices === 'auto' && !this._isSelectOneElement) { + rendererableChoices = choices.filter(function (choice) { + return !choice.selected; + }); + } // Split array into placeholders and "normal" choices + + + var _rendererableChoices$ = rendererableChoices.reduce(function (acc, choice) { + if (choice.placeholder) { + acc.placeholderChoices.push(choice); + } else { + acc.normalChoices.push(choice); + } + + return acc; + }, { + placeholderChoices: [], + normalChoices: [] + }), + placeholderChoices = _rendererableChoices$.placeholderChoices, + normalChoices = _rendererableChoices$.normalChoices; // If sorting is enabled or the user is searching, filter choices + + + if (this.config.shouldSort || this._isSearching) { + normalChoices.sort(filter); + } + + var choiceLimit = rendererableChoices.length; // Prepend placeholeder + + var sortedChoices = [].concat(placeholderChoices, normalChoices); + + if (this._isSearching) { + choiceLimit = searchResultLimit; + } else if (renderChoiceLimit > 0 && !withinGroup) { + choiceLimit = renderChoiceLimit; + } // Add each choice to dropdown within range + + + for (var i = 0; i < choiceLimit; i += 1) { + if (sortedChoices[i]) { + appendChoice(sortedChoices[i]); + } + } + + return choicesFragment; + } + }, { + key: "_createItemsFragment", + value: function _createItemsFragment(items) { + var _this15 = this; + + var fragment = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + // Create fragment to add elements to + var _this$config2 = this.config, + shouldSortItems = _this$config2.shouldSortItems, + sortFn = _this$config2.sortFn, + removeItemButton = _this$config2.removeItemButton; + var itemListFragment = fragment || document.createDocumentFragment(); // If sorting is enabled, filter items + + if (shouldSortItems && !this._isSelectOneElement) { + items.sort(sortFn); + } + + if (this._isTextElement) { + // Update the value of the hidden input + this.passedElement.value = items; + } else { + // Update the options of the hidden input + this.passedElement.options = items; + } + + var addItemToFragment = function addItemToFragment(item) { + // Create new list element + var listItem = _this15._getTemplate('item', item, removeItemButton); // Append it to list + + + itemListFragment.appendChild(listItem); + }; // Add each list item to list + + + items.forEach(function (item) { + return addItemToFragment(item); + }); + return itemListFragment; + } + }, { + key: "_triggerChange", + value: function _triggerChange(value) { + if (value === undefined || value === null) { + return; + } + + this.passedElement.triggerEvent(_constants.EVENTS.change, { + value: value + }); + } + }, { + key: "_selectPlaceholderChoice", + value: function _selectPlaceholderChoice() { + var placeholderChoice = this._store.placeholderChoice; + + if (placeholderChoice) { + this._addItem({ + value: placeholderChoice.value, + label: placeholderChoice.label, + choiceId: placeholderChoice.id, + groupId: placeholderChoice.groupId, + placeholder: placeholderChoice.placeholder + }); + + this._triggerChange(placeholderChoice.value); + } + } + }, { + key: "_handleButtonAction", + value: function _handleButtonAction(activeItems, element) { + if (!activeItems || !element || !this.config.removeItems || !this.config.removeItemButton) { + return; + } + + var itemId = element.parentNode.getAttribute('data-id'); + var itemToRemove = activeItems.find(function (item) { + return item.id === parseInt(itemId, 10); + }); // Remove item associated with button + + this._removeItem(itemToRemove); + + this._triggerChange(itemToRemove.value); + + if (this._isSelectOneElement) { + this._selectPlaceholderChoice(); + } + } + }, { + key: "_handleItemAction", + value: function _handleItemAction(activeItems, element) { + var _this16 = this; + + var hasShiftKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + if (!activeItems || !element || !this.config.removeItems || this._isSelectOneElement) { + return; + } + + var passedId = element.getAttribute('data-id'); // We only want to select one item with a click + // so we deselect any items that aren't the target + // unless shift is being pressed + + activeItems.forEach(function (item) { + if (item.id === parseInt(passedId, 10) && !item.highlighted) { + _this16.highlightItem(item); + } else if (!hasShiftKey && item.highlighted) { + _this16.unhighlightItem(item); + } + }); // Focus input as without focus, a user cannot do anything with a + // highlighted item + + this.input.focus(); + } + }, { + key: "_handleChoiceAction", + value: function _handleChoiceAction(activeItems, element) { + if (!activeItems || !element) { + return; + } // If we are clicking on an option + + + var id = element.getAttribute('data-id'); + + var choice = this._store.getChoiceById(id); + + var passedKeyCode = activeItems[0] && activeItems[0].keyCode ? activeItems[0].keyCode : null; + var hasActiveDropdown = this.dropdown.isActive; // Update choice keyCode + + choice.keyCode = passedKeyCode; + this.passedElement.triggerEvent(_constants.EVENTS.choice, { + choice: choice + }); + + if (choice && !choice.selected && !choice.disabled) { + var canAddItem = this._canAddItem(activeItems, choice.value); + + if (canAddItem.response) { + this._addItem({ + value: choice.value, + label: choice.label, + choiceId: choice.id, + groupId: choice.groupId, + customProperties: choice.customProperties, + placeholder: choice.placeholder, + keyCode: choice.keyCode + }); + + this._triggerChange(choice.value); + } + } + + this.clearInput(); // We wont to close the dropdown if we are dealing with a single select box + + if (hasActiveDropdown && this._isSelectOneElement) { + this.hideDropdown(true); + this.containerOuter.focus(); + } + } + }, { + key: "_handleBackspace", + value: function _handleBackspace(activeItems) { + if (!this.config.removeItems || !activeItems) { + return; + } + + var lastItem = activeItems[activeItems.length - 1]; + var hasHighlightedItems = activeItems.some(function (item) { + return item.highlighted; + }); // If editing the last item is allowed and there are not other selected items, + // we can edit the item value. Otherwise if we can remove items, remove all selected items + + if (this.config.editItems && !hasHighlightedItems && lastItem) { + this.input.value = lastItem.value; + this.input.setWidth(); + + this._removeItem(lastItem); + + this._triggerChange(lastItem.value); + } else { + if (!hasHighlightedItems) { + // Highlight last item if none already highlighted + this.highlightItem(lastItem, false); + } + + this.removeHighlightedItems(true); + } + } + }, { + key: "_setLoading", + value: function _setLoading(isLoading) { + this._store.dispatch((0, _general.setIsLoading)(isLoading)); + } + }, { + key: "_handleLoadingState", + value: function _handleLoadingState() { + var setLoading = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + var placeholderItem = this.itemList.getChild(".".concat(this.config.classNames.placeholder)); + + if (setLoading) { + this.disable(); + this.containerOuter.addLoadingState(); + + if (this._isSelectOneElement) { + if (!placeholderItem) { + placeholderItem = this._getTemplate('placeholder', this.config.loadingText); + this.itemList.append(placeholderItem); + } else { + placeholderItem.innerHTML = this.config.loadingText; + } + } else { + this.input.placeholder = this.config.loadingText; + } + } else { + this.enable(); + this.containerOuter.removeLoadingState(); + + if (this._isSelectOneElement) { + placeholderItem.innerHTML = this._placeholderValue || ''; + } else { + this.input.placeholder = this._placeholderValue || ''; + } + } + } + }, { + key: "_handleSearch", + value: function _handleSearch(value) { + if (!value || !this.input.isFocussed) { + return; + } + + var choices = this._store.choices; + var _this$config3 = this.config, + searchFloor = _this$config3.searchFloor, + searchChoices = _this$config3.searchChoices; + var hasUnactiveChoices = choices.some(function (option) { + return !option.active; + }); // Check that we have a value to search and the input was an alphanumeric character + + if (value && value.length >= searchFloor) { + var resultCount = searchChoices ? this._searchChoices(value) : 0; // Trigger search event + + this.passedElement.triggerEvent(_constants.EVENTS.search, { + value: value, + resultCount: resultCount + }); + } else if (hasUnactiveChoices) { + // Otherwise reset choices to active + this._isSearching = false; + + this._store.dispatch((0, _choices.activateChoices)(true)); + } + } + }, { + key: "_canAddItem", + value: function _canAddItem(activeItems, value) { + var canAddItem = true; + var notice = (0, _utils.isType)('Function', this.config.addItemText) ? this.config.addItemText(value) : this.config.addItemText; + + if (!this._isSelectOneElement) { + var isDuplicateValue = (0, _utils.existsInArray)(activeItems, value); + + if (this.config.maxItemCount > 0 && this.config.maxItemCount <= activeItems.length) { + // If there is a max entry limit and we have reached that limit + // don't update + canAddItem = false; + notice = (0, _utils.isType)('Function', this.config.maxItemText) ? this.config.maxItemText(this.config.maxItemCount) : this.config.maxItemText; + } + + if (!this.config.duplicateItemsAllowed && isDuplicateValue && canAddItem) { + canAddItem = false; + notice = (0, _utils.isType)('Function', this.config.uniqueItemText) ? this.config.uniqueItemText(value) : this.config.uniqueItemText; + } + + if (this._isTextElement && this.config.addItems && canAddItem && (0, _utils.isType)('Function', this.config.addItemFilterFn) && !this.config.addItemFilterFn(value)) { + canAddItem = false; + notice = (0, _utils.isType)('Function', this.config.customAddItemText) ? this.config.customAddItemText(value) : this.config.customAddItemText; + } + } + + return { + response: canAddItem, + notice: notice + }; + } + }, { + key: "_ajaxCallback", + value: function _ajaxCallback() { + var _this17 = this; + + return function (results, value, label) { + if (!results || !value) { + return; + } + + var parsedResults = (0, _utils.isType)('Object', results) ? [results] : results; + + if (parsedResults && (0, _utils.isType)('Array', parsedResults) && parsedResults.length) { + // Remove loading states/text + _this17._handleLoadingState(false); + + _this17._setLoading(true); // Add each result as a choice + + + parsedResults.forEach(function (result) { + if (result.choices) { + _this17._addGroup({ + group: result, + id: result.id || null, + valueKey: value, + labelKey: label + }); + } else { + _this17._addChoice({ + value: (0, _utils.fetchFromObject)(result, value), + label: (0, _utils.fetchFromObject)(result, label), + isSelected: result.selected, + isDisabled: result.disabled, + customProperties: result.customProperties, + placeholder: result.placeholder + }); + } + }); + + _this17._setLoading(false); + + if (_this17._isSelectOneElement) { + _this17._selectPlaceholderChoice(); + } + } else { + // No results, remove loading state + _this17._handleLoadingState(false); + } + }; + } + }, { + key: "_searchChoices", + value: function _searchChoices(value) { + var newValue = (0, _utils.isType)('String', value) ? value.trim() : value; + var currentValue = (0, _utils.isType)('String', this._currentValue) ? this._currentValue.trim() : this._currentValue; + + if (newValue.length < 1 && newValue === "".concat(currentValue, " ")) { + return 0; + } // If new value matches the desired length and is not the same as the current value with a space + + + var haystack = this._store.searchableChoices; + var needle = newValue; + var keys = [].concat(this.config.searchFields); + var options = Object.assign(this.config.fuseOptions, { + keys: keys + }); + var fuse = new _fuse.default(haystack, options); + var results = fuse.search(needle); + this._currentValue = newValue; + this._highlightPosition = 0; + this._isSearching = true; + + this._store.dispatch((0, _choices.filterChoices)(results)); + + return results.length; + } + }, { + key: "_addEventListeners", + value: function _addEventListeners() { + document.addEventListener('keyup', this._onKeyUp); + document.addEventListener('keydown', this._onKeyDown); + document.addEventListener('click', this._onClick); + document.addEventListener('touchmove', this._onTouchMove); + document.addEventListener('touchend', this._onTouchEnd); + document.addEventListener('mousedown', this._onMouseDown); + document.addEventListener('mouseover', this._onMouseOver); + + if (this._isSelectOneElement) { + this.containerOuter.element.addEventListener('focus', this._onFocus); + this.containerOuter.element.addEventListener('blur', this._onBlur); + } + + this.input.element.addEventListener('focus', this._onFocus); + this.input.element.addEventListener('blur', this._onBlur); + + if (this.input.element.form) { + this.input.element.form.addEventListener('reset', this._onFormReset); + } + + this.input.addEventListeners(); + } + }, { + key: "_removeEventListeners", + value: function _removeEventListeners() { + document.removeEventListener('keyup', this._onKeyUp); + document.removeEventListener('keydown', this._onKeyDown); + document.removeEventListener('click', this._onClick); + document.removeEventListener('touchmove', this._onTouchMove); + document.removeEventListener('touchend', this._onTouchEnd); + document.removeEventListener('mousedown', this._onMouseDown); + document.removeEventListener('mouseover', this._onMouseOver); + + if (this._isSelectOneElement) { + this.containerOuter.element.removeEventListener('focus', this._onFocus); + this.containerOuter.element.removeEventListener('blur', this._onBlur); + } + + this.input.element.removeEventListener('focus', this._onFocus); + this.input.element.removeEventListener('blur', this._onBlur); + + if (this.input.element.form) { + this.input.element.form.removeEventListener('reset', this._onFormReset); + } + + this.input.removeEventListeners(); + } + }, { + key: "_onKeyDown", + value: function _onKeyDown(event) { + var _keyDownActions; + + var target = event.target, + keyCode = event.keyCode, + ctrlKey = event.ctrlKey, + metaKey = event.metaKey; + + if (target !== this.input.element && !this.containerOuter.element.contains(target)) { + return; + } + + var activeItems = this._store.activeItems; + var hasFocusedInput = this.input.isFocussed; + var hasActiveDropdown = this.dropdown.isActive; + var hasItems = this.itemList.hasChildren; + var keyString = String.fromCharCode(keyCode); + var BACK_KEY = _constants.KEY_CODES.BACK_KEY, + DELETE_KEY = _constants.KEY_CODES.DELETE_KEY, + ENTER_KEY = _constants.KEY_CODES.ENTER_KEY, + A_KEY = _constants.KEY_CODES.A_KEY, + ESC_KEY = _constants.KEY_CODES.ESC_KEY, + UP_KEY = _constants.KEY_CODES.UP_KEY, + DOWN_KEY = _constants.KEY_CODES.DOWN_KEY, + PAGE_UP_KEY = _constants.KEY_CODES.PAGE_UP_KEY, + PAGE_DOWN_KEY = _constants.KEY_CODES.PAGE_DOWN_KEY; + var hasCtrlDownKeyPressed = ctrlKey || metaKey; // If a user is typing and the dropdown is not active + + if (!this._isTextElement && /[a-zA-Z0-9-_ ]/.test(keyString)) { + this.showDropdown(); + } // Map keys to key actions + + + var keyDownActions = (_keyDownActions = {}, _defineProperty(_keyDownActions, A_KEY, this._onAKey), _defineProperty(_keyDownActions, ENTER_KEY, this._onEnterKey), _defineProperty(_keyDownActions, ESC_KEY, this._onEscapeKey), _defineProperty(_keyDownActions, UP_KEY, this._onDirectionKey), _defineProperty(_keyDownActions, PAGE_UP_KEY, this._onDirectionKey), _defineProperty(_keyDownActions, DOWN_KEY, this._onDirectionKey), _defineProperty(_keyDownActions, PAGE_DOWN_KEY, this._onDirectionKey), _defineProperty(_keyDownActions, DELETE_KEY, this._onDeleteKey), _defineProperty(_keyDownActions, BACK_KEY, this._onDeleteKey), _keyDownActions); // If keycode has a function, run it + + if (keyDownActions[keyCode]) { + keyDownActions[keyCode]({ + event: event, + target: target, + keyCode: keyCode, + metaKey: metaKey, + activeItems: activeItems, + hasFocusedInput: hasFocusedInput, + hasActiveDropdown: hasActiveDropdown, + hasItems: hasItems, + hasCtrlDownKeyPressed: hasCtrlDownKeyPressed + }); + } + } + }, { + key: "_onKeyUp", + value: function _onKeyUp(_ref2) { + var target = _ref2.target, + keyCode = _ref2.keyCode; + + if (target !== this.input.element) { + return; + } + + var value = this.input.value; + var activeItems = this._store.activeItems; + + var canAddItem = this._canAddItem(activeItems, value); + + var backKey = _constants.KEY_CODES.BACK_KEY, + deleteKey = _constants.KEY_CODES.DELETE_KEY; // We are typing into a text input and have a value, we want to show a dropdown + // notice. Otherwise hide the dropdown + + if (this._isTextElement) { + var canShowDropdownNotice = canAddItem.notice && value; + + if (canShowDropdownNotice) { + var dropdownItem = this._getTemplate('notice', canAddItem.notice); + + this.dropdown.element.innerHTML = dropdownItem.outerHTML; + this.showDropdown(true); + } else { + this.hideDropdown(true); + } + } else { + var userHasRemovedValue = (keyCode === backKey || keyCode === deleteKey) && !target.value; + var canReactivateChoices = !this._isTextElement && this._isSearching; + var canSearch = this._canSearch && canAddItem.response; + + if (userHasRemovedValue && canReactivateChoices) { + this._isSearching = false; + + this._store.dispatch((0, _choices.activateChoices)(true)); + } else if (canSearch) { + this._handleSearch(this.input.value); + } + } + + this._canSearch = this.config.searchEnabled; + } + }, { + key: "_onAKey", + value: function _onAKey(_ref3) { + var hasItems = _ref3.hasItems, + hasCtrlDownKeyPressed = _ref3.hasCtrlDownKeyPressed; + + // If CTRL + A or CMD + A have been pressed and there are items to select + if (hasCtrlDownKeyPressed && hasItems) { + this._canSearch = false; + var shouldHightlightAll = this.config.removeItems && !this.input.value && this.input.element === document.activeElement; + + if (shouldHightlightAll) { + this.highlightAll(); + } + } + } + }, { + key: "_onEnterKey", + value: function _onEnterKey(_ref4) { + var event = _ref4.event, + target = _ref4.target, + activeItems = _ref4.activeItems, + hasActiveDropdown = _ref4.hasActiveDropdown; + var enterKey = _constants.KEY_CODES.ENTER_KEY; + var targetWasButton = target.hasAttribute('data-button'); + + if (this._isTextElement && target.value) { + var value = this.input.value; + + var canAddItem = this._canAddItem(activeItems, value); + + if (canAddItem.response) { + this.hideDropdown(true); + + this._addItem({ + value: value + }); + + this._triggerChange(value); + + this.clearInput(); + } + } + + if (targetWasButton) { + this._handleButtonAction(activeItems, target); + + event.preventDefault(); + } + + if (hasActiveDropdown) { + var highlightedChoice = this.dropdown.getChild(".".concat(this.config.classNames.highlightedState)); + + if (highlightedChoice) { + // add enter keyCode value + if (activeItems[0]) { + activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign + } + + this._handleChoiceAction(activeItems, highlightedChoice); + } + + event.preventDefault(); + } else if (this._isSelectOneElement) { + this.showDropdown(); + event.preventDefault(); + } + } + }, { + key: "_onEscapeKey", + value: function _onEscapeKey(_ref5) { + var hasActiveDropdown = _ref5.hasActiveDropdown; + + if (hasActiveDropdown) { + this.hideDropdown(true); + this.containerOuter.focus(); + } + } + }, { + key: "_onDirectionKey", + value: function _onDirectionKey(_ref6) { + var event = _ref6.event, + hasActiveDropdown = _ref6.hasActiveDropdown, + keyCode = _ref6.keyCode, + metaKey = _ref6.metaKey; + var downKey = _constants.KEY_CODES.DOWN_KEY, + pageUpKey = _constants.KEY_CODES.PAGE_UP_KEY, + pageDownKey = _constants.KEY_CODES.PAGE_DOWN_KEY; // If up or down key is pressed, traverse through options + + if (hasActiveDropdown || this._isSelectOneElement) { + this.showDropdown(); + this._canSearch = false; + var directionInt = keyCode === downKey || keyCode === pageDownKey ? 1 : -1; + var skipKey = metaKey || keyCode === pageDownKey || keyCode === pageUpKey; + var selectableChoiceIdentifier = '[data-choice-selectable]'; + var nextEl; + + if (skipKey) { + if (directionInt > 0) { + nextEl = Array.from(this.dropdown.element.querySelectorAll(selectableChoiceIdentifier)).pop(); + } else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } else { + var currentEl = this.dropdown.element.querySelector(".".concat(this.config.classNames.highlightedState)); + + if (currentEl) { + nextEl = (0, _utils.getAdjacentEl)(currentEl, selectableChoiceIdentifier, directionInt); + } else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } + + if (nextEl) { + // We prevent default to stop the cursor moving + // when pressing the arrow + if (!(0, _utils.isScrolledIntoView)(nextEl, this.choiceList.element, directionInt)) { + this.choiceList.scrollToChoice(nextEl, directionInt); + } + + this._highlightChoice(nextEl); + } // Prevent default to maintain cursor position whilst + // traversing dropdown options + + + event.preventDefault(); + } + } + }, { + key: "_onDeleteKey", + value: function _onDeleteKey(_ref7) { + var event = _ref7.event, + target = _ref7.target, + hasFocusedInput = _ref7.hasFocusedInput, + activeItems = _ref7.activeItems; + + // If backspace or delete key is pressed and the input has no value + if (hasFocusedInput && !target.value && !this._isSelectOneElement) { + this._handleBackspace(activeItems); + + event.preventDefault(); + } + } + }, { + key: "_onTouchMove", + value: function _onTouchMove() { + if (this._wasTap) { + this._wasTap = false; + } + } + }, { + key: "_onTouchEnd", + value: function _onTouchEnd(event) { + var _ref8 = event || event.touches[0], + target = _ref8.target; + + var touchWasWithinContainer = this._wasTap && this.containerOuter.element.contains(target); + + if (touchWasWithinContainer) { + var containerWasExactTarget = target === this.containerOuter.element || target === this.containerInner.element; + + if (containerWasExactTarget) { + if (this._isTextElement) { + this.input.focus(); + } else if (this._isSelectMultipleElement) { + this.showDropdown(); + } + } // Prevents focus event firing + + + event.stopPropagation(); + } + + this._wasTap = true; + } + }, { + key: "_onMouseDown", + value: function _onMouseDown(event) { + var target = event.target, + shiftKey = event.shiftKey; // If we have our mouse down on the scrollbar and are on IE11... + + if (this.choiceList.element.contains(target) && (0, _utils.isIE11)()) { + this._isScrollingOnIe = true; + } + + if (!this.containerOuter.element.contains(target) || target === this.input.element) { + return; + } + + var activeItems = this._store.activeItems; + var hasShiftKey = shiftKey; + var buttonTarget = (0, _utils.findAncestorByAttrName)(target, 'data-button'); + var itemTarget = (0, _utils.findAncestorByAttrName)(target, 'data-item'); + var choiceTarget = (0, _utils.findAncestorByAttrName)(target, 'data-choice'); + + if (buttonTarget) { + this._handleButtonAction(activeItems, buttonTarget); + } else if (itemTarget) { + this._handleItemAction(activeItems, itemTarget, hasShiftKey); + } else if (choiceTarget) { + this._handleChoiceAction(activeItems, choiceTarget); + } + + event.preventDefault(); + } + }, { + key: "_onMouseOver", + value: function _onMouseOver(_ref9) { + var target = _ref9.target; + var targetWithinDropdown = target === this.dropdown || this.dropdown.element.contains(target); + var shouldHighlightChoice = targetWithinDropdown && target.hasAttribute('data-choice'); + + if (shouldHighlightChoice) { + this._highlightChoice(target); + } + } + }, { + key: "_onClick", + value: function _onClick(_ref10) { + var target = _ref10.target; + var clickWasWithinContainer = this.containerOuter.element.contains(target); + + if (clickWasWithinContainer) { + if (!this.dropdown.isActive && !this.containerOuter.isDisabled) { + if (this._isTextElement) { + if (document.activeElement !== this.input.element) { + this.input.focus(); + } + } else { + this.showDropdown(); + this.containerOuter.focus(); + } + } else if (this._isSelectOneElement && target !== this.input.element && !this.dropdown.element.contains(target)) { + this.hideDropdown(); + } + } else { + var hasHighlightedItems = this._store.highlightedActiveItems; + + if (hasHighlightedItems) { + this.unhighlightAll(); + } + + this.containerOuter.removeFocusState(); + this.hideDropdown(true); + } + } + }, { + key: "_onFocus", + value: function _onFocus(_ref11) { + var _this18 = this; + + var target = _ref11.target; + var focusWasWithinContainer = this.containerOuter.element.contains(target); + + if (!focusWasWithinContainer) { + return; + } + + var focusActions = { + text: function text() { + if (target === _this18.input.element) { + _this18.containerOuter.addFocusState(); + } + }, + 'select-one': function selectOne() { + _this18.containerOuter.addFocusState(); + + if (target === _this18.input.element) { + _this18.showDropdown(true); + } + }, + 'select-multiple': function selectMultiple() { + if (target === _this18.input.element) { + _this18.showDropdown(true); // If element is a select box, the focused element is the container and the dropdown + // isn't already open, focus and show dropdown + + + _this18.containerOuter.addFocusState(); + } + } + }; + focusActions[this.passedElement.element.type](); + } + }, { + key: "_onBlur", + value: function _onBlur(_ref12) { + var _this19 = this; + + var target = _ref12.target; + var blurWasWithinContainer = this.containerOuter.element.contains(target); + + if (blurWasWithinContainer && !this._isScrollingOnIe) { + var activeItems = this._store.activeItems; + var hasHighlightedItems = activeItems.some(function (item) { + return item.highlighted; + }); + var blurActions = { + text: function text() { + if (target === _this19.input.element) { + _this19.containerOuter.removeFocusState(); + + if (hasHighlightedItems) { + _this19.unhighlightAll(); + } + + _this19.hideDropdown(true); + } + }, + 'select-one': function selectOne() { + _this19.containerOuter.removeFocusState(); + + if (target === _this19.input.element || target === _this19.containerOuter.element && !_this19._canSearch) { + _this19.hideDropdown(true); + } + }, + 'select-multiple': function selectMultiple() { + if (target === _this19.input.element) { + _this19.containerOuter.removeFocusState(); + + _this19.hideDropdown(true); + + if (hasHighlightedItems) { + _this19.unhighlightAll(); + } + } + } + }; + blurActions[this.passedElement.element.type](); + } else { + // On IE11, clicking the scollbar blurs our input and thus + // closes the dropdown. To stop this, we refocus our input + // if we know we are on IE *and* are scrolling. + this._isScrollingOnIe = false; + this.input.element.focus(); + } + } + }, { + key: "_onFormReset", + value: function _onFormReset() { + this._store.dispatch((0, _misc.resetTo)(this._initialState)); + } + }, { + key: "_highlightChoice", + value: function _highlightChoice() { + var _this20 = this; + + var el = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; + var choices = Array.from(this.dropdown.element.querySelectorAll('[data-choice-selectable]')); + + if (!choices.length) { + return; + } + + var passedEl = el; + var highlightedChoices = Array.from(this.dropdown.element.querySelectorAll(".".concat(this.config.classNames.highlightedState))); // Remove any highlighted choices + + highlightedChoices.forEach(function (choice) { + choice.classList.remove(_this20.config.classNames.highlightedState); + choice.setAttribute('aria-selected', 'false'); + }); + + if (passedEl) { + this._highlightPosition = choices.indexOf(passedEl); + } else { + // Highlight choice based on last known highlight location + if (choices.length > this._highlightPosition) { + // If we have an option to highlight + passedEl = choices[this._highlightPosition]; + } else { + // Otherwise highlight the option before + passedEl = choices[choices.length - 1]; + } + + if (!passedEl) { + passedEl = choices[0]; + } + } + + passedEl.classList.add(this.config.classNames.highlightedState); + passedEl.setAttribute('aria-selected', 'true'); + this.passedElement.triggerEvent(_constants.EVENTS.highlightChoice, { + el: passedEl + }); + + if (this.dropdown.isActive) { + // IE11 ignores aria-label and blocks virtual keyboard + // if aria-activedescendant is set without a dropdown + this.input.setActiveDescendant(passedEl.id); + this.containerOuter.setActiveDescendant(passedEl.id); + } + } + }, { + key: "_addItem", + value: function _addItem(_ref13) { + var value = _ref13.value, + _ref13$label = _ref13.label, + label = _ref13$label === void 0 ? null : _ref13$label, + _ref13$choiceId = _ref13.choiceId, + choiceId = _ref13$choiceId === void 0 ? -1 : _ref13$choiceId, + _ref13$groupId = _ref13.groupId, + groupId = _ref13$groupId === void 0 ? -1 : _ref13$groupId, + _ref13$customProperti = _ref13.customProperties, + customProperties = _ref13$customProperti === void 0 ? null : _ref13$customProperti, + _ref13$placeholder = _ref13.placeholder, + placeholder = _ref13$placeholder === void 0 ? false : _ref13$placeholder, + _ref13$keyCode = _ref13.keyCode, + keyCode = _ref13$keyCode === void 0 ? null : _ref13$keyCode; + var passedValue = (0, _utils.isType)('String', value) ? value.trim() : value; + var passedKeyCode = keyCode; + var passedCustomProperties = customProperties; + var items = this._store.items; + var passedLabel = label || passedValue; + var passedOptionId = parseInt(choiceId, 10) || -1; + var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + var id = items ? items.length + 1 : 1; // If a prepended value has been passed, prepend it + + if (this.config.prependValue) { + passedValue = this.config.prependValue + passedValue.toString(); + } // If an appended value has been passed, append it + + + if (this.config.appendValue) { + passedValue += this.config.appendValue.toString(); + } + + this._store.dispatch((0, _items.addItem)({ + value: passedValue, + label: passedLabel, + id: id, + choiceId: passedOptionId, + groupId: groupId, + customProperties: customProperties, + placeholder: placeholder, + keyCode: passedKeyCode + })); + + if (this._isSelectOneElement) { + this.removeActiveItems(id); + } // Trigger change event + + + this.passedElement.triggerEvent(_constants.EVENTS.addItem, { + id: id, + value: passedValue, + label: passedLabel, + customProperties: passedCustomProperties, + groupValue: group && group.value ? group.value : undefined, + keyCode: passedKeyCode + }); + return this; + } + }, { + key: "_removeItem", + value: function _removeItem(item) { + if (!item || !(0, _utils.isType)('Object', item)) { + return this; + } + + var id = item.id, + value = item.value, + label = item.label, + choiceId = item.choiceId, + groupId = item.groupId; + var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + + this._store.dispatch((0, _items.removeItem)(id, choiceId)); + + if (group && group.value) { + this.passedElement.triggerEvent(_constants.EVENTS.removeItem, { + id: id, + value: value, + label: label, + groupValue: group.value + }); + } else { + this.passedElement.triggerEvent(_constants.EVENTS.removeItem, { + id: id, + value: value, + label: label + }); + } + + return this; + } + }, { + key: "_addChoice", + value: function _addChoice(_ref14) { + var value = _ref14.value, + _ref14$label = _ref14.label, + label = _ref14$label === void 0 ? null : _ref14$label, + _ref14$isSelected = _ref14.isSelected, + isSelected = _ref14$isSelected === void 0 ? false : _ref14$isSelected, + _ref14$isDisabled = _ref14.isDisabled, + isDisabled = _ref14$isDisabled === void 0 ? false : _ref14$isDisabled, + _ref14$groupId = _ref14.groupId, + groupId = _ref14$groupId === void 0 ? -1 : _ref14$groupId, + _ref14$customProperti = _ref14.customProperties, + customProperties = _ref14$customProperti === void 0 ? null : _ref14$customProperti, + _ref14$placeholder = _ref14.placeholder, + placeholder = _ref14$placeholder === void 0 ? false : _ref14$placeholder, + _ref14$keyCode = _ref14.keyCode, + keyCode = _ref14$keyCode === void 0 ? null : _ref14$keyCode; + + if (typeof value === 'undefined' || value === null) { + return; + } // Generate unique id + + + var choices = this._store.choices; + var choiceLabel = label || value; + var choiceId = choices ? choices.length + 1 : 1; + var choiceElementId = "".concat(this._baseId, "-").concat(this._idNames.itemChoice, "-").concat(choiceId); + + this._store.dispatch((0, _choices.addChoice)({ + value: value, + label: choiceLabel, + id: choiceId, + groupId: groupId, + disabled: isDisabled, + elementId: choiceElementId, + customProperties: customProperties, + placeholder: placeholder, + keyCode: keyCode + })); + + if (isSelected) { + this._addItem({ + value: value, + label: choiceLabel, + choiceId: choiceId, + customProperties: customProperties, + placeholder: placeholder, + keyCode: keyCode + }); + } + } + }, { + key: "_addGroup", + value: function _addGroup(_ref15) { + var _this21 = this; + + var group = _ref15.group, + id = _ref15.id, + _ref15$valueKey = _ref15.valueKey, + valueKey = _ref15$valueKey === void 0 ? 'value' : _ref15$valueKey, + _ref15$labelKey = _ref15.labelKey, + labelKey = _ref15$labelKey === void 0 ? 'label' : _ref15$labelKey; + var groupChoices = (0, _utils.isType)('Object', group) ? group.choices : Array.from(group.getElementsByTagName('OPTION')); + var groupId = id || Math.floor(new Date().valueOf() * Math.random()); + var isDisabled = group.disabled ? group.disabled : false; + + if (groupChoices) { + this._store.dispatch((0, _groups.addGroup)(group.label, groupId, true, isDisabled)); + + var addGroupChoices = function addGroupChoices(choice) { + var isOptDisabled = choice.disabled || choice.parentNode && choice.parentNode.disabled; + + _this21._addChoice({ + value: choice[valueKey], + label: (0, _utils.isType)('Object', choice) ? choice[labelKey] : choice.innerHTML, + isSelected: choice.selected, + isDisabled: isOptDisabled, + groupId: groupId, + customProperties: choice.customProperties, + placeholder: choice.placeholder + }); + }; + + groupChoices.forEach(addGroupChoices); + } else { + this._store.dispatch((0, _groups.addGroup)(group.label, group.id, false, group.disabled)); + } + } + }, { + key: "_getTemplate", + value: function _getTemplate(template) { + var _templates$template; + + if (!template) { + return null; + } + + var _this$config4 = this.config, + templates = _this$config4.templates, + classNames = _this$config4.classNames; + + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + return (_templates$template = templates[template]).call.apply(_templates$template, [this, classNames].concat(args)); + } + }, { + key: "_createTemplates", + value: function _createTemplates() { + var callbackOnCreateTemplates = this.config.callbackOnCreateTemplates; + var userTemplates = {}; + + if (callbackOnCreateTemplates && (0, _utils.isType)('Function', callbackOnCreateTemplates)) { + userTemplates = callbackOnCreateTemplates.call(this, _utils.strToEl); + } + + this.config.templates = (0, _deepmerge.default)(_templates.TEMPLATES, userTemplates); + } + }, { + key: "_createElements", + value: function _createElements() { + this.containerOuter = new _components.Container({ + element: this._getTemplate('containerOuter', this._direction, this._isSelectElement, this._isSelectOneElement, this.config.searchEnabled, this.passedElement.element.type), + classNames: this.config.classNames, + type: this.passedElement.element.type, + position: this.config.position + }); + this.containerInner = new _components.Container({ + element: this._getTemplate('containerInner'), + classNames: this.config.classNames, + type: this.passedElement.element.type, + position: this.config.position + }); + this.input = new _components.Input({ + element: this._getTemplate('input'), + classNames: this.config.classNames, + type: this.passedElement.element.type + }); + this.choiceList = new _components.List({ + element: this._getTemplate('choiceList', this._isSelectOneElement) + }); + this.itemList = new _components.List({ + element: this._getTemplate('itemList', this._isSelectOneElement) + }); + this.dropdown = new _components.Dropdown({ + element: this._getTemplate('dropdown'), + classNames: this.config.classNames, + type: this.passedElement.element.type + }); + } + }, { + key: "_createStructure", + value: function _createStructure() { + // Hide original element + this.passedElement.conceal(); // Wrap input in container preserving DOM ordering + + this.containerInner.wrap(this.passedElement.element); // Wrapper inner container with outer container + + this.containerOuter.wrap(this.containerInner.element); + + if (this._isSelectOneElement) { + this.input.placeholder = this.config.searchPlaceholderValue || ''; + } else if (this._placeholderValue) { + this.input.placeholder = this._placeholderValue; + this.input.setWidth(true); + } + + this.containerOuter.element.appendChild(this.containerInner.element); + this.containerOuter.element.appendChild(this.dropdown.element); + this.containerInner.element.appendChild(this.itemList.element); + + if (!this._isTextElement) { + this.dropdown.element.appendChild(this.choiceList.element); + } + + if (!this._isSelectOneElement) { + this.containerInner.element.appendChild(this.input.element); + } else if (this.config.searchEnabled) { + this.dropdown.element.insertBefore(this.input.element, this.dropdown.element.firstChild); + } + + if (this._isSelectElement) { + this._addPredefinedChoices(); + } else if (this._isTextElement) { + this._addPredefinedItems(); + } + } + }, { + key: "_addPredefinedChoices", + value: function _addPredefinedChoices() { + var _this22 = this; + + var passedGroups = this.passedElement.optionGroups; + this._highlightPosition = 0; + this._isSearching = false; + + this._setLoading(true); + + if (passedGroups && passedGroups.length) { + // If we have a placeholder option + var placeholderChoice = this.passedElement.placeholderOption; + + if (placeholderChoice && placeholderChoice.parentNode.tagName === 'SELECT') { + this._addChoice({ + value: placeholderChoice.value, + label: placeholderChoice.innerHTML, + isSelected: placeholderChoice.selected, + isDisabled: placeholderChoice.disabled, + placeholder: true + }); + } + + passedGroups.forEach(function (group) { + return _this22._addGroup({ + group: group, + id: group.id || null + }); + }); + } else { + var passedOptions = this.passedElement.options; + var filter = this.config.sortFn; + var allChoices = this._presetChoices; // Create array of options from option elements + + passedOptions.forEach(function (o) { + allChoices.push({ + value: o.value, + label: o.innerHTML, + selected: o.selected, + disabled: o.disabled || o.parentNode.disabled, + placeholder: o.hasAttribute('placeholder'), + customProperties: o.getAttribute('data-custom-properties') + }); + }); // If sorting is enabled or the user is searching, filter choices + + if (this.config.shouldSort) allChoices.sort(filter); // Determine whether there is a selected choice + + var hasSelectedChoice = allChoices.some(function (choice) { + return choice.selected; + }); + + var handleChoice = function handleChoice(choice, index) { + var value = choice.value, + label = choice.label, + customProperties = choice.customProperties, + placeholder = choice.placeholder; + + if (_this22._isSelectElement) { + // If the choice is actually a group + if (choice.choices) { + _this22._addGroup({ + group: choice, + id: choice.id || null + }); + } else { + // If there is a selected choice already or the choice is not + // the first in the array, add each choice normally + // Otherwise pre-select the first choice in the array if it's a single select + var shouldPreselect = _this22._isSelectOneElement && !hasSelectedChoice && index === 0; + var isSelected = shouldPreselect ? true : choice.selected; + var isDisabled = shouldPreselect ? false : choice.disabled; + + _this22._addChoice({ + value: value, + label: label, + isSelected: isSelected, + isDisabled: isDisabled, + customProperties: customProperties, + placeholder: placeholder + }); + } + } else { + _this22._addChoice({ + value: value, + label: label, + isSelected: choice.selected, + isDisabled: choice.disabled, + customProperties: customProperties, + placeholder: placeholder + }); + } + }; // Add each choice + + + allChoices.forEach(function (choice, index) { + return handleChoice(choice, index); + }); + } + + this._setLoading(false); + } + }, { + key: "_addPredefinedItems", + value: function _addPredefinedItems() { + var _this23 = this; + + var handlePresetItem = function handlePresetItem(item) { + var itemType = (0, _utils.getType)(item); + + if (itemType === 'Object' && item.value) { + _this23._addItem({ + value: item.value, + label: item.label, + choiceId: item.id, + customProperties: item.customProperties, + placeholder: item.placeholder + }); + } else if (itemType === 'String') { + _this23._addItem({ + value: item + }); + } + }; + + this._presetItems.forEach(function (item) { + return handlePresetItem(item); + }); + } + }, { + key: "_setChoiceOrItem", + value: function _setChoiceOrItem(item) { + var _this24 = this; + + var itemType = (0, _utils.getType)(item).toLowerCase(); + var handleType = { + object: function object() { + if (!item.value) { + return; + } // If we are dealing with a select input, we need to create an option first + // that is then selected. For text inputs we can just add items normally. + + + if (!_this24._isTextElement) { + _this24._addChoice({ + value: item.value, + label: item.label, + isSelected: true, + isDisabled: false, + customProperties: item.customProperties, + placeholder: item.placeholder + }); + } else { + _this24._addItem({ + value: item.value, + label: item.label, + choiceId: item.id, + customProperties: item.customProperties, + placeholder: item.placeholder + }); + } + }, + string: function string() { + if (!_this24._isTextElement) { + _this24._addChoice({ + value: item, + label: item, + isSelected: true, + isDisabled: false + }); + } else { + _this24._addItem({ + value: item + }); + } + } + }; + handleType[itemType](); + } + }, { + key: "_findAndSelectChoiceByValue", + value: function _findAndSelectChoiceByValue(val) { + var _this25 = this; + + var choices = this._store.choices; // Check 'value' property exists and the choice isn't already selected + + var foundChoice = choices.find(function (choice) { + return _this25.config.itemComparer(choice.value, val); + }); + + if (foundChoice && !foundChoice.selected) { + this._addItem({ + value: foundChoice.value, + label: foundChoice.label, + choiceId: foundChoice.id, + groupId: foundChoice.groupId, + customProperties: foundChoice.customProperties, + placeholder: foundChoice.placeholder, + keyCode: foundChoice.keyCode + }); + } + } + }, { + key: "_generateInstances", + value: function _generateInstances(elements, config) { + return elements.reduce(function (instances, element) { + instances.push(new Choices(element, config)); + return instances; + }, [this]); + } + }, { + key: "_generatePlaceholderValue", + value: function _generatePlaceholderValue() { + if (this._isSelectOneElement) { + return false; + } + + return this.config.placeholder ? this.config.placeholderValue || this.passedElement.element.getAttribute('placeholder') : false; + } + /* ===== End of Private functions ====== */ + + }]); + + return Choices; +}(); + +Choices.userDefaults = {}; // We cannot export default here due to Webpack: https://github.com/webpack/webpack/issues/3929 + +module.exports = Choices; + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + +/*! + * Fuse.js v3.4.2 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2012-2017 Kirollos Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(true) + module.exports = factory(); + else {} +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./src/index.js"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./src/bitap/bitap_matched_indices.js": +/*!********************************************!*\ + !*** ./src/bitap/bitap_matched_indices.js ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = function () { + var matchmask = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var minMatchCharLength = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; + var matchedIndices = []; + var start = -1; + var end = -1; + var i = 0; + + for (var len = matchmask.length; i < len; i += 1) { + var match = matchmask[i]; + + if (match && start === -1) { + start = i; + } else if (!match && start !== -1) { + end = i - 1; + + if (end - start + 1 >= minMatchCharLength) { + matchedIndices.push([start, end]); + } + + start = -1; + } + } // (i-1 - start) + 1 => i - start + + + if (matchmask[i - 1] && i - start >= minMatchCharLength) { + matchedIndices.push([start, i - 1]); + } + + return matchedIndices; +}; + +/***/ }), + +/***/ "./src/bitap/bitap_pattern_alphabet.js": +/*!*********************************************!*\ + !*** ./src/bitap/bitap_pattern_alphabet.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = function (pattern) { + var mask = {}; + var len = pattern.length; + + for (var i = 0; i < len; i += 1) { + mask[pattern.charAt(i)] = 0; + } + + for (var _i = 0; _i < len; _i += 1) { + mask[pattern.charAt(_i)] |= 1 << len - _i - 1; + } + + return mask; +}; + +/***/ }), + +/***/ "./src/bitap/bitap_regex_search.js": +/*!*****************************************!*\ + !*** ./src/bitap/bitap_regex_search.js ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +var SPECIAL_CHARS_REGEX = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g; + +module.exports = function (text, pattern) { + var tokenSeparator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : / +/g; + var regex = new RegExp(pattern.replace(SPECIAL_CHARS_REGEX, '\\$&').replace(tokenSeparator, '|')); + var matches = text.match(regex); + var isMatch = !!matches; + var matchedIndices = []; + + if (isMatch) { + for (var i = 0, matchesLen = matches.length; i < matchesLen; i += 1) { + var match = matches[i]; + matchedIndices.push([text.indexOf(match), match.length - 1]); + } + } + + return { + // TODO: revisit this score + score: isMatch ? 0.5 : 1, + isMatch: isMatch, + matchedIndices: matchedIndices + }; +}; + +/***/ }), + +/***/ "./src/bitap/bitap_score.js": +/*!**********************************!*\ + !*** ./src/bitap/bitap_score.js ***! + \**********************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = function (pattern, _ref) { + var _ref$errors = _ref.errors, + errors = _ref$errors === void 0 ? 0 : _ref$errors, + _ref$currentLocation = _ref.currentLocation, + currentLocation = _ref$currentLocation === void 0 ? 0 : _ref$currentLocation, + _ref$expectedLocation = _ref.expectedLocation, + expectedLocation = _ref$expectedLocation === void 0 ? 0 : _ref$expectedLocation, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? 100 : _ref$distance; + var accuracy = errors / pattern.length; + var proximity = Math.abs(expectedLocation - currentLocation); + + if (!distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy; + } + + return accuracy + proximity / distance; +}; + +/***/ }), + +/***/ "./src/bitap/bitap_search.js": +/*!***********************************!*\ + !*** ./src/bitap/bitap_search.js ***! + \***********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +var bitapScore = __webpack_require__(/*! ./bitap_score */ "./src/bitap/bitap_score.js"); + +var matchedIndices = __webpack_require__(/*! ./bitap_matched_indices */ "./src/bitap/bitap_matched_indices.js"); + +module.exports = function (text, pattern, patternAlphabet, _ref) { + var _ref$location = _ref.location, + location = _ref$location === void 0 ? 0 : _ref$location, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? 100 : _ref$distance, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? 0.6 : _ref$threshold, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? false : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? 1 : _ref$minMatchCharLeng; + var expectedLocation = location; // Set starting location at beginning text and initialize the alphabet. + + var textLen = text.length; // Highest score beyond which we give up. + + var currentThreshold = threshold; // Is there a nearby exact match? (speedup) + + var bestLocation = text.indexOf(pattern, expectedLocation); + var patternLen = pattern.length; // a mask of the matches + + var matchMask = []; + + for (var i = 0; i < textLen; i += 1) { + matchMask[i] = 0; + } + + if (bestLocation !== -1) { + var score = bitapScore(pattern, { + errors: 0, + currentLocation: bestLocation, + expectedLocation: expectedLocation, + distance: distance + }); + currentThreshold = Math.min(score, currentThreshold); // What about in the other direction? (speed up) + + bestLocation = text.lastIndexOf(pattern, expectedLocation + patternLen); + + if (bestLocation !== -1) { + var _score = bitapScore(pattern, { + errors: 0, + currentLocation: bestLocation, + expectedLocation: expectedLocation, + distance: distance + }); + + currentThreshold = Math.min(_score, currentThreshold); + } + } // Reset the best location + + + bestLocation = -1; + var lastBitArr = []; + var finalScore = 1; + var binMax = patternLen + textLen; + var mask = 1 << patternLen - 1; + + for (var _i = 0; _i < patternLen; _i += 1) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from the match location we can stray + // at this error level. + var binMin = 0; + var binMid = binMax; + + while (binMin < binMid) { + var _score3 = bitapScore(pattern, { + errors: _i, + currentLocation: expectedLocation + binMid, + expectedLocation: expectedLocation, + distance: distance + }); + + if (_score3 <= currentThreshold) { + binMin = binMid; + } else { + binMax = binMid; + } + + binMid = Math.floor((binMax - binMin) / 2 + binMin); + } // Use the result from this iteration as the maximum for the next. + + + binMax = binMid; + var start = Math.max(1, expectedLocation - binMid + 1); + var finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen; // Initialize the bit array + + var bitArr = Array(finish + 2); + bitArr[finish + 1] = (1 << _i) - 1; + + for (var j = finish; j >= start; j -= 1) { + var currentLocation = j - 1; + var charMatch = patternAlphabet[text.charAt(currentLocation)]; + + if (charMatch) { + matchMask[currentLocation] = 1; + } // First pass: exact match + + + bitArr[j] = (bitArr[j + 1] << 1 | 1) & charMatch; // Subsequent passes: fuzzy match + + if (_i !== 0) { + bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1]; + } + + if (bitArr[j] & mask) { + finalScore = bitapScore(pattern, { + errors: _i, + currentLocation: currentLocation, + expectedLocation: expectedLocation, + distance: distance + }); // This match will almost certainly be better than any existing match. + // But check anyway. + + if (finalScore <= currentThreshold) { + // Indeed it is + currentThreshold = finalScore; + bestLocation = currentLocation; // Already passed `loc`, downhill from here on in. + + if (bestLocation <= expectedLocation) { + break; + } // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`. + + + start = Math.max(1, 2 * expectedLocation - bestLocation); + } + } + } // No hope for a (better) match at greater error levels. + + + var _score2 = bitapScore(pattern, { + errors: _i + 1, + currentLocation: expectedLocation, + expectedLocation: expectedLocation, + distance: distance + }); // console.log('score', score, finalScore) + + + if (_score2 > currentThreshold) { + break; + } + + lastBitArr = bitArr; + } // console.log('FINAL SCORE', finalScore) + // Count exact matches (those with a score of 0) to be "almost" exact + + + return { + isMatch: bestLocation >= 0, + score: finalScore === 0 ? 0.001 : finalScore, + matchedIndices: matchedIndices(matchMask, minMatchCharLength) + }; +}; + +/***/ }), + +/***/ "./src/bitap/index.js": +/*!****************************!*\ + !*** ./src/bitap/index.js ***! + \****************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var bitapRegexSearch = __webpack_require__(/*! ./bitap_regex_search */ "./src/bitap/bitap_regex_search.js"); + +var bitapSearch = __webpack_require__(/*! ./bitap_search */ "./src/bitap/bitap_search.js"); + +var patternAlphabet = __webpack_require__(/*! ./bitap_pattern_alphabet */ "./src/bitap/bitap_pattern_alphabet.js"); + +var Bitap = +/*#__PURE__*/ +function () { + function Bitap(pattern, _ref) { + var _ref$location = _ref.location, + location = _ref$location === void 0 ? 0 : _ref$location, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? 100 : _ref$distance, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? 0.6 : _ref$threshold, + _ref$maxPatternLength = _ref.maxPatternLength, + maxPatternLength = _ref$maxPatternLength === void 0 ? 32 : _ref$maxPatternLength, + _ref$isCaseSensitive = _ref.isCaseSensitive, + isCaseSensitive = _ref$isCaseSensitive === void 0 ? false : _ref$isCaseSensitive, + _ref$tokenSeparator = _ref.tokenSeparator, + tokenSeparator = _ref$tokenSeparator === void 0 ? / +/g : _ref$tokenSeparator, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? false : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? 1 : _ref$minMatchCharLeng; + + _classCallCheck(this, Bitap); + + this.options = { + location: location, + distance: distance, + threshold: threshold, + maxPatternLength: maxPatternLength, + isCaseSensitive: isCaseSensitive, + tokenSeparator: tokenSeparator, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength + }; + this.pattern = this.options.isCaseSensitive ? pattern : pattern.toLowerCase(); + + if (this.pattern.length <= maxPatternLength) { + this.patternAlphabet = patternAlphabet(this.pattern); + } + } + + _createClass(Bitap, [{ + key: "search", + value: function search(text) { + if (!this.options.isCaseSensitive) { + text = text.toLowerCase(); + } // Exact match + + + if (this.pattern === text) { + return { + isMatch: true, + score: 0, + matchedIndices: [[0, text.length - 1]] + }; + } // When pattern length is greater than the machine word length, just do a a regex comparison + + + var _this$options = this.options, + maxPatternLength = _this$options.maxPatternLength, + tokenSeparator = _this$options.tokenSeparator; + + if (this.pattern.length > maxPatternLength) { + return bitapRegexSearch(text, this.pattern, tokenSeparator); + } // Otherwise, use Bitap algorithm + + + var _this$options2 = this.options, + location = _this$options2.location, + distance = _this$options2.distance, + threshold = _this$options2.threshold, + findAllMatches = _this$options2.findAllMatches, + minMatchCharLength = _this$options2.minMatchCharLength; + return bitapSearch(text, this.pattern, this.patternAlphabet, { + location: location, + distance: distance, + threshold: threshold, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength + }); + } + }]); + + return Bitap; +}(); // let x = new Bitap("od mn war", {}) +// let result = x.search("Old Man's War") +// console.log(result) + + +module.exports = Bitap; + +/***/ }), + +/***/ "./src/helpers/deep_value.js": +/*!***********************************!*\ + !*** ./src/helpers/deep_value.js ***! + \***********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +var isArray = __webpack_require__(/*! ./is_array */ "./src/helpers/is_array.js"); + +var deepValue = function deepValue(obj, path, list) { + if (!path) { + // If there's no path left, we've gotten to the object we care about. + list.push(obj); + } else { + var dotIndex = path.indexOf('.'); + var firstSegment = path; + var remaining = null; + + if (dotIndex !== -1) { + firstSegment = path.slice(0, dotIndex); + remaining = path.slice(dotIndex + 1); + } + + var value = obj[firstSegment]; + + if (value !== null && value !== undefined) { + if (!remaining && (typeof value === 'string' || typeof value === 'number')) { + list.push(value.toString()); + } else if (isArray(value)) { + // Search each item in the array. + for (var i = 0, len = value.length; i < len; i += 1) { + deepValue(value[i], remaining, list); + } + } else if (remaining) { + // An object. Recurse further. + deepValue(value, remaining, list); + } + } + } + + return list; +}; + +module.exports = function (obj, path) { + return deepValue(obj, path, []); +}; + +/***/ }), + +/***/ "./src/helpers/is_array.js": +/*!*********************************!*\ + !*** ./src/helpers/is_array.js ***! + \*********************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +module.exports = function (obj) { + return !Array.isArray ? Object.prototype.toString.call(obj) === '[object Array]' : Array.isArray(obj); +}; + +/***/ }), + +/***/ "./src/index.js": +/*!**********************!*\ + !*** ./src/index.js ***! + \**********************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var Bitap = __webpack_require__(/*! ./bitap */ "./src/bitap/index.js"); + +var deepValue = __webpack_require__(/*! ./helpers/deep_value */ "./src/helpers/deep_value.js"); + +var isArray = __webpack_require__(/*! ./helpers/is_array */ "./src/helpers/is_array.js"); + +var Fuse = +/*#__PURE__*/ +function () { + function Fuse(list, _ref) { + var _ref$location = _ref.location, + location = _ref$location === void 0 ? 0 : _ref$location, + _ref$distance = _ref.distance, + distance = _ref$distance === void 0 ? 100 : _ref$distance, + _ref$threshold = _ref.threshold, + threshold = _ref$threshold === void 0 ? 0.6 : _ref$threshold, + _ref$maxPatternLength = _ref.maxPatternLength, + maxPatternLength = _ref$maxPatternLength === void 0 ? 32 : _ref$maxPatternLength, + _ref$caseSensitive = _ref.caseSensitive, + caseSensitive = _ref$caseSensitive === void 0 ? false : _ref$caseSensitive, + _ref$tokenSeparator = _ref.tokenSeparator, + tokenSeparator = _ref$tokenSeparator === void 0 ? / +/g : _ref$tokenSeparator, + _ref$findAllMatches = _ref.findAllMatches, + findAllMatches = _ref$findAllMatches === void 0 ? false : _ref$findAllMatches, + _ref$minMatchCharLeng = _ref.minMatchCharLength, + minMatchCharLength = _ref$minMatchCharLeng === void 0 ? 1 : _ref$minMatchCharLeng, + _ref$id = _ref.id, + id = _ref$id === void 0 ? null : _ref$id, + _ref$keys = _ref.keys, + keys = _ref$keys === void 0 ? [] : _ref$keys, + _ref$shouldSort = _ref.shouldSort, + shouldSort = _ref$shouldSort === void 0 ? true : _ref$shouldSort, + _ref$getFn = _ref.getFn, + getFn = _ref$getFn === void 0 ? deepValue : _ref$getFn, + _ref$sortFn = _ref.sortFn, + sortFn = _ref$sortFn === void 0 ? function (a, b) { + return a.score - b.score; + } : _ref$sortFn, + _ref$tokenize = _ref.tokenize, + tokenize = _ref$tokenize === void 0 ? false : _ref$tokenize, + _ref$matchAllTokens = _ref.matchAllTokens, + matchAllTokens = _ref$matchAllTokens === void 0 ? false : _ref$matchAllTokens, + _ref$includeMatches = _ref.includeMatches, + includeMatches = _ref$includeMatches === void 0 ? false : _ref$includeMatches, + _ref$includeScore = _ref.includeScore, + includeScore = _ref$includeScore === void 0 ? false : _ref$includeScore, + _ref$verbose = _ref.verbose, + verbose = _ref$verbose === void 0 ? false : _ref$verbose; + + _classCallCheck(this, Fuse); + + this.options = { + location: location, + distance: distance, + threshold: threshold, + maxPatternLength: maxPatternLength, + isCaseSensitive: caseSensitive, + tokenSeparator: tokenSeparator, + findAllMatches: findAllMatches, + minMatchCharLength: minMatchCharLength, + id: id, + keys: keys, + includeMatches: includeMatches, + includeScore: includeScore, + shouldSort: shouldSort, + getFn: getFn, + sortFn: sortFn, + verbose: verbose, + tokenize: tokenize, + matchAllTokens: matchAllTokens + }; + this.setCollection(list); + } + + _createClass(Fuse, [{ + key: "setCollection", + value: function setCollection(list) { + this.list = list; + return list; + } + }, { + key: "search", + value: function search(pattern) { + var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { + limit: false + }; + + this._log("---------\nSearch pattern: \"".concat(pattern, "\"")); + + var _this$_prepareSearche = this._prepareSearchers(pattern), + tokenSearchers = _this$_prepareSearche.tokenSearchers, + fullSearcher = _this$_prepareSearche.fullSearcher; + + var _this$_search = this._search(tokenSearchers, fullSearcher), + weights = _this$_search.weights, + results = _this$_search.results; + + this._computeScore(weights, results); + + if (this.options.shouldSort) { + this._sort(results); + } + + if (opts.limit && typeof opts.limit === 'number') { + results = results.slice(0, opts.limit); + } + + return this._format(results); + } + }, { + key: "_prepareSearchers", + value: function _prepareSearchers() { + var pattern = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; + var tokenSearchers = []; + + if (this.options.tokenize) { + // Tokenize on the separator + var tokens = pattern.split(this.options.tokenSeparator); + + for (var i = 0, len = tokens.length; i < len; i += 1) { + tokenSearchers.push(new Bitap(tokens[i], this.options)); + } + } + + var fullSearcher = new Bitap(pattern, this.options); + return { + tokenSearchers: tokenSearchers, + fullSearcher: fullSearcher + }; + } + }, { + key: "_search", + value: function _search() { + var tokenSearchers = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var fullSearcher = arguments.length > 1 ? arguments[1] : undefined; + var list = this.list; + var resultMap = {}; + var results = []; // Check the first item in the list, if it's a string, then we assume + // that every item in the list is also a string, and thus it's a flattened array. + + if (typeof list[0] === 'string') { + // Iterate over every item + for (var i = 0, len = list.length; i < len; i += 1) { + this._analyze({ + key: '', + value: list[i], + record: i, + index: i + }, { + resultMap: resultMap, + results: results, + tokenSearchers: tokenSearchers, + fullSearcher: fullSearcher + }); + } + + return { + weights: null, + results: results + }; + } // Otherwise, the first item is an Object (hopefully), and thus the searching + // is done on the values of the keys of each item. + + + var weights = {}; + + for (var _i = 0, _len = list.length; _i < _len; _i += 1) { + var item = list[_i]; // Iterate over every key + + for (var j = 0, keysLen = this.options.keys.length; j < keysLen; j += 1) { + var key = this.options.keys[j]; + + if (typeof key !== 'string') { + weights[key.name] = { + weight: 1 - key.weight || 1 + }; + + if (key.weight <= 0 || key.weight > 1) { + throw new Error('Key weight has to be > 0 and <= 1'); + } + + key = key.name; + } else { + weights[key] = { + weight: 1 + }; + } + + this._analyze({ + key: key, + value: this.options.getFn(item, key), + record: item, + index: _i + }, { + resultMap: resultMap, + results: results, + tokenSearchers: tokenSearchers, + fullSearcher: fullSearcher + }); + } + } + + return { + weights: weights, + results: results + }; + } + }, { + key: "_analyze", + value: function _analyze(_ref2, _ref3) { + var key = _ref2.key, + _ref2$arrayIndex = _ref2.arrayIndex, + arrayIndex = _ref2$arrayIndex === void 0 ? -1 : _ref2$arrayIndex, + value = _ref2.value, + record = _ref2.record, + index = _ref2.index; + var _ref3$tokenSearchers = _ref3.tokenSearchers, + tokenSearchers = _ref3$tokenSearchers === void 0 ? [] : _ref3$tokenSearchers, + _ref3$fullSearcher = _ref3.fullSearcher, + fullSearcher = _ref3$fullSearcher === void 0 ? [] : _ref3$fullSearcher, + _ref3$resultMap = _ref3.resultMap, + resultMap = _ref3$resultMap === void 0 ? {} : _ref3$resultMap, + _ref3$results = _ref3.results, + results = _ref3$results === void 0 ? [] : _ref3$results; + + // Check if the texvaluet can be searched + if (value === undefined || value === null) { + return; + } + + var exists = false; + var averageScore = -1; + var numTextMatches = 0; + + if (typeof value === 'string') { + this._log("\nKey: ".concat(key === '' ? '-' : key)); + + var mainSearchResult = fullSearcher.search(value); + + this._log("Full text: \"".concat(value, "\", score: ").concat(mainSearchResult.score)); + + if (this.options.tokenize) { + var words = value.split(this.options.tokenSeparator); + var scores = []; + + for (var i = 0; i < tokenSearchers.length; i += 1) { + var tokenSearcher = tokenSearchers[i]; + + this._log("\nPattern: \"".concat(tokenSearcher.pattern, "\"")); // let tokenScores = [] + + + var hasMatchInText = false; + + for (var j = 0; j < words.length; j += 1) { + var word = words[j]; + var tokenSearchResult = tokenSearcher.search(word); + var obj = {}; + + if (tokenSearchResult.isMatch) { + obj[word] = tokenSearchResult.score; + exists = true; + hasMatchInText = true; + scores.push(tokenSearchResult.score); + } else { + obj[word] = 1; + + if (!this.options.matchAllTokens) { + scores.push(1); + } + } + + this._log("Token: \"".concat(word, "\", score: ").concat(obj[word])); // tokenScores.push(obj) + + } + + if (hasMatchInText) { + numTextMatches += 1; + } + } + + averageScore = scores[0]; + var scoresLen = scores.length; + + for (var _i2 = 1; _i2 < scoresLen; _i2 += 1) { + averageScore += scores[_i2]; + } + + averageScore = averageScore / scoresLen; + + this._log('Token score average:', averageScore); + } + + var finalScore = mainSearchResult.score; + + if (averageScore > -1) { + finalScore = (finalScore + averageScore) / 2; + } + + this._log('Score average:', finalScore); + + var checkTextMatches = this.options.tokenize && this.options.matchAllTokens ? numTextMatches >= tokenSearchers.length : true; + + this._log("\nCheck Matches: ".concat(checkTextMatches)); // If a match is found, add the item to , including its score + + + if ((exists || mainSearchResult.isMatch) && checkTextMatches) { + // Check if the item already exists in our results + var existingResult = resultMap[index]; + + if (existingResult) { + // Use the lowest score + // existingResult.score, bitapResult.score + existingResult.output.push({ + key: key, + arrayIndex: arrayIndex, + value: value, + score: finalScore, + matchedIndices: mainSearchResult.matchedIndices + }); + } else { + // Add it to the raw result list + resultMap[index] = { + item: record, + output: [{ + key: key, + arrayIndex: arrayIndex, + value: value, + score: finalScore, + matchedIndices: mainSearchResult.matchedIndices + }] + }; + results.push(resultMap[index]); + } + } + } else if (isArray(value)) { + for (var _i3 = 0, len = value.length; _i3 < len; _i3 += 1) { + this._analyze({ + key: key, + arrayIndex: _i3, + value: value[_i3], + record: record, + index: index + }, { + resultMap: resultMap, + results: results, + tokenSearchers: tokenSearchers, + fullSearcher: fullSearcher + }); + } + } + } + }, { + key: "_computeScore", + value: function _computeScore(weights, results) { + this._log('\n\nComputing score:\n'); + + for (var i = 0, len = results.length; i < len; i += 1) { + var output = results[i].output; + var scoreLen = output.length; + var currScore = 1; + var bestScore = 1; + + for (var j = 0; j < scoreLen; j += 1) { + var weight = weights ? weights[output[j].key].weight : 1; + var score = weight === 1 ? output[j].score : output[j].score || 0.001; + var nScore = score * weight; + + if (weight !== 1) { + bestScore = Math.min(bestScore, nScore); + } else { + output[j].nScore = nScore; + currScore *= nScore; + } + } + + results[i].score = bestScore === 1 ? currScore : bestScore; + + this._log(results[i]); + } + } + }, { + key: "_sort", + value: function _sort(results) { + this._log('\n\nSorting....'); + + results.sort(this.options.sortFn); + } + }, { + key: "_format", + value: function _format(results) { + var finalOutput = []; + + if (this.options.verbose) { + var cache = []; + + this._log('\n\nOutput:\n\n', JSON.stringify(results, function (key, value) { + if (_typeof(value) === 'object' && value !== null) { + if (cache.indexOf(value) !== -1) { + // Circular reference found, discard key + return; + } // Store value in our collection + + + cache.push(value); + } + + return value; + })); + + cache = null; + } + + var transformers = []; + + if (this.options.includeMatches) { + transformers.push(function (result, data) { + var output = result.output; + data.matches = []; + + for (var i = 0, len = output.length; i < len; i += 1) { + var item = output[i]; + + if (item.matchedIndices.length === 0) { + continue; + } + + var obj = { + indices: item.matchedIndices, + value: item.value + }; + + if (item.key) { + obj.key = item.key; + } + + if (item.hasOwnProperty('arrayIndex') && item.arrayIndex > -1) { + obj.arrayIndex = item.arrayIndex; + } + + data.matches.push(obj); + } + }); + } + + if (this.options.includeScore) { + transformers.push(function (result, data) { + data.score = result.score; + }); + } + + for (var i = 0, len = results.length; i < len; i += 1) { + var result = results[i]; + + if (this.options.id) { + result.item = this.options.getFn(result.item, this.options.id)[0]; + } + + if (!transformers.length) { + finalOutput.push(result.item); + continue; + } + + var data = { + item: result.item + }; + + for (var j = 0, _len2 = transformers.length; j < _len2; j += 1) { + transformers[j](result, data); + } + + finalOutput.push(data); + } + + return finalOutput; + } + }, { + key: "_log", + value: function _log() { + if (this.options.verbose) { + var _console; + + (_console = console).log.apply(_console, arguments); + } + } + }]); + + return Fuse; +}(); + +module.exports = Fuse; + +/***/ }) + +/******/ }); +}); + +/***/ }), +/* 12 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +var isMergeableObject = function isMergeableObject(value) { + return isNonNullObject(value) + && !isSpecial(value) +}; + +function isNonNullObject(value) { + return !!value && typeof value === 'object' +} + +function isSpecial(value) { + var stringValue = Object.prototype.toString.call(value); + + return stringValue === '[object RegExp]' + || stringValue === '[object Date]' + || isReactElement(value) +} + +// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25 +var canUseSymbol = typeof Symbol === 'function' && Symbol.for; +var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7; + +function isReactElement(value) { + return value.$$typeof === REACT_ELEMENT_TYPE +} + +function emptyTarget(val) { + return Array.isArray(val) ? [] : {} +} + +function cloneUnlessOtherwiseSpecified(value, options) { + return (options.clone !== false && options.isMergeableObject(value)) + ? deepmerge(emptyTarget(value), value, options) + : value +} + +function defaultArrayMerge(target, source, options) { + return target.concat(source).map(function(element) { + return cloneUnlessOtherwiseSpecified(element, options) + }) +} + +function mergeObject(target, source, options) { + var destination = {}; + if (options.isMergeableObject(target)) { + Object.keys(target).forEach(function(key) { + destination[key] = cloneUnlessOtherwiseSpecified(target[key], options); + }); + } + Object.keys(source).forEach(function(key) { + if (!options.isMergeableObject(source[key]) || !target[key]) { + destination[key] = cloneUnlessOtherwiseSpecified(source[key], options); + } else { + destination[key] = deepmerge(target[key], source[key], options); + } + }); + return destination +} + +function deepmerge(target, source, options) { + options = options || {}; + options.arrayMerge = options.arrayMerge || defaultArrayMerge; + options.isMergeableObject = options.isMergeableObject || isMergeableObject; + + var sourceIsArray = Array.isArray(source); + var targetIsArray = Array.isArray(target); + var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray; + + if (!sourceAndTargetTypesMatch) { + return cloneUnlessOtherwiseSpecified(source, options) + } else if (sourceIsArray) { + return options.arrayMerge(target, source, options) + } else { + return mergeObject(target, source, options) + } +} + +deepmerge.all = function deepmergeAll(array, options) { + if (!Array.isArray(array)) { + throw new Error('first argument should be an array') + } + + return array.reduce(function(prev, next) { + return deepmerge(prev, next, options) + }, {}) +}; + +var deepmerge_1 = deepmerge; + +/* harmony default export */ __webpack_exports__["default"] = (deepmerge_1); + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _redux = __webpack_require__(6); + +var _index = _interopRequireDefault(__webpack_require__(15)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var Store = +/*#__PURE__*/ +function () { + function Store() { + _classCallCheck(this, Store); + + this._store = (0, _redux.createStore)(_index.default, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()); + } + /** + * Subscribe store to function call (wrapped Redux method) + * @param {Function} onChange Function to trigger when state changes + * @return + */ + + + _createClass(Store, [{ + key: "subscribe", + value: function subscribe(onChange) { + this._store.subscribe(onChange); + } + /** + * Dispatch event to store (wrapped Redux method) + * @param {Function} action Action function to trigger + * @return + */ + + }, { + key: "dispatch", + value: function dispatch(action) { + this._store.dispatch(action); + } + /** + * Get store object (wrapping Redux method) + * @return {Object} State + */ + + }, { + key: "isLoading", + + /** + * Get loading state from store + * @return {Boolean} Loading State + */ + value: function isLoading() { + return this.state.general.loading; + } + /** + * Get single choice by it's ID + * @return {Object} Found choice + */ + + }, { + key: "getChoiceById", + value: function getChoiceById(id) { + if (id) { + var choices = this.activeChoices; + var foundChoice = choices.find(function (choice) { + return choice.id === parseInt(id, 10); + }); + return foundChoice; + } + + return false; + } + /** + * Get group by group id + * @param {Number} id Group ID + * @return {Object} Group data + */ + + }, { + key: "getGroupById", + value: function getGroupById(id) { + return this.groups.find(function (group) { + return group.id === parseInt(id, 10); + }); + } + }, { + key: "state", + get: function get() { + return this._store.getState(); + } + /** + * Get items from store + * @return {Array} Item objects + */ + + }, { + key: "items", + get: function get() { + return this.state.items; + } + /** + * Get active items from store + * @return {Array} Item objects + */ + + }, { + key: "activeItems", + get: function get() { + return this.items.filter(function (item) { + return item.active === true; + }); + } + /** + * Get highlighted items from store + * @return {Array} Item objects + */ + + }, { + key: "highlightedActiveItems", + get: function get() { + return this.items.filter(function (item) { + return item.active && item.highlighted; + }); + } + /** + * Get choices from store + * @return {Array} Option objects + */ + + }, { + key: "choices", + get: function get() { + return this.state.choices; + } + /** + * Get active choices from store + * @return {Array} Option objects + */ + + }, { + key: "activeChoices", + get: function get() { + var choices = this.choices; + var values = choices.filter(function (choice) { + return choice.active === true; + }); + return values; + } + /** + * Get selectable choices from store + * @return {Array} Option objects + */ + + }, { + key: "selectableChoices", + get: function get() { + return this.choices.filter(function (choice) { + return choice.disabled !== true; + }); + } + /** + * Get choices that can be searched (excluding placeholders) + * @return {Array} Option objects + */ + + }, { + key: "searchableChoices", + get: function get() { + return this.selectableChoices.filter(function (choice) { + return choice.placeholder !== true; + }); + } + /** + * Get placeholder choice from store + * @return {Object} Found placeholder + */ + + }, { + key: "placeholderChoice", + get: function get() { + return [].concat(this.choices).reverse().find(function (choice) { + return choice.placeholder === true; + }); + } + /** + * Get groups from store + * @return {Array} Group objects + */ + + }, { + key: "groups", + get: function get() { + return this.state.groups; + } + /** + * Get active groups from store + * @return {Array} Group objects + */ + + }, { + key: "activeGroups", + get: function get() { + var groups = this.groups; + var choices = this.choices; + return groups.filter(function (group) { + var isActive = group.active === true && group.disabled === false; + var hasActiveOptions = choices.some(function (choice) { + return choice.active === true && choice.disabled === false; + }); + return isActive && hasActiveOptions; + }, []); + } + }]); + + return Store; +}(); + +exports.default = Store; + +/***/ }), +/* 14 */ +/***/ (function(module, exports) { + +module.exports = function(originalModule) { + if (!originalModule.webpackPolyfill) { + var module = Object.create(originalModule); + // module.parent = undefined by default + if (!module.children) module.children = []; + Object.defineProperty(module, "loaded", { + enumerable: true, + get: function() { + return module.l; + } + }); + Object.defineProperty(module, "id", { + enumerable: true, + get: function() { + return module.i; + } + }); + Object.defineProperty(module, "exports", { + enumerable: true + }); + module.webpackPolyfill = 1; + } + return module; +}; + + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _redux = __webpack_require__(6); + +var _items = _interopRequireDefault(__webpack_require__(16)); + +var _groups = _interopRequireDefault(__webpack_require__(17)); + +var _choices = _interopRequireDefault(__webpack_require__(18)); + +var _general = _interopRequireDefault(__webpack_require__(19)); + +var _utils = __webpack_require__(0); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var appReducer = (0, _redux.combineReducers)({ + items: _items.default, + groups: _groups.default, + choices: _choices.default, + general: _general.default +}); + +var rootReducer = function rootReducer(passedState, action) { + var state = passedState; // If we are clearing all items, groups and options we reassign + // state and then pass that state to our proper reducer. This isn't + // mutating our actual state + // See: http://stackoverflow.com/a/35641992 + + if (action.type === 'CLEAR_ALL') { + state = undefined; + } else if (action.type === 'RESET_TO') { + return (0, _utils.cloneObject)(action.state); + } + + return appReducer(state, action); +}; + +var _default = rootReducer; +exports.default = _default; + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = items; +exports.defaultState = void 0; +var defaultState = []; +exports.defaultState = defaultState; + +function items() { + var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultState; + var action = arguments.length > 1 ? arguments[1] : undefined; + + switch (action.type) { + case 'ADD_ITEM': + { + // Add object to items array + var newState = [].concat(state, [{ + id: action.id, + choiceId: action.choiceId, + groupId: action.groupId, + value: action.value, + label: action.label, + active: true, + highlighted: false, + customProperties: action.customProperties, + placeholder: action.placeholder || false, + keyCode: null + }]); + return newState.map(function (obj) { + var item = obj; + item.highlighted = false; + return item; + }); + } + + case 'REMOVE_ITEM': + { + // Set item to inactive + return state.map(function (obj) { + var item = obj; + + if (item.id === action.id) { + item.active = false; + } + + return item; + }); + } + + case 'HIGHLIGHT_ITEM': + { + return state.map(function (obj) { + var item = obj; + + if (item.id === action.id) { + item.highlighted = action.highlighted; + } + + return item; + }); + } + + default: + { + return state; + } + } +} + +/***/ }), +/* 17 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = groups; +exports.defaultState = void 0; +var defaultState = []; +exports.defaultState = defaultState; + +function groups() { + var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultState; + var action = arguments.length > 1 ? arguments[1] : undefined; + + switch (action.type) { + case 'ADD_GROUP': + { + return [].concat(state, [{ + id: action.id, + value: action.value, + active: action.active, + disabled: action.disabled + }]); + } + + case 'CLEAR_CHOICES': + { + return []; + } + + default: + { + return state; + } + } +} + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = choices; +exports.defaultState = void 0; +var defaultState = []; +exports.defaultState = defaultState; + +function choices() { + var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultState; + var action = arguments.length > 1 ? arguments[1] : undefined; + + switch (action.type) { + case 'ADD_CHOICE': + { + /* + A disabled choice appears in the choice dropdown but cannot be selected + A selected choice has been added to the passed input's value (added as an item) + An active choice appears within the choice dropdown + */ + return [].concat(state, [{ + id: action.id, + elementId: action.elementId, + groupId: action.groupId, + value: action.value, + label: action.label || action.value, + disabled: action.disabled || false, + selected: false, + active: true, + score: 9999, + customProperties: action.customProperties, + placeholder: action.placeholder || false, + keyCode: null + }]); + } + + case 'ADD_ITEM': + { + // If all choices need to be activated + if (action.activateOptions) { + return state.map(function (obj) { + var choice = obj; + choice.active = action.active; + return choice; + }); + } // When an item is added and it has an associated choice, + // we want to disable it so it can't be chosen again + + + if (action.choiceId > -1) { + return state.map(function (obj) { + var choice = obj; + + if (choice.id === parseInt(action.choiceId, 10)) { + choice.selected = true; + } + + return choice; + }); + } + + return state; + } + + case 'REMOVE_ITEM': + { + // When an item is removed and it has an associated choice, + // we want to re-enable it so it can be chosen again + if (action.choiceId > -1) { + return state.map(function (obj) { + var choice = obj; + + if (choice.id === parseInt(action.choiceId, 10)) { + choice.selected = false; + } + + return choice; + }); + } + + return state; + } + + case 'FILTER_CHOICES': + { + return state.map(function (obj) { + var choice = obj; // Set active state based on whether choice is + // within filtered results + + choice.active = action.results.some(function (_ref) { + var item = _ref.item, + score = _ref.score; + + if (item.id === choice.id) { + choice.score = score; + return true; + } + + return false; + }); + return choice; + }); + } + + case 'ACTIVATE_CHOICES': + { + return state.map(function (obj) { + var choice = obj; + choice.active = action.active; + return choice; + }); + } + + case 'CLEAR_CHOICES': + { + return defaultState; + } + + default: + { + return state; + } + } +} + +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.defaultState = void 0; +var defaultState = { + loading: false +}; +exports.defaultState = defaultState; + +var general = function general() { + var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultState; + var action = arguments.length > 1 ? arguments[1] : undefined; + + switch (action.type) { + case 'SET_IS_LOADING': + { + return { + loading: action.isLoading + }; + } + + default: + { + return state; + } + } +}; + +var _default = general; +exports.default = _default; + +/***/ }), +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "Dropdown", { + enumerable: true, + get: function get() { + return _dropdown.default; + } +}); +Object.defineProperty(exports, "Container", { + enumerable: true, + get: function get() { + return _container.default; + } +}); +Object.defineProperty(exports, "Input", { + enumerable: true, + get: function get() { + return _input.default; + } +}); +Object.defineProperty(exports, "List", { + enumerable: true, + get: function get() { + return _list.default; + } +}); +Object.defineProperty(exports, "WrappedInput", { + enumerable: true, + get: function get() { + return _wrappedInput.default; + } +}); +Object.defineProperty(exports, "WrappedSelect", { + enumerable: true, + get: function get() { + return _wrappedSelect.default; + } +}); + +var _dropdown = _interopRequireDefault(__webpack_require__(21)); + +var _container = _interopRequireDefault(__webpack_require__(22)); + +var _input = _interopRequireDefault(__webpack_require__(23)); + +var _list = _interopRequireDefault(__webpack_require__(24)); + +var _wrappedInput = _interopRequireDefault(__webpack_require__(25)); + +var _wrappedSelect = _interopRequireDefault(__webpack_require__(26)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/***/ }), +/* 21 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var Dropdown = +/*#__PURE__*/ +function () { + function Dropdown(_ref) { + var element = _ref.element, + type = _ref.type, + classNames = _ref.classNames; + + _classCallCheck(this, Dropdown); + + Object.assign(this, { + element: element, + type: type, + classNames: classNames + }); + this.isActive = false; + } + /** + * Determine how far the top of our element is from + * the top of the window + * @return {Number} Vertical position + */ + + + _createClass(Dropdown, [{ + key: "distanceFromTopWindow", + value: function distanceFromTopWindow() { + this.dimensions = this.element.getBoundingClientRect(); + this.position = Math.ceil(this.dimensions.top + window.pageYOffset + this.element.offsetHeight); + return this.position; + } + /** + * Find element that matches passed selector + * @return {HTMLElement} + */ + + }, { + key: "getChild", + value: function getChild(selector) { + return this.element.querySelector(selector); + } + /** + * Show dropdown to user by adding active state class + * @return {Object} Class instance + * @public + */ + + }, { + key: "show", + value: function show() { + this.element.classList.add(this.classNames.activeState); + this.element.setAttribute('aria-expanded', 'true'); + this.isActive = true; + return this; + } + /** + * Hide dropdown from user + * @return {Object} Class instance + * @public + */ + + }, { + key: "hide", + value: function hide() { + this.element.classList.remove(this.classNames.activeState); + this.element.setAttribute('aria-expanded', 'false'); + this.isActive = false; + return this; + } + }]); + + return Dropdown; +}(); + +exports.default = Dropdown; + +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _utils = __webpack_require__(0); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var Container = +/*#__PURE__*/ +function () { + function Container(_ref) { + var element = _ref.element, + type = _ref.type, + classNames = _ref.classNames, + position = _ref.position; + + _classCallCheck(this, Container); + + Object.assign(this, { + element: element, + classNames: classNames, + type: type, + position: position + }); + this.isOpen = false; + this.isFlipped = false; + this.isFocussed = false; + this.isDisabled = false; + this.isLoading = false; + this._onFocus = this._onFocus.bind(this); + this._onBlur = this._onBlur.bind(this); + } + /** + * Add event listeners + */ + + + _createClass(Container, [{ + key: "addEventListeners", + value: function addEventListeners() { + this.element.addEventListener('focus', this._onFocus); + this.element.addEventListener('blur', this._onBlur); + } + /** + * Remove event listeners + */ + + /** */ + + }, { + key: "removeEventListeners", + value: function removeEventListeners() { + this.element.removeEventListener('focus', this._onFocus); + this.element.removeEventListener('blur', this._onBlur); + } + /** + * Determine whether container should be flipped + * based on passed dropdown position + * @param {Number} dropdownPos + * @returns + */ + + }, { + key: "shouldFlip", + value: function shouldFlip(dropdownPos) { + var windowHeight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : (0, _utils.getWindowHeight)(); + + if (dropdownPos === undefined) { + return false; + } // If flip is enabled and the dropdown bottom position is + // greater than the window height flip the dropdown. + + + var shouldFlip = false; + + if (this.position === 'auto') { + shouldFlip = dropdownPos >= windowHeight; + } else if (this.position === 'top') { + shouldFlip = true; + } + + return shouldFlip; + } + /** + * Set active descendant attribute + * @param {Number} activeDescendant ID of active descendant + */ + + }, { + key: "setActiveDescendant", + value: function setActiveDescendant(activeDescendantID) { + this.element.setAttribute('aria-activedescendant', activeDescendantID); + } + /** + * Remove active descendant attribute + */ + + }, { + key: "removeActiveDescendant", + value: function removeActiveDescendant() { + this.element.removeAttribute('aria-activedescendant'); + } + }, { + key: "open", + value: function open(dropdownPos) { + this.element.classList.add(this.classNames.openState); + this.element.setAttribute('aria-expanded', 'true'); + this.isOpen = true; + + if (this.shouldFlip(dropdownPos)) { + this.element.classList.add(this.classNames.flippedState); + this.isFlipped = true; + } + } + }, { + key: "close", + value: function close() { + this.element.classList.remove(this.classNames.openState); + this.element.setAttribute('aria-expanded', 'false'); + this.removeActiveDescendant(); + this.isOpen = false; // A dropdown flips if it does not have space within the page + + if (this.isFlipped) { + this.element.classList.remove(this.classNames.flippedState); + this.isFlipped = false; + } + } + }, { + key: "focus", + value: function focus() { + if (!this.isFocussed) { + this.element.focus(); + } + } + }, { + key: "addFocusState", + value: function addFocusState() { + this.element.classList.add(this.classNames.focusState); + } + }, { + key: "removeFocusState", + value: function removeFocusState() { + this.element.classList.remove(this.classNames.focusState); + } + /** + * Remove disabled state + */ + + }, { + key: "enable", + value: function enable() { + this.element.classList.remove(this.classNames.disabledState); + this.element.removeAttribute('aria-disabled'); + + if (this.type === 'select-one') { + this.element.setAttribute('tabindex', '0'); + } + + this.isDisabled = false; + } + /** + * Set disabled state + */ + + }, { + key: "disable", + value: function disable() { + this.element.classList.add(this.classNames.disabledState); + this.element.setAttribute('aria-disabled', 'true'); + + if (this.type === 'select-one') { + this.element.setAttribute('tabindex', '-1'); + } + + this.isDisabled = true; + } + }, { + key: "wrap", + value: function wrap(element) { + (0, _utils.wrap)(element, this.element); + } + }, { + key: "unwrap", + value: function unwrap(element) { + // Move passed element outside this element + this.element.parentNode.insertBefore(element, this.element); // Remove this element + + this.element.parentNode.removeChild(this.element); + } + /** + * Add loading state to element + */ + + }, { + key: "addLoadingState", + value: function addLoadingState() { + this.element.classList.add(this.classNames.loadingState); + this.element.setAttribute('aria-busy', 'true'); + this.isLoading = true; + } + /** + * Remove loading state from element + */ + + }, { + key: "removeLoadingState", + value: function removeLoadingState() { + this.element.classList.remove(this.classNames.loadingState); + this.element.removeAttribute('aria-busy'); + this.isLoading = false; + } + /** + * Set focussed state + */ + + }, { + key: "_onFocus", + value: function _onFocus() { + this.isFocussed = true; + } + /** + * Remove blurred state + */ + + }, { + key: "_onBlur", + value: function _onBlur() { + this.isFocussed = false; + } + }]); + + return Container; +}(); + +exports.default = Container; + +/***/ }), +/* 23 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _utils = __webpack_require__(0); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var Input = +/*#__PURE__*/ +function () { + function Input(_ref) { + var element = _ref.element, + type = _ref.type, + classNames = _ref.classNames, + placeholderValue = _ref.placeholderValue; + + _classCallCheck(this, Input); + + Object.assign(this, { + element: element, + type: type, + classNames: classNames, + placeholderValue: placeholderValue + }); + this.element = element; + this.classNames = classNames; + this.isFocussed = this.element === document.activeElement; + this.isDisabled = false; + this._onPaste = this._onPaste.bind(this); + this._onInput = this._onInput.bind(this); + this._onFocus = this._onFocus.bind(this); + this._onBlur = this._onBlur.bind(this); + } + + _createClass(Input, [{ + key: "addEventListeners", + value: function addEventListeners() { + this.element.addEventListener('input', this._onInput); + this.element.addEventListener('paste', this._onPaste); + this.element.addEventListener('focus', this._onFocus); + this.element.addEventListener('blur', this._onBlur); + + if (this.element.form) { + this.element.form.addEventListener('reset', this._onFormReset); + } + } + }, { + key: "removeEventListeners", + value: function removeEventListeners() { + this.element.removeEventListener('input', this._onInput); + this.element.removeEventListener('paste', this._onPaste); + this.element.removeEventListener('focus', this._onFocus); + this.element.removeEventListener('blur', this._onBlur); + + if (this.element.form) { + this.element.form.removeEventListener('reset', this._onFormReset); + } + } + }, { + key: "enable", + value: function enable() { + this.element.removeAttribute('disabled'); + this.isDisabled = false; + } + }, { + key: "disable", + value: function disable() { + this.element.setAttribute('disabled', ''); + this.isDisabled = true; + } + }, { + key: "focus", + value: function focus() { + if (!this.isFocussed) { + this.element.focus(); + } + } + }, { + key: "blur", + value: function blur() { + if (this.isFocussed) { + this.element.blur(); + } + } + /** + * Set value of input to blank + * @return {Object} Class instance + * @public + */ + + }, { + key: "clear", + value: function clear() { + var setWidth = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + + if (this.element.value) { + this.element.value = ''; + } + + if (setWidth) { + this.setWidth(); + } + + return this; + } + /** + * Set the correct input width based on placeholder + * value or input value + * @return + */ + + }, { + key: "setWidth", + value: function setWidth(enforceWidth) { + var _this = this; + + var callback = function callback(width) { + _this.element.style.width = width; + }; + + if (this._placeholderValue) { + // If there is a placeholder, we only want to set the width of the input when it is a greater + // length than 75% of the placeholder. This stops the input jumping around. + var valueHasDesiredLength = this.element.value.length >= this._placeholderValue.length / 1.25; + + if (this.element.value && valueHasDesiredLength || enforceWidth) { + this.calcWidth(callback); + } + } else { + // If there is no placeholder, resize input to contents + this.calcWidth(callback); + } + } + }, { + key: "calcWidth", + value: function calcWidth(callback) { + return (0, _utils.calcWidthOfInput)(this.element, callback); + } + }, { + key: "setActiveDescendant", + value: function setActiveDescendant(activeDescendantID) { + this.element.setAttribute('aria-activedescendant', activeDescendantID); + } + }, { + key: "removeActiveDescendant", + value: function removeActiveDescendant() { + this.element.removeAttribute('aria-activedescendant'); + } + }, { + key: "_onInput", + value: function _onInput() { + if (this.type !== 'select-one') { + this.setWidth(); + } + } + }, { + key: "_onPaste", + value: function _onPaste(event) { + var target = event.target; + + if (target === this.element && this.preventPaste) { + event.preventDefault(); + } + } + }, { + key: "_onFocus", + value: function _onFocus() { + this.isFocussed = true; + } + }, { + key: "_onBlur", + value: function _onBlur() { + this.isFocussed = false; + } + }, { + key: "placeholder", + set: function set(placeholder) { + this.element.placeholder = placeholder; + } + }, { + key: "value", + set: function set(value) { + this.element.value = value; + }, + get: function get() { + return (0, _utils.sanitise)(this.element.value); + } + }]); + + return Input; +}(); + +exports.default = Input; + +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _constants = __webpack_require__(1); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var List = +/*#__PURE__*/ +function () { + function List(_ref) { + var element = _ref.element; + + _classCallCheck(this, List); + + Object.assign(this, { + element: element + }); + this.scrollPos = this.element.scrollTop; + this.height = this.element.offsetHeight; + this.hasChildren = !!this.element.children; + } + + _createClass(List, [{ + key: "clear", + value: function clear() { + this.element.innerHTML = ''; + } + }, { + key: "append", + value: function append(node) { + this.element.appendChild(node); + } + }, { + key: "getChild", + value: function getChild(selector) { + return this.element.querySelector(selector); + } + }, { + key: "scrollToTop", + value: function scrollToTop() { + this.element.scrollTop = 0; + } + }, { + key: "scrollToChoice", + value: function scrollToChoice(choice, direction) { + var _this = this; + + if (!choice) { + return; + } + + var dropdownHeight = this.element.offsetHeight; + var choiceHeight = choice.offsetHeight; // Distance from bottom of element to top of parent + + var choicePos = choice.offsetTop + choiceHeight; // Scroll position of dropdown + + var containerScrollPos = this.element.scrollTop + dropdownHeight; // Difference between the choice and scroll position + + var endpoint = direction > 0 ? this.element.scrollTop + choicePos - containerScrollPos : choice.offsetTop; + requestAnimationFrame(function (time) { + _this._animateScroll(time, endpoint, direction); + }); + } + }, { + key: "_scrollDown", + value: function _scrollDown(scrollPos, strength, endpoint) { + var easing = (endpoint - scrollPos) / strength; + var distance = easing > 1 ? easing : 1; + this.element.scrollTop = scrollPos + distance; + } + }, { + key: "_scrollUp", + value: function _scrollUp(scrollPos, strength, endpoint) { + var easing = (scrollPos - endpoint) / strength; + var distance = easing > 1 ? easing : 1; + this.element.scrollTop = scrollPos - distance; + } + }, { + key: "_animateScroll", + value: function _animateScroll(time, endpoint, direction) { + var _this2 = this; + + var strength = _constants.SCROLLING_SPEED; + var choiceListScrollTop = this.element.scrollTop; + var continueAnimation = false; + + if (direction > 0) { + this._scrollDown(choiceListScrollTop, strength, endpoint); + + if (choiceListScrollTop < endpoint) { + continueAnimation = true; + } + } else { + this._scrollUp(choiceListScrollTop, strength, endpoint); + + if (choiceListScrollTop > endpoint) { + continueAnimation = true; + } + } + + if (continueAnimation) { + requestAnimationFrame(function () { + _this2._animateScroll(time, endpoint, direction); + }); + } + } + }]); + + return List; +}(); + +exports.default = List; + +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _wrappedElement = _interopRequireDefault(__webpack_require__(4)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } + +function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } + +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +var WrappedInput = +/*#__PURE__*/ +function (_WrappedElement) { + _inherits(WrappedInput, _WrappedElement); + + function WrappedInput(_ref) { + var _this; + + var element = _ref.element, + classNames = _ref.classNames, + delimiter = _ref.delimiter; + + _classCallCheck(this, WrappedInput); + + _this = _possibleConstructorReturn(this, _getPrototypeOf(WrappedInput).call(this, { + element: element, + classNames: classNames + })); + _this.delimiter = delimiter; + return _this; + } + + _createClass(WrappedInput, [{ + key: "value", + set: function set(items) { + var itemValues = items.map(function (_ref2) { + var value = _ref2.value; + return value; + }); + var joinedValues = itemValues.join(this.delimiter); + this.element.setAttribute('value', joinedValues); + this.element.value = joinedValues; + } // @todo figure out why we need this? Perhaps a babel issue + , + get: function get() { + return _get(_getPrototypeOf(WrappedInput.prototype), "value", this); + } + }]); + + return WrappedInput; +}(_wrappedElement.default); + +exports.default = WrappedInput; + +/***/ }), +/* 26 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _wrappedElement = _interopRequireDefault(__webpack_require__(4)); + +var _templates = _interopRequireDefault(__webpack_require__(5)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +var WrappedSelect = +/*#__PURE__*/ +function (_WrappedElement) { + _inherits(WrappedSelect, _WrappedElement); + + function WrappedSelect(_ref) { + var element = _ref.element, + classNames = _ref.classNames; + + _classCallCheck(this, WrappedSelect); + + return _possibleConstructorReturn(this, _getPrototypeOf(WrappedSelect).call(this, { + element: element, + classNames: classNames + })); + } + + _createClass(WrappedSelect, [{ + key: "appendDocFragment", + value: function appendDocFragment(fragment) { + this.element.innerHTML = ''; + this.element.appendChild(fragment); + } + }, { + key: "placeholderOption", + get: function get() { + return this.element.querySelector('option[placeholder]'); + } + }, { + key: "optionGroups", + get: function get() { + return Array.from(this.element.getElementsByTagName('OPTGROUP')); + } + }, { + key: "options", + get: function get() { + return Array.from(this.element.options); + }, + set: function set(options) { + var fragment = document.createDocumentFragment(); + + var addOptionToFragment = function addOptionToFragment(data) { + // Create a standard select option + var template = _templates.default.option(data); // Append it to fragment + + + fragment.appendChild(template); + }; // Add each list item to list + + + options.forEach(function (optionData) { + return addOptionToFragment(optionData); + }); + this.appendDocFragment(fragment); + } + }]); + + return WrappedSelect; +}(_wrappedElement.default); + +exports.default = WrappedSelect; + +/***/ }), +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { + +var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! + Copyright (c) 2017 Jed Watson. + Licensed under the MIT License (MIT), see + http://jedwatson.github.io/classnames +*/ +/* global define */ + +(function () { + 'use strict'; + + var hasOwn = {}.hasOwnProperty; + + function classNames () { + var classes = []; + + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + if (!arg) continue; + + var argType = typeof arg; + + if (argType === 'string' || argType === 'number') { + classes.push(arg); + } else if (Array.isArray(arg) && arg.length) { + var inner = classNames.apply(null, arg); + if (inner) { + classes.push(inner); + } + } else if (argType === 'object') { + for (var key in arg) { + if (hasOwn.call(arg, key) && arg[key]) { + classes.push(key); + } + } + } + } + + return classes.join(' '); + } + + if ( true && module.exports) { + classNames.default = classNames; + module.exports = classNames; + } else if (true) { + // register as 'classnames', consistent with npm package name + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function () { + return classNames; + }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), + __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else {} +}()); + + +/***/ }), +/* 28 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.clearChoices = exports.activateChoices = exports.filterChoices = exports.addChoice = void 0; + +var _constants = __webpack_require__(1); + +var addChoice = function addChoice(_ref) { + var value = _ref.value, + label = _ref.label, + id = _ref.id, + groupId = _ref.groupId, + disabled = _ref.disabled, + elementId = _ref.elementId, + customProperties = _ref.customProperties, + placeholder = _ref.placeholder, + keyCode = _ref.keyCode; + return { + type: _constants.ACTION_TYPES.ADD_CHOICE, + value: value, + label: label, + id: id, + groupId: groupId, + disabled: disabled, + elementId: elementId, + customProperties: customProperties, + placeholder: placeholder, + keyCode: keyCode + }; +}; + +exports.addChoice = addChoice; + +var filterChoices = function filterChoices(results) { + return { + type: _constants.ACTION_TYPES.FILTER_CHOICES, + results: results + }; +}; + +exports.filterChoices = filterChoices; + +var activateChoices = function activateChoices() { + var active = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + return { + type: _constants.ACTION_TYPES.ACTIVATE_CHOICES, + active: active + }; +}; + +exports.activateChoices = activateChoices; + +var clearChoices = function clearChoices() { + return { + type: _constants.ACTION_TYPES.CLEAR_CHOICES + }; +}; + +exports.clearChoices = clearChoices; + +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.highlightItem = exports.removeItem = exports.addItem = void 0; + +var _constants = __webpack_require__(1); + +var addItem = function addItem(_ref) { + var value = _ref.value, + label = _ref.label, + id = _ref.id, + choiceId = _ref.choiceId, + groupId = _ref.groupId, + customProperties = _ref.customProperties, + placeholder = _ref.placeholder, + keyCode = _ref.keyCode; + return { + type: _constants.ACTION_TYPES.ADD_ITEM, + value: value, + label: label, + id: id, + choiceId: choiceId, + groupId: groupId, + customProperties: customProperties, + placeholder: placeholder, + keyCode: keyCode + }; +}; + +exports.addItem = addItem; + +var removeItem = function removeItem(id, choiceId) { + return { + type: _constants.ACTION_TYPES.REMOVE_ITEM, + id: id, + choiceId: choiceId + }; +}; + +exports.removeItem = removeItem; + +var highlightItem = function highlightItem(id, highlighted) { + return { + type: _constants.ACTION_TYPES.HIGHLIGHT_ITEM, + id: id, + highlighted: highlighted + }; +}; + +exports.highlightItem = highlightItem; + +/***/ }), +/* 30 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.addGroup = void 0; + +var _constants = __webpack_require__(1); + +/* eslint-disable import/prefer-default-export */ +var addGroup = function addGroup(value, id, active, disabled) { + return { + type: _constants.ACTION_TYPES.ADD_GROUP, + value: value, + id: id, + active: active, + disabled: disabled + }; +}; + +exports.addGroup = addGroup; + +/***/ }), +/* 31 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.resetTo = exports.clearAll = void 0; + +var clearAll = function clearAll() { + return { + type: 'CLEAR_ALL' + }; +}; + +exports.clearAll = clearAll; + +var resetTo = function resetTo(state) { + return { + type: 'RESET_TO', + state: state + }; +}; + +exports.resetTo = resetTo; + +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.setIsLoading = void 0; + +/* eslint-disable import/prefer-default-export */ +var setIsLoading = function setIsLoading(isLoading) { + return { + type: 'SET_IS_LOADING', + isLoading: isLoading + }; +}; + +exports.setIsLoading = setIsLoading; + +/***/ }) +/******/ ]); +}); \ No newline at end of file diff --git a/bin/htdocs/vendor/choices/choices.min.css b/bin/htdocs/vendor/choices/choices.min.css new file mode 100644 index 0000000..d4268fb --- /dev/null +++ b/bin/htdocs/vendor/choices/choices.min.css @@ -0,0 +1 @@ +.choices{position:relative;margin-bottom:24px;font-size:16px}.choices:focus{outline:none}.choices:last-child{margin-bottom:0}.choices.is-disabled .choices__inner,.choices.is-disabled .choices__input{background-color:#eaeaea;cursor:not-allowed;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.choices.is-disabled .choices__item{cursor:not-allowed}.choices[data-type*=select-one]{cursor:pointer}.choices[data-type*=select-one] .choices__inner{padding-bottom:7.5px}.choices[data-type*=select-one] .choices__input{display:block;width:100%;padding:10px;border-bottom:1px solid #ddd;background-color:#fff;margin:0}.choices[data-type*=select-one] .choices__button{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);padding:0;background-size:8px;position:absolute;top:50%;right:0;margin-top:-10px;margin-right:25px;height:20px;width:20px;border-radius:10em;opacity:.5}.choices[data-type*=select-one] .choices__button:focus,.choices[data-type*=select-one] .choices__button:hover{opacity:1}.choices[data-type*=select-one] .choices__button:focus{box-shadow:0 0 0 2px #00bcd4}.choices[data-type*=select-one]:after{content:"";height:0;width:0;border-style:solid;border-color:#333 transparent transparent transparent;border-width:5px;position:absolute;right:11.5px;top:50%;margin-top:-2.5px;pointer-events:none}.choices[data-type*=select-one].is-open:after{border-color:transparent transparent #333 transparent;margin-top:-7.5px}.choices[data-type*=select-one][dir=rtl]:after{left:11.5px;right:auto}.choices[data-type*=select-one][dir=rtl] .choices__button{right:auto;left:0;margin-left:25px;margin-right:0}.choices[data-type*=select-multiple] .choices__inner,.choices[data-type*=text] .choices__inner{cursor:text}.choices[data-type*=select-multiple] .choices__button,.choices[data-type*=text] .choices__button{position:relative;display:inline-block;margin:0 -4px 0 8px;padding-left:16px;border-left:1px solid #008fa1;background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);background-size:8px;width:8px;line-height:1;opacity:.75;border-radius:0}.choices[data-type*=select-multiple] .choices__button:focus,.choices[data-type*=select-multiple] .choices__button:hover,.choices[data-type*=text] .choices__button:focus,.choices[data-type*=text] .choices__button:hover{opacity:1}.choices__inner{display:inline-block;vertical-align:top;width:100%;background-color:#f9f9f9;padding:7.5px 7.5px 3.75px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;min-height:44px;overflow:hidden}.is-focused .choices__inner,.is-open .choices__inner{border-color:#b7b7b7}.is-open .choices__inner{border-radius:2.5px 2.5px 0 0}.is-flipped.is-open .choices__inner{border-radius:0 0 2.5px 2.5px}.choices__list{margin:0;padding-left:0;list-style:none}.choices__list--single{display:inline-block;padding:4px 16px 4px 4px;width:100%}[dir=rtl] .choices__list--single{padding-right:4px;padding-left:16px}.choices__list--single .choices__item{width:100%}.choices__list--multiple{display:inline}.choices__list--multiple .choices__item{display:inline-block;vertical-align:middle;border-radius:20px;padding:4px 10px;font-size:12px;font-weight:500;margin-right:3.75px;margin-bottom:3.75px;background-color:#00bcd4;border:1px solid #00a5bb;color:#fff;word-break:break-all}.choices__list--multiple .choices__item[data-deletable]{padding-right:5px}[dir=rtl] .choices__list--multiple .choices__item{margin-right:0;margin-left:3.75px}.choices__list--multiple .choices__item.is-highlighted{background-color:#00a5bb;border:1px solid #008fa1}.is-disabled .choices__list--multiple .choices__item{background-color:#aaa;border:1px solid #919191}.choices__list--dropdown{display:none;z-index:1;position:absolute;width:100%;background-color:#fff;border:1px solid #ddd;top:100%;margin-top:-1px;border-bottom-left-radius:2.5px;border-bottom-right-radius:2.5px;overflow:hidden;word-break:break-all}.choices__list--dropdown.is-active{display:block}.is-open .choices__list--dropdown{border-color:#b7b7b7}.is-flipped .choices__list--dropdown{top:auto;bottom:100%;margin-top:0;margin-bottom:-1px;border-radius:.25rem .25rem 0 0}.choices__list--dropdown .choices__list{position:relative;max-height:300px;overflow:auto;-webkit-overflow-scrolling:touch;will-change:scroll-position}.choices__list--dropdown .choices__item{position:relative;padding:10px;font-size:14px}[dir=rtl] .choices__list--dropdown .choices__item{text-align:right}@media (min-width:640px){.choices__list--dropdown .choices__item--selectable{padding-right:100px}.choices__list--dropdown .choices__item--selectable:after{content:attr(data-select-text);font-size:12px;opacity:0;position:absolute;right:10px;top:50%;transform:translateY(-50%)}[dir=rtl] .choices__list--dropdown .choices__item--selectable{text-align:right;padding-left:100px;padding-right:10px}[dir=rtl] .choices__list--dropdown .choices__item--selectable:after{right:auto;left:10px}}.choices__list--dropdown .choices__item--selectable.is-highlighted{background-color:#f2f2f2}.choices__list--dropdown .choices__item--selectable.is-highlighted:after{opacity:.5}.choices__item{cursor:default}.choices__item--selectable{cursor:pointer}.choices__item--disabled{cursor:not-allowed;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;opacity:.5}.choices__heading{font-weight:600;font-size:12px;padding:10px;border-bottom:1px solid #f7f7f7;color:gray}.choices__button{text-indent:-9999px;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:0;background-color:transparent;background-repeat:no-repeat;background-position:center;cursor:pointer}.choices__button:focus{outline:none}.choices__input{display:inline-block;vertical-align:baseline;background-color:#f9f9f9;font-size:14px;margin-bottom:5px;border:0;border-radius:0;max-width:100%;padding:4px 0 4px 2px}.choices__input:focus{outline:0}[dir=rtl] .choices__input{padding-right:2px;padding-left:0}.choices__placeholder{opacity:.5}.choices[data-type*=select-multiple] .choices__input.is-hidden,.choices[data-type*=select-one] .choices__input.is-hidden,.choices__input.is-hidden{display:none} \ No newline at end of file diff --git a/bin/htdocs/vendor/choices/choices.min.js b/bin/htdocs/vendor/choices/choices.min.js new file mode 100644 index 0000000..5d105dc --- /dev/null +++ b/bin/htdocs/vendor/choices/choices.min.js @@ -0,0 +1,58 @@ +/*! choices.js v7.0.0 | (c) 2019 Josh Johnson | https://github.com/jshjohnson/Choices#readme */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Choices=t():e.Choices=t()}(window,function(){return function(e){var t={};function i(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,i),o.l=!0,o.exports}return i.m=e,i.c=t,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)i.d(n,o,function(t){return e[t]}.bind(null,o));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/public/assets/scripts/",i(i.s=9)}([function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.diff=t.cloneObject=t.existsInArray=t.isIE11=t.fetchFromObject=t.getWindowHeight=t.dispatchEvent=t.sortByScore=t.sortByAlpha=t.calcWidthOfInput=t.strToEl=t.sanitise=t.isScrolledIntoView=t.getAdjacentEl=t.findAncestorByAttrName=t.wrap=t.isElement=t.isType=t.getType=t.generateId=t.generateChars=t.getRandomNumber=void 0;var n=function(e,t){return Math.floor(Math.random()*(t-e)+e)};t.getRandomNumber=n;var o=function(e){for(var t="",i=0;i1&&void 0!==arguments[1]?arguments[1]:document.createElement("div");return e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t),t.appendChild(e)};t.findAncestorByAttrName=function(e,t){for(var i=e;i;){if(i.hasAttribute(t))return i;i=i.parentElement}return null};t.getAdjacentEl=function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;if(e&&t){var n=e.parentNode.parentNode,o=Array.from(n.querySelectorAll(t)),r=o.indexOf(e);return o[r+(i>0?1:-1)]}};t.isScrolledIntoView=function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;if(e)return i>0?t.scrollTop+t.offsetHeight>=e.offsetTop+e.offsetHeight:e.offsetTop>=t.scrollTop};var a=function(e){return s("String",e)?e.replace(/&/g,"&").replace(/>/g,"&rt;").replace(/".concat(a(i),"
"));if(o.style.position="absolute",o.style.padding="0",o.style.top="-9999px",o.style.left="-9999px",o.style.width="auto",o.style.whiteSpace="pre",document.body.contains(e)&&window.getComputedStyle){var r=window.getComputedStyle(e);r&&(o.style.fontSize=r.fontSize,o.style.fontFamily=r.fontFamily,o.style.fontWeight=r.fontWeight,o.style.fontStyle=r.fontStyle,o.style.letterSpacing=r.letterSpacing,o.style.textTransform=r.textTransform,o.style.padding=r.padding)}document.body.appendChild(o),requestAnimationFrame(function(){i&&o.offsetWidth!==e.offsetWidth&&(n=o.offsetWidth+4),document.body.removeChild(o),t.call(void 0,"".concat(n,"px"))})}else t.call(void 0,"".concat(n,"px"))};t.sortByAlpha=function(e,t){var i="".concat(e.label||e.value).toLowerCase(),n="".concat(t.label||t.value).toLowerCase();return in?1:0};t.sortByScore=function(e,t){return e.score-t.score};t.dispatchEvent=function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,n=new CustomEvent(t,{detail:i,bubbles:!0,cancelable:!0});return e.dispatchEvent(n)};t.getWindowHeight=function(){var e=document.body,t=document.documentElement;return Math.max(e.scrollHeight,e.offsetHeight,t.clientHeight,t.scrollHeight,t.offsetHeight)};t.fetchFromObject=function e(t,i){var n=i.indexOf(".");return n>-1?e(t[i.substring(0,n)],i.substr(n+1)):t[i]};t.isIE11=function(){return!(!navigator.userAgent.match(/Trident/)||!navigator.userAgent.match(/rv[ :]11/))};t.existsInArray=function(e,t){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"value";return e.some(function(e){return s("String",t)?e[i]===t.trim():e[i]===t})};t.cloneObject=function(e){return JSON.parse(JSON.stringify(e))};t.diff=function(e,t){var i=Object.keys(e).sort(),n=Object.keys(t).sort();return i.filter(function(e){return n.indexOf(e)<0})}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SCROLLING_SPEED=t.KEY_CODES=t.ACTION_TYPES=t.EVENTS=t.DEFAULT_CONFIG=t.DEFAULT_CLASSNAMES=void 0;var n=i(0),o={containerOuter:"choices",containerInner:"choices__inner",input:"choices__input",inputCloned:"choices__input--cloned",list:"choices__list",listItems:"choices__list--multiple",listSingle:"choices__list--single",listDropdown:"choices__list--dropdown",item:"choices__item",itemSelectable:"choices__item--selectable",itemDisabled:"choices__item--disabled",itemChoice:"choices__item--choice",placeholder:"choices__placeholder",group:"choices__group",groupHeading:"choices__heading",button:"choices__button",activeState:"is-active",focusState:"is-focused",openState:"is-open",disabledState:"is-disabled",highlightedState:"is-highlighted",hiddenState:"is-hidden",flippedState:"is-flipped",loadingState:"is-loading",noResults:"has-no-results",noChoices:"has-no-choices"};t.DEFAULT_CLASSNAMES=o;var r={items:[],choices:[],silent:!1,renderChoiceLimit:-1,maxItemCount:-1,addItems:!0,addItemFilterFn:null,removeItems:!0,removeItemButton:!1,editItems:!1,duplicateItemsAllowed:!0,delimiter:",",paste:!0,searchEnabled:!0,searchChoices:!0,searchFloor:1,searchResultLimit:4,searchFields:["label","value"],position:"auto",resetScrollPosition:!0,shouldSort:!0,shouldSortItems:!1,sortFn:n.sortByAlpha,placeholder:!0,placeholderValue:null,searchPlaceholderValue:null,prependValue:null,appendValue:null,renderSelectedChoices:"auto",loadingText:"Loading...",noResultsText:"No results found",noChoicesText:"No choices to choose from",itemSelectText:"Press to select",uniqueItemText:"Only unique values can be added",customAddItemText:"Only values matching specific conditions can be added",addItemText:function(e){return'Press Enter to add "'.concat((0,n.sanitise)(e),'"')},maxItemText:function(e){return"Only ".concat(e," values can be added")},itemComparer:function(e,t){return e===t},fuseOptions:{includeScore:!0},callbackOnInit:null,callbackOnCreateTemplates:null,classNames:o};t.DEFAULT_CONFIG=r;t.EVENTS={showDropdown:"showDropdown",hideDropdown:"hideDropdown",change:"change",choice:"choice",search:"search",addItem:"addItem",removeItem:"removeItem",highlightItem:"highlightItem",highlightChoice:"highlightChoice"};t.ACTION_TYPES={ADD_CHOICE:"ADD_CHOICE",FILTER_CHOICES:"FILTER_CHOICES",ACTIVATE_CHOICES:"ACTIVATE_CHOICES",CLEAR_CHOICES:"CLEAR_CHOICES",ADD_GROUP:"ADD_GROUP",ADD_ITEM:"ADD_ITEM",REMOVE_ITEM:"REMOVE_ITEM",HIGHLIGHT_ITEM:"HIGHLIGHT_ITEM",CLEAR_ALL:"CLEAR_ALL"};t.KEY_CODES={BACK_KEY:46,DELETE_KEY:8,ENTER_KEY:13,A_KEY:65,ESC_KEY:27,UP_KEY:38,DOWN_KEY:40,PAGE_UP_KEY:33,PAGE_DOWN_KEY:34};t.SCROLLING_SPEED=4},function(e,t,i){"use strict";(function(e,n){var o,r=i(7);o="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==e?e:n;var s=Object(r.a)(o);t.a=s}).call(this,i(3),i(14)(e))},function(e,t){var i;i=function(){return this}();try{i=i||new Function("return this")()}catch(e){"object"==typeof window&&(i=window)}e.exports=i},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=i(0);function o(e,t){for(var i=0;i