From 551b79d8c2cf93f17e7fbf591fde52ec9b35dfd8 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 29 Apr 2018 04:02:23 +0900 Subject: [PATCH 001/304] =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=ED=8C=A8?= =?UTF-8?q?=EB=84=90=20=EC=A0=91=EC=86=8D=20=ED=97=88=EC=9A=A9,=20?= =?UTF-8?q?=EC=A0=91=EC=86=8D=20=EC=A0=9C=ED=95=9C=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/_admin2_submit.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/_admin2_submit.php b/hwe/_admin2_submit.php index a14bab7f..f6fa480b 100644 --- a/hwe/_admin2_submit.php +++ b/hwe/_admin2_submit.php @@ -191,12 +191,12 @@ switch($btn) { ], '`no` IN %li', $genlist); break; case "접속 허용": - $db->update('genera',[ + $db->update('general',[ 'con'=>0 ], '`no` IN %li', $genlist); break; case "접속 제한": - $db->update('genera',[ + $db->update('general',[ 'con'=>1000 ], '`no` IN %li', $genlist); break; -- 2.54.0 From d5be6e3e083a57ceb33e2773b167d250f0c68ebb Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 29 Apr 2018 04:29:46 +0900 Subject: [PATCH 002/304] =?UTF-8?q?=EB=82=B4=20=EC=A0=95=EB=B3=B4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=88=99=EB=A0=A8=EB=8F=84=EB=A5=BC=20=EC=88=98?= =?UTF-8?q?=EC=B9=98=EB=A1=9C=20=ED=91=9C=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func.php | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/hwe/func.php b/hwe/func.php index c96998ca..33e1e3c9 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -1002,11 +1002,17 @@ function generalInfo2($no) { if($dex30 > 100) { $dex30 = 100; } if($dex40 > 100) { $dex40 = 100; } - $general['dex0'] = getDexCall($general['dex0']); - $general['dex10'] = getDexCall($general['dex10']); - $general['dex20'] = getDexCall($general['dex20']); - $general['dex30'] = getDexCall($general['dex30']); - $general['dex40'] = getDexCall($general['dex40']); + $general['dex0_text'] = getDexCall($general['dex0']); + $general['dex10_text'] = getDexCall($general['dex10']); + $general['dex20_text'] = getDexCall($general['dex20']); + $general['dex30_text'] = getDexCall($general['dex30']); + $general['dex40_text'] = getDexCall($general['dex40']); + + $general['dex0_short'] = sprintf('%.1fK', $general['dex0']/1000); + $general['dex10_short'] = sprintf('%.1fK', $general['dex10']/1000); + $general['dex20_short'] = sprintf('%.1fK', $general['dex20']/1000); + $general['dex30_short'] = sprintf('%.1fK', $general['dex30']/1000); + $general['dex40_short'] = sprintf('%.1fK', $general['dex40']/1000); echo " @@ -1042,30 +1048,35 @@ function generalInfo2($no) {
추 가 정 보
- + - - + + + - + + - + + - + + - + +
숙 련 도
숙 련 도
보병  {$general['dex0']}".bar($dex0, 16)." {$general['dex0_text']}{$general['dex0_short']} ".bar($dex0, 16)."
궁병  {$general['dex10']} {$general['dex10_text']}{$general['dex10_short']}  ".bar($dex10, 16)."
기병  {$general['dex20']} {$general['dex20_text']}{$general['dex20_short']}  ".bar($dex20, 16)."
귀병  {$general['dex30']} {$general['dex30_text']}{$general['dex30_short']}  ".bar($dex30, 16)."
차병  {$general['dex40']} {$general['dex40_text']}{$general['dex40_short']}  ".bar($dex40, 16)."
"; -- 2.54.0 From 953196ec78289d0ede78e95428264f7922388f57 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 29 Apr 2018 12:55:16 +0900 Subject: [PATCH 003/304] =?UTF-8?q?=EC=98=88=EC=95=BD=20=EC=98=A4=ED=94=88?= =?UTF-8?q?=20=EC=8B=9C=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=A5=BC=20=EB=B0=94?= =?UTF-8?q?=EB=A1=9C=20=EB=82=A0=EB=A6=AC=EC=A7=84=20=EC=95=8A=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/j_autoreset.php | 62 +++++++++++++++++++++++++++++++++++++++++---- hwe/j_install.php | 15 ++++++++--- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/hwe/j_autoreset.php b/hwe/j_autoreset.php index 36e6360f..a18f8588 100644 --- a/hwe/j_autoreset.php +++ b/hwe/j_autoreset.php @@ -6,15 +6,67 @@ include "func.php"; $db = DB::db(); -$reserved = $db->queryFirstRow( - 'SELECT * FROM reserved_open WHERE `date` <= %s LIMIT 1', - (new \DateTime())->format('Y-m-d H:i:s') -); +$reserved = $db->queryFirstRow('SELECT `date`, options FROM reserved_open ORDER BY `date` ASC LIMIT 1'); if(!$reserved){ Json::die([ 'result'=>true, - 'affected'=>0 + 'affected'=>0, + 'status'=>'no_reserved' + ]); +} + +$reservedDate = new \DateTime($reserved['date']); +$now = new \DateTime(); + + +$status = 'not_yet'; + +if ($db->queryFirstField("SHOW TABLES LIKE 'game'")) { + list($isUnited, $lastTurn) = $db->queryFirstList('SELECT isUnited, turntime FROM game LIMIT 1'); +} +else{ + $isUnited = 2; + $lastTurn = '2000-01-01'; +} + +if($lastTurn !== null){ + $lastTurn = new \DateTime($lastTurn); +} + +if($lastTurn === null){ + //이미 리셋된 상태임 +} +else if(file_exists(__dir__.'/.htaccess')){ + //일단 서버는 닫혀 있음 +} +else if( + $isUnited == 2 && + $now->getTimestamp() - $lastTurn->getTimestamp() > $reservedDate->getTimestamp() - $now->getTimestamp() +){ + //정지 상태 & 중간 넘음 + AppConf::getList()[DB::prefix()]->closeServer(); + $status = 'closed'; +} +else if( + $isUnited > 0 && + $now->getTimestamp() - $lastTurn->getTimestamp() > ($reservedDate->getTimestamp() - $now->getTimestamp()) * 2 +){ + //천통 & 비정지 상태 & 2/3 넘음 + AppConf::getList()[DB::prefix()]->closeServer(); + $status = 'closed'; +} +else if($reservedDate->getTimestamp() - $now->getTimestamp() <= 60*10){ + //어쨌든 간에 10분 남았다면. + AppConf::getList()[DB::prefix()]->closeServer(); + $status = 'closed'; +} + +if($now < $reservedDate){ + Json::die([ + 'result'=>true, + 'affected'=>0, + 'status'=>$status ]); } diff --git a/hwe/j_install.php b/hwe/j_install.php index 4b535e53..f4cc8af0 100644 --- a/hwe/j_install.php +++ b/hwe/j_install.php @@ -52,9 +52,18 @@ if($reserve_open){ $reserve_open = new \DateTime($reserve_open); $db = DB::db(); - $clearResult = ResetHelper::clearDB(); - if(!$clearResult['result']){ - Json::die($clearResult); + if (!$db->queryFirstField("SHOW TABLES LIKE 'game'")) { + $clearResult = ResetHelper::clearDB(); + if(!$clearResult['result']){ + Json::die($clearResult); + } + } + + if (!$db->queryFirstField("SHOW TABLES LIKE 'reserved_open'")) { + Json::die([ + 'result'=>false, + 'reason'=>'예약 테이블이 없음!' + ]); } $scenarioObj = new Scenario($scenario, true); -- 2.54.0 From a19661199e97b4ec27d664872d952165359c27c7 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 29 Apr 2018 23:13:49 +0900 Subject: [PATCH 004/304] =?UTF-8?q?Date=EC=9D=98=20=EB=B9=84=EA=B5=90=20?= =?UTF-8?q?=EB=8C=80=EC=83=81=20lhs,=20rhs=EA=B0=80=20=EB=B0=94=EB=80=90?= =?UTF-8?q?=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/Event/Condition/Date.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hwe/sammo/Event/Condition/Date.php b/hwe/sammo/Event/Condition/Date.php index 23acff06..d34261d3 100644 --- a/hwe/sammo/Event/Condition/Date.php +++ b/hwe/sammo/Event/Condition/Date.php @@ -50,13 +50,15 @@ class Date extends \sammo\Event\Condition{ } $lhs = [ - $this->year, - $this->month - ]; - $rhs = [ $this->year!==null?(int)$env['year']:null, $this->month!==null?(int)$env['month']:null ]; + + $rhs = [ + $this->year, + $this->month + ]; + $value = false; switch($this->cmp){ -- 2.54.0 From 7db3675e71f3b1c2df456cf00cd10b2abd00bf1f Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 29 Apr 2018 23:31:21 +0900 Subject: [PATCH 005/304] =?UTF-8?q?=EC=84=B1=EC=9D=B8=20=EB=93=B1=EC=9E=A5?= =?UTF-8?q?=EC=9D=B4=20=EA=BC=AD=201=EC=9B=94=EC=9D=B4=20=EC=95=84?= =?UTF-8?q?=EB=8B=88=EC=96=B4=EB=8F=84=20=EB=90=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/Scenario/NPC.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/sammo/Scenario/NPC.php b/hwe/sammo/Scenario/NPC.php index 2508bd9a..b2d2278a 100644 --- a/hwe/sammo/Scenario/NPC.php +++ b/hwe/sammo/Scenario/NPC.php @@ -107,8 +107,8 @@ class NPC{ $db = DB::db(); - if($age == \sammo\GameConst::$adultAge && $month == 1){ - \sammo\pushWorldHistory(["●1월:$name(이)가 성인이 되어 등장했습니다."], $year, $month); + if($age == \sammo\GameConst::$adultAge){ + \sammo\pushWorldHistory(["●$month월:$name(이)가 성인이 되어 등장했습니다."], $year, $month); } if($this->ego == null || $isFictionMode){ -- 2.54.0 From 45fd06c195a5abe880a654188849770c938275e2 Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 30 Apr 2018 00:01:11 +0900 Subject: [PATCH 006/304] =?UTF-8?q?=EC=84=B1=EC=9D=B8=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=93=B1=EC=9E=A5=EC=8B=9C=20=EC=9B=94=EC=9D=B4=20=EC=A0=9C?= =?UTF-8?q?=EB=8C=80=EB=A1=9C=20=EB=A1=9C=EA=B7=B8=EB=A1=9C=20=EB=82=A8?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/Scenario/NPC.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/sammo/Scenario/NPC.php b/hwe/sammo/Scenario/NPC.php index b2d2278a..54b13f87 100644 --- a/hwe/sammo/Scenario/NPC.php +++ b/hwe/sammo/Scenario/NPC.php @@ -108,7 +108,7 @@ class NPC{ $db = DB::db(); if($age == \sammo\GameConst::$adultAge){ - \sammo\pushWorldHistory(["●$month월:$name(이)가 성인이 되어 등장했습니다."], $year, $month); + \sammo\pushWorldHistory(["●{$month}월:{$name}(이)가 성인이 되어 등장했습니다."], $year, $month); } if($this->ego == null || $isFictionMode){ -- 2.54.0 From ebf9b4b75d0986973a8ef8e2280923c8de6c7ed3 Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 30 Apr 2018 00:15:43 +0900 Subject: [PATCH 007/304] =?UTF-8?q?=EA=B0=80=EC=83=81=20=EC=83=81=EC=84=B1?= =?UTF-8?q?=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20=EC=83=88=20=EC=9E=A5=EC=88=98?= =?UTF-8?q?=EB=8A=94=20=EC=9E=AC=EC=95=BC=EC=9E=84.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/Scenario/NPC.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/hwe/sammo/Scenario/NPC.php b/hwe/sammo/Scenario/NPC.php index 54b13f87..80000859 100644 --- a/hwe/sammo/Scenario/NPC.php +++ b/hwe/sammo/Scenario/NPC.php @@ -86,10 +86,6 @@ class NPC{ public function build($env=[]){ //scenario에 life==1인 경우 수명 제한이 없어지는 모양. - $nationID = $this->nationID; - if(!\sammo\getNationStaticInfo($nationID)){ - $nationID = 0; - }; $isFictionMode = (Util::array_get($env['fiction'], 0)!=0); @@ -105,9 +101,21 @@ class NPC{ return false; //예약. } + $isNewGeneral = ($age == \sammo\GameConst::$adultAge); + + $nationID = $this->nationID; + if($isFictionMode && $isNewGeneral){ + $nationID = 0; + } + + if(!\sammo\getNationStaticInfo($nationID)){ + $nationID = 0; + }; + + $db = DB::db(); - if($age == \sammo\GameConst::$adultAge){ + if($isNewGeneral){ \sammo\pushWorldHistory(["●{$month}월:{$name}(이)가 성인이 되어 등장했습니다."], $year, $month); } -- 2.54.0 From 063b3d37a6a36af14476e9010cbd3fd4442c1c05 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 19:44:22 +0900 Subject: [PATCH 008/304] =?UTF-8?q?exception=EC=9D=B4=20trace=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- f_config/config.php | 1 + 1 file changed, 1 insertion(+) diff --git a/f_config/config.php b/f_config/config.php index 217c995b..04a18445 100644 --- a/f_config/config.php +++ b/f_config/config.php @@ -82,6 +82,7 @@ function logExceptionByCustomHandler(\Throwable $e){ ], Json::PRETTY); file_put_contents(ROOT.'/d_log/err_log.txt',"$data\n", FILE_APPEND); + echo $e->getTraceAsString(); throw $e; } set_exception_handler('\\sammo\\logExceptionByCustomHandler'); \ No newline at end of file -- 2.54.0 From 69af23b60a2fbc557de4ef19c87ecf790f9c38c5 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 19:45:10 +0900 Subject: [PATCH 009/304] =?UTF-8?q?Session=EC=97=90=EC=84=9C=20requireLogi?= =?UTF-8?q?n=20=EC=9D=B8=EC=9E=90=20=EC=9D=B4=EB=A6=84=EC=9D=84=20?= =?UTF-8?q?=EC=A7=81=EA=B4=80=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sammo/Session.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sammo/Session.php b/src/sammo/Session.php index 6bd6de08..fdfe52f6 100644 --- a/src/sammo/Session.php +++ b/src/sammo/Session.php @@ -72,25 +72,25 @@ class Session Json::die($result + $jsonResult); } - public static function requireLogin($result = '..'): Session + public static function requireLogin($actionOnError = '..'): Session { $session = Session::getInstance(); if ($session->isLoggedIn()) { return $session; } - static::die($result); + static::die($actionOnError); } - public static function requireGameLogin($result = '..'): Session + public static function requireGameLogin($actionOnError = '..'): Session { - $session = Session::requireLogin($result)->loginGame(); + $session = Session::requireLogin($actionOnError)->loginGame(); if ($session->generalID) { return $session; } - static::die($result); + static::die($actionOnError); } public function __construct() -- 2.54.0 From a1392652ae586cdc84282c4d0f0209bb4a41dca8 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 19:48:38 +0900 Subject: [PATCH 010/304] =?UTF-8?q?config.css=EC=97=90=EC=84=9C=20div,=20b?= =?UTF-8?q?utton=EC=97=90=20=EC=A0=81=EC=9A=A9=EB=90=98=EB=8D=98=20?= =?UTF-8?q?=EC=A0=84=EC=97=AD=20style=20=EC=A0=9C=EA=B1=B0=20=EB=AA=A8?= =?UTF-8?q?=EB=B0=94=EC=9D=BC=20=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=9E=85=EA=B5=AC=20=ED=96=84=EB=B2=84=EA=B1=B0=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EB=8F=99=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/config.css | 34 ++++++++++------------------------ css/user_info.css | 14 ++++++-------- i_entrance/entrance.php | 2 +- i_entrance/user_info.html | 3 ++- js/admin_server.js | 20 ++++++++++---------- 5 files changed, 29 insertions(+), 44 deletions(-) diff --git a/css/config.css b/css/config.css index d09bdb69..34b9b848 100644 --- a/css/config.css +++ b/css/config.css @@ -1,8 +1,9 @@ @charset "utf-8"; -* { margin: 0px; padding: 0px; } -body { color:white; background-color:black; border-width:1px; border-color:gray; } -table { +body { + color:white; background-color:black; font-family: '맑은 고딕'; +} +table { border: solid 1px; border-spacing: 0px; border-collapse: separate; border-top-color: gray; border-left-color: gray; border-right-color: black; border-bottom-color: black; padding: 0px; font-size: 13px; @@ -23,13 +24,6 @@ a.just_link { text-decoration: none; } -div { - margin:0px; - padding: 0px; - word-break: break-all; - font-family: '맑은 고딕'; -} - .with_border{ border: solid 1px; border-spacing: 0px; @@ -58,8 +52,6 @@ div { font-family: '맑은 고딕';; } -font { font-family: '맑은 고딕';} - .obj_fill{ display:block; width:100%; @@ -72,24 +64,18 @@ button.with_skin { font-size: 13px; } +button.with_skin:disabled { + background-color:#333333; +} + input.with_skin { background-color: black; color: white; font-family: '맑은 고딕'; font-size: 13px; } -select { - width: 100px; height: 18px; - background-color: black; color: white; - font-family: '맑은 고딕'; - font-size: 13px; -} -button { - width: 100px; height: 20px; - background-color: black; color: white; - font-family: '맑은 고딕'; - font-size: 13px; - line-height:1.3; +input.with_skin:disabled { + background-color: #333333; } .font0 { font-size: 10px; } diff --git a/css/user_info.css b/css/user_info.css index 92665087..3b9ed297 100644 --- a/css/user_info.css +++ b/css/user_info.css @@ -21,6 +21,7 @@ right: 6px; margin:6px; height:40px; + line-height:40px; } #btn_delete_me{ @@ -37,15 +38,15 @@ #image_upload_filename{ position:absolute; - height:16px; + height:20px; left:10px; width:130px; - top:16px; + top:12px; } #image_upload_fake_btn{ - top:16px; - height:20px; + top:12px; + height:22px; right:10px; position:absolute; } @@ -53,8 +54,7 @@ #image_upload{ opacity: 0; position:absolute; - top:16px; - height:22px; + top:12px; left:0; height:20px; width:100%; @@ -70,7 +70,6 @@ bottom:0; left:0; width:50%; - height:20px; } #btn_remove_icon{ @@ -78,7 +77,6 @@ bottom:0; right:0; width:50%; - height:20px; } #third_use_disallow{ diff --git a/i_entrance/entrance.php b/i_entrance/entrance.php index e7d026c1..2a944abe 100644 --- a/i_entrance/entrance.php +++ b/i_entrance/entrance.php @@ -23,12 +23,12 @@ $userGrade = $session->userGrade; - + diff --git a/i_entrance/user_info.html b/i_entrance/user_info.html index c741f1f1..fb207a06 100644 --- a/i_entrance/user_info.html +++ b/i_entrance/user_info.html @@ -15,6 +15,7 @@ + @@ -36,7 +37,7 @@ diff --git a/js/admin_server.js b/js/admin_server.js index e8ab64cc..bd157e95 100644 --- a/js/admin_server.js +++ b/js/admin_server.js @@ -4,13 +4,13 @@ var serverAdminTemplate = '\ \ \ \ - \ - \ - \ - \ - \ - \ - \ + \ + \ + \ + \ + \ + \ + \ \ '; @@ -77,14 +77,14 @@ function drawServerAdminList(serverList){ var $tr = $(TemplateEngine(serverAdminTemplate, server)); $table.append($tr); if(!server.valid){ - $tr.find('.valid_if_set').css('background','#333333').prop('disabled', true); + $tr.find('.valid_if_set').prop('disabled', true); } if(!server.installed){ - $tr.find('.valid_if_installed').css('background','#333333').prop('disabled', true); + $tr.find('.valid_if_installed').prop('disabled', true); } }); if(serverList.grade == 5){ - $table.find('.only_admin').css('background','#333333').prop('disabled', true); + $table.find('.only_admin').prop('disabled', true); } } -- 2.54.0 From cb2fbf1a32bf724f045bcd99e238d844e033f48e Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 19:49:07 +0900 Subject: [PATCH 011/304] =?UTF-8?q?common.js=EC=97=90=20iconpath=20?= =?UTF-8?q?=EC=97=B0=EC=82=B0=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/js/common.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hwe/js/common.js b/hwe/js/common.js index 142575e0..0ac528d6 100644 --- a/hwe/js/common.js +++ b/hwe/js/common.js @@ -118,4 +118,14 @@ var TemplateEngine = function (html, options) { try { result = new Function('obj', code).apply(options, [options]); } catch (err) { console.error("'" + err.message + "'", " in \n\nCode:\n", code, "\n"); } return result; +} + +function getIconPath(imgsvr, picture){ + // ../d_shared/common_path.js 필요 + if(!imgsvr){ + return pathConfig.sharedIcon+'/'+picture; + } + else{ + return pathConfig.root+'/d_pic/'+picture; + } } \ No newline at end of file -- 2.54.0 From a534b8f8128c9020e7cde94d6f2f7747405ddd30 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 19:49:22 +0900 Subject: [PATCH 012/304] =?UTF-8?q?=EC=8B=9C=EB=82=98=EB=A6=AC=EC=98=A4=20?= =?UTF-8?q?30=EB=B2=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/scenario/scenario_30.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hwe/scenario/scenario_30.json b/hwe/scenario/scenario_30.json index 7060459b..1f433d76 100644 --- a/hwe/scenario/scenario_30.json +++ b/hwe/scenario/scenario_30.json @@ -47,7 +47,7 @@ [0, "강영식", -1, 0, null, 63, 69, 60, 0, 160, 300, null, null], [0, "김사율", -1, 0, null, 74, 66, 67, 0, 160, 300, null, null], [0, "배장호", -1, 0, null, 62, 48, 60, 0, 160, 300, null, null], - [0, "손민한", -1, 0, null, 85, 84, 94, 0, 160, 300, null, null, "팝콘이 먹고싶다"], + [0, "손민한", -1, 0, null, 85, 84, 94, 0, 160, 300, null, "환술", "팝콘이 먹고싶다"], [0, "송승준", -1, 0, null, 65, 84, 67, 0, 160, 300, null, null], [0, "이용훈", -1, 0, null, 48, 58, 63, 0, 160, 300, null, null], [0, "임경완", -1, 0, null, 68, 57, 39, 0, 160, 300, null, null], @@ -70,7 +70,7 @@ [0, "정보명", -1, 0, null, 69, 31, 39, 0, 160, 300, null, null], [0, "이재곤", -1, 0, null, 40, 41, 39, 0, 160, 300, null, null], [0, "염종석", -1, 0, null, 83, 90, 93, 0, 160, 300, null, null], - [0, "황재균", -1, 0, null, 79, 77, 83, 0, 160, 300, null, null, "안녕하세요 섹머입니다"], + [0, "황재균", -1, 0, null, 79, 77, 83, 0, 160, 300, null, "집중", "안녕하세요 섹머입니다"], [0, "윤학길", -1, 0, null, 81, 76, 82, 0, 160, 300, null, null], [0, "가득염", -1, 0, null, 60, 67, 61, 0, 160, 300, null, null], [0, "조경환", -1, 0, null, 57, 78, 20, 0, 160, 300, null, null], @@ -84,7 +84,7 @@ [0, "금광옥", -1, 0, null, 31, 30, 39, 0, 160, 300, null, null], [0, "김무관", -1, 0, null, 20, 7, 43, 0, 160, 300, null, null], [0, "양승관", -1, 0, null, 46, 44, 22, 0, 160, 300, null, null], - [0, "박철순", -1, 0, null, 89, 82, 80, 0, 160, 300, null, "무쌍"], + [0, "박철순", -1, 0, null, 89, 82, 80, 0, 160, 300, null, "필살"], [0, "하기룡", -1, 0, null, 82, 58, 64, 0, 160, 300, null, null], [0, "황규봉", -1, 0, null, 83, 48, 72, 0, 160, 300, null, null], [0, "계형철", -1, 0, null, 74, 75, 70, 0, 160, 300, null, null], @@ -133,8 +133,8 @@ [0, "최정", -1, 0, null, 89, 91, 88, 0, 160, 300, null, "무쌍"], [0, "조동화", -1, 0, null, 48, 11, 83, 0, 160, 300, null, null], [0, "김강민", -1, 0, null, 77, 77, 82, 0, 160, 300, null, null], - [0, "박용택", -1, 0, null, 92, 90, 92, 0, 160, 300, null, "신중", "팬 여러분 올해는 유광잠바 준비하셔야 될 겁니다"], - [0, "이병규", -1, 0, null, 93, 90, 89, 0, 160, 300, null, "필살"], + [0, "박용택", -1, 0, null, 92, 90, 92, 0, 160, 300, null, "환술", "팬 여러분 올해는 유광잠바 준비하셔야 될 겁니다"], + [0, "이병규", -1, 0, null, 93, 90, 89, 0, 160, 300, null, "위압"], [0, "조인성", -1, 0, null, 49, 86, 25, 0, 160, 300, null, null], [0, "김동수", -1, 0, null, 65, 86, 65, 0, 160, 300, null, null], [0, "유지현", -1, 0, null, 76, 57, 87, 0, 160, 300, null, null], -- 2.54.0 From 5974028c4d3651d492b0c09f543e7001be989ea0 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 19:51:56 +0900 Subject: [PATCH 013/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=20=EC=9E=A5=EC=88=98?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80(=EC=84=A0=ED=83=9D?= =?UTF-8?q?=EC=9D=80=20=EB=AF=B8=EC=99=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/config.css | 2 + hwe/css/select_npc.css | 26 +++++ hwe/j_get_select_npc_token.php | 172 +++++++++++++++++++++++++++++++++ hwe/js/select_npc.js | 76 +++++++++++++++ hwe/new_select_npc.php | 103 ++++++++++++++++++++ 5 files changed, 379 insertions(+) create mode 100644 hwe/css/select_npc.css create mode 100644 hwe/j_get_select_npc_token.php create mode 100644 hwe/js/select_npc.js create mode 100644 hwe/new_select_npc.php diff --git a/css/config.css b/css/config.css index 34b9b848..53b22574 100644 --- a/css/config.css +++ b/css/config.css @@ -3,6 +3,7 @@ body { color:white; background-color:black; font-family: '맑은 고딕'; } + table { border: solid 1px; border-spacing: 0px; border-collapse: separate; border-top-color: gray; border-left-color: gray; border-right-color: black; border-bottom-color: black; padding: 0px; @@ -25,6 +26,7 @@ a.just_link { } .with_border{ + margin:0; border: solid 1px; border-spacing: 0px; border-collapse: separate; diff --git a/hwe/css/select_npc.css b/hwe/css/select_npc.css new file mode 100644 index 00000000..d86b3365 --- /dev/null +++ b/hwe/css/select_npc.css @@ -0,0 +1,26 @@ +.card_holder{ + text-align:center; +} +.general_card{ + width:100px; + display:inline-block; +} + + +.general_card label { + display: block; + padding-left: 15px; + text-indent: -15px; + } + +.general_card input { + width: 13px; + height: 13px; + padding: 0; + margin:0; + vertical-align: bottom; + position: relative; + top: -1px; + *overflow: hidden; + } + \ No newline at end of file diff --git a/hwe/j_get_select_npc_token.php b/hwe/j_get_select_npc_token.php new file mode 100644 index 00000000..e924cb8f --- /dev/null +++ b/hwe/j_get_select_npc_token.php @@ -0,0 +1,172 @@ +setReadOnly(); +$userID = Session::getUserID(); + + +$oNow = new \DateTimeImmutable(); + +$now = $oNow->format('Y-m-d H:i:s'); + +$db = DB::db(); + +$oldGeneral = $db->queryFirstField('SELECT `no` FROM general WHERE `owner`=%i', $userID); +if($oldGeneral !== null){ + Json::die([ + 'result'=>false, + 'reason'=>'이미 장수가 생성되었습니다' + ]); +} + +list( + $year, + $month, + $maxgeneral, + $turnterm, + $genius, + $npcmode +) = $db->queryFirstList('SELECT year,month,maxgeneral,turnterm,genius,npcmode from game limit 1'); + +if(!$npcmode){ + Json::die([ + 'result'=>false, + 'reason'=>'빙의 가능한 서버가 아닙니다' + ]); +} + + +$token = $db->queryFirstRow('SELECT * FROM select_npc_token WHERE `owner`=%i AND `valid_until`>=%s', $userID, $now); +$pickResult = []; + + +if($token && $refresh){ + $pickMoreFrom = (new \DateTime($token['pick_more_from']))->getTimestamp(); + $nowT = $oNow->getTimestamp(); + + if($nowT >= $pickMoreFrom){ + $oldPickResult = Json::decode($token['pick_result']); + + foreach($keepResult as $keepId){ + if(\key_exists($keepId, $oldPickResult) && $oldPickResult[$keepId]['keepCnt'] > 0){ + $pickResult[$keepId] = $oldPickResult[$keepId]; + $pickResult[$keepId]['keepCnt']-=1; + } + } + + if(count($pickResult) == count($oldPickResult)){ + $refresh = false; + } + } + else{ + Json::die([ + 'result'=>false, + 'reason'=>'아직 다시 뽑을 수 없습니다', + ]); + } +} + +if($token && !$refresh){ + Json::die([ + 'result'=>true, + 'pick'=>Json::decode($token['pick_result']), + 'pickMoreFrom'=>$token['pick_more_from'], + 'validUntil'=>$token['valid_until'] + ]); +} + +$candidates = []; + +$weight = []; +foreach($db->query('SELECT `no`, `name`, leader, power, intel, imgsvr, picture, special, special2 FROM general WHERE npc=2') as $general){ + $general['special'] = \sammo\SpecialityConst::DOMESTIC[$general['special']][0]??'-'; + $general['special2'] = \sammo\SpecialityConst::WAR[$general['special2']][0]??'-'; + $candidates[$general['no']] = $general + ['keepCnt'=>KEEP_CNT]; + $allStat = $general['leader'] + $general['power'] + $general['intel']; + $weight[$general['no']] = pow($allStat, 1.7); +} + +foreach($db->queryFirstColumn('SELECT pick_result FROM select_npc_token WHERE `owner`!=%i AND valid_until >=%s', $userID, $now) as $reserved){ + $reserved = Json::decode($reserved); + foreach(array_keys($reserved) as $reservedNPC){ + if(key_exists($reservedNPC, $weight)){ + unset($candidates[$reservedNPC]); + unset($weight[$reservedNPC]); + } + } +} + +$pickLimit = min(count($candidates), 5); + +while(count($pickResult) < $pickLimit){ + $generalID = Util::choiceRandomUsingWeight($weight); + if(!key_exists($generalID, $pickResult)){ + $pickResult[$generalID] = $candidates[$generalID]; + } +} + +$newNonce = mt_rand(0, 0xfffffff); + +$validMinute = max(VALID_MINUTE, intdiv($turnterm, 2)); +$pickMoreMinute = max(PICK_MORE_MINUTE, intdiv($turnterm, 5)); + +$validUntil = $oNow->add(new \DateInterval(sprintf('PT%dM', $validMinute))); +$pickMoreFrom = $oNow->add(new \DateInterval(sprintf('PT%dM', $pickMoreMinute))); + +$db->query('LOCK TABLES select_npc_token WRITE'); + +$db->delete('select_npc_token', 'valid_until < %s', $now); + +$inserted = 0; + +if($token){ + $db->update('select_npc_token', [ + 'valid_until'=>$validUntil->format('Y-m-d H:i:s'), + 'pick_more_from'=>$pickMoreFrom->format('Y-m-d H:i:s'), + 'pick_result'=>Json::encode($pickResult), + 'nonce'=>$newNonce + ], 'owner = %i AND nonce = %i', $userID, $token['nonce']); + if($db->affectedRows()){ + $inserted = -1; + } +} +else{ + $db->insertIgnore('select_npc_token', [ + 'owner'=>$userID, + 'valid_until'=>$validUntil->format('Y-m-d H:i:s'), + 'pick_more_from'=>'2000-01-01 01:00:00', + 'pick_result'=>Json::encode($pickResult), + 'nonce'=>$newNonce + ]); + + if($db->affectedRows()){ + $inserted = 1; + } +} + +$db->query('UNLOCK TABLES'); + +if($inserted === 0){ + Json::die([ + 'result'=>false, + 'reason'=>'중복 요청, 다시 랜덤 토큰을 확인해주세요' + ]); +} + +Json::die([ + 'result'=>true, + 'pick'=>$pickResult, + 'pickMoreFrom'=>($inserted===-1)?$pickMoreFrom->format('Y-m-d H:i:s'):'2000-01-01 01:00:00', + 'validUntil'=>$validUntil->format('Y-m-d H:i:s') +]); \ No newline at end of file diff --git a/hwe/js/select_npc.js b/hwe/js/select_npc.js new file mode 100644 index 00000000..8229d686 --- /dev/null +++ b/hwe/js/select_npc.js @@ -0,0 +1,76 @@ +var templateGeneralCard = +'
\ +

<%name%>

\ +

\ +

<%leader%> / <%power%> / <%intel%>
\ + <%special%> / <%special2%>

\ + \ + \ +
'; + +function updatePickMoreTimer(){ + var $btn = $('#btn_pick_more'); + var now = Date.now(); + var remain = ($btn.data('available') - now) / 1000; + if(remain <= 0){ + $btn.prop('disabled', false) + $btn.html('다른 장수 보기'); + return; + } + + $btn.html('다른 장수 보기({0}초)'.format(Math.ceil(remain))); + + setTimeout(updatePickMoreTimer, 250); +} + +function printGenerals(value){ + $('.card_holder').empty(); + $('#valid_until_text').html(value.validUntil); + $('#btn_pick_more').data('available', new Date(value.pickMoreFrom).getTime()).prop('disabled',true); + $.each(value.pick, function(idx, cardData){ + cardData.iconPath = getIconPath(cardData.imgsvr, cardData.picture); + console.log(cardData); + + var $card = $(TemplateEngine(templateGeneralCard, cardData)); + console.log($card); + + $('.card_holder').append($card); + }); + + updatePickMoreTimer(); +} + +jQuery(function($){ + +$.post('j_get_select_npc_token.php').then(function(value){ + if(!value.result){ + alert(value.reason); + return; + } + + console.log(value); + printGenerals(value); +}); + +$('#btn_pick_more').click(function(){ + var generals = $.map($('.keep_select:checked'), function(value){ + return $(value).val(); + }); + console.log(generals); + $.post({ + url:'j_get_select_npc_token.php', + dataType:'json', + data:{ + refresh:true, + keep:generals + } + }).then(function(result){ + if(!result.result){ + alert(result.reason); + location.refresh(); + } + printGenerals(result); + }); +}); + +}); \ No newline at end of file diff --git a/hwe/new_select_npc.php b/hwe/new_select_npc.php new file mode 100644 index 00000000..d5e18c35 --- /dev/null +++ b/hwe/new_select_npc.php @@ -0,0 +1,103 @@ +setReadOnly(); +$userID = Session::getUserID(); +$rootDB = RootDB::db(); +$db = DB::db(); + +//회원 테이블에서 정보확인 +$member = $rootDB->queryFirstRow('select no,name,picture,grade from member where no=%i', $userID); + +if(!$member) { + $session->logout(); + header('location:..'); + die(); +} + +list($npcmode, $maxgeneral) = $db->queryFirstList('SELECT npcmode,maxgeneral FROM game LIMIT 1'); + +if(!$npcmode) { + header('location:..'); + die(); +} + +$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2'); + +$nations = $db->queryAllLists('SELECT `name`, scoutmsg, color FROM nation'); +?> + + + +<?=UniqueConst::$serverName?>: NPC빙의 + + + + + + + + + + + + + + += $maxgeneral) { +?> + + + + + + + +
+
장 수 선 택
+
계 정 관 리 - +
<%korName%>(<%name%>)<%status%><%version%>
+ +
+ + + + + + + + + + + + + + + +
임관 권유 메세지
+ +
+
장수 빙의
+
+( 까지 유효)
+
+
+
+
+
+
+
+
+
+ + + -- 2.54.0 From 3c1c33949896d0d422f0e0979f1e0bdd5a6e42e2 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 21:20:15 +0900 Subject: [PATCH 014/304] =?UTF-8?q?=EC=9E=A5=EC=88=98=20=EB=B9=99=EC=9D=98?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/css/common.css | 3 +- hwe/css/select_npc.css | 17 ++++ hwe/j_get_select_npc_token.php | 12 +-- hwe/j_select_npc.php | 109 ++++++++++++++++++++ hwe/js/select_npc.js | 25 ++++- hwe/new_select_npc.php | 103 ------------------- hwe/select_npc.php | 179 ++++++++++++--------------------- hwe/select_npc_post.php | 144 -------------------------- js/common.js | 10 ++ 9 files changed, 226 insertions(+), 376 deletions(-) create mode 100644 hwe/j_select_npc.php delete mode 100644 hwe/new_select_npc.php delete mode 100644 hwe/select_npc_post.php diff --git a/hwe/css/common.css b/hwe/css/common.css index 488dad05..eecb5928 100644 --- a/hwe/css/common.css +++ b/hwe/css/common.css @@ -3,11 +3,12 @@ html, body{ background-color:black; color:white; + line-height:1.3; } table { font-family:'맑은 고딕'; } font { font-family:'맑은 고딕'; } -input { font-family:'맑은 고딕'; height:20px } +input { font-family:'맑은 고딕'; font-size:13px; } select { font-family:'굴림'; line-height:100%; } /* */ diff --git a/hwe/css/select_npc.css b/hwe/css/select_npc.css index d86b3365..d6935563 100644 --- a/hwe/css/select_npc.css +++ b/hwe/css/select_npc.css @@ -1,3 +1,8 @@ +.container{ + width:1000px; + margin:0 auto; +} + .card_holder{ text-align:center; } @@ -6,6 +11,18 @@ display:inline-block; } +.general_card h4 { + margin:0; +} + +.general_card p { + margin:0; +} + + +.general_card .select_btn { + width:100%; +} .general_card label { display: block; diff --git a/hwe/j_get_select_npc_token.php b/hwe/j_get_select_npc_token.php index e924cb8f..915b2f30 100644 --- a/hwe/j_get_select_npc_token.php +++ b/hwe/j_get_select_npc_token.php @@ -31,13 +31,10 @@ if($oldGeneral !== null){ } list( - $year, - $month, $maxgeneral, $turnterm, - $genius, $npcmode -) = $db->queryFirstList('SELECT year,month,maxgeneral,turnterm,genius,npcmode from game limit 1'); +) = $db->queryFirstList('SELECT maxgeneral,turnterm,npcmode from game limit 1'); if(!$npcmode){ Json::die([ @@ -46,7 +43,6 @@ if(!$npcmode){ ]); } - $token = $db->queryFirstRow('SELECT * FROM select_npc_token WHERE `owner`=%i AND `valid_until`>=%s', $userID, $now); $pickResult = []; @@ -94,7 +90,7 @@ foreach($db->query('SELECT `no`, `name`, leader, power, intel, imgsvr, picture, $general['special2'] = \sammo\SpecialityConst::WAR[$general['special2']][0]??'-'; $candidates[$general['no']] = $general + ['keepCnt'=>KEEP_CNT]; $allStat = $general['leader'] + $general['power'] + $general['intel']; - $weight[$general['no']] = pow($allStat, 1.7); + $weight[$general['no']] = pow($allStat, 1.5); } foreach($db->queryFirstColumn('SELECT pick_result FROM select_npc_token WHERE `owner`!=%i AND valid_until >=%s', $userID, $now) as $reserved){ @@ -124,8 +120,6 @@ $pickMoreMinute = max(PICK_MORE_MINUTE, intdiv($turnterm, 5)); $validUntil = $oNow->add(new \DateInterval(sprintf('PT%dM', $validMinute))); $pickMoreFrom = $oNow->add(new \DateInterval(sprintf('PT%dM', $pickMoreMinute))); -$db->query('LOCK TABLES select_npc_token WRITE'); - $db->delete('select_npc_token', 'valid_until < %s', $now); $inserted = 0; @@ -155,8 +149,6 @@ else{ } } -$db->query('UNLOCK TABLES'); - if($inserted === 0){ Json::die([ 'result'=>false, diff --git a/hwe/j_select_npc.php b/hwe/j_select_npc.php new file mode 100644 index 00000000..51fb91e9 --- /dev/null +++ b/hwe/j_select_npc.php @@ -0,0 +1,109 @@ +false, + 'reason'=>'장수를 선택하지 않았습니다' + ]); +} + +$session = Session::requireLogin()->setReadOnly(); +$userID = Session::getUserID(); +$db = DB::db(); +$rootDB = RootDB::db(); + +$oNow = new \DateTimeImmutable(); +$now = $oNow->format('Y-m-d H:i:s'); + +$userNick = RootDB::db()->queryFirstField('SELECT `NAME` FROM member WHERE `NO`=%i',$userID); +if(!$userNick){ + Json::die([ + 'result'=>false, + 'reason'=>'멤버 정보를 가져오지 못했습니다.' + ]); +} + +$pickResult = $db->queryFirstField('SELECT pick_result FROM select_npc_token WHERE `owner`=%i AND `valid_until`>=%s', $userID, $now); +if(!$pickResult){ + Json::die([ + 'result'=>false, + 'reason'=>'유효한 장수 목록이 없습니다.' + ]); +} + + +$pickResult = Json::decode($pickResult); +if(!key_exists($pick, $pickResult)){ + Json::die([ + 'result'=>false, + 'reason'=>'선택한 장수가 목록에 없습니다.' + ]); +} +$pickedNPC = $pickResult[$pick]; + + +$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2'); +list( + $year, + $month, + $maxgeneral, + $npcmode +) = $db->queryFirstList('SELECT year,month,maxgeneral,npcmode from game limit 1'); + +if(!$npcmode){ + Json::die([ + 'result'=>false, + 'reason'=>'빙의 가능한 서버가 아닙니다' + ]); +} + +if ($gencount >= $maxgeneral) { + Json::die([ + 'result'=>false, + 'reason'=>'더 이상 등록 할 수 없습니다.' + ]); +} + +//등록 시작 +$db->update('general', [ + 'owner'=>$userID, + 'npc'=>1 +], 'owner <= 0 AND npc = 2 AND no = %i', $pick); + +if(!$db->affectedRows()){ + Json::die([ + 'result'=>false, + 'reason'=>'장수 등록에 실패했습니다.' + ]); +} + +$db->delete('select_npc_token', 'owner=%i', $userID); + +pushGeneralHistory($pickedNPC, "●{$year}년 {$month}월:{$pickedNPC['name']}의 육체에 {$userNick}(이)가 빙의되다."); +//pushGenLog($me, $mylog); +pushGeneralPublicRecord(["●{$month}월:{$pickedNPC['name']}의 육체에 {$userNick}(이)가 빙의됩니다!"], $year, $month); + +pushAdminLog(["가입 : {$userID} // {$session->userName} // {$pick} // ".getenv("REMOTE_ADDR")]); + +$rootDB->insert('member_log', [ + 'member_no' => $userID, + 'date'=>date('Y-m-d H:i:s'), + 'action_type'=>'make_general', + 'action'=>Json::encode([ + 'server'=>DB::prefix(), + 'type'=>'npc', + 'generalID'=>$npc['no'], + 'generalName'=>$npc['name'] + ]) +]); + +Json::die([ + 'result'=>true, + 'reason'=>'success' +]); \ No newline at end of file diff --git a/hwe/js/select_npc.js b/hwe/js/select_npc.js index 8229d686..5170d58d 100644 --- a/hwe/js/select_npc.js +++ b/hwe/js/select_npc.js @@ -8,6 +8,27 @@ var templateGeneralCard = \ '; +function pickGeneral(){ + $btn = $(this); + + $.post({ + url:'j_select_npc.php', + dataType:'json', + data:{ + pick:$btn.val() + } + }).then(function(result){ + if(!result.result){ + alert(result.reason); + location.refresh(); + } + + alert('빙의에 성공했습니다.'); + location.href = './'; + }); + return false; +} + function updatePickMoreTimer(){ var $btn = $('#btn_pick_more'); var now = Date.now(); @@ -29,18 +50,18 @@ function printGenerals(value){ $('#btn_pick_more').data('available', new Date(value.pickMoreFrom).getTime()).prop('disabled',true); $.each(value.pick, function(idx, cardData){ cardData.iconPath = getIconPath(cardData.imgsvr, cardData.picture); - console.log(cardData); var $card = $(TemplateEngine(templateGeneralCard, cardData)); console.log($card); $('.card_holder').append($card); + $card.find('.select_btn').click(pickGeneral); }); updatePickMoreTimer(); } -jQuery(function($){ +$(function($){ $.post('j_get_select_npc_token.php').then(function(value){ if(!value.result){ diff --git a/hwe/new_select_npc.php b/hwe/new_select_npc.php deleted file mode 100644 index d5e18c35..00000000 --- a/hwe/new_select_npc.php +++ /dev/null @@ -1,103 +0,0 @@ -setReadOnly(); -$userID = Session::getUserID(); -$rootDB = RootDB::db(); -$db = DB::db(); - -//회원 테이블에서 정보확인 -$member = $rootDB->queryFirstRow('select no,name,picture,grade from member where no=%i', $userID); - -if(!$member) { - $session->logout(); - header('location:..'); - die(); -} - -list($npcmode, $maxgeneral) = $db->queryFirstList('SELECT npcmode,maxgeneral FROM game LIMIT 1'); - -if(!$npcmode) { - header('location:..'); - die(); -} - -$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2'); - -$nations = $db->queryAllLists('SELECT `name`, scoutmsg, color FROM nation'); -?> - - - -<?=UniqueConst::$serverName?>: NPC빙의 - - - - - - - - - - - - - - -= $maxgeneral) { -?> - - - - - - - -
-
장 수 선 택
- - -
- - - - - - - - - - - - - - - -
임관 권유 메세지
- -
-
장수 빙의
-
-( 까지 유효)
-
-
-
-
-
-
-
-
-
-
- - diff --git a/hwe/select_npc.php b/hwe/select_npc.php index 868318b9..99e4890d 100644 --- a/hwe/select_npc.php +++ b/hwe/select_npc.php @@ -6,141 +6,88 @@ include "func.php"; $session = Session::requireLogin()->setReadOnly(); $userID = Session::getUserID(); -$rootDB = RootDB::db(); $db = DB::db(); -//회원 테이블에서 정보확인 -$member = $rootDB->queryFirstRow('select no,name,picture,grade from member where no=%i', $userID); +list($npcmode, $maxgeneral) = $db->queryFirstList('SELECT npcmode,maxgeneral FROM game LIMIT 1'); -if(!$member) { - MessageBox("잘못된 접근입니다!!!"); - echo ""; - exit(1); -} - -$admin = $db->queryFirstRow('select npcmode,maxgeneral,show_img_level from game limit 1'); - -if($admin['npcmode'] != 1) { - header('Location:join.php'); +if(!$npcmode) { + header('location:..'); die(); } +$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2'); -$connect=$db->get(); - +$nations = $db->queryAllLists('SELECT `name`, scoutmsg, color FROM nation'); ?> -<?=UniqueConst::$serverName?>: NPC선택 +<?=UniqueConst::$serverName?>: NPC빙의 + + + + + + + - - - -
장 수 선 택
- - -
-= $admin['maxgeneral']) { - echo ""; - echo ""; - exit(); -} += $maxgeneral) { ?> - - -"; - } else { - echo " - "; - } -} -?> -
임관 권유 메세지
{$nation['name']}-
{$nation['name']}{$nation['scoutmsg']}
- -
- - - - -= 3) { -?> - - - - - - - - - - - - - - -
장수 선택
장수/1001.jpg border=0 name=picture width=64 height=64>
- -
- 컴퓨터가 조작중이던 NPC장수를 조종하게 됩니다.
- 80시간동안 휴식을 취하면 다시 컴퓨터가 조종하게 되고 장수의 소유권을 잃습니다. -
-
- - - -
- - + + + + +
+
장 수 선 택
+ + +
+ + + + + + + + + + + + + + + +
임관 권유 메세지
+ +
+
장수 빙의
+
+( 까지 유효)
+
+
+
+
+
+
+
+
+
+
+ diff --git a/hwe/select_npc_post.php b/hwe/select_npc_post.php deleted file mode 100644 index 4387baf7..00000000 --- a/hwe/select_npc_post.php +++ /dev/null @@ -1,144 +0,0 @@ -rule('required', 'face') -->rule('integer', 'face'); -if(!$v->validate()){ - MessageBox($v->errorStr()); - echo ""; - exit(1); -} - -$session = Session::requireLogin()->setReadOnly(); -$userID = Session::getUserID(); - -$face = (int)$_POST['face']; -$rootDB = RootDB::db(); - -//회원 테이블에서 정보확인 -$member = $rootDB->queryFirstRow('SELECT `no`, id, picture, grade, `name` FROM member WHERE no=%i', $userID); - -if(!$member) { - MessageBox("잘못된 접근입니다!!!"); - echo ""; - exit(1); -} - -$db = DB::db(); - -$npc = $db->queryFirstRow('SELECT `no`, `name`, `npc`, `level` FROM general WHERE `no`=%i', $face); -if(!$npc){ - echo ""; - echo ""; - exit(); -} - - -########## 동일 정보 존재여부 확인. ########## - -list( - $year, - $month, - $maxgeneral, - $turnterm, - $genius, - $npcmode -) = $db->queryFirstList('SELECT year,month,maxgeneral,turnterm,genius,npcmode from game limit 1'); - -$gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2'); -$oldGeneral = $db->queryFirstField('SELECT `no` FROM general WHERE `owner`=%i', $userID); - -if($npcmode != 1) { - echo ""; - echo ""; - exit(); -} -if($oldGeneral) { - echo(""); - exit; -} -if($maxgeneral <= $gencount) { - echo(""); - exit; -} -if($npc['npc'] < 2) { - echo(""); - exit; -} -if($npc['npc'] != 2) { - echo(""); - exit; -} -/*if($npc['level'] >= 5) { - echo(""); - exit; - -} */ - -$userNick = RootDB::db()->queryFirstField('SELECT `NAME` FROM member WHERE `NO`=%i',Session::getUserID()); - -$npcID = $npc['no']; -$db->update('general', [ - 'name2'=>$userNick, - 'npc'=>1, - 'killturn'=>6, - 'mode'=>2, - 'owner'=>$userID -], 'no=%i and npc=2', $npcID); -$affected = $db->affectedRows(); -if(!$affected){ - echo(""); - exit; -} - -$me = [ - 'no'=>$npcID -]; - -pushGeneralHistory($me, "●{$year}년 {$month}월:{$npc['name']}의 육체에 {$userNick}(이)가 빙의되다."); -//pushGenLog($me, $mylog); -pushGeneralPublicRecord(["●{$month}월:{$npc['name']}의 육체에 {$userNick}(이)가 빙의됩니다!"], $year, $month); - -pushAdminLog(["가입 : {$userID} // {$session->userName} // {$npcID} // ".getenv("REMOTE_ADDR")]); - -$rootDB->insert('member_log', [ - 'member_no' => $userID, - 'date'=>date('Y-m-d H:i:s'), - 'action_type'=>'make_general', - 'action'=>Json::encode([ - 'server'=>DB::prefix(), - 'type'=>'npc', - 'generalID'=>$npc['no'], - 'generalName'=>$npc['name'] - ]) -]); - -?> - diff --git a/js/common.js b/js/common.js index 142575e0..0ac528d6 100644 --- a/js/common.js +++ b/js/common.js @@ -118,4 +118,14 @@ var TemplateEngine = function (html, options) { try { result = new Function('obj', code).apply(options, [options]); } catch (err) { console.error("'" + err.message + "'", " in \n\nCode:\n", code, "\n"); } return result; +} + +function getIconPath(imgsvr, picture){ + // ../d_shared/common_path.js 필요 + if(!imgsvr){ + return pathConfig.sharedIcon+'/'+picture; + } + else{ + return pathConfig.root+'/d_pic/'+picture; + } } \ No newline at end of file -- 2.54.0 From 20f991a20571c66994dc2503e47ad5833cb01e7f Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 21:22:38 +0900 Subject: [PATCH 015/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=EC=8B=9C=20=EC=98=A4?= =?UTF-8?q?=EB=9E=98=EB=90=9C=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/j_select_npc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/j_select_npc.php b/hwe/j_select_npc.php index 51fb91e9..e4ff95a4 100644 --- a/hwe/j_select_npc.php +++ b/hwe/j_select_npc.php @@ -83,7 +83,7 @@ if(!$db->affectedRows()){ ]); } -$db->delete('select_npc_token', 'owner=%i', $userID); +$db->delete('select_npc_token', 'owner=%i or valid_until < %s', $userID, $now); pushGeneralHistory($pickedNPC, "●{$year}년 {$month}월:{$pickedNPC['name']}의 육체에 {$userNick}(이)가 빙의되다."); //pushGenLog($me, $mylog); -- 2.54.0 From 5dfb844c7eadf994ed4d3b1f201ff92a522c5add Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 21:40:59 +0900 Subject: [PATCH 016/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=20=EC=9E=AC=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EC=8B=9C=EA=B0=84=EC=9D=84=20=EB=B9=A0=EB=A5=B4?= =?UTF-8?q?=EA=B2=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/j_get_select_npc_token.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hwe/j_get_select_npc_token.php b/hwe/j_get_select_npc_token.php index 915b2f30..53863ade 100644 --- a/hwe/j_get_select_npc_token.php +++ b/hwe/j_get_select_npc_token.php @@ -4,8 +4,8 @@ namespace sammo; include "lib.php"; include "func.php"; -const VALID_MINUTE = 5; -const PICK_MORE_MINUTE = 2; +const VALID_SECOND = 25; +const PICK_MORE_SECOND = 10; const KEEP_CNT = 3; @@ -114,11 +114,11 @@ while(count($pickResult) < $pickLimit){ $newNonce = mt_rand(0, 0xfffffff); -$validMinute = max(VALID_MINUTE, intdiv($turnterm, 2)); -$pickMoreMinute = max(PICK_MORE_MINUTE, intdiv($turnterm, 5)); +$validSecond = max(VALID_SECOND, $turnterm*25); +$pickMoreSecond = max(PICK_MORE_SECOND, $turnterm*10); -$validUntil = $oNow->add(new \DateInterval(sprintf('PT%dM', $validMinute))); -$pickMoreFrom = $oNow->add(new \DateInterval(sprintf('PT%dM', $pickMoreMinute))); +$validUntil = $oNow->add(new \DateInterval(sprintf('PT%dS', $validSecond))); +$pickMoreFrom = $oNow->add(new \DateInterval(sprintf('PT%dS', $pickMoreSecond))); $db->delete('select_npc_token', 'valid_until < %s', $now); -- 2.54.0 From a75eb5b74e853756ed2c8f3644018d577a6ba056 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 21:44:15 +0900 Subject: [PATCH 017/304] =?UTF-8?q?=EC=9E=A5=EC=88=98=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=20=ED=85=8C=EC=9D=B4=EB=B8=94=EC=9D=84=20schema=EC=97=90=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sql/reset.sql | 4 +++- hwe/sql/schema.sql | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/hwe/sql/reset.sql b/hwe/sql/reset.sql index ef31cd89..f2ecae16 100644 --- a/hwe/sql/reset.sql +++ b/hwe/sql/reset.sql @@ -46,4 +46,6 @@ DROP TABLE IF EXISTS world_history; # 전체 이벤트 테이블 삭제(연감 대체?) DROP TABLE IF EXISTS general_public_record; -DROP TABLE IF EXISTS reserved_open; \ No newline at end of file +DROP TABLE IF EXISTS reserved_open; + +DROP TABLE IF EXISTS select_npc_token; \ No newline at end of file diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 7ecd0e54..911c594f 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -656,3 +656,19 @@ CREATE TABLE `reserved_open` ( ) DEFAULT CHARSET=utf8mb4 ENGINE=MyISAM; + +###### +# 장수 선택 토큰 +CREATE TABLE `select_npc_token` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `owner` INT(11) NOT NULL, + `valid_until` DATETIME NOT NULL, + `pick_more_from` DATETIME NOT NULL, + `pick_result` TEXT NOT NULL COMMENT 'json', + `nonce` INT(11) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `owner` (`owner`), + INDEX `valid_until` (`valid_until`) +) +DEFAULT CHARSET=utf8mb4 +ENGINE=MyISAM; -- 2.54.0 From 683a844a086d3f7d966763444b73a5cc3baa328e Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 22:39:21 +0900 Subject: [PATCH 018/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0=20-=20=EC=9C=A0=ED=9A=A8=20=EA=B8=B0?= =?UTF-8?q?=EA=B0=84=20=EA=B4=80=EB=A0=A8=20=EB=8A=98=EB=A6=BC.=20-=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=20=EA=B8=B0=EA=B0=84=20=EA=B0=80=EA=B9=8C?= =?UTF-8?q?=EC=9B=8C=EC=A7=80=EB=A9=B4=20=EC=83=89=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C.=20=EB=A7=8C=EB=A3=8C=20=ED=91=9C=EA=B8=B0?= =?UTF-8?q?=EB=8F=84=20=EC=B6=94=EA=B0=80=20-=20=EB=8B=A4=EB=A5=B8=20?= =?UTF-8?q?=EC=9E=A5=EC=88=98=20=EB=B3=B4=EA=B8=B0=EB=A5=BC=20=EB=82=A8?= =?UTF-8?q?=EC=9D=80=20=EC=B4=88=20=EA=B8=B0=EC=A4=80=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/css/select_npc.css | 9 +++++++-- hwe/j_get_select_npc_token.php | 9 +++++++-- hwe/js/select_npc.js | 29 +++++++++++++++++++++++++++-- hwe/select_npc.php | 16 ++++++++-------- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/hwe/css/select_npc.css b/hwe/css/select_npc.css index d6935563..8d345056 100644 --- a/hwe/css/select_npc.css +++ b/hwe/css/select_npc.css @@ -39,5 +39,10 @@ position: relative; top: -1px; *overflow: hidden; - } - \ No newline at end of file +} + + +#outdate_token{ + color:red; + display:none; +} \ No newline at end of file diff --git a/hwe/j_get_select_npc_token.php b/hwe/j_get_select_npc_token.php index 53863ade..613eff5d 100644 --- a/hwe/j_get_select_npc_token.php +++ b/hwe/j_get_select_npc_token.php @@ -4,7 +4,7 @@ namespace sammo; include "lib.php"; include "func.php"; -const VALID_SECOND = 25; +const VALID_SECOND = 90; const PICK_MORE_SECOND = 10; const KEEP_CNT = 3; @@ -74,10 +74,14 @@ if($token && $refresh){ } if($token && !$refresh){ + $pickMoreFrom = (new \DateTime($token['pick_more_from']))->getTimestamp(); + $nowT = $oNow->getTimestamp(); + Json::die([ 'result'=>true, 'pick'=>Json::decode($token['pick_result']), 'pickMoreFrom'=>$token['pick_more_from'], + 'pickMoreSeconds'=>$pickMoreFrom-$nowT, 'validUntil'=>$token['valid_until'] ]); } @@ -114,7 +118,7 @@ while(count($pickResult) < $pickLimit){ $newNonce = mt_rand(0, 0xfffffff); -$validSecond = max(VALID_SECOND, $turnterm*25); +$validSecond = max(VALID_SECOND, $turnterm*40); $pickMoreSecond = max(PICK_MORE_SECOND, $turnterm*10); $validUntil = $oNow->add(new \DateInterval(sprintf('PT%dS', $validSecond))); @@ -160,5 +164,6 @@ Json::die([ 'result'=>true, 'pick'=>$pickResult, 'pickMoreFrom'=>($inserted===-1)?$pickMoreFrom->format('Y-m-d H:i:s'):'2000-01-01 01:00:00', + 'pickMoreSeconds'=>($inserted===-1)?$pickMoreSecond:0, 'validUntil'=>$validUntil->format('Y-m-d H:i:s') ]); \ No newline at end of file diff --git a/hwe/js/select_npc.js b/hwe/js/select_npc.js index 5170d58d..eae34bc7 100644 --- a/hwe/js/select_npc.js +++ b/hwe/js/select_npc.js @@ -29,8 +29,29 @@ function pickGeneral(){ return false; } +function updateOutdateTimer(){ + var $validUntilText = $('#valid_until_text'); + var now = Date.now(); + var validUntil = $validUntilText.data('until'); + if(validUntil <= 0){ + return; + } + else if(validUntil < now){ + $validUntilText.data('until',0); + $('#valid_until').hide(); + $('#outdate_token').show(); + return; + } + else if(validUntil - now <= 30000){ + $validUntilText.css('color', "rgb(255, {0}, {0})".format(255*(validUntil - now)/30000)); + } + + setTimeout(updateOutdateTimer, 1000); +} + function updatePickMoreTimer(){ var $btn = $('#btn_pick_more'); + var now = Date.now(); var remain = ($btn.data('available') - now) / 1000; if(remain <= 0){ @@ -46,8 +67,10 @@ function updatePickMoreTimer(){ function printGenerals(value){ $('.card_holder').empty(); - $('#valid_until_text').html(value.validUntil); - $('#btn_pick_more').data('available', new Date(value.pickMoreFrom).getTime()).prop('disabled',true); + $('#valid_until_text').html(value.validUntil).data('until', (new Date(value.validUntil)).getTime()).show().css('color','white'); + $('#outdate_token').hide(); + var time = Date.now() + value.pickMoreSeconds*1000; + $('#btn_pick_more').data('available', time).prop('disabled',true); $.each(value.pick, function(idx, cardData){ cardData.iconPath = getIconPath(cardData.imgsvr, cardData.picture); @@ -59,6 +82,7 @@ function printGenerals(value){ }); updatePickMoreTimer(); + updateOutdateTimer(); } $(function($){ @@ -90,6 +114,7 @@ $('#btn_pick_more').click(function(){ alert(result.reason); location.refresh(); } + console.log(result); printGenerals(result); }); }); diff --git a/hwe/select_npc.php b/hwe/select_npc.php index 99e4890d..ee13d4f8 100644 --- a/hwe/select_npc.php +++ b/hwe/select_npc.php @@ -24,15 +24,15 @@ $nations = $db->queryAllLists('SELECT `name`, scoutmsg, color FROM nation'); <?=UniqueConst::$serverName?>: NPC빙의 - - - - - + + + + + - - + + @@ -78,7 +78,7 @@ history.go(-1);
장수 빙의
-( 까지 유효)
+( 까지 유효)- 만료 -
-- 2.54.0 From 95445d8f8af94835b6384c239b19caca2d0adb00 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 22:46:56 +0900 Subject: [PATCH 019/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=EC=B0=BD=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=A7=8C=EB=A3=8C=20=ED=9B=84=20=EB=8B=A4=EB=A5=B8?= =?UTF-8?q?=20=EC=9E=A5=EC=88=98=20=EB=B3=B4=EA=B8=B0=20=EC=8B=9C=20?= =?UTF-8?q?=EC=95=88=EB=B3=B4=EC=9D=B4=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/js/select_npc.js | 3 ++- hwe/select_npc.php | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hwe/js/select_npc.js b/hwe/js/select_npc.js index eae34bc7..31646829 100644 --- a/hwe/js/select_npc.js +++ b/hwe/js/select_npc.js @@ -67,7 +67,8 @@ function updatePickMoreTimer(){ function printGenerals(value){ $('.card_holder').empty(); - $('#valid_until_text').html(value.validUntil).data('until', (new Date(value.validUntil)).getTime()).show().css('color','white'); + $('#valid_until').show(); + $('#valid_until_text').html(value.validUntil).data('until', (new Date(value.validUntil)).getTime()).css('color','white'); $('#outdate_token').hide(); var time = Date.now() + value.pickMoreSeconds*1000; $('#btn_pick_more').data('available', time).prop('disabled',true); diff --git a/hwe/select_npc.php b/hwe/select_npc.php index ee13d4f8..f6808847 100644 --- a/hwe/select_npc.php +++ b/hwe/select_npc.php @@ -32,7 +32,7 @@ $nations = $db->queryAllLists('SELECT `name`, scoutmsg, color FROM nation'); - + @@ -78,7 +78,7 @@ history.go(-1);
장수 빙의
-( 까지 유효)- 만료 -
+(까지 유효)- 만료 -
-- 2.54.0 From dec24e09cfa35a7a225d256199b2149a9c326b90 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 22:52:53 +0900 Subject: [PATCH 020/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=EC=8B=9C=20=EC=9E=A5?= =?UTF-8?q?=EC=88=98=EB=AA=85=20=EB=B9=BC=EB=A8=B9=EB=8A=94=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/j_select_npc.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hwe/j_select_npc.php b/hwe/j_select_npc.php index e4ff95a4..4cde200b 100644 --- a/hwe/j_select_npc.php +++ b/hwe/j_select_npc.php @@ -72,8 +72,11 @@ if ($gencount >= $maxgeneral) { //등록 시작 $db->update('general', [ + 'name2'=>$userNick, + 'npc'=>1, + 'killturn'=>6, + 'mode'=>2, 'owner'=>$userID, - 'npc'=>1 ], 'owner <= 0 AND npc = 2 AND no = %i', $pick); if(!$db->affectedRows()){ -- 2.54.0 From 5e4493b49a56b19203e18cb6901e0778fc147147 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 23:03:10 +0900 Subject: [PATCH 021/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=EC=9D=84=20=EC=A2=85=EB=8A=A5=20=EC=88=9C=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC=ED=95=B4=EC=84=9C=20=ED=91=9C=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/js/select_npc.js | 13 ++++++++++++- hwe/select_npc.php | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/hwe/js/select_npc.js b/hwe/js/select_npc.js index 31646829..b41713ff 100644 --- a/hwe/js/select_npc.js +++ b/hwe/js/select_npc.js @@ -72,7 +72,18 @@ function printGenerals(value){ $('#outdate_token').hide(); var time = Date.now() + value.pickMoreSeconds*1000; $('#btn_pick_more').data('available', time).prop('disabled',true); - $.each(value.pick, function(idx, cardData){ + + var pick = $.map(value.pick, function(value, key) { + return value; + }); + + pick.sort(function(lhs, rhs){ + var lsum = lhs.leader+lhs.power+lhs.intel; + var rsum = rhs.leader+rhs.power+rhs.intel; + return lsum - rsum; + }); + + $.each(pick, function(idx, cardData){ cardData.iconPath = getIconPath(cardData.imgsvr, cardData.picture); var $card = $(TemplateEngine(templateGeneralCard, cardData)); diff --git a/hwe/select_npc.php b/hwe/select_npc.php index f6808847..6de57857 100644 --- a/hwe/select_npc.php +++ b/hwe/select_npc.php @@ -32,7 +32,7 @@ $nations = $db->queryAllLists('SELECT `name`, scoutmsg, color FROM nation'); - + -- 2.54.0 From 70b1f23a491dfaec625dc1dc0b93aa090d0af7d5 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 5 May 2018 23:25:21 +0900 Subject: [PATCH 022/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=20=ED=95=B4=EC=A0=9C?= =?UTF-8?q?=EC=9A=A9=20=EC=82=AD=ED=84=B4=20=EC=84=B8=ED=8C=85.=20-=2010?= =?UTF-8?q?=EB=B6=84=20=EB=AF=B8=EB=A7=8C=EC=9D=80=2030=EB=B6=84=20-=2010?= =?UTF-8?q?=EB=B6=84=20=EC=9D=B4=ED=95=98=EB=8A=94=2060=EB=B6=84=20?= =?UTF-8?q?=EB=8C=80=EA=B8=B0=20=ED=95=84=EC=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/b_myPage.php | 53 +++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/hwe/b_myPage.php b/hwe/b_myPage.php index 4e83baca..449e5ede 100644 --- a/hwe/b_myPage.php +++ b/hwe/b_myPage.php @@ -7,6 +7,7 @@ include "func.php"; $btn = Util::getReq('btn'); $mode = Util::getReq('mode', 'int', 2); $tnmt = Util::getReq('tnmt', 'int', 1); +$detachNPC = Util::getReq('detachNPC', 'bool'); extractMissingPostToGlobals(); @@ -27,33 +28,48 @@ $connect=$db->get(); increaseRefresh("내정보", 1); -$query = "select myset from general where owner='{$userID}'"; +$query = "select no,npc,mode,tnmt,myset from general where owner='{$userID}'"; $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect), ""); $me = MYDB_fetch_array($result); + if ($me['myset'] > 0) { $submit = 'submit'; } else { $submit = 'hidden'; } -if ($btn == "설정저장" && $me['myset'] > 0) { +if (($btn == "설정저장" || $detachNPC) && $me['myset'] > 0) { if ($me['myset'] > 1) { $submit = 'submit'; } else { $submit = 'hidden'; } + $me['myset'] -= 1; + $db->update('general', [ 'myset'=>$db->sqleval('myset-1'), 'mode'=>$mode, 'tnmt'=>$tnmt ], 'owner=%i', $userID); -} -$query = "select no,mode,tnmt,myset from general where owner='{$userID}'"; -$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect), ""); -$me = MYDB_fetch_array($result); + if($me['npc'] == 1 && $detachNPC){ + $turnterm = $db->queryFirstField('SELECT turnterm FROM game LIMIT 1'); + + if($turnterm < 10){ + $targetKillTurn = 30 / $turnterm; + } + else{ + $targetKillTurn = 60 / $turnterm; + } + $db->update('general', [ + 'killturn'=>$targetKillTurn + ], 'owner=%i AND npc=1', $userID); + + $me['killturn']=$targetKillTurn; + } +} ?> @@ -63,13 +79,6 @@ $me = MYDB_fetch_array($result); <?=UniqueConst::$serverName?>: 내정보 - @@ -83,23 +92,25 @@ function go(type) { - +
-     토너먼트 【 + 토너먼트 【 >수동참여 >자동참여 】
-     ∞개막직전 남는자리가 있을경우 랜덤하게 참여합니다.

-     수비 【 + ∞개막직전 남는자리가 있을경우 랜덤하게 참여합니다.

+ 수비 【 >◎(훈사80) >○(훈사60) >× 】

-      name=btn style=background-color:;color:white;width:160px;height:30px;font-size:13px; value=설정저장>
-     ∞설정저장은 이달중 회 남았습니다.

+ name=btn style=background-color:;color:white;width:160px;height:30px;font-size:13px; value=설정저장>
+ ∞설정저장은 이달중 회 남았습니다.

-     휴 가 신 청
-     ;color:white;width:160px;height:30px;font-size:13px; value=휴가신청 onclick='go(0)'> + 휴 가 신 청
+

+ 빙의 해제용 삭턴 조절
+ -- 2.54.0 From 776e16b58b8ad43873770fe71758b5a3b79bcfdf Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 7 May 2018 12:39:03 +0900 Subject: [PATCH 023/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=20=EB=8C=80=EA=B8=B0=20=EC=8B=9C=EA=B0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/j_get_select_npc_token.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/j_get_select_npc_token.php b/hwe/j_get_select_npc_token.php index 613eff5d..93de3b31 100644 --- a/hwe/j_get_select_npc_token.php +++ b/hwe/j_get_select_npc_token.php @@ -119,7 +119,7 @@ while(count($pickResult) < $pickLimit){ $newNonce = mt_rand(0, 0xfffffff); $validSecond = max(VALID_SECOND, $turnterm*40); -$pickMoreSecond = max(PICK_MORE_SECOND, $turnterm*10); +$pickMoreSecond = max(PICK_MORE_SECOND, Util::round(pow($turnterm, 0.672)*8)); $validUntil = $oNow->add(new \DateInterval(sprintf('PT%dS', $validSecond))); $pickMoreFrom = $oNow->add(new \DateInterval(sprintf('PT%dS', $pickMoreSecond))); -- 2.54.0 From 330853c98d8c2d6dddc03d117986ffa018654f4a Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 7 May 2018 19:34:56 +0900 Subject: [PATCH 024/304] =?UTF-8?q?30=EB=B2=88=20=EC=8B=9C=EB=82=98?= =?UTF-8?q?=EB=A6=AC=EC=98=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/scenario/scenario_30.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/scenario/scenario_30.json b/hwe/scenario/scenario_30.json index 1f433d76..c0f7056b 100644 --- a/hwe/scenario/scenario_30.json +++ b/hwe/scenario/scenario_30.json @@ -70,8 +70,8 @@ [0, "정보명", -1, 0, null, 69, 31, 39, 0, 160, 300, null, null], [0, "이재곤", -1, 0, null, 40, 41, 39, 0, 160, 300, null, null], [0, "염종석", -1, 0, null, 83, 90, 93, 0, 160, 300, null, null], - [0, "황재균", -1, 0, null, 79, 77, 83, 0, 160, 300, null, "집중", "안녕하세요 섹머입니다"], - [0, "윤학길", -1, 0, null, 81, 76, 82, 0, 160, 300, null, null], + [0, "황재균", -1, 0, null, 79, 77, 83, 0, 160, 300, null, null, "안녕하세요 섹머입니다"], + [0, "윤학길", -1, 0, null, 81, 76, 82, 0, 160, 300, null, "집중"], [0, "가득염", -1, 0, null, 60, 67, 61, 0, 160, 300, null, null], [0, "조경환", -1, 0, null, 57, 78, 20, 0, 160, 300, null, null], [0, "강상수", -1, 0, null, 65, 66, 58, 0, 160, 300, null, null], -- 2.54.0 From a3f6a8e8abedbf34dc316f900ee1527ce726aed2 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 8 May 2018 00:38:29 +0900 Subject: [PATCH 025/304] =?UTF-8?q?symfony/lock=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 3 +- composer.lock | 171 ++++++++- vendor/composer/autoload_classmap.php | 7 + vendor/composer/autoload_files.php | 2 + vendor/composer/autoload_psr4.php | 2 + vendor/composer/autoload_static.php | 22 ++ vendor/composer/installed.json | 175 +++++++++ vendor/paragonie/random_compat/LICENSE | 22 ++ vendor/paragonie/random_compat/build-phar.sh | 5 + vendor/paragonie/random_compat/composer.json | 37 ++ .../dist/random_compat.phar.pubkey | 5 + .../dist/random_compat.phar.pubkey.asc | 11 + .../random_compat/lib/byte_safe_strings.php | 181 +++++++++ .../random_compat/lib/cast_to_int.php | 75 ++++ .../random_compat/lib/error_polyfill.php | 49 +++ vendor/paragonie/random_compat/lib/random.php | 225 +++++++++++ .../lib/random_bytes_com_dotnet.php | 88 +++++ .../lib/random_bytes_dev_urandom.php | 167 ++++++++ .../lib/random_bytes_libsodium.php | 88 +++++ .../lib/random_bytes_libsodium_legacy.php | 92 +++++ .../random_compat/lib/random_bytes_mcrypt.php | 77 ++++ .../random_compat/lib/random_int.php | 190 ++++++++++ .../random_compat/other/build_phar.php | 57 +++ .../random_compat/psalm-autoload.php | 9 + vendor/paragonie/random_compat/psalm.xml | 18 + vendor/symfony/lock/.gitignore | 3 + vendor/symfony/lock/CHANGELOG.md | 7 + .../lock/Exception/ExceptionInterface.php | 21 ++ .../Exception/InvalidArgumentException.php | 19 + .../lock/Exception/LockAcquiringException.php | 21 ++ .../Exception/LockConflictedException.php | 21 ++ .../lock/Exception/LockExpiredException.php | 21 ++ .../lock/Exception/LockReleasingException.php | 21 ++ .../lock/Exception/LockStorageException.php | 21 ++ .../lock/Exception/NotSupportedException.php | 21 ++ vendor/symfony/lock/Factory.php | 52 +++ vendor/symfony/lock/Key.php | 109 ++++++ vendor/symfony/lock/LICENSE | 19 + vendor/symfony/lock/Lock.php | 173 +++++++++ vendor/symfony/lock/LockInterface.php | 59 +++ vendor/symfony/lock/README.md | 11 + vendor/symfony/lock/Store/CombinedStore.php | 186 +++++++++ vendor/symfony/lock/Store/FlockStore.php | 141 +++++++ vendor/symfony/lock/Store/MemcachedStore.php | 187 +++++++++ vendor/symfony/lock/Store/RedisStore.php | 166 ++++++++ .../symfony/lock/Store/RetryTillSaveStore.php | 102 +++++ vendor/symfony/lock/Store/SemaphoreStore.php | 129 +++++++ vendor/symfony/lock/Store/StoreFactory.php | 40 ++ vendor/symfony/lock/StoreInterface.php | 64 ++++ .../lock/Strategy/ConsensusStrategy.php | 36 ++ .../lock/Strategy/StrategyInterface.php | 43 +++ .../lock/Strategy/UnanimousStrategy.php | 36 ++ vendor/symfony/lock/Tests/FactoryTest.php | 36 ++ vendor/symfony/lock/Tests/LockTest.php | 254 +++++++++++++ .../Tests/Store/AbstractRedisStoreTest.php | 45 +++ .../lock/Tests/Store/AbstractStoreTest.php | 115 ++++++ .../Tests/Store/BlockingStoreTestTrait.php | 97 +++++ .../lock/Tests/Store/CombinedStoreTest.php | 356 ++++++++++++++++++ .../Tests/Store/ExpiringStoreTestTrait.php | 107 ++++++ .../lock/Tests/Store/FlockStoreTest.php | 78 ++++ .../lock/Tests/Store/MemcachedStoreTest.php | 60 +++ .../lock/Tests/Store/PredisStoreTest.php | 36 ++ .../lock/Tests/Store/RedisArrayStoreTest.php | 38 ++ .../lock/Tests/Store/RedisStoreTest.php | 36 ++ .../Tests/Store/RetryTillSaveStoreTest.php | 35 ++ .../lock/Tests/Store/SemaphoreStoreTest.php | 36 ++ .../Tests/Strategy/ConsensusStrategyTest.php | 89 +++++ .../Tests/Strategy/UnanimousStrategyTest.php | 89 +++++ vendor/symfony/lock/composer.json | 38 ++ vendor/symfony/lock/phpunit.xml.dist | 32 ++ vendor/symfony/polyfill-php70/LICENSE | 19 + vendor/symfony/polyfill-php70/Php70.php | 74 ++++ vendor/symfony/polyfill-php70/README.md | 28 ++ .../Resources/stubs/ArithmeticError.php | 5 + .../Resources/stubs/AssertionError.php | 5 + .../Resources/stubs/DivisionByZeroError.php | 5 + .../polyfill-php70/Resources/stubs/Error.php | 5 + .../Resources/stubs/ParseError.php | 5 + ...SessionUpdateTimestampHandlerInterface.php | 23 ++ .../Resources/stubs/TypeError.php | 5 + vendor/symfony/polyfill-php70/bootstrap.php | 27 ++ vendor/symfony/polyfill-php70/composer.json | 33 ++ 82 files changed, 5316 insertions(+), 2 deletions(-) create mode 100644 vendor/paragonie/random_compat/LICENSE create mode 100644 vendor/paragonie/random_compat/build-phar.sh create mode 100644 vendor/paragonie/random_compat/composer.json create mode 100644 vendor/paragonie/random_compat/dist/random_compat.phar.pubkey create mode 100644 vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc create mode 100644 vendor/paragonie/random_compat/lib/byte_safe_strings.php create mode 100644 vendor/paragonie/random_compat/lib/cast_to_int.php create mode 100644 vendor/paragonie/random_compat/lib/error_polyfill.php create mode 100644 vendor/paragonie/random_compat/lib/random.php create mode 100644 vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php create mode 100644 vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php create mode 100644 vendor/paragonie/random_compat/lib/random_bytes_libsodium.php create mode 100644 vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php create mode 100644 vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php create mode 100644 vendor/paragonie/random_compat/lib/random_int.php create mode 100644 vendor/paragonie/random_compat/other/build_phar.php create mode 100644 vendor/paragonie/random_compat/psalm-autoload.php create mode 100644 vendor/paragonie/random_compat/psalm.xml create mode 100644 vendor/symfony/lock/.gitignore create mode 100644 vendor/symfony/lock/CHANGELOG.md create mode 100644 vendor/symfony/lock/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/lock/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/lock/Exception/LockAcquiringException.php create mode 100644 vendor/symfony/lock/Exception/LockConflictedException.php create mode 100644 vendor/symfony/lock/Exception/LockExpiredException.php create mode 100644 vendor/symfony/lock/Exception/LockReleasingException.php create mode 100644 vendor/symfony/lock/Exception/LockStorageException.php create mode 100644 vendor/symfony/lock/Exception/NotSupportedException.php create mode 100644 vendor/symfony/lock/Factory.php create mode 100644 vendor/symfony/lock/Key.php create mode 100644 vendor/symfony/lock/LICENSE create mode 100644 vendor/symfony/lock/Lock.php create mode 100644 vendor/symfony/lock/LockInterface.php create mode 100644 vendor/symfony/lock/README.md create mode 100644 vendor/symfony/lock/Store/CombinedStore.php create mode 100644 vendor/symfony/lock/Store/FlockStore.php create mode 100644 vendor/symfony/lock/Store/MemcachedStore.php create mode 100644 vendor/symfony/lock/Store/RedisStore.php create mode 100644 vendor/symfony/lock/Store/RetryTillSaveStore.php create mode 100644 vendor/symfony/lock/Store/SemaphoreStore.php create mode 100644 vendor/symfony/lock/Store/StoreFactory.php create mode 100644 vendor/symfony/lock/StoreInterface.php create mode 100644 vendor/symfony/lock/Strategy/ConsensusStrategy.php create mode 100644 vendor/symfony/lock/Strategy/StrategyInterface.php create mode 100644 vendor/symfony/lock/Strategy/UnanimousStrategy.php create mode 100644 vendor/symfony/lock/Tests/FactoryTest.php create mode 100644 vendor/symfony/lock/Tests/LockTest.php create mode 100644 vendor/symfony/lock/Tests/Store/AbstractRedisStoreTest.php create mode 100644 vendor/symfony/lock/Tests/Store/AbstractStoreTest.php create mode 100644 vendor/symfony/lock/Tests/Store/BlockingStoreTestTrait.php create mode 100644 vendor/symfony/lock/Tests/Store/CombinedStoreTest.php create mode 100644 vendor/symfony/lock/Tests/Store/ExpiringStoreTestTrait.php create mode 100644 vendor/symfony/lock/Tests/Store/FlockStoreTest.php create mode 100644 vendor/symfony/lock/Tests/Store/MemcachedStoreTest.php create mode 100644 vendor/symfony/lock/Tests/Store/PredisStoreTest.php create mode 100644 vendor/symfony/lock/Tests/Store/RedisArrayStoreTest.php create mode 100644 vendor/symfony/lock/Tests/Store/RedisStoreTest.php create mode 100644 vendor/symfony/lock/Tests/Store/RetryTillSaveStoreTest.php create mode 100644 vendor/symfony/lock/Tests/Store/SemaphoreStoreTest.php create mode 100644 vendor/symfony/lock/Tests/Strategy/ConsensusStrategyTest.php create mode 100644 vendor/symfony/lock/Tests/Strategy/UnanimousStrategyTest.php create mode 100644 vendor/symfony/lock/composer.json create mode 100644 vendor/symfony/lock/phpunit.xml.dist create mode 100644 vendor/symfony/polyfill-php70/LICENSE create mode 100644 vendor/symfony/polyfill-php70/Php70.php create mode 100644 vendor/symfony/polyfill-php70/README.md create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/AssertionError.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/Error.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/ParseError.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/TypeError.php create mode 100644 vendor/symfony/polyfill-php70/bootstrap.php create mode 100644 vendor/symfony/polyfill-php70/composer.json diff --git a/composer.json b/composer.json index c8b0e570..ea4ebc53 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ "firephp/firephp-core": "^0.4.0", "vlucas/valitron": "^1.4", "php-extended/php-tail": "^2.1", - "pguardiario/phpuri": "^1.0" + "pguardiario/phpuri": "^1.0", + "symfony/lock": "^3.4" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 43411eaf..5ce4f436 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "1753c2c2ad9fe40ecc5c6ae541dd2e78", + "content-hash": "a329d39be5161c87438d41f9adf4417b", "packages": [ { "name": "brandonwamboldt/utilphp", @@ -454,6 +454,54 @@ ], "time": "2017-06-19T01:22:40+00:00" }, + { + "name": "paragonie/random_compat", + "version": "v2.0.12", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2018-04-04T21:24:14+00:00" + }, { "name": "pguardiario/phpuri", "version": "1.0", @@ -702,6 +750,127 @@ ], "time": "2014-06-16T22:40:22+00:00" }, + { + "name": "symfony/lock", + "version": "v3.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/lock.git", + "reference": "3a707193326d31603824706e34a95af899a59788" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/lock/zipball/3a707193326d31603824706e34a95af899a59788", + "reference": "3a707193326d31603824706e34a95af899a59788", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0", + "symfony/polyfill-php70": "~1.0" + }, + "require-dev": { + "predis/predis": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Lock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Jérémy Derussé", + "email": "jeremy@derusse.com" + } + ], + "description": "Symfony Lock Component", + "homepage": "https://symfony.com", + "keywords": [ + "cas", + "flock", + "locking", + "mutex", + "redlock", + "semaphore" + ], + "time": "2018-04-29T14:44:26+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "77454693d8f10dd23bb24955cffd2d82db1007a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/77454693d8f10dd23bb24955cffd2d82db1007a6", + "reference": "77454693d8f10dd23bb24955cffd2d82db1007a6", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2018-04-26T10:06:28+00:00" + }, { "name": "vlucas/valitron", "version": "v1.4.2", diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 0f375530..e8398d99 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,14 +6,21 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php', + 'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php', 'DB' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', 'DBHelper' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', 'DBTransaction' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', + 'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', + 'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php', 'FB' => $vendorDir . '/firephp/firephp-core/lib/FirePHPCore/fb.php', 'FirePHP' => $vendorDir . '/firephp/firephp-core/lib/FirePHPCore/FirePHP.class.php', 'MeekroDB' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', 'MeekroDBEval' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', 'MeekroDBException' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', + 'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php', + 'SessionUpdateTimestampHandlerInterface' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php', + 'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', 'WhereClause' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', 'kakao\\KakaoKey' => $baseDir . '/d_setting/KakaoKey.php', 'sammo\\RootDB' => $baseDir . '/d_setting/RootDB.php', diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 325499f6..350100b3 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -6,8 +6,10 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', + '023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '5b154887902198b16314243c6e0e3e19' => $vendorDir . '/pguardiario/phpuri/phpuri.php', '870dc64919afa8b0f700701bb2c6a783' => $baseDir . '/f_config/config.php', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index a422d325..c11d2d90 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -8,6 +8,8 @@ $baseDir = dirname($vendorDir); return array( 'sammo\\' => array($baseDir . '/src/sammo'), 'kakao\\' => array($baseDir . '/src/kakao'), + 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), + 'Symfony\\Component\\Lock\\' => array($vendorDir . '/symfony/lock'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), 'PhpExtended\\Tail\\' => array($vendorDir . '/php-extended/php-tail/src'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 78b46bcf..0811ecbf 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -7,8 +7,10 @@ namespace Composer\Autoload; class ComposerStaticInit67b09c83b85c3bc0027caefe5bba171a { public static $files = array ( + '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', + '023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '5b154887902198b16314243c6e0e3e19' => __DIR__ . '/..' . '/pguardiario/phpuri/phpuri.php', '870dc64919afa8b0f700701bb2c6a783' => __DIR__ . '/../..' . '/f_config/config.php', @@ -23,6 +25,11 @@ class ComposerStaticInit67b09c83b85c3bc0027caefe5bba171a array ( 'kakao\\' => 6, ), + 'S' => + array ( + 'Symfony\\Polyfill\\Php70\\' => 23, + 'Symfony\\Component\\Lock\\' => 23, + ), 'P' => array ( 'Psr\\Log\\' => 8, @@ -56,6 +63,14 @@ class ComposerStaticInit67b09c83b85c3bc0027caefe5bba171a array ( 0 => __DIR__ . '/../..' . '/src/kakao', ), + 'Symfony\\Polyfill\\Php70\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-php70', + ), + 'Symfony\\Component\\Lock\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/lock', + ), 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', @@ -116,14 +131,21 @@ class ComposerStaticInit67b09c83b85c3bc0027caefe5bba171a ); public static $classMap = array ( + 'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php', + 'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php', 'DB' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', 'DBHelper' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', 'DBTransaction' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', + 'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', + 'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php', 'FB' => __DIR__ . '/..' . '/firephp/firephp-core/lib/FirePHPCore/fb.php', 'FirePHP' => __DIR__ . '/..' . '/firephp/firephp-core/lib/FirePHPCore/FirePHP.class.php', 'MeekroDB' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', 'MeekroDBEval' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', 'MeekroDBException' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', + 'ParseError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ParseError.php', + 'SessionUpdateTimestampHandlerInterface' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php', + 'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', 'WhereClause' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', 'kakao\\KakaoKey' => __DIR__ . '/../..' . '/d_setting/KakaoKey.php', 'sammo\\RootDB' => __DIR__ . '/../..' . '/d_setting/RootDB.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index c2931551..5c44110b 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -463,6 +463,56 @@ "psr-3" ] }, + { + "name": "paragonie/random_compat", + "version": "v2.0.12", + "version_normalized": "2.0.12.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "time": "2018-04-04T21:24:14+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ] + }, { "name": "pguardiario/phpuri", "version": "1.0", @@ -723,6 +773,131 @@ "pdo" ] }, + { + "name": "symfony/lock", + "version": "v3.4.9", + "version_normalized": "3.4.9.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/lock.git", + "reference": "3a707193326d31603824706e34a95af899a59788" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/lock/zipball/3a707193326d31603824706e34a95af899a59788", + "reference": "3a707193326d31603824706e34a95af899a59788", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0", + "symfony/polyfill-php70": "~1.0" + }, + "require-dev": { + "predis/predis": "~1.0" + }, + "time": "2018-04-29T14:44:26+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Lock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Jérémy Derussé", + "email": "jeremy@derusse.com" + } + ], + "description": "Symfony Lock Component", + "homepage": "https://symfony.com", + "keywords": [ + "cas", + "flock", + "locking", + "mutex", + "redlock", + "semaphore" + ] + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.8.0", + "version_normalized": "1.8.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "77454693d8f10dd23bb24955cffd2d82db1007a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/77454693d8f10dd23bb24955cffd2d82db1007a6", + "reference": "77454693d8f10dd23bb24955cffd2d82db1007a6", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "time": "2018-04-26T10:06:28+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ] + }, { "name": "vlucas/valitron", "version": "v1.4.2", diff --git a/vendor/paragonie/random_compat/LICENSE b/vendor/paragonie/random_compat/LICENSE new file mode 100644 index 00000000..45c7017d --- /dev/null +++ b/vendor/paragonie/random_compat/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paragon Initiative Enterprises + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/paragonie/random_compat/build-phar.sh b/vendor/paragonie/random_compat/build-phar.sh new file mode 100644 index 00000000..b4a5ba31 --- /dev/null +++ b/vendor/paragonie/random_compat/build-phar.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) ) + +php -dphar.readonly=0 "$basedir/other/build_phar.php" $* \ No newline at end of file diff --git a/vendor/paragonie/random_compat/composer.json b/vendor/paragonie/random_compat/composer.json new file mode 100644 index 00000000..1c5978c6 --- /dev/null +++ b/vendor/paragonie/random_compat/composer.json @@ -0,0 +1,37 @@ +{ + "name": "paragonie/random_compat", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "random", + "pseudorandom" + ], + "license": "MIT", + "type": "library", + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "support": { + "issues": "https://github.com/paragonie/random_compat/issues", + "email": "info@paragonie.com", + "source": "https://github.com/paragonie/random_compat" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "autoload": { + "files": [ + "lib/random.php" + ] + } +} diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey new file mode 100644 index 00000000..eb50ebfc --- /dev/null +++ b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm +pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p ++h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc +-----END PUBLIC KEY----- diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc new file mode 100644 index 00000000..6a1d7f30 --- /dev/null +++ b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v2.0.22 (MingW32) + +iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip +QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg +1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW +NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA +NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV +JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74= +=B6+8 +-----END PGP SIGNATURE----- diff --git a/vendor/paragonie/random_compat/lib/byte_safe_strings.php b/vendor/paragonie/random_compat/lib/byte_safe_strings.php new file mode 100644 index 00000000..3de86b22 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/byte_safe_strings.php @@ -0,0 +1,181 @@ + RandomCompat_strlen($binary_string)) { + return ''; + } + + return (string) mb_substr($binary_string, $start, $length, '8bit'); + } + + } else { + + /** + * substr() implementation that isn't brittle to mbstring.func_overload + * + * This version just uses the default substr() + * + * @param string $binary_string + * @param int $start + * @param int $length (optional) + * + * @throws TypeError + * + * @return string + */ + function RandomCompat_substr($binary_string, $start, $length = null) + { + if (!is_string($binary_string)) { + throw new TypeError( + 'RandomCompat_substr(): First argument should be a string' + ); + } + + if (!is_int($start)) { + throw new TypeError( + 'RandomCompat_substr(): Second argument should be an integer' + ); + } + + if ($length !== null) { + if (!is_int($length)) { + throw new TypeError( + 'RandomCompat_substr(): Third argument should be an integer, or omitted' + ); + } + + return (string) substr($binary_string, $start, $length); + } + + return (string) substr($binary_string, $start); + } + } +} diff --git a/vendor/paragonie/random_compat/lib/cast_to_int.php b/vendor/paragonie/random_compat/lib/cast_to_int.php new file mode 100644 index 00000000..9a4fab99 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/cast_to_int.php @@ -0,0 +1,75 @@ + operators might accidentally let a float + * through. + * + * @param int|float $number The number we want to convert to an int + * @param bool $fail_open Set to true to not throw an exception + * + * @return float|int + * @psalm-suppress InvalidReturnType + * + * @throws TypeError + */ + function RandomCompat_intval($number, $fail_open = false) + { + if (is_int($number) || is_float($number)) { + $number += 0; + } elseif (is_numeric($number)) { + $number += 0; + } + + if ( + is_float($number) + && + $number > ~PHP_INT_MAX + && + $number < PHP_INT_MAX + ) { + $number = (int) $number; + } + + if (is_int($number)) { + return (int) $number; + } elseif (!$fail_open) { + throw new TypeError( + 'Expected an integer.' + ); + } + return $number; + } +} diff --git a/vendor/paragonie/random_compat/lib/error_polyfill.php b/vendor/paragonie/random_compat/lib/error_polyfill.php new file mode 100644 index 00000000..6a91990c --- /dev/null +++ b/vendor/paragonie/random_compat/lib/error_polyfill.php @@ -0,0 +1,49 @@ += 70000) { + return; +} + +if (!defined('RANDOM_COMPAT_READ_BUFFER')) { + define('RANDOM_COMPAT_READ_BUFFER', 8); +} + +$RandomCompatDIR = dirname(__FILE__); + +require_once $RandomCompatDIR . '/byte_safe_strings.php'; +require_once $RandomCompatDIR . '/cast_to_int.php'; +require_once $RandomCompatDIR . '/error_polyfill.php'; + +if (!is_callable('random_bytes')) { + /** + * PHP 5.2.0 - 5.6.x way to implement random_bytes() + * + * We use conditional statements here to define the function in accordance + * to the operating environment. It's a micro-optimization. + * + * In order of preference: + * 1. Use libsodium if available. + * 2. fread() /dev/urandom if available (never on Windows) + * 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM) + * 4. COM('CAPICOM.Utilities.1')->GetRandom() + * + * See RATIONALE.md for our reasoning behind this particular order + */ + if (extension_loaded('libsodium')) { + // See random_bytes_libsodium.php + if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) { + require_once $RandomCompatDIR . '/random_bytes_libsodium.php'; + } elseif (method_exists('Sodium', 'randombytes_buf')) { + require_once $RandomCompatDIR . '/random_bytes_libsodium_legacy.php'; + } + } + + /** + * Reading directly from /dev/urandom: + */ + if (DIRECTORY_SEPARATOR === '/') { + // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast + // way to exclude Windows. + $RandomCompatUrandom = true; + $RandomCompat_basedir = ini_get('open_basedir'); + + if (!empty($RandomCompat_basedir)) { + $RandomCompat_open_basedir = explode( + PATH_SEPARATOR, + strtolower($RandomCompat_basedir) + ); + $RandomCompatUrandom = (array() !== array_intersect( + array('/dev', '/dev/', '/dev/urandom'), + $RandomCompat_open_basedir + )); + $RandomCompat_open_basedir = null; + } + + if ( + !is_callable('random_bytes') + && + $RandomCompatUrandom + && + @is_readable('/dev/urandom') + ) { + // Error suppression on is_readable() in case of an open_basedir + // or safe_mode failure. All we care about is whether or not we + // can read it at this point. If the PHP environment is going to + // panic over trying to see if the file can be read in the first + // place, that is not helpful to us here. + + // See random_bytes_dev_urandom.php + require_once $RandomCompatDIR . '/random_bytes_dev_urandom.php'; + } + // Unset variables after use + $RandomCompat_basedir = null; + } else { + $RandomCompatUrandom = false; + } + + /** + * mcrypt_create_iv() + * + * We only want to use mcypt_create_iv() if: + * + * - random_bytes() hasn't already been defined + * - the mcrypt extensions is loaded + * - One of these two conditions is true: + * - We're on Windows (DIRECTORY_SEPARATOR !== '/') + * - We're not on Windows and /dev/urandom is readabale + * (i.e. we're not in a chroot jail) + * - Special case: + * - If we're not on Windows, but the PHP version is between + * 5.6.10 and 5.6.12, we don't want to use mcrypt. It will + * hang indefinitely. This is bad. + * - If we're on Windows, we want to use PHP >= 5.3.7 or else + * we get insufficient entropy errors. + */ + if ( + !is_callable('random_bytes') + && + // Windows on PHP < 5.3.7 is broken, but non-Windows is not known to be. + (DIRECTORY_SEPARATOR === '/' || PHP_VERSION_ID >= 50307) + && + // Prevent this code from hanging indefinitely on non-Windows; + // see https://bugs.php.net/bug.php?id=69833 + ( + DIRECTORY_SEPARATOR !== '/' || + (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613) + ) + && + extension_loaded('mcrypt') + ) { + // See random_bytes_mcrypt.php + require_once $RandomCompatDIR . '/random_bytes_mcrypt.php'; + } + $RandomCompatUrandom = null; + + /** + * This is a Windows-specific fallback, for when the mcrypt extension + * isn't loaded. + */ + if ( + !is_callable('random_bytes') + && + extension_loaded('com_dotnet') + && + class_exists('COM') + ) { + $RandomCompat_disabled_classes = preg_split( + '#\s*,\s*#', + strtolower(ini_get('disable_classes')) + ); + + if (!in_array('com', $RandomCompat_disabled_classes)) { + try { + $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); + if (method_exists($RandomCompatCOMtest, 'GetRandom')) { + // See random_bytes_com_dotnet.php + require_once $RandomCompatDIR . '/random_bytes_com_dotnet.php'; + } + } catch (com_exception $e) { + // Don't try to use it. + } + } + $RandomCompat_disabled_classes = null; + $RandomCompatCOMtest = null; + } + + /** + * throw new Exception + */ + if (!is_callable('random_bytes')) { + /** + * We don't have any more options, so let's throw an exception right now + * and hope the developer won't let it fail silently. + * + * @param mixed $length + * @psalm-suppress MissingReturnType + * @throws Exception + * @return string + */ + function random_bytes($length) + { + unset($length); // Suppress "variable not used" warnings. + throw new Exception( + 'There is no suitable CSPRNG installed on your system' + ); + return ''; + } + } +} + +if (!is_callable('random_int')) { + require_once $RandomCompatDIR . '/random_int.php'; +} + +$RandomCompatDIR = null; diff --git a/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php b/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php new file mode 100644 index 00000000..fc1926e5 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php @@ -0,0 +1,88 @@ +GetRandom($bytes, 0)); + if (RandomCompat_strlen($buf) >= $bytes) { + /** + * Return our random entropy buffer here: + */ + return RandomCompat_substr($buf, 0, $bytes); + } + ++$execCount; + } while ($execCount < $bytes); + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} \ No newline at end of file diff --git a/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php b/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php new file mode 100644 index 00000000..df5b9152 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php @@ -0,0 +1,167 @@ + 0); + + /** + * Is our result valid? + */ + if (is_string($buf)) { + if (RandomCompat_strlen($buf) === $bytes) { + /** + * Return our random entropy buffer here: + */ + return $buf; + } + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Error reading from source device' + ); + } +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php b/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php new file mode 100644 index 00000000..4af1a242 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php @@ -0,0 +1,88 @@ + 2147483647) { + $buf = ''; + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= \Sodium\randombytes_buf($n); + } + } else { + $buf = \Sodium\randombytes_buf($bytes); + } + + if ($buf !== false) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php b/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php new file mode 100644 index 00000000..705af526 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php @@ -0,0 +1,92 @@ + 2147483647) { + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= Sodium::randombytes_buf((int) $n); + } + } else { + $buf .= Sodium::randombytes_buf((int) $bytes); + } + + if (is_string($buf)) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php b/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php new file mode 100644 index 00000000..aac9c013 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php @@ -0,0 +1,77 @@ + operators might accidentally let a float + * through. + */ + + try { + $min = RandomCompat_intval($min); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $min must be an integer' + ); + } + + try { + $max = RandomCompat_intval($max); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $max must be an integer' + ); + } + + /** + * Now that we've verified our weak typing system has given us an integer, + * let's validate the logic then we can move forward with generating random + * integers along a given range. + */ + if ($min > $max) { + throw new Error( + 'Minimum value must be less than or equal to the maximum value' + ); + } + + if ($max === $min) { + return (int) $min; + } + + /** + * Initialize variables to 0 + * + * We want to store: + * $bytes => the number of random bytes we need + * $mask => an integer bitmask (for use with the &) operator + * so we can minimize the number of discards + */ + $attempts = $bits = $bytes = $mask = $valueShift = 0; + + /** + * At this point, $range is a positive number greater than 0. It might + * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to + * a float and we will lose some precision. + */ + $range = $max - $min; + + /** + * Test for integer overflow: + */ + if (!is_int($range)) { + + /** + * Still safely calculate wider ranges. + * Provided by @CodesInChaos, @oittaa + * + * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 + * + * We use ~0 as a mask in this case because it generates all 1s + * + * @ref https://eval.in/400356 (32-bit) + * @ref http://3v4l.org/XX9r5 (64-bit) + */ + $bytes = PHP_INT_SIZE; + $mask = ~0; + + } else { + + /** + * $bits is effectively ceil(log($range, 2)) without dealing with + * type juggling + */ + while ($range > 0) { + if ($bits % 8 === 0) { + ++$bytes; + } + ++$bits; + $range >>= 1; + $mask = $mask << 1 | 1; + } + $valueShift = $min; + } + + $val = 0; + /** + * Now that we have our parameters set up, let's begin generating + * random integers until one falls between $min and $max + */ + do { + /** + * The rejection probability is at most 0.5, so this corresponds + * to a failure probability of 2^-128 for a working RNG + */ + if ($attempts > 128) { + throw new Exception( + 'random_int: RNG is broken - too many rejections' + ); + } + + /** + * Let's grab the necessary number of random bytes + */ + $randomByteString = random_bytes($bytes); + + /** + * Let's turn $randomByteString into an integer + * + * This uses bitwise operators (<< and |) to build an integer + * out of the values extracted from ord() + * + * Example: [9F] | [6D] | [32] | [0C] => + * 159 + 27904 + 3276800 + 201326592 => + * 204631455 + */ + $val &= 0; + for ($i = 0; $i < $bytes; ++$i) { + $val |= ord($randomByteString[$i]) << ($i * 8); + } + + /** + * Apply mask + */ + $val &= $mask; + $val += $valueShift; + + ++$attempts; + /** + * If $val overflows to a floating point number, + * ... or is larger than $max, + * ... or smaller than $min, + * then try again. + */ + } while (!is_int($val) || $val > $max || $val < $min); + + return (int) $val; + } +} diff --git a/vendor/paragonie/random_compat/other/build_phar.php b/vendor/paragonie/random_compat/other/build_phar.php new file mode 100644 index 00000000..70ef4b2e --- /dev/null +++ b/vendor/paragonie/random_compat/other/build_phar.php @@ -0,0 +1,57 @@ +buildFromDirectory(dirname(__DIR__).'/lib'); +rename( + dirname(__DIR__).'/lib/index.php', + dirname(__DIR__).'/lib/random.php' +); + +/** + * If we pass an (optional) path to a private key as a second argument, we will + * sign the Phar with OpenSSL. + * + * If you leave this out, it will produce an unsigned .phar! + */ +if ($argc > 1) { + if (!@is_readable($argv[1])) { + echo 'Could not read the private key file:', $argv[1], "\n"; + exit(255); + } + $pkeyFile = file_get_contents($argv[1]); + + $private = openssl_get_privatekey($pkeyFile); + if ($private !== false) { + $pkey = ''; + openssl_pkey_export($private, $pkey); + $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey); + + /** + * Save the corresponding public key to the file + */ + if (!@is_readable($dist.'/random_compat.phar.pubkey')) { + $details = openssl_pkey_get_details($private); + file_put_contents( + $dist.'/random_compat.phar.pubkey', + $details['key'] + ); + } + } else { + echo 'An error occurred reading the private key from OpenSSL.', "\n"; + exit(255); + } +} diff --git a/vendor/paragonie/random_compat/psalm-autoload.php b/vendor/paragonie/random_compat/psalm-autoload.php new file mode 100644 index 00000000..d71d1b81 --- /dev/null +++ b/vendor/paragonie/random_compat/psalm-autoload.php @@ -0,0 +1,9 @@ + + + + + + + + + + + + + + diff --git a/vendor/symfony/lock/.gitignore b/vendor/symfony/lock/.gitignore new file mode 100644 index 00000000..5414c2c6 --- /dev/null +++ b/vendor/symfony/lock/.gitignore @@ -0,0 +1,3 @@ +composer.lock +phpunit.xml +vendor/ diff --git a/vendor/symfony/lock/CHANGELOG.md b/vendor/symfony/lock/CHANGELOG.md new file mode 100644 index 00000000..8e992b98 --- /dev/null +++ b/vendor/symfony/lock/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +3.4.0 +----- + + * added the component diff --git a/vendor/symfony/lock/Exception/ExceptionInterface.php b/vendor/symfony/lock/Exception/ExceptionInterface.php new file mode 100644 index 00000000..0d419584 --- /dev/null +++ b/vendor/symfony/lock/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Exception; + +/** + * Base ExceptionInterface for the Lock Component. + * + * @author Jérémy Derussé + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/lock/Exception/InvalidArgumentException.php b/vendor/symfony/lock/Exception/InvalidArgumentException.php new file mode 100644 index 00000000..f2f74b37 --- /dev/null +++ b/vendor/symfony/lock/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Exception; + +/** + * @author Jérémy Derussé + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/lock/Exception/LockAcquiringException.php b/vendor/symfony/lock/Exception/LockAcquiringException.php new file mode 100644 index 00000000..e6756aec --- /dev/null +++ b/vendor/symfony/lock/Exception/LockAcquiringException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Exception; + +/** + * LockAcquiringException is thrown when an issue happens during the acquisition of a lock. + * + * @author Jérémy Derussé + */ +class LockAcquiringException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/lock/Exception/LockConflictedException.php b/vendor/symfony/lock/Exception/LockConflictedException.php new file mode 100644 index 00000000..8fcd6a83 --- /dev/null +++ b/vendor/symfony/lock/Exception/LockConflictedException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Exception; + +/** + * LockConflictedException is thrown when a lock is acquired by someone else. + * + * @author Jérémy Derussé + */ +class LockConflictedException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/lock/Exception/LockExpiredException.php b/vendor/symfony/lock/Exception/LockExpiredException.php new file mode 100644 index 00000000..9b712ae0 --- /dev/null +++ b/vendor/symfony/lock/Exception/LockExpiredException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Exception; + +/** + * LockExpiredException is thrown when a lock may conflict due to a TTL expiration. + * + * @author Jérémy Derussé + */ +class LockExpiredException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/lock/Exception/LockReleasingException.php b/vendor/symfony/lock/Exception/LockReleasingException.php new file mode 100644 index 00000000..56eedde7 --- /dev/null +++ b/vendor/symfony/lock/Exception/LockReleasingException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Exception; + +/** + * LockReleasingException is thrown when an issue happens during the release of a lock. + * + * @author Jérémy Derussé + */ +class LockReleasingException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/lock/Exception/LockStorageException.php b/vendor/symfony/lock/Exception/LockStorageException.php new file mode 100644 index 00000000..8c393fc1 --- /dev/null +++ b/vendor/symfony/lock/Exception/LockStorageException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Exception; + +/** + * LockStorageException is thrown when an issue happens during the manipulation of a lock in a store. + * + * @author Jérémy Derussé + */ +class LockStorageException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/lock/Exception/NotSupportedException.php b/vendor/symfony/lock/Exception/NotSupportedException.php new file mode 100644 index 00000000..c9a7f013 --- /dev/null +++ b/vendor/symfony/lock/Exception/NotSupportedException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Exception; + +/** + * NotSupportedException is thrown when an unsupported method is called. + * + * @author Jérémy Derussé + */ +class NotSupportedException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/lock/Factory.php b/vendor/symfony/lock/Factory.php new file mode 100644 index 00000000..e9fdde97 --- /dev/null +++ b/vendor/symfony/lock/Factory.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock; + +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\NullLogger; + +/** + * Factory provides method to create locks. + * + * @author Jérémy Derussé + */ +class Factory implements LoggerAwareInterface +{ + use LoggerAwareTrait; + + private $store; + + public function __construct(StoreInterface $store) + { + $this->store = $store; + + $this->logger = new NullLogger(); + } + + /** + * Creates a lock for the given resource. + * + * @param string $resource The resource to lock + * @param float $ttl Maximum expected lock duration in seconds + * @param bool $autoRelease Whether to automatically release the lock or not when the lock instance is destroyed + * + * @return Lock + */ + public function createLock($resource, $ttl = 300.0, $autoRelease = true) + { + $lock = new Lock(new Key($resource), $this->store, $ttl, $autoRelease); + $lock->setLogger($this->logger); + + return $lock; + } +} diff --git a/vendor/symfony/lock/Key.php b/vendor/symfony/lock/Key.php new file mode 100644 index 00000000..8be4f3a2 --- /dev/null +++ b/vendor/symfony/lock/Key.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock; + +/** + * Key is a container for the state of the locks in stores. + * + * @author Jérémy Derussé + */ +final class Key +{ + private $resource; + private $expiringTime; + private $state = array(); + + /** + * @param string $resource + */ + public function __construct($resource) + { + $this->resource = (string) $resource; + } + + public function __toString() + { + return $this->resource; + } + + /** + * @param string $stateKey + * + * @return bool + */ + public function hasState($stateKey) + { + return isset($this->state[$stateKey]); + } + + /** + * @param string $stateKey + * @param mixed $state + */ + public function setState($stateKey, $state) + { + $this->state[$stateKey] = $state; + } + + /** + * @param string $stateKey + */ + public function removeState($stateKey) + { + unset($this->state[$stateKey]); + } + + /** + * @param $stateKey + * + * @return mixed + */ + public function getState($stateKey) + { + return $this->state[$stateKey]; + } + + public function resetLifetime() + { + $this->expiringTime = null; + } + + /** + * @param float $ttl the expiration delay of locks in seconds + */ + public function reduceLifetime($ttl) + { + $newTime = microtime(true) + $ttl; + + if (null === $this->expiringTime || $this->expiringTime > $newTime) { + $this->expiringTime = $newTime; + } + } + + /** + * Returns the remaining lifetime. + * + * @return float|null Remaining lifetime in seconds. Null when the key won't expire. + */ + public function getRemainingLifetime() + { + return null === $this->expiringTime ? null : $this->expiringTime - microtime(true); + } + + /** + * @return bool + */ + public function isExpired() + { + return null !== $this->expiringTime && $this->expiringTime <= microtime(true); + } +} diff --git a/vendor/symfony/lock/LICENSE b/vendor/symfony/lock/LICENSE new file mode 100644 index 00000000..fcd3fa76 --- /dev/null +++ b/vendor/symfony/lock/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016-2018 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/lock/Lock.php b/vendor/symfony/lock/Lock.php new file mode 100644 index 00000000..77fd032b --- /dev/null +++ b/vendor/symfony/lock/Lock.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock; + +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\NullLogger; +use Symfony\Component\Lock\Exception\InvalidArgumentException; +use Symfony\Component\Lock\Exception\LockAcquiringException; +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Exception\LockExpiredException; +use Symfony\Component\Lock\Exception\LockReleasingException; + +/** + * Lock is the default implementation of the LockInterface. + * + * @author Jérémy Derussé + */ +final class Lock implements LockInterface, LoggerAwareInterface +{ + use LoggerAwareTrait; + + private $store; + private $key; + private $ttl; + private $autoRelease; + private $dirty = false; + + /** + * @param Key $key Resource to lock + * @param StoreInterface $store Store used to handle lock persistence + * @param float|null $ttl Maximum expected lock duration in seconds + * @param bool $autoRelease Whether to automatically release the lock or not when the lock instance is destroyed + */ + public function __construct(Key $key, StoreInterface $store, $ttl = null, $autoRelease = true) + { + $this->store = $store; + $this->key = $key; + $this->ttl = $ttl; + $this->autoRelease = (bool) $autoRelease; + + $this->logger = new NullLogger(); + } + + /** + * Automatically releases the underlying lock when the object is destructed. + */ + public function __destruct() + { + if (!$this->autoRelease || !$this->dirty || !$this->isAcquired()) { + return; + } + + $this->release(); + } + + /** + * {@inheritdoc} + */ + public function acquire($blocking = false) + { + try { + if (!$blocking) { + $this->store->save($this->key); + } else { + $this->store->waitAndSave($this->key); + } + + $this->dirty = true; + $this->logger->info('Successfully acquired the "{resource}" lock.', array('resource' => $this->key)); + + if ($this->ttl) { + $this->refresh(); + } + + if ($this->key->isExpired()) { + throw new LockExpiredException(sprintf('Failed to store the "%s" lock.', $this->key)); + } + + return true; + } catch (LockConflictedException $e) { + $this->dirty = false; + $this->logger->notice('Failed to acquire the "{resource}" lock. Someone else already acquired the lock.', array('resource' => $this->key)); + + if ($blocking) { + throw $e; + } + + return false; + } catch (\Exception $e) { + $this->logger->notice('Failed to acquire the "{resource}" lock.', array('resource' => $this->key, 'exception' => $e)); + throw new LockAcquiringException(sprintf('Failed to acquire the "%s" lock.', $this->key), 0, $e); + } + } + + /** + * {@inheritdoc} + */ + public function refresh() + { + if (!$this->ttl) { + throw new InvalidArgumentException('You have to define an expiration duration.'); + } + + try { + $this->key->resetLifetime(); + $this->store->putOffExpiration($this->key, $this->ttl); + $this->dirty = true; + + if ($this->key->isExpired()) { + throw new LockExpiredException(sprintf('Failed to put off the expiration of the "%s" lock within the specified time.', $this->key)); + } + + $this->logger->info('Expiration defined for "{resource}" lock for "{ttl}" seconds.', array('resource' => $this->key, 'ttl' => $this->ttl)); + } catch (LockConflictedException $e) { + $this->dirty = false; + $this->logger->notice('Failed to define an expiration for the "{resource}" lock, someone else acquired the lock.', array('resource' => $this->key)); + throw $e; + } catch (\Exception $e) { + $this->logger->notice('Failed to define an expiration for the "{resource}" lock.', array('resource' => $this->key, 'exception' => $e)); + throw new LockAcquiringException(sprintf('Failed to define an expiration for the "%s" lock.', $this->key), 0, $e); + } + } + + /** + * {@inheritdoc} + */ + public function isAcquired() + { + return $this->dirty = $this->store->exists($this->key); + } + + /** + * {@inheritdoc} + */ + public function release() + { + $this->store->delete($this->key); + $this->dirty = false; + + if ($this->store->exists($this->key)) { + $this->logger->notice('Failed to release the "{resource}" lock.', array('resource' => $this->key)); + throw new LockReleasingException(sprintf('Failed to release the "%s" lock.', $this->key)); + } + } + + /** + * @return bool + */ + public function isExpired() + { + return $this->key->isExpired(); + } + + /** + * Returns the remaining lifetime. + * + * @return float|null Remaining lifetime in seconds. Null when the lock won't expire. + */ + public function getRemainingLifetime() + { + return $this->key->getRemainingLifetime(); + } +} diff --git a/vendor/symfony/lock/LockInterface.php b/vendor/symfony/lock/LockInterface.php new file mode 100644 index 00000000..437a4cc2 --- /dev/null +++ b/vendor/symfony/lock/LockInterface.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock; + +use Symfony\Component\Lock\Exception\LockAcquiringException; +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Exception\LockReleasingException; + +/** + * LockInterface defines an interface to manipulate the status of a lock. + * + * @author Jérémy Derussé + */ +interface LockInterface +{ + /** + * Acquires the lock. If the lock is acquired by someone else, the parameter `blocking` determines whether or not + * the the call should block until the release of the lock. + * + * @param bool $blocking Whether or not the Lock should wait for the release of someone else + * + * @return bool whether or not the lock had been acquired + * + * @throws LockConflictedException If the lock is acquired by someone else in blocking mode + * @throws LockAcquiringException If the lock can not be acquired + */ + public function acquire($blocking = false); + + /** + * Increase the duration of an acquired lock. + * + * @throws LockConflictedException If the lock is acquired by someone else + * @throws LockAcquiringException If the lock can not be refreshed + */ + public function refresh(); + + /** + * Returns whether or not the lock is acquired. + * + * @return bool + */ + public function isAcquired(); + + /** + * Release the lock. + * + * @throws LockReleasingException If the lock can not be released + */ + public function release(); +} diff --git a/vendor/symfony/lock/README.md b/vendor/symfony/lock/README.md new file mode 100644 index 00000000..0be0bfd4 --- /dev/null +++ b/vendor/symfony/lock/README.md @@ -0,0 +1,11 @@ +Lock Component +============== + +Resources +--------- + + * [Documentation](https://symfony.com/doc/master/components/lock.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/lock/Store/CombinedStore.php b/vendor/symfony/lock/Store/CombinedStore.php new file mode 100644 index 00000000..50258fe6 --- /dev/null +++ b/vendor/symfony/lock/Store/CombinedStore.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Store; + +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\NullLogger; +use Symfony\Component\Lock\Exception\InvalidArgumentException; +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Exception\LockExpiredException; +use Symfony\Component\Lock\Exception\NotSupportedException; +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\Strategy\StrategyInterface; +use Symfony\Component\Lock\StoreInterface; + +/** + * CombinedStore is a StoreInterface implementation able to manage and synchronize several StoreInterfaces. + * + * @author Jérémy Derussé + */ +class CombinedStore implements StoreInterface, LoggerAwareInterface +{ + use LoggerAwareTrait; + + /** @var StoreInterface[] */ + private $stores; + /** @var StrategyInterface */ + private $strategy; + + /** + * @param StoreInterface[] $stores The list of synchronized stores + * @param StrategyInterface $strategy + * + * @throws InvalidArgumentException + */ + public function __construct(array $stores, StrategyInterface $strategy) + { + foreach ($stores as $store) { + if (!$store instanceof StoreInterface) { + throw new InvalidArgumentException(sprintf('The store must implement "%s". Got "%s".', StoreInterface::class, get_class($store))); + } + } + + $this->stores = $stores; + $this->strategy = $strategy; + $this->logger = new NullLogger(); + } + + /** + * {@inheritdoc} + */ + public function save(Key $key) + { + $successCount = 0; + $failureCount = 0; + $storesCount = count($this->stores); + + foreach ($this->stores as $store) { + try { + $store->save($key); + ++$successCount; + } catch (\Exception $e) { + $this->logger->warning('One store failed to save the "{resource}" lock.', array('resource' => $key, 'store' => $store, 'exception' => $e)); + ++$failureCount; + } + + if (!$this->strategy->canBeMet($failureCount, $storesCount)) { + break; + } + } + + if ($this->strategy->isMet($successCount, $storesCount)) { + return; + } + + $this->logger->warning('Failed to store the "{resource}" lock. Quorum has not been met.', array('resource' => $key, 'success' => $successCount, 'failure' => $failureCount)); + + // clean up potential locks + $this->delete($key); + + throw new LockConflictedException(); + } + + public function waitAndSave(Key $key) + { + throw new NotSupportedException(sprintf('The store "%s" does not supports blocking locks.', get_class($this))); + } + + /** + * {@inheritdoc} + */ + public function putOffExpiration(Key $key, $ttl) + { + $successCount = 0; + $failureCount = 0; + $storesCount = count($this->stores); + $expireAt = microtime(true) + $ttl; + + foreach ($this->stores as $store) { + try { + if (0.0 >= $adjustedTtl = $expireAt - microtime(true)) { + $this->logger->warning('Stores took to long to put off the expiration of the "{resource}" lock.', array('resource' => $key, 'store' => $store, 'ttl' => $ttl)); + $key->reduceLifetime(0); + break; + } + + $store->putOffExpiration($key, $adjustedTtl); + ++$successCount; + } catch (\Exception $e) { + $this->logger->warning('One store failed to put off the expiration of the "{resource}" lock.', array('resource' => $key, 'store' => $store, 'exception' => $e)); + ++$failureCount; + } + + if (!$this->strategy->canBeMet($failureCount, $storesCount)) { + break; + } + } + + if ($key->isExpired()) { + throw new LockExpiredException(sprintf('Failed to put off the expiration of the "%s" lock within the specified time.', $key)); + } + + if ($this->strategy->isMet($successCount, $storesCount)) { + return; + } + + $this->logger->warning('Failed to define the expiration for the "{resource}" lock. Quorum has not been met.', array('resource' => $key, 'success' => $successCount, 'failure' => $failureCount)); + + // clean up potential locks + $this->delete($key); + + throw new LockConflictedException(); + } + + /** + * {@inheritdoc} + */ + public function delete(Key $key) + { + foreach ($this->stores as $store) { + try { + $store->delete($key); + } catch (\Exception $e) { + $this->logger->notice('One store failed to delete the "{resource}" lock.', array('resource' => $key, 'store' => $store, 'exception' => $e)); + } catch (\Throwable $e) { + $this->logger->notice('One store failed to delete the "{resource}" lock.', array('resource' => $key, 'store' => $store, 'exception' => $e)); + } + } + } + + /** + * {@inheritdoc} + */ + public function exists(Key $key) + { + $successCount = 0; + $failureCount = 0; + $storesCount = count($this->stores); + + foreach ($this->stores as $store) { + if ($store->exists($key)) { + ++$successCount; + } else { + ++$failureCount; + } + + if ($this->strategy->isMet($successCount, $storesCount)) { + return true; + } + if (!$this->strategy->canBeMet($failureCount, $storesCount)) { + return false; + } + } + + return false; + } +} diff --git a/vendor/symfony/lock/Store/FlockStore.php b/vendor/symfony/lock/Store/FlockStore.php new file mode 100644 index 00000000..287d6fb5 --- /dev/null +++ b/vendor/symfony/lock/Store/FlockStore.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Store; + +use Symfony\Component\Lock\Exception\InvalidArgumentException; +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Exception\LockStorageException; +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\StoreInterface; + +/** + * FlockStore is a StoreInterface implementation using the FileSystem flock. + * + * Original implementation in \Symfony\Component\Filesystem\LockHandler. + * + * @author Jérémy Derussé + * @author Grégoire Pineau + * @author Romain Neutron + * @author Nicolas Grekas + */ +class FlockStore implements StoreInterface +{ + private $lockPath; + + /** + * @param string|null $lockPath the directory to store the lock, defaults to the system's temporary directory + * + * @throws LockStorageException If the lock directory doesn’t exist or is not writable + */ + public function __construct($lockPath = null) + { + if (null === $lockPath) { + $lockPath = sys_get_temp_dir(); + } + if (!is_dir($lockPath) || !is_writable($lockPath)) { + throw new InvalidArgumentException(sprintf('The directory "%s" is not writable.', $lockPath)); + } + + $this->lockPath = $lockPath; + } + + /** + * {@inheritdoc} + */ + public function save(Key $key) + { + $this->lock($key, false); + } + + /** + * {@inheritdoc} + */ + public function waitAndSave(Key $key) + { + $this->lock($key, true); + } + + private function lock(Key $key, $blocking) + { + // The lock is maybe already acquired. + if ($key->hasState(__CLASS__)) { + return; + } + + $fileName = sprintf('%s/sf.%s.%s.lock', + $this->lockPath, + preg_replace('/[^a-z0-9\._-]+/i', '-', $key), + strtr(substr(base64_encode(hash('sha256', $key, true)), 0, 7), '/', '_') + ); + + // Silence error reporting + set_error_handler(function () { + }); + if (!$handle = fopen($fileName, 'r')) { + if ($handle = fopen($fileName, 'x')) { + chmod($fileName, 0444); + } elseif (!$handle = fopen($fileName, 'r')) { + usleep(100); // Give some time for chmod() to complete + $handle = fopen($fileName, 'r'); + } + } + restore_error_handler(); + + if (!$handle) { + $error = error_get_last(); + throw new LockStorageException($error['message'], 0, null); + } + + // On Windows, even if PHP doc says the contrary, LOCK_NB works, see + // https://bugs.php.net/54129 + if (!flock($handle, LOCK_EX | ($blocking ? 0 : LOCK_NB))) { + fclose($handle); + throw new LockConflictedException(); + } + + $key->setState(__CLASS__, $handle); + } + + /** + * {@inheritdoc} + */ + public function putOffExpiration(Key $key, $ttl) + { + // do nothing, the flock locks forever. + } + + /** + * {@inheritdoc} + */ + public function delete(Key $key) + { + // The lock is maybe not acquired. + if (!$key->hasState(__CLASS__)) { + return; + } + + $handle = $key->getState(__CLASS__); + + flock($handle, LOCK_UN | LOCK_NB); + fclose($handle); + + $key->removeState(__CLASS__); + } + + /** + * {@inheritdoc} + */ + public function exists(Key $key) + { + return $key->hasState(__CLASS__); + } +} diff --git a/vendor/symfony/lock/Store/MemcachedStore.php b/vendor/symfony/lock/Store/MemcachedStore.php new file mode 100644 index 00000000..8e9db10c --- /dev/null +++ b/vendor/symfony/lock/Store/MemcachedStore.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Store; + +use Symfony\Component\Lock\Exception\InvalidArgumentException; +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Exception\LockExpiredException; +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\StoreInterface; + +/** + * MemcachedStore is a StoreInterface implementation using Memcached as store engine. + * + * @author Jérémy Derussé + */ +class MemcachedStore implements StoreInterface +{ + private $memcached; + private $initialTtl; + /** @var bool */ + private $useExtendedReturn; + + public static function isSupported() + { + return extension_loaded('memcached'); + } + + /** + * @param \Memcached $memcached + * @param int $initialTtl the expiration delay of locks in seconds + */ + public function __construct(\Memcached $memcached, $initialTtl = 300) + { + if (!static::isSupported()) { + throw new InvalidArgumentException('Memcached extension is required'); + } + + if ($initialTtl < 1) { + throw new InvalidArgumentException(sprintf('%s() expects a strictly positive TTL. Got %d.', __METHOD__, $initialTtl)); + } + + $this->memcached = $memcached; + $this->initialTtl = $initialTtl; + } + + /** + * {@inheritdoc} + */ + public function save(Key $key) + { + $token = $this->getToken($key); + $key->reduceLifetime($this->initialTtl); + if (!$this->memcached->add((string) $key, $token, (int) ceil($this->initialTtl))) { + // the lock is already acquired. It could be us. Let's try to put off. + $this->putOffExpiration($key, $this->initialTtl); + } + + if ($key->isExpired()) { + throw new LockExpiredException(sprintf('Failed to store the "%s" lock.', $key)); + } + } + + public function waitAndSave(Key $key) + { + throw new InvalidArgumentException(sprintf('The store "%s" does not supports blocking locks.', get_class($this))); + } + + /** + * {@inheritdoc} + */ + public function putOffExpiration(Key $key, $ttl) + { + if ($ttl < 1) { + throw new InvalidArgumentException(sprintf('%s() expects a TTL greater or equals to 1. Got %s.', __METHOD__, $ttl)); + } + + // Interface defines a float value but Store required an integer. + $ttl = (int) ceil($ttl); + + $token = $this->getToken($key); + + list($value, $cas) = $this->getValueAndCas($key); + + $key->reduceLifetime($ttl); + // Could happens when we ask a putOff after a timeout but in luck nobody steal the lock + if (\Memcached::RES_NOTFOUND === $this->memcached->getResultCode()) { + if ($this->memcached->add((string) $key, $token, $ttl)) { + return; + } + + // no luck, with concurrency, someone else acquire the lock + throw new LockConflictedException(); + } + + // Someone else steal the lock + if ($value !== $token) { + throw new LockConflictedException(); + } + + if (!$this->memcached->cas($cas, (string) $key, $token, $ttl)) { + throw new LockConflictedException(); + } + + if ($key->isExpired()) { + throw new LockExpiredException(sprintf('Failed to put off the expiration of the "%s" lock within the specified time.', $key)); + } + } + + /** + * {@inheritdoc} + */ + public function delete(Key $key) + { + $token = $this->getToken($key); + + list($value, $cas) = $this->getValueAndCas($key); + + if ($value !== $token) { + // we are not the owner of the lock. Nothing to do. + return; + } + + // To avoid concurrency in deletion, the trick is to extends the TTL then deleting the key + if (!$this->memcached->cas($cas, (string) $key, $token, 2)) { + // Someone steal our lock. It does not belongs to us anymore. Nothing to do. + return; + } + + // Now, we are the owner of the lock for 2 more seconds, we can delete it. + $this->memcached->delete((string) $key); + } + + /** + * {@inheritdoc} + */ + public function exists(Key $key) + { + return $this->memcached->get((string) $key) === $this->getToken($key); + } + + /** + * Retrieve an unique token for the given key. + * + * @param Key $key + * + * @return string + */ + private function getToken(Key $key) + { + if (!$key->hasState(__CLASS__)) { + $token = base64_encode(random_bytes(32)); + $key->setState(__CLASS__, $token); + } + + return $key->getState(__CLASS__); + } + + private function getValueAndCas(Key $key) + { + if (null === $this->useExtendedReturn) { + $this->useExtendedReturn = version_compare(phpversion('memcached'), '2.9.9', '>'); + } + + if ($this->useExtendedReturn) { + $extendedReturn = $this->memcached->get((string) $key, null, \Memcached::GET_EXTENDED); + if (\Memcached::GET_ERROR_RETURN_VALUE === $extendedReturn) { + return array($extendedReturn, 0.0); + } + + return array($extendedReturn['value'], $extendedReturn['cas']); + } + + $cas = 0.0; + $value = $this->memcached->get((string) $key, null, $cas); + + return array($value, $cas); + } +} diff --git a/vendor/symfony/lock/Store/RedisStore.php b/vendor/symfony/lock/Store/RedisStore.php new file mode 100644 index 00000000..4d057337 --- /dev/null +++ b/vendor/symfony/lock/Store/RedisStore.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Store; + +use Symfony\Component\Cache\Traits\RedisProxy; +use Symfony\Component\Lock\Exception\InvalidArgumentException; +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Exception\LockExpiredException; +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\StoreInterface; + +/** + * RedisStore is a StoreInterface implementation using Redis as store engine. + * + * @author Jérémy Derussé + */ +class RedisStore implements StoreInterface +{ + private $redis; + private $initialTtl; + + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient + * @param float $initialTtl the expiration delay of locks in seconds + */ + public function __construct($redisClient, $initialTtl = 300.0) + { + if (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \RedisCluster && !$redisClient instanceof \Predis\Client && !$redisClient instanceof RedisProxy) { + throw new InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, is_object($redisClient) ? get_class($redisClient) : gettype($redisClient))); + } + + if ($initialTtl <= 0) { + throw new InvalidArgumentException(sprintf('%s() expects a strictly positive TTL. Got %d.', __METHOD__, $initialTtl)); + } + + $this->redis = $redisClient; + $this->initialTtl = $initialTtl; + } + + /** + * {@inheritdoc} + */ + public function save(Key $key) + { + $script = ' + if redis.call("GET", KEYS[1]) == ARGV[1] then + return redis.call("PEXPIRE", KEYS[1], ARGV[2]) + else + return redis.call("set", KEYS[1], ARGV[1], "NX", "PX", ARGV[2]) + end + '; + + $key->reduceLifetime($this->initialTtl); + if (!$this->evaluate($script, (string) $key, array($this->getToken($key), (int) ceil($this->initialTtl * 1000)))) { + throw new LockConflictedException(); + } + + if ($key->isExpired()) { + throw new LockExpiredException(sprintf('Failed to store the "%s" lock.', $key)); + } + } + + public function waitAndSave(Key $key) + { + throw new InvalidArgumentException(sprintf('The store "%s" does not supports blocking locks.', get_class($this))); + } + + /** + * {@inheritdoc} + */ + public function putOffExpiration(Key $key, $ttl) + { + $script = ' + if redis.call("GET", KEYS[1]) == ARGV[1] then + return redis.call("PEXPIRE", KEYS[1], ARGV[2]) + else + return 0 + end + '; + + $key->reduceLifetime($ttl); + if (!$this->evaluate($script, (string) $key, array($this->getToken($key), (int) ceil($ttl * 1000)))) { + throw new LockConflictedException(); + } + + if ($key->isExpired()) { + throw new LockExpiredException(sprintf('Failed to put off the expiration of the "%s" lock within the specified time.', $key)); + } + } + + /** + * {@inheritdoc} + */ + public function delete(Key $key) + { + $script = ' + if redis.call("GET", KEYS[1]) == ARGV[1] then + return redis.call("DEL", KEYS[1]) + else + return 0 + end + '; + + $this->evaluate($script, (string) $key, array($this->getToken($key))); + } + + /** + * {@inheritdoc} + */ + public function exists(Key $key) + { + return $this->redis->get((string) $key) === $this->getToken($key); + } + + /** + * Evaluates a script in the corresponding redis client. + * + * @param string $script + * @param string $resource + * @param array $args + * + * @return mixed + */ + private function evaluate($script, $resource, array $args) + { + if ($this->redis instanceof \Redis || $this->redis instanceof \RedisCluster || $this->redis instanceof RedisProxy) { + return $this->redis->eval($script, array_merge(array($resource), $args), 1); + } + + if ($this->redis instanceof \RedisArray) { + return $this->redis->_instance($this->redis->_target($resource))->eval($script, array_merge(array($resource), $args), 1); + } + + if ($this->redis instanceof \Predis\Client) { + return call_user_func_array(array($this->redis, 'eval'), array_merge(array($script, 1, $resource), $args)); + } + + throw new InvalidArgumentException(sprintf('%s() expects been initialized with a Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, is_object($this->redis) ? get_class($this->redis) : gettype($this->redis))); + } + + /** + * Retrieves an unique token for the given key. + * + * @param Key $key + * + * @return string + */ + private function getToken(Key $key) + { + if (!$key->hasState(__CLASS__)) { + $token = base64_encode(random_bytes(32)); + $key->setState(__CLASS__, $token); + } + + return $key->getState(__CLASS__); + } +} diff --git a/vendor/symfony/lock/Store/RetryTillSaveStore.php b/vendor/symfony/lock/Store/RetryTillSaveStore.php new file mode 100644 index 00000000..dfc3b266 --- /dev/null +++ b/vendor/symfony/lock/Store/RetryTillSaveStore.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Store; + +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\NullLogger; +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\StoreInterface; + +/** + * RetryTillSaveStore is a StoreInterface implementation which decorate a non blocking StoreInterface to provide a + * blocking storage. + * + * @author Jérémy Derussé + */ +class RetryTillSaveStore implements StoreInterface, LoggerAwareInterface +{ + use LoggerAwareTrait; + + private $decorated; + private $retrySleep; + private $retryCount; + + /** + * @param StoreInterface $decorated The decorated StoreInterface + * @param int $retrySleep Duration in ms between 2 retry + * @param int $retryCount Maximum amount of retry + */ + public function __construct(StoreInterface $decorated, $retrySleep = 100, $retryCount = PHP_INT_MAX) + { + $this->decorated = $decorated; + $this->retrySleep = $retrySleep; + $this->retryCount = $retryCount; + + $this->logger = new NullLogger(); + } + + /** + * {@inheritdoc} + */ + public function save(Key $key) + { + $this->decorated->save($key); + } + + /** + * {@inheritdoc} + */ + public function waitAndSave(Key $key) + { + $retry = 0; + $sleepRandomness = (int) ($this->retrySleep / 10); + do { + try { + $this->decorated->save($key); + + return; + } catch (LockConflictedException $e) { + usleep(($this->retrySleep + random_int(-$sleepRandomness, $sleepRandomness)) * 1000); + } + } while (++$retry < $this->retryCount); + + $this->logger->warning('Failed to store the "{resource}" lock. Abort after {retry} retry.', array('resource' => $key, 'retry' => $retry)); + + throw new LockConflictedException(); + } + + /** + * {@inheritdoc} + */ + public function putOffExpiration(Key $key, $ttl) + { + $this->decorated->putOffExpiration($key, $ttl); + } + + /** + * {@inheritdoc} + */ + public function delete(Key $key) + { + $this->decorated->delete($key); + } + + /** + * {@inheritdoc} + */ + public function exists(Key $key) + { + return $this->decorated->exists($key); + } +} diff --git a/vendor/symfony/lock/Store/SemaphoreStore.php b/vendor/symfony/lock/Store/SemaphoreStore.php new file mode 100644 index 00000000..a6cc9ea8 --- /dev/null +++ b/vendor/symfony/lock/Store/SemaphoreStore.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Store; + +use Symfony\Component\Lock\Exception\InvalidArgumentException; +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Exception\NotSupportedException; +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\StoreInterface; + +/** + * SemaphoreStore is a StoreInterface implementation using Semaphore as store engine. + * + * @author Jérémy Derussé + */ +class SemaphoreStore implements StoreInterface +{ + /** + * Returns whether or not the store is supported. + * + * @param bool|null $blocking when not null, checked again the blocking mode + * + * @return bool + * + * @internal + */ + public static function isSupported($blocking = null) + { + if (!extension_loaded('sysvsem')) { + return false; + } + + if (false === $blocking && \PHP_VERSION_ID < 50601) { + return false; + } + + return true; + } + + public function __construct() + { + if (!static::isSupported()) { + throw new InvalidArgumentException('Semaphore extension (sysvsem) is required'); + } + } + + /** + * {@inheritdoc} + */ + public function save(Key $key) + { + $this->lock($key, false); + } + + /** + * {@inheritdoc} + */ + public function waitAndSave(Key $key) + { + $this->lock($key, true); + } + + private function lock(Key $key, $blocking) + { + if ($key->hasState(__CLASS__)) { + return; + } + + $resource = sem_get(crc32($key)); + + if (\PHP_VERSION_ID < 50601) { + if (!$blocking) { + throw new NotSupportedException(sprintf('The store "%s" does not supports non blocking locks.', get_class($this))); + } + + $acquired = sem_acquire($resource); + } else { + $acquired = sem_acquire($resource, !$blocking); + } + + if (!$acquired) { + throw new LockConflictedException(); + } + + $key->setState(__CLASS__, $resource); + } + + /** + * {@inheritdoc} + */ + public function delete(Key $key) + { + // The lock is maybe not acquired. + if (!$key->hasState(__CLASS__)) { + return; + } + + $resource = $key->getState(__CLASS__); + + sem_release($resource); + + $key->removeState(__CLASS__); + } + + /** + * {@inheritdoc} + */ + public function putOffExpiration(Key $key, $ttl) + { + // do nothing, the semaphore locks forever. + } + + /** + * {@inheritdoc} + */ + public function exists(Key $key) + { + return $key->hasState(__CLASS__); + } +} diff --git a/vendor/symfony/lock/Store/StoreFactory.php b/vendor/symfony/lock/Store/StoreFactory.php new file mode 100644 index 00000000..a768d197 --- /dev/null +++ b/vendor/symfony/lock/Store/StoreFactory.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Store; + +use Symfony\Component\Cache\Traits\RedisProxy; +use Symfony\Component\Lock\Exception\InvalidArgumentException; + +/** + * StoreFactory create stores and connections. + * + * @author Jérémy Derussé + */ +class StoreFactory +{ + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client|\Memcached $connection + * + * @return RedisStore|MemcachedStore + */ + public static function createStore($connection) + { + if ($connection instanceof \Redis || $connection instanceof \RedisArray || $connection instanceof \RedisCluster || $connection instanceof \Predis\Client || $connection instanceof RedisProxy) { + return new RedisStore($connection); + } + if ($connection instanceof \Memcached) { + return new MemcachedStore($connection); + } + + throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', get_class($connection))); + } +} diff --git a/vendor/symfony/lock/StoreInterface.php b/vendor/symfony/lock/StoreInterface.php new file mode 100644 index 00000000..985c4476 --- /dev/null +++ b/vendor/symfony/lock/StoreInterface.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock; + +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Exception\NotSupportedException; + +/** + * StoreInterface defines an interface to manipulate a lock store. + * + * @author Jérémy Derussé + */ +interface StoreInterface +{ + /** + * Stores the resource if it's not locked by someone else. + * + * @throws LockConflictedException + */ + public function save(Key $key); + + /** + * Waits until a key becomes free, then stores the resource. + * + * If the store does not support this feature it should throw a NotSupportedException. + * + * @throws LockConflictedException + * @throws NotSupportedException + */ + public function waitAndSave(Key $key); + + /** + * Extends the ttl of a resource. + * + * If the store does not support this feature it should throw a NotSupportedException. + * + * @param float $ttl amount of second to keep the lock in the store + * + * @throws LockConflictedException + * @throws NotSupportedException + */ + public function putOffExpiration(Key $key, $ttl); + + /** + * Removes a resource from the storage. + */ + public function delete(Key $key); + + /** + * Returns whether or not the resource exists in the storage. + * + * @return bool + */ + public function exists(Key $key); +} diff --git a/vendor/symfony/lock/Strategy/ConsensusStrategy.php b/vendor/symfony/lock/Strategy/ConsensusStrategy.php new file mode 100644 index 00000000..047820a4 --- /dev/null +++ b/vendor/symfony/lock/Strategy/ConsensusStrategy.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Strategy; + +/** + * ConsensusStrategy is a StrategyInterface implementation where strictly more than 50% items should be successful. + * + * @author Jérémy Derussé + */ +class ConsensusStrategy implements StrategyInterface +{ + /** + * {@inheritdoc} + */ + public function isMet($numberOfSuccess, $numberOfItems) + { + return $numberOfSuccess > ($numberOfItems / 2); + } + + /** + * {@inheritdoc} + */ + public function canBeMet($numberOfFailure, $numberOfItems) + { + return $numberOfFailure < ($numberOfItems / 2); + } +} diff --git a/vendor/symfony/lock/Strategy/StrategyInterface.php b/vendor/symfony/lock/Strategy/StrategyInterface.php new file mode 100644 index 00000000..beaa7280 --- /dev/null +++ b/vendor/symfony/lock/Strategy/StrategyInterface.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Strategy; + +/** + * StrategyInterface defines an interface to indicate when a quorum is met and can be met. + * + * @author Jérémy Derussé + */ +interface StrategyInterface +{ + /** + * Returns whether or not the quorum is met. + * + * @param int $numberOfSuccess + * @param int $numberOfItems + * + * @return bool + */ + public function isMet($numberOfSuccess, $numberOfItems); + + /** + * Returns whether or not the quorum *could* be met. + * + * This method does not mean the quorum *would* be met for sure, but can be useful to stop a process early when you + * known there is no chance to meet the quorum. + * + * @param int $numberOfFailure + * @param int $numberOfItems + * + * @return bool + */ + public function canBeMet($numberOfFailure, $numberOfItems); +} diff --git a/vendor/symfony/lock/Strategy/UnanimousStrategy.php b/vendor/symfony/lock/Strategy/UnanimousStrategy.php new file mode 100644 index 00000000..27404f3e --- /dev/null +++ b/vendor/symfony/lock/Strategy/UnanimousStrategy.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Strategy; + +/** + * UnanimousStrategy is a StrategyInterface implementation where 100% of elements should be successful. + * + * @author Jérémy Derussé + */ +class UnanimousStrategy implements StrategyInterface +{ + /** + * {@inheritdoc} + */ + public function isMet($numberOfSuccess, $numberOfItems) + { + return $numberOfSuccess === $numberOfItems; + } + + /** + * {@inheritdoc} + */ + public function canBeMet($numberOfFailure, $numberOfItems) + { + return 0 === $numberOfFailure; + } +} diff --git a/vendor/symfony/lock/Tests/FactoryTest.php b/vendor/symfony/lock/Tests/FactoryTest.php new file mode 100644 index 00000000..d6794909 --- /dev/null +++ b/vendor/symfony/lock/Tests/FactoryTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Symfony\Component\Lock\Factory; +use Symfony\Component\Lock\LockInterface; +use Symfony\Component\Lock\StoreInterface; + +/** + * @author Jérémy Derussé + */ +class FactoryTest extends TestCase +{ + public function testCreateLock() + { + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); + $factory = new Factory($store); + $factory->setLogger($logger); + + $lock = $factory->createLock('foo'); + + $this->assertInstanceOf(LockInterface::class, $lock); + } +} diff --git a/vendor/symfony/lock/Tests/LockTest.php b/vendor/symfony/lock/Tests/LockTest.php new file mode 100644 index 00000000..7913644b --- /dev/null +++ b/vendor/symfony/lock/Tests/LockTest.php @@ -0,0 +1,254 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\Lock; +use Symfony\Component\Lock\StoreInterface; + +/** + * @author Jérémy Derussé + */ +class LockTest extends TestCase +{ + public function testAcquireNoBlocking() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store); + + $store + ->expects($this->once()) + ->method('save'); + + $this->assertTrue($lock->acquire(false)); + } + + public function testAcquireReturnsFalse() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store); + + $store + ->expects($this->once()) + ->method('save') + ->willThrowException(new LockConflictedException()); + + $this->assertFalse($lock->acquire(false)); + } + + public function testAcquireBlocking() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store); + + $store + ->expects($this->never()) + ->method('save'); + $store + ->expects($this->once()) + ->method('waitAndSave'); + + $this->assertTrue($lock->acquire(true)); + } + + public function testAcquireSetsTtl() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store, 10); + + $store + ->expects($this->once()) + ->method('save'); + $store + ->expects($this->once()) + ->method('putOffExpiration') + ->with($key, 10); + + $lock->acquire(); + } + + public function testRefresh() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store, 10); + + $store + ->expects($this->once()) + ->method('putOffExpiration') + ->with($key, 10); + + $lock->refresh(); + } + + public function testIsAquired() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store, 10); + + $store + ->expects($this->any()) + ->method('exists') + ->with($key) + ->will($this->onConsecutiveCalls(true, false)); + + $this->assertTrue($lock->isAcquired()); + } + + public function testRelease() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store, 10); + + $store + ->expects($this->once()) + ->method('delete') + ->with($key); + + $store + ->expects($this->once()) + ->method('exists') + ->with($key) + ->willReturn(false); + + $lock->release(); + } + + public function testReleaseOnDestruction() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store, 10); + + $store + ->method('exists') + ->willReturnOnConsecutiveCalls(array(true, false)) + ; + $store + ->expects($this->once()) + ->method('delete') + ; + + $lock->acquire(false); + unset($lock); + } + + public function testNoAutoReleaseWhenNotConfigured() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store, 10, false); + + $store + ->method('exists') + ->willReturnOnConsecutiveCalls(array(true, false)) + ; + $store + ->expects($this->never()) + ->method('delete') + ; + + $lock->acquire(false); + unset($lock); + } + + /** + * @expectedException \Symfony\Component\Lock\Exception\LockReleasingException + */ + public function testReleaseThrowsExceptionIfNotWellDeleted() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store, 10); + + $store + ->expects($this->once()) + ->method('delete') + ->with($key); + + $store + ->expects($this->once()) + ->method('exists') + ->with($key) + ->willReturn(true); + + $lock->release(); + } + + /** + * @expectedException \Symfony\Component\Lock\Exception\LockReleasingException + */ + public function testReleaseThrowsAndLog() + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); + $lock = new Lock($key, $store, 10, true); + $lock->setLogger($logger); + + $logger->expects($this->atLeastOnce()) + ->method('notice') + ->with('Failed to release the "{resource}" lock.', array('resource' => $key)); + + $store + ->expects($this->once()) + ->method('delete') + ->with($key); + + $store + ->expects($this->once()) + ->method('exists') + ->with($key) + ->willReturn(true); + + $lock->release(); + } + + /** + * @dataProvider provideExpiredDates + */ + public function testExpiration($ttls, $expected) + { + $key = new Key(uniqid(__METHOD__, true)); + $store = $this->getMockBuilder(StoreInterface::class)->getMock(); + $lock = new Lock($key, $store, 10); + + foreach ($ttls as $ttl) { + if (null === $ttl) { + $key->resetLifetime(); + } else { + $key->reduceLifetime($ttl); + } + } + $this->assertSame($expected, $lock->isExpired()); + } + + public function provideExpiredDates() + { + yield array(array(-0.1), true); + yield array(array(0.1, -0.1), true); + yield array(array(-0.1, 0.1), true); + + yield array(array(), false); + yield array(array(0.1), false); + yield array(array(-0.1, null), false); + } +} diff --git a/vendor/symfony/lock/Tests/Store/AbstractRedisStoreTest.php b/vendor/symfony/lock/Tests/Store/AbstractRedisStoreTest.php new file mode 100644 index 00000000..4b9c81bd --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/AbstractRedisStoreTest.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +use Symfony\Component\Lock\Store\RedisStore; + +/** + * @author Jérémy Derussé + */ +abstract class AbstractRedisStoreTest extends AbstractStoreTest +{ + use ExpiringStoreTestTrait; + + /** + * {@inheritdoc} + */ + protected function getClockDelay() + { + return 250000; + } + + /** + * Return a RedisConnection. + * + * @return \Redis|\RedisArray|\RedisCluster|\Predis\Client + */ + abstract protected function getRedisConnection(); + + /** + * {@inheritdoc} + */ + public function getStore() + { + return new RedisStore($this->getRedisConnection()); + } +} diff --git a/vendor/symfony/lock/Tests/Store/AbstractStoreTest.php b/vendor/symfony/lock/Tests/Store/AbstractStoreTest.php new file mode 100644 index 00000000..2ab030b2 --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/AbstractStoreTest.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\StoreInterface; + +/** + * @author Jérémy Derussé + */ +abstract class AbstractStoreTest extends TestCase +{ + /** + * @return StoreInterface + */ + abstract protected function getStore(); + + public function testSave() + { + $store = $this->getStore(); + + $key = new Key(uniqid(__METHOD__, true)); + + $this->assertFalse($store->exists($key)); + $store->save($key); + $this->assertTrue($store->exists($key)); + $store->delete($key); + $this->assertFalse($store->exists($key)); + } + + public function testSaveWithDifferentResources() + { + $store = $this->getStore(); + + $key1 = new Key(uniqid(__METHOD__, true)); + $key2 = new Key(uniqid(__METHOD__, true)); + + $store->save($key1); + $this->assertTrue($store->exists($key1)); + $this->assertFalse($store->exists($key2)); + + $store->save($key2); + $this->assertTrue($store->exists($key1)); + $this->assertTrue($store->exists($key2)); + + $store->delete($key1); + $this->assertFalse($store->exists($key1)); + $this->assertTrue($store->exists($key2)); + + $store->delete($key2); + $this->assertFalse($store->exists($key1)); + $this->assertFalse($store->exists($key2)); + } + + public function testSaveWithDifferentKeysOnSameResources() + { + $store = $this->getStore(); + + $resource = uniqid(__METHOD__, true); + $key1 = new Key($resource); + $key2 = new Key($resource); + + $store->save($key1); + $this->assertTrue($store->exists($key1)); + $this->assertFalse($store->exists($key2)); + + try { + $store->save($key2); + $this->fail('The store shouldn\'t save the second key'); + } catch (LockConflictedException $e) { + } + + // The failure of previous attempt should not impact the state of current locks + $this->assertTrue($store->exists($key1)); + $this->assertFalse($store->exists($key2)); + + $store->delete($key1); + $this->assertFalse($store->exists($key1)); + $this->assertFalse($store->exists($key2)); + + $store->save($key2); + $this->assertFalse($store->exists($key1)); + $this->assertTrue($store->exists($key2)); + + $store->delete($key2); + $this->assertFalse($store->exists($key1)); + $this->assertFalse($store->exists($key2)); + } + + public function testSaveTwice() + { + $store = $this->getStore(); + + $resource = uniqid(__METHOD__, true); + $key = new Key($resource); + + $store->save($key); + $store->save($key); + // just asserts it don't throw an exception + $this->addToAssertionCount(1); + + $store->delete($key); + } +} diff --git a/vendor/symfony/lock/Tests/Store/BlockingStoreTestTrait.php b/vendor/symfony/lock/Tests/Store/BlockingStoreTestTrait.php new file mode 100644 index 00000000..93e5a547 --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/BlockingStoreTestTrait.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\StoreInterface; + +/** + * @author Jérémy Derussé + */ +trait BlockingStoreTestTrait +{ + /** + * @see AbstractStoreTest::getStore() + */ + abstract protected function getStore(); + + /** + * Tests blocking locks thanks to pcntl. + * + * This test is time sensible: the $clockDelay could be adjust. + * + * @requires extension pcntl + * @requires function pcntl_sigwaitinfo + */ + public function testBlockingLocks() + { + // Amount a microsecond used to order async actions + $clockDelay = 50000; + + if (\PHP_VERSION_ID < 50600 || defined('HHVM_VERSION_ID')) { + $this->markTestSkipped('The PHP engine does not keep resource in child forks'); + + return; + } + + /** @var StoreInterface $store */ + $store = $this->getStore(); + $key = new Key(uniqid(__METHOD__, true)); + $parentPID = posix_getpid(); + + // Block SIGHUP signal + pcntl_sigprocmask(SIG_BLOCK, array(SIGHUP)); + + if ($childPID = pcntl_fork()) { + // Wait the start of the child + pcntl_sigwaitinfo(array(SIGHUP), $info); + + try { + // This call should failed given the lock should already by acquired by the child + $store->save($key); + $this->fail('The store saves a locked key.'); + } catch (LockConflictedException $e) { + } + + // send the ready signal to the child + posix_kill($childPID, SIGHUP); + + // This call should be blocked by the child #1 + $store->waitAndSave($key); + $this->assertTrue($store->exists($key)); + $store->delete($key); + + // Now, assert the child process worked well + pcntl_waitpid($childPID, $status1); + $this->assertSame(0, pcntl_wexitstatus($status1), 'The child process couldn\'t lock the resource'); + } else { + // Block SIGHUP signal + pcntl_sigprocmask(SIG_BLOCK, array(SIGHUP)); + try { + $store->save($key); + // send the ready signal to the parent + posix_kill($parentPID, SIGHUP); + + // Wait for the parent to be ready + pcntl_sigwaitinfo(array(SIGHUP), $info); + + // Wait ClockDelay to let parent assert to finish + usleep($clockDelay); + $store->delete($key); + exit(0); + } catch (\Exception $e) { + exit(1); + } + } + } +} diff --git a/vendor/symfony/lock/Tests/Store/CombinedStoreTest.php b/vendor/symfony/lock/Tests/Store/CombinedStoreTest.php new file mode 100644 index 00000000..c7232d40 --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/CombinedStoreTest.php @@ -0,0 +1,356 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +use Symfony\Component\Lock\Exception\LockConflictedException; +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\Strategy\UnanimousStrategy; +use Symfony\Component\Lock\Strategy\StrategyInterface; +use Symfony\Component\Lock\Store\CombinedStore; +use Symfony\Component\Lock\Store\RedisStore; +use Symfony\Component\Lock\StoreInterface; + +/** + * @author Jérémy Derussé + */ +class CombinedStoreTest extends AbstractStoreTest +{ + use ExpiringStoreTestTrait; + + /** + * {@inheritdoc} + */ + protected function getClockDelay() + { + return 250000; + } + + /** + * {@inheritdoc} + */ + public function getStore() + { + $redis = new \Predis\Client('tcp://'.getenv('REDIS_HOST').':6379'); + try { + $redis->connect(); + } catch (\Exception $e) { + self::markTestSkipped($e->getMessage()); + } + + return new CombinedStore(array(new RedisStore($redis)), new UnanimousStrategy()); + } + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $strategy; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $store1; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $store2; + /** @var CombinedStore */ + private $store; + + public function setup() + { + $this->strategy = $this->getMockBuilder(StrategyInterface::class)->getMock(); + $this->store1 = $this->getMockBuilder(StoreInterface::class)->getMock(); + $this->store2 = $this->getMockBuilder(StoreInterface::class)->getMock(); + + $this->store = new CombinedStore(array($this->store1, $this->store2), $this->strategy); + } + + /** + * @expectedException \Symfony\Component\Lock\Exception\LockConflictedException + */ + public function testSaveThrowsExceptionOnFailure() + { + $key = new Key(uniqid(__METHOD__, true)); + + $this->store1 + ->expects($this->once()) + ->method('save') + ->with($key) + ->willThrowException(new LockConflictedException()); + $this->store2 + ->expects($this->once()) + ->method('save') + ->with($key) + ->willThrowException(new LockConflictedException()); + + $this->strategy + ->expects($this->any()) + ->method('canBeMet') + ->willReturn(true); + $this->strategy + ->expects($this->any()) + ->method('isMet') + ->willReturn(false); + + $this->store->save($key); + } + + public function testSaveCleanupOnFailure() + { + $key = new Key(uniqid(__METHOD__, true)); + + $this->store1 + ->expects($this->once()) + ->method('save') + ->with($key) + ->willThrowException(new LockConflictedException()); + $this->store2 + ->expects($this->once()) + ->method('save') + ->with($key) + ->willThrowException(new LockConflictedException()); + + $this->store1 + ->expects($this->once()) + ->method('delete'); + $this->store2 + ->expects($this->once()) + ->method('delete'); + + $this->strategy + ->expects($this->any()) + ->method('canBeMet') + ->willReturn(true); + $this->strategy + ->expects($this->any()) + ->method('isMet') + ->willReturn(false); + + try { + $this->store->save($key); + } catch (LockConflictedException $e) { + // Catch the exception given this is not what we want to assert in this tests + } + } + + public function testSaveAbortWhenStrategyCantBeMet() + { + $key = new Key(uniqid(__METHOD__, true)); + + $this->store1 + ->expects($this->once()) + ->method('save') + ->with($key) + ->willThrowException(new LockConflictedException()); + $this->store2 + ->expects($this->never()) + ->method('save'); + + $this->strategy + ->expects($this->once()) + ->method('canBeMet') + ->willReturn(false); + $this->strategy + ->expects($this->any()) + ->method('isMet') + ->willReturn(false); + + try { + $this->store->save($key); + } catch (LockConflictedException $e) { + // Catch the exception given this is not what we want to assert in this tests + } + } + + /** + * @expectedException \Symfony\Component\Lock\Exception\LockConflictedException + */ + public function testputOffExpirationThrowsExceptionOnFailure() + { + $key = new Key(uniqid(__METHOD__, true)); + $ttl = random_int(1, 10); + + $this->store1 + ->expects($this->once()) + ->method('putOffExpiration') + ->with($key, $this->lessThanOrEqual($ttl)) + ->willThrowException(new LockConflictedException()); + $this->store2 + ->expects($this->once()) + ->method('putOffExpiration') + ->with($key, $this->lessThanOrEqual($ttl)) + ->willThrowException(new LockConflictedException()); + + $this->strategy + ->expects($this->any()) + ->method('canBeMet') + ->willReturn(true); + $this->strategy + ->expects($this->any()) + ->method('isMet') + ->willReturn(false); + + $this->store->putOffExpiration($key, $ttl); + } + + public function testputOffExpirationCleanupOnFailure() + { + $key = new Key(uniqid(__METHOD__, true)); + $ttl = random_int(1, 10); + + $this->store1 + ->expects($this->once()) + ->method('putOffExpiration') + ->with($key, $this->lessThanOrEqual($ttl)) + ->willThrowException(new LockConflictedException()); + $this->store2 + ->expects($this->once()) + ->method('putOffExpiration') + ->with($key, $this->lessThanOrEqual($ttl)) + ->willThrowException(new LockConflictedException()); + + $this->store1 + ->expects($this->once()) + ->method('delete'); + $this->store2 + ->expects($this->once()) + ->method('delete'); + + $this->strategy + ->expects($this->any()) + ->method('canBeMet') + ->willReturn(true); + $this->strategy + ->expects($this->any()) + ->method('isMet') + ->willReturn(false); + + try { + $this->store->putOffExpiration($key, $ttl); + } catch (LockConflictedException $e) { + // Catch the exception given this is not what we want to assert in this tests + } + } + + public function testputOffExpirationAbortWhenStrategyCantBeMet() + { + $key = new Key(uniqid(__METHOD__, true)); + $ttl = random_int(1, 10); + + $this->store1 + ->expects($this->once()) + ->method('putOffExpiration') + ->with($key, $this->lessThanOrEqual($ttl)) + ->willThrowException(new LockConflictedException()); + $this->store2 + ->expects($this->never()) + ->method('putOffExpiration'); + + $this->strategy + ->expects($this->once()) + ->method('canBeMet') + ->willReturn(false); + $this->strategy + ->expects($this->any()) + ->method('isMet') + ->willReturn(false); + + try { + $this->store->putOffExpiration($key, $ttl); + } catch (LockConflictedException $e) { + // Catch the exception given this is not what we want to assert in this tests + } + } + + public function testPutOffExpirationIgnoreNonExpiringStorage() + { + $store1 = $this->getMockBuilder(StoreInterface::class)->getMock(); + $store2 = $this->getMockBuilder(StoreInterface::class)->getMock(); + + $store = new CombinedStore(array($store1, $store2), $this->strategy); + + $key = new Key(uniqid(__METHOD__, true)); + $ttl = random_int(1, 10); + + $this->strategy + ->expects($this->any()) + ->method('canBeMet') + ->willReturn(true); + $this->strategy + ->expects($this->once()) + ->method('isMet') + ->with(2, 2) + ->willReturn(true); + + $store->putOffExpiration($key, $ttl); + } + + public function testExistsDontAskToEveryBody() + { + $key = new Key(uniqid(__METHOD__, true)); + + $this->store1 + ->expects($this->any()) + ->method('exists') + ->with($key) + ->willReturn(false); + $this->store2 + ->expects($this->never()) + ->method('exists'); + + $this->strategy + ->expects($this->any()) + ->method('canBeMet') + ->willReturn(true); + $this->strategy + ->expects($this->once()) + ->method('isMet') + ->willReturn(true); + + $this->assertTrue($this->store->exists($key)); + } + + public function testExistsAbortWhenStrategyCantBeMet() + { + $key = new Key(uniqid(__METHOD__, true)); + + $this->store1 + ->expects($this->any()) + ->method('exists') + ->with($key) + ->willReturn(false); + $this->store2 + ->expects($this->never()) + ->method('exists'); + + $this->strategy + ->expects($this->once()) + ->method('canBeMet') + ->willReturn(false); + $this->strategy + ->expects($this->once()) + ->method('isMet') + ->willReturn(false); + + $this->assertFalse($this->store->exists($key)); + } + + public function testDeleteDontStopOnFailure() + { + $key = new Key(uniqid(__METHOD__, true)); + + $this->store1 + ->expects($this->once()) + ->method('delete') + ->with($key) + ->willThrowException(new \Exception()); + $this->store2 + ->expects($this->once()) + ->method('delete') + ->with($key); + + $this->store->delete($key); + } +} diff --git a/vendor/symfony/lock/Tests/Store/ExpiringStoreTestTrait.php b/vendor/symfony/lock/Tests/Store/ExpiringStoreTestTrait.php new file mode 100644 index 00000000..10b13273 --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/ExpiringStoreTestTrait.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\StoreInterface; + +/** + * @author Jérémy Derussé + */ +trait ExpiringStoreTestTrait +{ + /** + * Amount a microsecond used to order async actions. + * + * @return int + */ + abstract protected function getClockDelay(); + + /** + * @see AbstractStoreTest::getStore() + */ + abstract protected function getStore(); + + /** + * Tests the store automatically delete the key when it expire. + * + * This test is time sensible: the $clockDelay could be adjust. + */ + public function testExpiration() + { + $key = new Key(uniqid(__METHOD__, true)); + $clockDelay = $this->getClockDelay(); + + /** @var StoreInterface $store */ + $store = $this->getStore(); + + $store->save($key); + $store->putOffExpiration($key, $clockDelay / 1000000); + $this->assertTrue($store->exists($key)); + + usleep(2 * $clockDelay); + $this->assertFalse($store->exists($key)); + } + + /** + * Tests the store thrown exception when TTL expires. + * + * @expectedException \Symfony\Component\Lock\Exception\LockExpiredException + */ + public function testAbortAfterExpiration() + { + $key = new Key(uniqid(__METHOD__, true)); + + /** @var StoreInterface $store */ + $store = $this->getStore(); + + $store->save($key); + $store->putOffExpiration($key, 1 / 1000000); + } + + /** + * Tests the refresh can push the limits to the expiration. + * + * This test is time sensible: the $clockDelay could be adjust. + */ + public function testRefreshLock() + { + // Amount a microsecond used to order async actions + $clockDelay = $this->getClockDelay(); + + // Amount a microsecond used to order async actions + $key = new Key(uniqid(__METHOD__, true)); + + /** @var StoreInterface $store */ + $store = $this->getStore(); + + $store->save($key); + $store->putOffExpiration($key, $clockDelay / 1000000); + $this->assertTrue($store->exists($key)); + + usleep(2 * $clockDelay); + $this->assertFalse($store->exists($key)); + } + + public function testSetExpiration() + { + $key = new Key(uniqid(__METHOD__, true)); + + /** @var StoreInterface $store */ + $store = $this->getStore(); + + $store->save($key); + $store->putOffExpiration($key, 1); + $this->assertGreaterThanOrEqual(0, $key->getRemainingLifetime()); + $this->assertLessThanOrEqual(1, $key->getRemainingLifetime()); + } +} diff --git a/vendor/symfony/lock/Tests/Store/FlockStoreTest.php b/vendor/symfony/lock/Tests/Store/FlockStoreTest.php new file mode 100644 index 00000000..ef3650c3 --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/FlockStoreTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +use Symfony\Component\Lock\Key; +use Symfony\Component\Lock\Store\FlockStore; + +/** + * @author Jérémy Derussé + */ +class FlockStoreTest extends AbstractStoreTest +{ + use BlockingStoreTestTrait; + + /** + * {@inheritdoc} + */ + protected function getStore() + { + return new FlockStore(); + } + + /** + * @expectedException \Symfony\Component\Lock\Exception\InvalidArgumentException + * @expectedExceptionMessage The directory "/a/b/c/d/e" is not writable. + */ + public function testConstructWhenRepositoryDoesNotExist() + { + if (!getenv('USER') || 'root' === getenv('USER')) { + $this->markTestSkipped('This test will fail if run under superuser'); + } + + new FlockStore('/a/b/c/d/e'); + } + + /** + * @expectedException \Symfony\Component\Lock\Exception\InvalidArgumentException + * @expectedExceptionMessage The directory "/" is not writable. + */ + public function testConstructWhenRepositoryIsNotWriteable() + { + if (!getenv('USER') || 'root' === getenv('USER')) { + $this->markTestSkipped('This test will fail if run under superuser'); + } + + new FlockStore('/'); + } + + public function testSaveSanitizeName() + { + $store = $this->getStore(); + + $key = new Key(''); + + $file = sprintf( + '%s/sf.-php-echo-hello-word-.%s.lock', + sys_get_temp_dir(), + strtr(substr(base64_encode(hash('sha256', $key, true)), 0, 7), '/', '_') + ); + // ensure the file does not exist before the store + @unlink($file); + + $store->save($key); + + $this->assertFileExists($file); + + $store->delete($key); + } +} diff --git a/vendor/symfony/lock/Tests/Store/MemcachedStoreTest.php b/vendor/symfony/lock/Tests/Store/MemcachedStoreTest.php new file mode 100644 index 00000000..f4ab571f --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/MemcachedStoreTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +use Symfony\Component\Lock\Store\MemcachedStore; + +/** + * @author Jérémy Derussé + * + * @requires extension memcached + */ +class MemcachedStoreTest extends AbstractStoreTest +{ + use ExpiringStoreTestTrait; + + public static function setupBeforeClass() + { + $memcached = new \Memcached(); + $memcached->addServer(getenv('MEMCACHED_HOST'), 11211); + $memcached->get('foo'); + $code = $memcached->getResultCode(); + + if (\Memcached::RES_SUCCESS !== $code && \Memcached::RES_NOTFOUND !== $code) { + self::markTestSkipped('Unable to connect to the memcache host'); + } + } + + /** + * {@inheritdoc} + */ + protected function getClockDelay() + { + return 1000000; + } + + /** + * {@inheritdoc} + */ + public function getStore() + { + $memcached = new \Memcached(); + $memcached->addServer(getenv('MEMCACHED_HOST'), 11211); + + return new MemcachedStore($memcached); + } + + public function testAbortAfterExpiration() + { + $this->markTestSkipped('Memcached expects a TTL greater than 1 sec. Simulating a slow network is too hard'); + } +} diff --git a/vendor/symfony/lock/Tests/Store/PredisStoreTest.php b/vendor/symfony/lock/Tests/Store/PredisStoreTest.php new file mode 100644 index 00000000..621affec --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/PredisStoreTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +/** + * @author Jérémy Derussé + */ +class PredisStoreTest extends AbstractRedisStoreTest +{ + public static function setupBeforeClass() + { + $redis = new \Predis\Client('tcp://'.getenv('REDIS_HOST').':6379'); + try { + $redis->connect(); + } catch (\Exception $e) { + self::markTestSkipped($e->getMessage()); + } + } + + protected function getRedisConnection() + { + $redis = new \Predis\Client('tcp://'.getenv('REDIS_HOST').':6379'); + $redis->connect(); + + return $redis; + } +} diff --git a/vendor/symfony/lock/Tests/Store/RedisArrayStoreTest.php b/vendor/symfony/lock/Tests/Store/RedisArrayStoreTest.php new file mode 100644 index 00000000..180da461 --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/RedisArrayStoreTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +/** + * @author Jérémy Derussé + * + * @requires extension redis + */ +class RedisArrayStoreTest extends AbstractRedisStoreTest +{ + public static function setupBeforeClass() + { + if (!class_exists('RedisArray')) { + self::markTestSkipped('The RedisArray class is required.'); + } + if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) { + $e = error_get_last(); + self::markTestSkipped($e['message']); + } + } + + protected function getRedisConnection() + { + $redis = new \RedisArray(array(getenv('REDIS_HOST'))); + + return $redis; + } +} diff --git a/vendor/symfony/lock/Tests/Store/RedisStoreTest.php b/vendor/symfony/lock/Tests/Store/RedisStoreTest.php new file mode 100644 index 00000000..6c7d2441 --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/RedisStoreTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +/** + * @author Jérémy Derussé + * + * @requires extension redis + */ +class RedisStoreTest extends AbstractRedisStoreTest +{ + public static function setupBeforeClass() + { + if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) { + $e = error_get_last(); + self::markTestSkipped($e['message']); + } + } + + protected function getRedisConnection() + { + $redis = new \Redis(); + $redis->connect(getenv('REDIS_HOST')); + + return $redis; + } +} diff --git a/vendor/symfony/lock/Tests/Store/RetryTillSaveStoreTest.php b/vendor/symfony/lock/Tests/Store/RetryTillSaveStoreTest.php new file mode 100644 index 00000000..febd48f2 --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/RetryTillSaveStoreTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +use Symfony\Component\Lock\Store\RedisStore; +use Symfony\Component\Lock\Store\RetryTillSaveStore; + +/** + * @author Jérémy Derussé + */ +class RetryTillSaveStoreTest extends AbstractStoreTest +{ + use BlockingStoreTestTrait; + + public function getStore() + { + $redis = new \Predis\Client('tcp://'.getenv('REDIS_HOST').':6379'); + try { + $redis->connect(); + } catch (\Exception $e) { + self::markTestSkipped($e->getMessage()); + } + + return new RetryTillSaveStore(new RedisStore($redis), 100, 100); + } +} diff --git a/vendor/symfony/lock/Tests/Store/SemaphoreStoreTest.php b/vendor/symfony/lock/Tests/Store/SemaphoreStoreTest.php new file mode 100644 index 00000000..bb37ec1f --- /dev/null +++ b/vendor/symfony/lock/Tests/Store/SemaphoreStoreTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Store; + +use Symfony\Component\Lock\Store\SemaphoreStore; + +/** + * @author Jérémy Derussé + * + * @requires extension sysvsem + */ +class SemaphoreStoreTest extends AbstractStoreTest +{ + use BlockingStoreTestTrait; + + /** + * {@inheritdoc} + */ + protected function getStore() + { + if (\PHP_VERSION_ID < 50601) { + $this->markTestSkipped('Non blocking semaphore are supported by PHP version greater or equals than 5.6.1'); + } + + return new SemaphoreStore(); + } +} diff --git a/vendor/symfony/lock/Tests/Strategy/ConsensusStrategyTest.php b/vendor/symfony/lock/Tests/Strategy/ConsensusStrategyTest.php new file mode 100644 index 00000000..87e83a99 --- /dev/null +++ b/vendor/symfony/lock/Tests/Strategy/ConsensusStrategyTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Strategy; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Lock\Strategy\ConsensusStrategy; + +/** + * @author Jérémy Derussé + */ +class ConsensusStrategyTest extends TestCase +{ + /** @var ConsensusStrategy */ + private $strategy; + + public function setup() + { + $this->strategy = new ConsensusStrategy(); + } + + public function provideMetResults() + { + // success, failure, total, isMet + yield array(3, 0, 3, true); + yield array(2, 1, 3, true); + yield array(2, 0, 3, true); + yield array(1, 2, 3, false); + yield array(1, 1, 3, false); + yield array(1, 0, 3, false); + yield array(0, 3, 3, false); + yield array(0, 2, 3, false); + yield array(0, 1, 3, false); + yield array(0, 0, 3, false); + + yield array(2, 0, 2, true); + yield array(1, 1, 2, false); + yield array(1, 0, 2, false); + yield array(0, 2, 2, false); + yield array(0, 1, 2, false); + yield array(0, 0, 2, false); + } + + public function provideIndeterminate() + { + // success, failure, total, canBeMet + yield array(3, 0, 3, true); + yield array(2, 1, 3, true); + yield array(2, 0, 3, true); + yield array(1, 2, 3, false); + yield array(1, 1, 3, true); + yield array(1, 0, 3, true); + yield array(0, 3, 3, false); + yield array(0, 2, 3, false); + yield array(0, 1, 3, true); + yield array(0, 0, 3, true); + + yield array(2, 0, 2, true); + yield array(1, 1, 2, false); + yield array(1, 0, 2, true); + yield array(0, 2, 2, false); + yield array(0, 1, 2, false); + yield array(0, 0, 2, true); + } + + /** + * @dataProvider provideMetResults + */ + public function testMet($success, $failure, $total, $isMet) + { + $this->assertSame($isMet, $this->strategy->isMet($success, $total)); + } + + /** + * @dataProvider provideIndeterminate + */ + public function testCanBeMet($success, $failure, $total, $isMet) + { + $this->assertSame($isMet, $this->strategy->canBeMet($failure, $total)); + } +} diff --git a/vendor/symfony/lock/Tests/Strategy/UnanimousStrategyTest.php b/vendor/symfony/lock/Tests/Strategy/UnanimousStrategyTest.php new file mode 100644 index 00000000..beff54a6 --- /dev/null +++ b/vendor/symfony/lock/Tests/Strategy/UnanimousStrategyTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Lock\Tests\Strategy; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Lock\Strategy\UnanimousStrategy; + +/** + * @author Jérémy Derussé + */ +class UnanimousStrategyTest extends TestCase +{ + /** @var UnanimousStrategy */ + private $strategy; + + public function setup() + { + $this->strategy = new UnanimousStrategy(); + } + + public function provideMetResults() + { + // success, failure, total, isMet + yield array(3, 0, 3, true); + yield array(2, 1, 3, false); + yield array(2, 0, 3, false); + yield array(1, 2, 3, false); + yield array(1, 1, 3, false); + yield array(1, 0, 3, false); + yield array(0, 3, 3, false); + yield array(0, 2, 3, false); + yield array(0, 1, 3, false); + yield array(0, 0, 3, false); + + yield array(2, 0, 2, true); + yield array(1, 1, 2, false); + yield array(1, 0, 2, false); + yield array(0, 2, 2, false); + yield array(0, 1, 2, false); + yield array(0, 0, 2, false); + } + + public function provideIndeterminate() + { + // success, failure, total, canBeMet + yield array(3, 0, 3, true); + yield array(2, 1, 3, false); + yield array(2, 0, 3, true); + yield array(1, 2, 3, false); + yield array(1, 1, 3, false); + yield array(1, 0, 3, true); + yield array(0, 3, 3, false); + yield array(0, 2, 3, false); + yield array(0, 1, 3, false); + yield array(0, 0, 3, true); + + yield array(2, 0, 2, true); + yield array(1, 1, 2, false); + yield array(1, 0, 2, true); + yield array(0, 2, 2, false); + yield array(0, 1, 2, false); + yield array(0, 0, 2, true); + } + + /** + * @dataProvider provideMetResults + */ + public function testMet($success, $failure, $total, $isMet) + { + $this->assertSame($isMet, $this->strategy->isMet($success, $total)); + } + + /** + * @dataProvider provideIndeterminate + */ + public function testCanBeMet($success, $failure, $total, $isMet) + { + $this->assertSame($isMet, $this->strategy->canBeMet($failure, $total)); + } +} diff --git a/vendor/symfony/lock/composer.json b/vendor/symfony/lock/composer.json new file mode 100644 index 00000000..9a6e7687 --- /dev/null +++ b/vendor/symfony/lock/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/lock", + "type": "library", + "description": "Symfony Lock Component", + "keywords": ["locking", "redlock", "mutex", "semaphore", "flock", "cas"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Jérémy Derussé", + "email": "jeremy@derusse.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-php70": "~1.0", + "psr/log": "~1.0" + }, + "require-dev": { + "predis/predis": "~1.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Lock\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/vendor/symfony/lock/phpunit.xml.dist b/vendor/symfony/lock/phpunit.xml.dist new file mode 100644 index 00000000..be3ca215 --- /dev/null +++ b/vendor/symfony/lock/phpunit.xml.dist @@ -0,0 +1,32 @@ + + + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/polyfill-php70/LICENSE b/vendor/symfony/polyfill-php70/LICENSE new file mode 100644 index 00000000..24fa32c2 --- /dev/null +++ b/vendor/symfony/polyfill-php70/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2018 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-php70/Php70.php b/vendor/symfony/polyfill-php70/Php70.php new file mode 100644 index 00000000..39e66450 --- /dev/null +++ b/vendor/symfony/polyfill-php70/Php70.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php70; + +/** + * @author Nicolas Grekas + * + * @internal + */ +final class Php70 +{ + public static function intdiv($dividend, $divisor) + { + $dividend = self::intArg($dividend, __FUNCTION__, 1); + $divisor = self::intArg($divisor, __FUNCTION__, 2); + + if (0 === $divisor) { + throw new \DivisionByZeroError('Division by zero'); + } + if (-1 === $divisor && ~PHP_INT_MAX === $dividend) { + throw new \ArithmeticError('Division of PHP_INT_MIN by -1 is not an integer'); + } + + return ($dividend - ($dividend % $divisor)) / $divisor; + } + + public static function preg_replace_callback_array(array $patterns, $subject, $limit = -1, &$count = 0) + { + $count = 0; + $result = (string) $subject; + if (0 === $limit = self::intArg($limit, __FUNCTION__, 3)) { + return $result; + } + + foreach ($patterns as $pattern => $callback) { + $result = preg_replace_callback($pattern, $callback, $result, $limit, $c); + $count += $c; + } + + return $result; + } + + public static function error_clear_last() + { + static $handler; + if (!$handler) { + $handler = function() { return false; }; + } + set_error_handler($handler); + @trigger_error(''); + restore_error_handler(); + } + + private static function intArg($value, $caller, $pos) + { + if (\is_int($value)) { + return $value; + } + if (!\is_numeric($value) || PHP_INT_MAX <= ($value += 0) || ~PHP_INT_MAX >= $value) { + throw new \TypeError(sprintf('%s() expects parameter %d to be integer, %s given', $caller, $pos, gettype($value))); + } + + return (int) $value; + } +} diff --git a/vendor/symfony/polyfill-php70/README.md b/vendor/symfony/polyfill-php70/README.md new file mode 100644 index 00000000..04988c6f --- /dev/null +++ b/vendor/symfony/polyfill-php70/README.md @@ -0,0 +1,28 @@ +Symfony Polyfill / Php70 +======================== + +This component provides features unavailable in releases prior to PHP 7.0: + +- [`intdiv`](http://php.net/intdiv) +- [`preg_replace_callback_array`](http://php.net/preg_replace_callback_array) +- [`error_clear_last`](http://php.net/error_clear_last) +- `random_bytes` and `random_int` (from [paragonie/random_compat](https://github.com/paragonie/random_compat)) +- [`*Error` throwable classes](http://php.net/Error) +- [`PHP_INT_MIN`](http://php.net/manual/en/reserved.constants.php#constant.php-int-min) +- `SessionUpdateTimestampHandlerInterface` + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +Compatibility notes +=================== + +To write portable code between PHP5 and PHP7, some care must be taken: +- `\*Error` exceptions must be caught before `\Exception`; +- after calling `error_clear_last()`, the result of `$e = error_get_last()` must be + verified using `isset($e['message'][0])` instead of `null !== $e`. + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php b/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php new file mode 100644 index 00000000..68191244 --- /dev/null +++ b/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php @@ -0,0 +1,5 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php70 as p; + +if (PHP_VERSION_ID < 70000) { + if (!defined('PHP_INT_MIN')) { + define('PHP_INT_MIN', ~PHP_INT_MAX); + } + if (!function_exists('intdiv')) { + function intdiv($dividend, $divisor) { return p\Php70::intdiv($dividend, $divisor); } + } + if (!function_exists('preg_replace_callback_array')) { + function preg_replace_callback_array(array $patterns, $subject, $limit = -1, &$count = 0) { return p\Php70::preg_replace_callback_array($patterns, $subject, $limit, $count); } + } + if (!function_exists('error_clear_last')) { + function error_clear_last() { return p\Php70::error_clear_last(); } + } +} diff --git a/vendor/symfony/polyfill-php70/composer.json b/vendor/symfony/polyfill-php70/composer.json new file mode 100644 index 00000000..871045d2 --- /dev/null +++ b/vendor/symfony/polyfill-php70/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/polyfill-php70", + "type": "library", + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "paragonie/random_compat": "~1.0|~2.0" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php70\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + } +} -- 2.54.0 From 64e279d8597f9582830fc13dda76b1fd3dea1830 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 8 May 2018 23:39:48 +0900 Subject: [PATCH 026/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=EC=8B=9C=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EA=B0=80=20=EC=9E=98=EB=AA=BB=20=EB=82=A8=EB=8A=94=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/j_select_npc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/j_select_npc.php b/hwe/j_select_npc.php index 4cde200b..ffa83201 100644 --- a/hwe/j_select_npc.php +++ b/hwe/j_select_npc.php @@ -101,8 +101,8 @@ $rootDB->insert('member_log', [ 'action'=>Json::encode([ 'server'=>DB::prefix(), 'type'=>'npc', - 'generalID'=>$npc['no'], - 'generalName'=>$npc['name'] + 'generalID'=>$pickedNPC['no'], + 'generalName'=>$pickedNPC['name'] ]) ]); -- 2.54.0 From 766d0b3ad59ed23c35135ee8c6a73f4e51fa52dd Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 8 May 2018 23:40:35 +0900 Subject: [PATCH 027/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=20=ED=83=88=EC=B6=9C?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=9E=84=EC=8B=9C=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/b_myPage.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hwe/b_myPage.php b/hwe/b_myPage.php index 449e5ede..2be84cc8 100644 --- a/hwe/b_myPage.php +++ b/hwe/b_myPage.php @@ -7,7 +7,8 @@ include "func.php"; $btn = Util::getReq('btn'); $mode = Util::getReq('mode', 'int', 2); $tnmt = Util::getReq('tnmt', 'int', 1); -$detachNPC = Util::getReq('detachNPC', 'bool'); +//$detachNPC = Util::getReq('detachNPC', 'bool'); +$detachNPC = false; extractMissingPostToGlobals(); @@ -109,8 +110,8 @@ if (($btn == "설정저장" || $detachNPC) && $me['myset'] > 0) { 휴 가 신 청


- 빙의 해제용 삭턴 조절
- + -- 2.54.0 From beea13ff4b443d30bc66d5dd33c549407dcbc58f Mon Sep 17 00:00:00 2001 From: hide_d Date: Wed, 9 May 2018 01:44:22 +0900 Subject: [PATCH 028/304] =?UTF-8?q?checkSupply()=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EA=B5=AD=EA=B0=80=20select=20=EC=BF=BC=EB=A6=AC=20=EC=A7=81?= =?UTF-8?q?=EC=A0=91=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_gamerule.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hwe/func_gamerule.php b/hwe/func_gamerule.php index e3006343..51baa14e 100644 --- a/hwe/func_gamerule.php +++ b/hwe/func_gamerule.php @@ -257,12 +257,15 @@ function checkSupply() { } $queue = new \SplQueue(); - foreach(getAllNationStaticInfo() as $nation){ + foreach($db->query('SELECT capital, nation FROM nation WHERE `level` > 0') as $nation){ $capitalID = $nation['capital']; if(!$capitalID){ continue; } $city = &$cities[$capitalID]; + if($nation['nation'] != $city['nation']){ + continue; + } $city['supply'] = true; $queue->enqueue($city['id']); } -- 2.54.0 From aa46fc9668df0793aece06742b027d978b969cc4 Mon Sep 17 00:00:00 2001 From: hide_d Date: Fri, 11 May 2018 03:16:24 +0900 Subject: [PATCH 029/304] =?UTF-8?q?html5=20=EB=B9=84=ED=98=B8=ED=99=98=20b?= =?UTF-8?q?order=3D1,=20border=3D0=20=EC=A0=9C=EA=B1=B0.=20bar=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=EB=A5=BC=20div=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/_119.php | 4 +- hwe/_admin1.php | 10 +- hwe/_admin2.php | 12 +- hwe/_admin4.php | 12 +- hwe/_admin5.php | 14 +- hwe/_admin6.php | 10 +- hwe/_admin7.php | 10 +- hwe/_admin8.php | 10 +- hwe/_simul.php | 6 +- hwe/a_bestGeneral.php | 12 +- hwe/a_emperior.php | 10 +- hwe/a_emperior2.php | 10 +- hwe/a_genList.php | 8 +- hwe/a_hallOfFame.php | 8 +- hwe/a_history.php | 8 +- hwe/a_kingdomList.php | 10 +- hwe/a_npcList.php | 8 +- hwe/a_status.php | 8 +- hwe/a_traffic.php | 228 +++++++++----------------- hwe/a_vote.php | 70 ++++---- hwe/b_auction.php | 10 +- hwe/b_battleCenter.php | 8 +- hwe/b_betting.php | 27 +-- hwe/b_chiefboard.php | 6 +- hwe/b_chiefcenter.php | 8 +- hwe/b_currentCity.php | 14 +- hwe/b_dipcenter.php | 12 +- hwe/b_diplomacy.php | 32 +--- hwe/b_genList.php | 8 +- hwe/b_myBossInfo.php | 14 +- hwe/b_myCityInfo.php | 8 +- hwe/b_myGenInfo.php | 8 +- hwe/b_myKingdomInfo.php | 8 +- hwe/b_myPage.php | 8 +- hwe/b_nationboard.php | 6 +- hwe/b_nationrule.php | 4 +- hwe/b_tournament.php | 25 +-- hwe/b_troop.php | 20 +-- hwe/commandlist.php | 4 +- hwe/css/common.css | 52 ++++++ hwe/css/main.css | 4 + hwe/css/msg.css | 10 +- hwe/func.php | 316 ++++++++++++++++++------------------ hwe/func_legacy.php | 16 +- hwe/index.php | 111 +++++++------ hwe/join.php | 14 +- hwe/processing.php | 10 +- hwe/select_npc.php | 2 +- hwe/templates/msg_plate.php | 53 ------ i_other/help.php | 32 ++-- i_other/screenshot.php | 13 +- tmp_map/map.html | 2 +- tmp_msg/msg.html | 2 +- 53 files changed, 612 insertions(+), 723 deletions(-) delete mode 100644 hwe/templates/msg_plate.php diff --git a/hwe/_119.php b/hwe/_119.php index 9ac164fc..82875fef 100644 --- a/hwe/_119.php +++ b/hwe/_119.php @@ -13,7 +13,7 @@ if($session->userGrade < 4) { 관리메뉴 - + 관리자가 아닙니다.
@@ -41,7 +41,7 @@ $plock = MYDB_fetch_array($result); 삼국지 모의전투 HiDCHe - +
diff --git a/hwe/_admin1.php b/hwe/_admin1.php index 1855a5a4..3adebcba 100644 --- a/hwe/_admin1.php +++ b/hwe/_admin1.php @@ -13,7 +13,7 @@ if($session->userGrade < 5) { 관리메뉴 - + 관리자가 아닙니다.
@@ -37,14 +37,14 @@ $admin = getAdmin(); 게임관리 - + - +
게 임 관 리
- +
@@ -81,7 +81,7 @@ $admin = getAdmin();
운영자메세지 '>
- +
diff --git a/hwe/_admin2.php b/hwe/_admin2.php index ee68de3e..d0f2602b 100644 --- a/hwe/_admin2.php +++ b/hwe/_admin2.php @@ -13,7 +13,7 @@ if($session->userGrade < 5) { 관리메뉴 - + 관리자가 아닙니다.
@@ -39,20 +39,20 @@ $admin = MYDB_fetch_array($result); 회원관리 - + - +
회 원 관 리
- +
접속제한
- +
회원선택

NPC
NPC유저
접속제한
블럭회원
@@ -131,7 +131,7 @@ for($i=0; $i < 27; $i++) {
- +
diff --git a/hwe/_admin4.php b/hwe/_admin4.php index d000476a..8c07ac13 100644 --- a/hwe/_admin4.php +++ b/hwe/_admin4.php @@ -13,7 +13,7 @@ if($session->userGrade < 5) { 관리메뉴 - + 관리자가 아닙니다.
@@ -39,14 +39,14 @@ $admin = MYDB_fetch_array($result); 멀티관리 - + - +
멀 티 관 리
- +
회원선택

NPC
NPC유저
접속제한
블럭회원
@@ -90,7 +90,7 @@ echo "
- +
@@ -137,7 +137,7 @@ echo " //NOTE: password의 md5 해시가 같은지 확인하는 방식으로는 앞으로 잡아낼 수 없다. 폐기 ?> -
장수명 최근로그인
+
diff --git a/hwe/_admin5.php b/hwe/_admin5.php index e460fbc4..7ccaa8c9 100644 --- a/hwe/_admin5.php +++ b/hwe/_admin5.php @@ -26,7 +26,7 @@ if($session->userGrade < 5) { 관리메뉴 - + 관리자가 아닙니다.
@@ -57,10 +57,10 @@ $admin = MYDB_fetch_array($result); 일제정보 - + - +
일 제 정 보
정렬순서 : @@ -115,7 +115,7 @@ for($i=1; $i <= $count; $i++) {
- +
@@ -262,11 +262,11 @@ for($i=0; $i < $nationCount; $i++) { ?>
국명 접률
- +
- +
@@ -317,7 +317,7 @@ for($i=0; $i < $count; $i++) { ?>
- +
diff --git a/hwe/_admin6.php b/hwe/_admin6.php index 8ad66ef1..3a1226f8 100644 --- a/hwe/_admin6.php +++ b/hwe/_admin6.php @@ -20,7 +20,7 @@ if($session->userGrade < 5) { 관리메뉴 - + 관리자가 아닙니다.
@@ -45,10 +45,10 @@ $sel[$type] = "selected"; 접속정보 - + - +
접 속 정 보
정렬순서 :
- +
@@ -100,7 +100,7 @@ for($i=0; $i < $gencount; $i++) { ?>
장수명 접속률
- +
diff --git a/hwe/_admin7.php b/hwe/_admin7.php index 8be7c7ff..f7049853 100644 --- a/hwe/_admin7.php +++ b/hwe/_admin7.php @@ -24,7 +24,7 @@ if($session->userGrade < 5) { 관리메뉴 - + 관리자가 아닙니다.
@@ -52,10 +52,10 @@ $sel[$type] = "selected"; 로그정보 - + - +
로 그 정 보
@@ -99,7 +99,7 @@ for($i=0; $i < $gencount; $i++) {
- +
@@ -136,7 +136,7 @@ for($i=0; $i < $gencount; $i++) {
장 수 정 보 -
- +
diff --git a/hwe/_admin8.php b/hwe/_admin8.php index cff5f832..c9bfcfef 100644 --- a/hwe/_admin8.php +++ b/hwe/_admin8.php @@ -20,7 +20,7 @@ if ($session->userGrade < 5) { 관리메뉴 - + 관리자가 아닙니다.
@@ -48,10 +48,10 @@ $sel[$type] = "selected"; 외교정보 - + - +
외 교 정 보
@@ -63,7 +63,7 @@ $sel[$type] = "selected";
- +
@@ -155,7 +155,7 @@ for ($i=0; $i < $dipcount; $i++) { } ?>
외 교 관 계
국 가 명
- +
diff --git a/hwe/_simul.php b/hwe/_simul.php index fe992f6f..034cf629 100644 --- a/hwe/_simul.php +++ b/hwe/_simul.php @@ -1141,7 +1141,7 @@ switch($dex240) { 전투시뮬레이션 - + - +
트 래 픽 정 보

- +
- +
$value){ - $w = round($refresh[$i] / $game['maxrefresh'] * 100, 1); - if ($w >= 100) { - $w -= 0.1; - } - if ($refresh[$i] < 10 && $w < 3) { - $w = 3; - } elseif ($refresh[$i] < 100 && $w < 6) { - $w = 6; - } elseif ($refresh[$i] < 1000 && $w < 9) { - $w = 9; - } - $w2 = round(100 - $w, 1); +$refresh[] = $game['refresh']; +foreach($refresh as $i=>$value){ + $w = round($value / $game['maxrefresh'] * 100, 1); $color = getTrafficColor($w); $dt = substr($date[$i], 11, 5); ?> - = 100) { - $w -= 0.1; -} -if ($game['refresh'] < 10 && $w < 3) { - $w = 3; -} elseif ($game['refresh'] < 100 && $w < 6) { - $w = 6; -} elseif ($game['refresh'] < 1000 && $w < 9) { - $w = 9; -} -$w2 = round(100 - $w, 1); -$color = getTrafficColor($w); -$dt = date('H:i'); ?> - - - - - - @@ -130,137 +122,71 @@ $dt = date('H:i');
접 속 량
- - - - - -
% bgcolor= align=right> % id=bg0> 
+
+ + + +
+ +
+ +
- - - - - -
% bgcolor= align=right> % id=bg0> 
-
최고기록:
- +
$value){ - $w = round($online[$i] / $game['maxonline'] * 100, 1); - if ($w >= 100) { - $w -= 0.1; - } - if ($online[$i] < 10 && $w < 3) { - $w = 3; - } elseif ($online[$i] < 100 && $w < 6) { - $w = 6; - } elseif ($online[$i] < 1000 && $w < 9) { - $w = 9; - } - $w2 = round(100 - $w, 1); +$online[] = $curonline; +foreach($online as $i=>$value){ + $w = round($value / $game['maxonline'] * 100, 1); $color = getTrafficColor($w); $dt = substr($date[$i], 11, 5); ?> - = 100) { - $w -= 0.1; -} -if ($curonline < 10 && $w < 3) { - $w = 3; -} elseif ($curonline < 100 && $w < 6) { - $w = 6; -} elseif ($curonline < 1000 && $w < 9) { - $w = 9; -} -$w2 = round(100 - $w, 1); -$color = getTrafficColor($w); -$dt = date('H:i'); -echo " - - - - - - +?> - + -"; -?>
접 속 자
- - - - - -
% bgcolor= align=right> % id=bg0> 
+
+ + + +
+ +
+ +
{$game['year']}년 {$game['month']}월{$dt} - - - - - -
{$curonline}  
-
최고기록: {$game['maxonline']}최고기록:

- +
queryFirstRow('SELECT sum(refresh) as refresh, sum(`connect`) as `connect` from general'); +$max_refresh['name'] = '접속자 총합'; -$w = round($maxrefresh / $maxrefresh * 100, 1); -$w2 = round(100 - $w, 1); -$color = getTrafficColor($w); -echo " - - - - - - - - - - -"; +$refresh_result = array_merge([$max_refresh], $db->query('SELECT `name`,refresh,`connect` FROM general ORDER BY refresh DESC LIMIT 5')); -$query = "select name,refresh,connect from general order by refresh desc limit 0,5"; -$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect), ""); -$count = MYDB_num_rows($result); -for ($i=0; $i < $count; $i++) { - $user = MYDB_fetch_array($result); - - $w = round($user['refresh'] / $maxrefresh * 100, 1); +foreach ($refresh_result as $i=>$user) { + $w = round($user['refresh'] / $max_refresh['refresh'] * 100, 1); $w2 = round(100 - $w, 1); $color = getTrafficColor($w); - echo " +?> - - - + + - "; +
주 의 대 상 자 (순간과도갱신)
주의대상자벌점(순간갱신)전체 대비
접속자 총합{$user['connect']}({$maxrefresh}) - - - - - -
  
-
{$user['name']}{$user['connect']}({$user['refresh']}) - - - - - -
  
+
() + + +
+ +
+

- +
@@ -273,7 +199,7 @@ function getTrafficColor($per) $r = getHex($per); $b = getHex(100 - $per); $color = $r . "00" . $b; - return $color; + return '#'.$color; } function getHex($dec) diff --git a/hwe/a_vote.php b/hwe/a_vote.php index 4b6c08d3..771cdc42 100644 --- a/hwe/a_vote.php +++ b/hwe/a_vote.php @@ -31,7 +31,16 @@ if ($vote[0] == "") { <?=UniqueConst::$serverName?>: 설문조사 - + + - + - +
중 원 정 보

- +

-
align=center bgcolor=blue>외 교 현 황
+
- + - + - + -- 2.54.0 From 0e5b321e5858ba31a492c842da8cb1544aa24a7f Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 3 Jul 2018 20:48:34 +0900 Subject: [PATCH 276/304] =?UTF-8?q?=EA=B5=AD=EA=B0=80=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=20=EB=B9=88=EC=B9=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/templates/oldNation.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/templates/oldNation.php b/hwe/templates/oldNation.php index 46f445e3..1f8ae712 100644 --- a/hwe/templates/oldNation.php +++ b/hwe/templates/oldNation.php @@ -17,8 +17,8 @@ - - + + -- 2.54.0 From b02d5b368d608004c8d523588513ebdddac11386 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 3 Jul 2018 21:35:17 +0900 Subject: [PATCH 277/304] =?UTF-8?q?parseJsonPost=20=EA=B8=B0=EB=B0=98=20?= =?UTF-8?q?=EB=8F=99=EC=9E=91=20=ED=8F=AC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/j_map.php | 2 +- hwe/j_msg_decide_opt.php | 2 +- hwe/j_msg_get_recent.php | 3 +-- hwe/j_msg_submit.php | 2 +- hwe/js/map.js | 17 +++++++++-------- hwe/js/msg.js | 35 ++++++++++++++--------------------- 6 files changed, 27 insertions(+), 34 deletions(-) diff --git a/hwe/j_map.php b/hwe/j_map.php index 71e13179..f68a380c 100644 --- a/hwe/j_map.php +++ b/hwe/j_map.php @@ -13,7 +13,7 @@ $defaultPost = [ 'neutralView' => false, 'showMe' => true ]; -$post = WebUtil::parseJsonPost() + $defaultPost; +$post = Json::decode(Util::getReq('data', 'string', '{}')); if(!$session->isGameLoggedIn()){ $post['neutralView'] = true; diff --git a/hwe/j_msg_decide_opt.php b/hwe/j_msg_decide_opt.php index d0e438ef..ccad9d53 100644 --- a/hwe/j_msg_decide_opt.php +++ b/hwe/j_msg_decide_opt.php @@ -17,7 +17,7 @@ if (!$generalID) { ]); } -$jsonPost = WebUtil::parseJsonPost(); +$jsonPost = Json::decode(Util::getReq('data', 'string', '{}')); $msgID = Util::toInt($jsonPost['msgID']??null); $msgResponse = $jsonPost['response']??null; diff --git a/hwe/j_msg_get_recent.php b/hwe/j_msg_get_recent.php index d41cad51..c2a422e8 100644 --- a/hwe/j_msg_get_recent.php +++ b/hwe/j_msg_get_recent.php @@ -7,8 +7,7 @@ include('func.php'); $session = Session::requireGameLogin([])->setReadOnly(); $userID = Session::getUserID(); -$jsonPost = WebUtil::parseJsonPost(); -$reqSequence = (int)Util::array_get($jsonPost['sequence'], 0); +$reqSequence = Util::getReq('sequence', 'int', null); list($generalID, $nationID, $generalName) = DB::db()->queryFirstList( diff --git a/hwe/j_msg_submit.php b/hwe/j_msg_submit.php index f5e2a066..a4b54bcb 100644 --- a/hwe/j_msg_submit.php +++ b/hwe/j_msg_submit.php @@ -4,7 +4,7 @@ namespace sammo; include 'lib.php'; include 'func.php'; -$post = WebUtil::parseJsonPost(); +$post = Json::decode(Util::getReq('data', 'string', '{}')); '@phan-var mixed[] $post'; $v = new Validator($post); diff --git a/hwe/js/map.js b/hwe/js/map.js index 23ac1e9a..ec8d4af8 100644 --- a/hwe/js/map.js +++ b/hwe/js/map.js @@ -439,14 +439,15 @@ function reloadWorldMap(option){ url: option.targetJson, type: 'post', dataType:'json', - contentType: 'application/json', - data: JSON.stringify({ - neutralView:option.neutralView, - year:option.year, - month:option.month, - showMe:option.showMe, - aux:option.aux - }) + data: { + data: JSON.stringify({ + neutralView:option.neutralView, + year:option.year, + month:option.month, + showMe:option.showMe, + aux:option.aux + }) + } }); deferred diff --git a/hwe/js/msg.js b/hwe/js/msg.js index 8f3558a0..07d11238 100644 --- a/hwe/js/msg.js +++ b/hwe/js/msg.js @@ -34,11 +34,12 @@ function responseMessage(msgID, response){ url: 'j_msg_decide_opt.php', type: 'post', dataType:'json', - contentType: 'application/json', - data: JSON.stringify({ - msgID:msgID, - response:response - }) + data: { + data: JSON.stringify({ + msgID:msgID, + response:response + }) + } }).then(refreshMsg); } @@ -54,10 +55,9 @@ function fetchMsg(){ url: 'j_msg_get_recent.php', type: 'post', dataType:'json', - contentType: 'application/json', - data: JSON.stringify({ + data: { sequence:sequence - }) + } }); } @@ -373,11 +373,12 @@ function activateMessageForm(){ url:'j_msg_submit.php', type: 'post', dataType:'json', - contentType: 'application/json', - data: JSON.stringify({ - mailbox:parseInt(targetMailbox), - text:text - }) + data: { + data: JSON.stringify({ + mailbox:parseInt(targetMailbox), + text:text + }) + } }); } else{ @@ -404,10 +405,6 @@ jQuery(function($){ url:'j_basic_info.php', type: 'post', dataType:'json', - contentType: 'application/json', - data: JSON.stringify({ - - }) }).then(registerGlobal); //sender_list.json 은 서버측에선 캐시 가능한 데이터임. @@ -415,10 +412,6 @@ jQuery(function($){ url: 'j_msg_contact_list.php', type: 'post', dataType:'json', - contentType: 'application/json', - data: JSON.stringify({ - - }) }); var MessageList = fetchMsg(); -- 2.54.0 From 2b712baa5068a01270f32414d0636a00aeeb3eb8 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 3 Jul 2018 22:09:07 +0900 Subject: [PATCH 278/304] =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/j_msg_get_recent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/j_msg_get_recent.php b/hwe/j_msg_get_recent.php index c2a422e8..0ff1f231 100644 --- a/hwe/j_msg_get_recent.php +++ b/hwe/j_msg_get_recent.php @@ -7,7 +7,7 @@ include('func.php'); $session = Session::requireGameLogin([])->setReadOnly(); $userID = Session::getUserID(); -$reqSequence = Util::getReq('sequence', 'int', null); +$reqSequence = Util::getReq('sequence', 'int', 0); list($generalID, $nationID, $generalName) = DB::db()->queryFirstList( -- 2.54.0 From 3007f1ba971a58f873ec19de8650521614d8e2a1 Mon Sep 17 00:00:00 2001 From: hide_d Date: Thu, 5 Jul 2018 00:05:48 +0900 Subject: [PATCH 279/304] =?UTF-8?q?=EC=99=95=EC=A1=B0=20=EC=9D=BC=EB=9E=8C?= =?UTF-8?q?=EC=97=90=20=ED=98=84=EC=9E=AC=20=EA=B5=AD=EA=B0=80=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_emperior.php | 189 ++++++------------------------------ hwe/a_emperior_detail.php | 197 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+), 159 deletions(-) create mode 100644 hwe/a_emperior_detail.php diff --git a/hwe/a_emperior.php b/hwe/a_emperior.php index 7f4b5c3a..9a71e9ed 100644 --- a/hwe/a_emperior.php +++ b/hwe/a_emperior.php @@ -4,13 +4,8 @@ namespace sammo; include "lib.php"; include "func.php"; -$select = Util::getReq('select', 'int', 0); - $db = DB::db(); -$templates = new \League\Plates\Engine('templates'); -$templates->registerFunction('ConvertLog', '\sammo\ConvertLog'); -$templates->registerFunction('newColor', '\sammo\newColor'); increaseRefresh("왕조일람", 1); ?> @@ -35,17 +30,42 @@ increaseRefresh("왕조일람", 1); query('SELECT * FROM emperior ORDER BY `no` DESC') as $emperior){ +$showCurrentNation = true; +foreach($db->query('SELECT * FROM emperior ORDER BY `no` DESC') as $emperior){ + $serverID = $emperior['server_id']??($emperior['serverID']??null); + if($showCurrentNation && $serverID == UniqueConst::$serverID){ + $showCurrentNation = false; + } + if ($showCurrentNation) { + $gameStor = KVStorage::getStorage($db, 'game_env'); + [$year, $month] = $gameStor->getValuesAsArray(['year', 'month']); ?> -
분 쟁 현 황
- +
- + -- 2.54.0 From a619ca4e72610efa5f59a13fad87b490835a43b2 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 09:44:26 +0900 Subject: [PATCH 219/304] =?UTF-8?q?=ED=81=AC=EB=A1=AC=EA=B3=BC=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=ED=8F=AD=EC=8A=A4=EC=97=90=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index deb8b5e0..b3bbce0e 100644 --- a/index.php +++ b/index.php @@ -172,6 +172,6 @@ function postOAuthResult(result){
+
크롬와 파이어폭스에 최적화되어있습니다. -- 2.54.0 From b5ee605b30f9662c09c6a09e92e32515466cf101 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 09:45:15 +0900 Subject: [PATCH 220/304] =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index b3bbce0e..7b6b7d0a 100644 --- a/index.php +++ b/index.php @@ -172,6 +172,6 @@ function postOAuthResult(result){
개인정보처리방침 & 이용약관
© 2018 • HideD -
크롬와 파이어폭스에 최적화되어있습니다.
+
크롬과 파이어폭스에 최적화되어있습니다. -- 2.54.0 From 756b0de34bf093568dcf7ea4dc9bcd1298eabd86 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 16:18:58 +0900 Subject: [PATCH 221/304] =?UTF-8?q?=EB=AA=85=EC=9E=A5=20=EC=9D=BC=EB=9E=8C?= =?UTF-8?q?,=20=EC=84=A4=EC=A0=84=20=EC=8A=B9=EB=A5=A0=20%=20=ED=91=9C?= =?UTF-8?q?=EA=B8=B0=20=EC=98=A4=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_bestGeneral.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/a_bestGeneral.php b/hwe/a_bestGeneral.php index 65813261..c119fd92 100644 --- a/hwe/a_bestGeneral.php +++ b/hwe/a_bestGeneral.php @@ -78,7 +78,7 @@ $types = [ ["전 력 전 승 률", "percent", function($v){$v['value'] = $v['ttw']/max(1, $v['ttw']+$v['ttd']+$v['ttl']); return $v; }], ["통 솔 전 승 률", "percent", function($v){$v['value'] = $v['tlw']/max(1, $v['tlw']+$v['tld']+$v['tll']); return $v; }], ["일 기 토 승 률", "percent", function($v){$v['value'] = $v['tpw']/max(1, $v['tpw']+$v['tpd']+$v['tpl']); return $v; }], - ["설 전 승 률", "percent", function($v){$v['value'] = $v['ttw']/max(1, $v['tiw']+$v['tid']+$v['til']); return $v; }], + ["설 전 승 률", "percent", function($v){$v['value'] = $v['tiw']/max(1, $v['tiw']+$v['tid']+$v['til']); return $v; }], ["베 팅 투 자 액", "int", function($v){$v['value'] = $v['betgold']; return $v; }], ["베 팅 당 첨", "int", function($v){$v['value'] = $v['betwin']; return $v; }], ["베 팅 수 익 금", "int", function($v){$v['value'] = $v['betwingold']; return $v; }], -- 2.54.0 From 33c7dc29dad2e09bc1495e5e5bcf2d90df9c1b65 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 16:21:47 +0900 Subject: [PATCH 222/304] =?UTF-8?q?=EB=AA=85=EC=9E=A5=20=EC=9D=BC=EB=9E=8C?= =?UTF-8?q?=20=EB=B3=B4=EC=A0=95=20=EC=8B=9C=EB=8F=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_bestGeneral.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/a_bestGeneral.php b/hwe/a_bestGeneral.php index c119fd92..e0fbb980 100644 --- a/hwe/a_bestGeneral.php +++ b/hwe/a_bestGeneral.php @@ -127,12 +127,12 @@ foreach($types as $idx=>[$typeName, $typeValue, $typeFunc]){ usort($typeGenerals, function($lhs, $rhs){ //내림차순 - return -($lhs['value'] - $rhs['value']); + return $rhs['value'] - $lhs['value']; }); echo $templates->render('hallOfFrame', [ 'typeName'=>$typeName, - 'generals'=>array_slice($typeGenerals, 0, 10, true) + 'generals'=>array_slice($typeGenerals, 0, 10) ]); } ?> -- 2.54.0 From 44c060141449588a7600303b3bbdb3f6989b13db Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 16:27:51 +0900 Subject: [PATCH 223/304] =?UTF-8?q?=EB=AA=85=EC=9E=A5=EC=9D=BC=EB=9E=8C=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_bestGeneral.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/a_bestGeneral.php b/hwe/a_bestGeneral.php index e0fbb980..98d512cd 100644 --- a/hwe/a_bestGeneral.php +++ b/hwe/a_bestGeneral.php @@ -127,7 +127,7 @@ foreach($types as $idx=>[$typeName, $typeValue, $typeFunc]){ usort($typeGenerals, function($lhs, $rhs){ //내림차순 - return $rhs['value'] - $lhs['value']; + return -($lhs['value'] <=> $rhs['value']); }); echo $templates->render('hallOfFrame', [ -- 2.54.0 From addc8fc404c6dea15e772ff59b3d875c560d72a4 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 22:45:47 +0900 Subject: [PATCH 224/304] =?UTF-8?q?=EC=B2=9C=ED=86=B5=EA=B5=AD=20=EC=A0=95?= =?UTF-8?q?=EC=83=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_gamerule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/func_gamerule.php b/hwe/func_gamerule.php index 00d8e168..3794ed58 100644 --- a/hwe/func_gamerule.php +++ b/hwe/func_gamerule.php @@ -1243,7 +1243,7 @@ function checkEmperior() { $db->insert('ng_old_nations', [ 'server_id'=>UniqueConst::$serverID, - 'nation'=>$city['nation'], + 'nation'=>$nation['nation'], 'data'=>Json::encode($oldNation) ]); -- 2.54.0 From 1539321521c4fdaeac853fa8840ae85e07b083b0 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 23:01:43 +0900 Subject: [PATCH 225/304] =?UTF-8?q?=EC=B2=9C=ED=86=B5=20=EC=8B=9C=20?= =?UTF-8?q?=EC=99=95=EC=A1=B0=20=EC=9D=BC=EB=9E=8C=EC=9A=A9=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EC=97=90=20server=5Fid=20=EA=B8=B0=EB=A1=9D.?= =?UTF-8?q?=20=EC=97=B0=EA=B0=90=EC=97=90=20server=5Fid=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EC=A0=84=20=EC=97=B0=EA=B0=90=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_history.php | 15 ++++++++++----- hwe/func_gamerule.php | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/hwe/a_history.php b/hwe/a_history.php index 72e127ba..f9416cc3 100644 --- a/hwe/a_history.php +++ b/hwe/a_history.php @@ -5,6 +5,7 @@ include "lib.php"; include "func.php"; $btn = Util::getReq('btn'); $yearmonth = Util::getReq('yearmonth', 'int'); +$serverID = Util::getReq('server_id', 'string', null); extractMissingPostToGlobals(); @@ -30,10 +31,14 @@ if ($con >= 2) { exit(); } -[$s_year, $s_month] = $db->queryFirstList('SELECT year, month FROM history WHERE server_id = %s ORDER BY year ASC, month ASC LIMIT 1', UniqueConst::$serverID); +if(!$serverID){ + $serverID = UniqueConst::$serverID; +} + +[$s_year, $s_month] = $db->queryFirstList('SELECT year, month FROM history WHERE server_id = %s ORDER BY year ASC, month ASC LIMIT 1', $serverID); $s = $s_year * 12 + $s_month; -[$e_year, $e_month] = $db->queryFirstList('SELECT year, month FROM history WHERE server_id = %s ORDER BY year DESC, month DESC LIMIT 1', UniqueConst::$serverID); +[$e_year, $e_month] = $db->queryFirstList('SELECT year, month FROM history WHERE server_id = %s ORDER BY year DESC, month DESC LIMIT 1', $serverID); $e = $e_year * 12 + $e_month; //FIXME: $yearmonth가 올바르지 않을 경우에 처리가 필요. @@ -94,7 +99,7 @@ if ($month <= 0) { @@ -144,7 +149,7 @@ $history = $db->queryFirstRow('SELECT log,genlog,nation,power,gen,city FROM hist

- +
@@ -244,7 +226,7 @@ for($i=0; $i < $nationcount; $i++) {
중 원 지 도

- +
diff --git a/hwe/b_genList.php b/hwe/b_genList.php index a38ba92d..3dc2f811 100644 --- a/hwe/b_genList.php +++ b/hwe/b_genList.php @@ -56,12 +56,12 @@ $sel[$type] = "selected"; <?=UniqueConst::$serverName?>: 암행부 - + - +
-- 2.54.0 From f707da36016cdfdd0569bead8adcad7f8cf55942 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 00:45:31 +0900 Subject: [PATCH 207/304] =?UTF-8?q?=EB=93=B1=EC=9A=A9=EC=9E=A5=20=EB=B0=8F?= =?UTF-8?q?=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=9D=BC=EA=B4=84=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=20TODO=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/_admin4_submit.php | 15 ++++++++++++++- hwe/func_command.php | 2 +- hwe/process_war.php | 12 ++++-------- hwe/sammo/ScoutMessage.php | 2 +- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/hwe/_admin4_submit.php b/hwe/_admin4_submit.php index 41dd5aca..b6686296 100644 --- a/hwe/_admin4_submit.php +++ b/hwe/_admin4_submit.php @@ -13,11 +13,11 @@ if($session->userGrade < 5) { $btn = Util::getReq('btn'); $genlist = Util::getReq('genlist', 'array_int'); +$msg = Util::getReq('msg','string', '메시지'); extractMissingPostToGlobals(); $db = DB::db(); -$connect=$db->get(); //NOTE: 왜 기능이 admin2와 admin4가 같이 있는가? //NOTE: 왜 블럭 시 admin4에선 금쌀을 없애지 않는가? @@ -57,7 +57,20 @@ switch($btn) { case "메세지 전달": //TODO:새 갠메 시스템으로 변경 $date = date('Y-m-d H:i:s'); + $src = MessageTarget::buildQuick($session->generalID); for($i=0; $i < count($genlist); $i++) { + $msgObj = new Message( + Message::MSGTYPE_PRIVATE, + $src, + MessageTarget::buildQuick($genlist[$i]), + $msg, + new DateTime(), + new \DateTime('9999-12-31'), + [] + ); + if($msgObj){ + $msgObj->send(true); + } } break; } diff --git a/hwe/func_command.php b/hwe/func_command.php index cd8293d9..1e4df148 100644 --- a/hwe/func_command.php +++ b/hwe/func_command.php @@ -583,7 +583,7 @@ function processCommand($no) { case 17: process_17($general); break; //소집해제 case 21: process_21($general); break; //이동 - case 22: process_22($general); break; //등용 //TODO:등용장 재 디자인 + case 22: process_22($general); break; //등용 case 25: process_25($general); break; //임관 case 26: process_26($general); break; //집합 case 28: process_28($general); break; //귀환 diff --git a/hwe/process_war.php b/hwe/process_war.php index b2e10e49..cabdb1b1 100644 --- a/hwe/process_war.php +++ b/hwe/process_war.php @@ -1833,14 +1833,10 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) { || $nation['name'] == "왜족") { //등용장 미발부 } elseif(Util::randBool(0.5)) { - //TODO:등용장 보낼것 - /*sendScoutMsg([ - 'id' => $ruler['no'], - 'nation_id' => $ruler['nation'] - ],[ - 'id' => $gen['no'], - 'nation_id' => $gen['nation'] - ],$general['turntime']);*/ + $msg = ScoutMessage::buildScoutMessage($general['no'], $gen['no'], $reason); + if($msg){ + $msg->send(true); + } } //NPC인 경우 10% 확률로 임관(엔장, 인재, 의병) diff --git a/hwe/sammo/ScoutMessage.php b/hwe/sammo/ScoutMessage.php index d84b9bd8..66f67871 100644 --- a/hwe/sammo/ScoutMessage.php +++ b/hwe/sammo/ScoutMessage.php @@ -170,7 +170,7 @@ class ScoutMessage extends Message{ return self::DECLINED; } - public static function buildScoutMessage(int $srcGeneralID, int $destGeneralID, &$reason = null, \DateTime $date = null){ + public static function buildScoutMessage(int $srcGeneralID, int $destGeneralID, &$reason = null, \DateTime $date = null):?self{ if($srcGeneralID == $destGeneralID){ if($reason !== null){ $reason = '같은 장수에게 등용장을 보낼 수 없습니다'; -- 2.54.0 From f5f06dbd3e2ab04b6523a84985b6a4881e96f39b Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 00:47:54 +0900 Subject: [PATCH 208/304] =?UTF-8?q?=EA=B5=AD=EA=B0=80=20=EB=A9=B8=EB=A7=9D?= =?UTF-8?q?=EC=8B=9C=20=EA=B5=AD=EA=B0=80=20=EC=A0=95=EB=B3=B4=20=EB=B0=B1?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/process_war.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hwe/process_war.php b/hwe/process_war.php index cabdb1b1..01bb6b1e 100644 --- a/hwe/process_war.php +++ b/hwe/process_war.php @@ -1891,6 +1891,14 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) { // 외교 삭제 $query = "delete from diplomacy where me='{$city['nation']}' or you='{$city['nation']}'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + + // 국가 백업 + $oldNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation=%i', $city['nation']); + $db->insert('ng_old_nations', [ + 'server_id'=>UniqueConst::$serverID, + 'nation'=>$city['nation'], + 'data'=>Json::encode($oldNation) + ]); // 국가 삭제 $query = "delete from nation where nation='{$city['nation']}'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); -- 2.54.0 From 2fdc8e179e25d6a9b4b898d88bc898cecf7e168e Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 02:36:20 +0900 Subject: [PATCH 209/304] =?UTF-8?q?=EB=AA=85=EC=9E=A5=20=EC=9D=BC=EB=9E=8C?= =?UTF-8?q?,=20=EB=AA=85=EC=98=88=EC=9D=98=20=EC=A0=84=EB=8B=B9=20?= =?UTF-8?q?=EC=B5=9C=EC=A2=85=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_bestGeneral.php | 4 +- hwe/a_hallOfFame.php | 107 ++++++++++++++------ hwe/css/common.css | 36 +++++++ hwe/css/{hallOfFrame.css => hallOfFame.css} | 20 +++- hwe/func.php | 49 +++++---- hwe/func_gamerule.php | 2 +- hwe/func_history.php | 28 +++++ hwe/js/hallOfFame.js | 7 ++ hwe/process_war.php | 19 ++-- hwe/sql/schema.sql | 11 +- hwe/templates/hallOfFrame.php | 11 +- 11 files changed, 222 insertions(+), 72 deletions(-) rename hwe/css/{hallOfFrame.css => hallOfFame.css} (83%) create mode 100644 hwe/js/hallOfFame.js diff --git a/hwe/a_bestGeneral.php b/hwe/a_bestGeneral.php index 76c4b99a..65813261 100644 --- a/hwe/a_bestGeneral.php +++ b/hwe/a_bestGeneral.php @@ -26,7 +26,7 @@ increaseRefresh("명장일람", 1); <?=UniqueConst::$serverName?>: 명장일람 - + @@ -160,7 +160,7 @@ foreach($generals as $general){ $general['rankName'] = $itemName; $general['value'] = $itemCode; $itemTypes[$itemIdx][5][$itemCode] = $general; - } + } } foreach($itemTypes as [$itemNameType, $itemType, $itemFunc, $itemMinCode, $itemMaxCode, $itemOwners]){ diff --git a/hwe/a_hallOfFame.php b/hwe/a_hallOfFame.php index 1ef2d2c2..d5b0bf8d 100644 --- a/hwe/a_hallOfFame.php +++ b/hwe/a_hallOfFame.php @@ -5,11 +5,28 @@ include "lib.php"; include "func.php"; $session = Session::getInstance()->setReadOnly(); +$scenarioIdx = Util::getReq('scenarioIdx', 'int', null); $db = DB::db(); -$connect=$db->get(); increaseRefresh("명예의전당", 1); + +$scenarioList= []; +foreach($db->query('SELECT distinct(scenario_name) as name, count(scenario) as cnt, scenario from ng_games order by scenario asc') as $scenarioInfo){ + $scenarioList[$scenarioInfo['scenario']] = $scenarioInfo; +} + + + +if($scenarioIdx !== null || key_exists($scenarioIdx, $scenarioList)){ + $searchScenarioName = $scenarioList[$scenarioIdx]['name']; + $searchFilter = $db->sqleval('scenario = %i', $scenarioIdx); +} +else{ + $searchScenarioName = '* 모두 *'; + $searchFilter = $db->sqleval(true); +} + ?> @@ -19,51 +36,70 @@ increaseRefresh("명예의전당", 1); <?=UniqueConst::$serverName?>: 명예의 전당 - + + + +
암 행 부
정렬순서 : +
@@ -206,7 +206,7 @@ echo " ?> -
이 름 통무지
+
diff --git a/hwe/b_myBossInfo.php b/hwe/b_myBossInfo.php index 89ecd255..c0b30db2 100644 --- a/hwe/b_myBossInfo.php +++ b/hwe/b_myBossInfo.php @@ -32,7 +32,7 @@ if($meLevel == 0) { <?=UniqueConst::$serverName?>: 인사부 - + - - + + - + @@ -93,50 +115,27 @@ $(function(){
- +
- - - - - - "; -} -?> + + + + + + - - - - + + + + - "; ?> - - - - + + + + userGrade >= 5) { echo " @@ -227,7 +226,7 @@ else if($session->userGrade == 4){ ?>
삼국지 모의전투 HiDCHe ()
{$scenario}NPC수 : {$extend}NPC상성 : {$fiction}NPC선택 : {$npcmode}
>>NPC수 : >NPC상성 : >NPC선택 :
전체 접속자 수 : 턴당 갱신횟수 : 전체 접속자 수 : 턴당 갱신횟수 :
+ 서버 가동중"; @@ -195,10 +194,10 @@ echo "
접속중인 국가:
【 국가방침 】
【 접속자 】
접속중인 국가:
【 국가방침 】
【 접속자 】
- +
- + -
@@ -237,7 +236,7 @@ else if($session->userGrade == 4){
@@ -259,31 +258,31 @@ else if($session->userGrade == 4){
+ ;color:white;width:110px;font-size:13px; value='실 행' onclick='refreshing(this, 3,form2)'>;color:white;width:110px;font-size:13px; value='갱 신' onclick='refreshing(this, 0,0)'>;color:white;width:160px;font-size:13px; value='로비로' onclick=location.replace('../')>
- +
- - + +
- +
- - + + - - + + - - + +
장수 동향개인 기록장수 동향개인 기록
중원 정세
중원 정세
+
장 수 생 성
- +
= $admin['maxgeneral']) { } ?> - +
-
임관 권유 메세지
+
@@ -98,7 +98,7 @@ if ($admin['show_img_level'] >= 1 && $member['grade'] >= 1 && $member['picture']
장수 생성
전콘 사용 여부 - + 사용 @@ -172,7 +172,7 @@ if ($admin['show_img_level'] >= 1 && $member['grade'] >= 1 && $member['picture']
- +
diff --git a/hwe/processing.php b/hwe/processing.php index c2ed4bc1..f085741f 100644 --- a/hwe/processing.php +++ b/hwe/processing.php @@ -145,14 +145,14 @@ $(function(){ - + '; echo ' - +
- + userGrade >= 5) { -- 2.54.0 From b534da34ad91bd3236378c2b0caeda17777a0c0d Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 24 Jun 2018 04:44:34 +0900 Subject: [PATCH 201/304] =?UTF-8?q?=EC=8A=A4=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EB=B0=94=20=EB=93=B1=20=EB=AA=87=EB=AA=87=20=EB=AC=B8=EC=9E=90?= =?UTF-8?q?=EC=97=B4=20=EA=B0=80=EB=8A=A5=ED=95=98=EA=B2=8C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20&=20=EB=AC=B8=EC=9E=90=EC=97=B4=20=EB=B3=B4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/c_double.php | 35 +++++++++++++++++----------------- hwe/func_process_personnel.php | 9 +++++++-- hwe/join_post.php | 3 +++ src/sammo/StringUtil.php | 13 +++++++++---- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/hwe/c_double.php b/hwe/c_double.php index 941a619e..dec5ac2f 100644 --- a/hwe/c_double.php +++ b/hwe/c_double.php @@ -44,12 +44,12 @@ $comStr = EncodeCommand($fourth, $third, $double, $command); // 건국 if($command == 46) { - $name = addslashes(SQ2DQ($name)); - $name = str_replace("|", "", $name); - $name = str_replace(" ", "", $name); - $name = str_replace(" ", "뷁", $name); - if($name == "") { $name = "무명"; } + $name = htmlspecialchars($name); + $name = StringUtil::removeSpecialCharacter($name); $name = StringUtil::subStringForWidth($name, 0, 18); + $name = WebUtil::htmlPurify($name); + $name = StringUtil::textStrip($name); + if($name == "") { $name = "무명"; } $db->update('general', [ 'makenation'=>$name @@ -72,15 +72,16 @@ if($command == 53) { $me = MYDB_fetch_array($result); if($me['level'] >= 5) { - $nationname = addslashes(SQ2DQ($nationname)); - $nationname = str_replace("|", "", $nationname); - $nationname = str_replace(" ", "", $nationname); - $nationname = str_replace(" ", "뷁", $nationname); - if($nationname == "") { $nationname = "무명"; } + $nationname = htmlspecialchars($nationname); + $nationname = StringUtil::removeSpecialCharacter($nationname); $nationname = StringUtil::subStringForWidth($nationname, 0, 18); - - $query = "update general set makenation='{$nationname}' where level>=5 and nation='{$me['nation']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + $nationname = WebUtil::htmlPurify($nationname); + $nationname = StringUtil::textStrip($nationname); + if($nationname == "") { $nationname = "무명"; } + + $db->update('general', [ + 'makenation'=>$nationname + ], 'level>=5 and nation=%i', $me['nation']); $count = count($turn); $str = "type=type"; @@ -101,11 +102,11 @@ if($command == 61) { $me = MYDB_fetch_array($result); if($me['level'] >= 5) { - $note = addslashes(SQ2DQ($note)); - $note = str_replace("|", "", $note); - $note = str_replace(" ", "", $note); - $note = str_replace(" ", "뷁", $note); + $note = htmlspecialchars($note); + $note = StringUtil::removeSpecialCharacter($note); $note = StringUtil::subStringForWidth($note, 0, 90); + $note = WebUtil::htmlPurify($note); + $note = StringUtil::textStrip($note); $query = "update diplomacy set reserved='{$note}' where me='{$me['nation']}' and you='$double'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); diff --git a/hwe/func_process_personnel.php b/hwe/func_process_personnel.php index 4d5ecf24..aa645a92 100644 --- a/hwe/func_process_personnel.php +++ b/hwe/func_process_personnel.php @@ -663,8 +663,13 @@ function process_46(&$general) { } elseif($city['level'] != 5 && $city['level'] != 6) { $log[] = "●{$admin['month']}월:중, 소 도시에서만 가능합니다. 건국 실패. <1>$date"; } else { - $query = "update nation set name='{$general['makenation']}',color='$color',level=1,type='$type',capital='{$general['city']}' where nation='{$general['nation']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + $db->update('nation', [ + 'name'=>$general['makenation'], + 'color'=>$color, + 'level'=>1, + 'type'=>$type, + 'capital'=>$general['city'], + ], 'nation=%i', $general['nation']); refreshNationStaticInfo(); $nation = getNationStaticInfo($general['nation']); diff --git a/hwe/join_post.php b/hwe/join_post.php index ef265a27..3cabf165 100644 --- a/hwe/join_post.php +++ b/hwe/join_post.php @@ -43,7 +43,10 @@ $userID = Session::getUserID(); //NOTE: 이 페이지에서는 세션에 데이터를 등록하지 않음. 로그인은 이후에. $name = Util::getReq('name'); +$name = htmlspecialchars($name); $name = StringUtil::removeSpecialCharacter($name); +$name = WebUtil::htmlPurify($name); +$name = StringUtil::textStrip($name); $pic = (int)Util::getReq('pic', 'bool', 0); $character = Util::getReq('character', 'int', 0); diff --git a/src/sammo/StringUtil.php b/src/sammo/StringUtil.php index daf32e86..31f2f3a8 100644 --- a/src/sammo/StringUtil.php +++ b/src/sammo/StringUtil.php @@ -150,13 +150,18 @@ class StringUtil return $str; } + public static function textStrip(?string $str):string{ + if(!$str){ + return ''; + } + return preg_replace('/^[\pZ\pC]+|[\pZ\pC]+$/u','',$str); + } + public static function removeSpecialCharacter($str) { return str_replace([ - ' ', '"', '\'', 'ⓝ', 'ⓜ', 'ⓖ', '\\', '/', '`', - '-', '=', '[', ']', ';', ',', '.', '~', '!', '@', - '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', - '|', '{', '}', ':', '', '<', '>', '?', ' ' + '"', '\'', 'ⓝ', 'ⓜ', 'ⓖ', '\\', '/', '`', '#', + '-', '#', '|' ], '', $str); } -- 2.54.0 From 6147e207a1883679cfb14c65de5a9092befc09d0 Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 25 Jun 2018 00:46:07 +0900 Subject: [PATCH 202/304] =?UTF-8?q?=EA=B1=B4=EA=B5=AD=20=EB=93=B1=20?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=EC=97=B4=20=EC=9E=85=EB=A0=A5=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/c_double.php | 18 +++--------------- src/sammo/StringUtil.php | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/hwe/c_double.php b/hwe/c_double.php index dec5ac2f..a6e4c385 100644 --- a/hwe/c_double.php +++ b/hwe/c_double.php @@ -44,11 +44,7 @@ $comStr = EncodeCommand($fourth, $third, $double, $command); // 건국 if($command == 46) { - $name = htmlspecialchars($name); - $name = StringUtil::removeSpecialCharacter($name); - $name = StringUtil::subStringForWidth($name, 0, 18); - $name = WebUtil::htmlPurify($name); - $name = StringUtil::textStrip($name); + $name = StringUtil::neutralize($name); if($name == "") { $name = "무명"; } $db->update('general', [ @@ -72,11 +68,7 @@ if($command == 53) { $me = MYDB_fetch_array($result); if($me['level'] >= 5) { - $nationname = htmlspecialchars($nationname); - $nationname = StringUtil::removeSpecialCharacter($nationname); - $nationname = StringUtil::subStringForWidth($nationname, 0, 18); - $nationname = WebUtil::htmlPurify($nationname); - $nationname = StringUtil::textStrip($nationname); + $nationname = StringUtil::neutralize($nationname, 18); if($nationname == "") { $nationname = "무명"; } $db->update('general', [ @@ -102,11 +94,7 @@ if($command == 61) { $me = MYDB_fetch_array($result); if($me['level'] >= 5) { - $note = htmlspecialchars($note); - $note = StringUtil::removeSpecialCharacter($note); - $note = StringUtil::subStringForWidth($note, 0, 90); - $note = WebUtil::htmlPurify($note); - $note = StringUtil::textStrip($note); + $note = StringUtil::neutralize($note, 90); $query = "update diplomacy set reserved='{$note}' where me='{$me['nation']}' and you='$double'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); diff --git a/src/sammo/StringUtil.php b/src/sammo/StringUtil.php index 31f2f3a8..dfb6c363 100644 --- a/src/sammo/StringUtil.php +++ b/src/sammo/StringUtil.php @@ -143,13 +143,30 @@ class StringUtil return static::padString($str, $maxsize, $ch, 0); } - public static function escapeTag($str) + public static function escapeTag(?string $str):string { + if(!$str){ + return ''; + } $str = htmlspecialchars($str); $str = str_replace(["\r\n", "\r", "\n"], '
', $str); return $str; } + public static function neutralize(?string $str, ?int $maxLen = null){ + if(!$str){ + return ''; + } + if($maxLen && $maxLen > 0){ + $str = StringUtil::subStringForWidth($str, 0, $maxLen); + } + $str = htmlspecialchars($str); + $str = StringUtil::removeSpecialCharacter($str); + $str = WebUtil::htmlPurify($str); + $str = StringUtil::textStrip($str); + return $str; + } + public static function textStrip(?string $str):string{ if(!$str){ return ''; -- 2.54.0 From 64ff1439b9d9357736b882c8b0b836784bb6fc27 Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 25 Jun 2018 01:45:23 +0900 Subject: [PATCH 203/304] =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EC=98=A4=ED=94=88?= =?UTF-8?q?=20=EC=8B=9C=20logs=EC=97=90=20=EC=84=9C=EB=B8=8C=20=EB=94=94?= =?UTF-8?q?=EB=A0=89=ED=86=A0=EB=A6=AC=EB=A5=BC=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 2 +- hwe/a_traffic.php | 2 +- hwe/func.php | 6 +++--- hwe/func_history.php | 36 ++++++++++++++++++------------------ hwe/lib.php | 2 +- hwe/sammo/ResetHelper.php | 8 +++++--- hwe/sql/schema.sql | 31 +++++++++++++++++++++++++++++++ 7 files changed, 60 insertions(+), 27 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4447baa4..87d45fb0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,7 @@ "**/.hg": true, "**/CVS": true, "**/.DS_Store": true, - "**/logs/*.txt": true, + "**/logs/**/*.txt": true, "**/session/sess*":true, "/che":true, "/kwe":true, diff --git a/hwe/a_traffic.php b/hwe/a_traffic.php index fbfc4ff9..b3e021c7 100644 --- a/hwe/a_traffic.php +++ b/hwe/a_traffic.php @@ -12,7 +12,7 @@ increaseRefresh("갱신정보", 2); $admin = $gameStor->getValues(['year','month','refresh','maxrefresh','maxonline']); -$log = getRawFileLogRecent(__dir__.'/logs/_traffic.txt', 11, 100); +$log = getRawFileLogRecent(__dir__.'/logs/'.UniqueConst::$serverID.'/_traffic.txt', 11, 100); $date = []; $year = []; diff --git a/hwe/func.php b/hwe/func.php index 53bda63e..53e44080 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -1266,7 +1266,7 @@ function increaseRefresh($type="", $cnt=1) { $date2 = substr($date, 0, 10); $online = getOnlineNum(); file_put_contents( - __dir__."/logs/_{$date2}_refresh.txt", + __dir__."/logs/".UniqueConst::$serverID."/_{$date2}_refresh.txt", sprintf( "%s, %s, %s, %s, %s, %d\n", $date, @@ -1303,7 +1303,7 @@ function increaseRefresh($type="", $cnt=1) { } if($str != "") { file_put_contents( - __dir__."/logs/_{$date2}_ipcheck.txt", + __dir__."/logs/".UniqueConst::$serverID."/_{$date2}_ipcheck.txt", sprintf( "%s, %s, %s%s\n", $session->userName, @@ -1338,7 +1338,7 @@ function updateTraffic() { $date = date('Y-m-d H:i:s'); //일시|년|월|총갱신|접속자|최다갱신자 - file_put_contents(__dir__."/logs/_traffic.txt", + file_put_contents(__dir__."/logs/".UniqueConst::$serverID."/_traffic.txt", Json::encode([ $date, $admin['year'], diff --git a/hwe/func_history.php b/hwe/func_history.php index 43c4a217..813916ec 100644 --- a/hwe/func_history.php +++ b/hwe/func_history.php @@ -5,7 +5,7 @@ namespace sammo; function delStepLog() { $date = date('Y_m_d'); - @unlink(__dir__."/logs/_{$date}_steplog.txt"); + @unlink(__dir__."/logs/".UniqueConst::$serverID."/_{$date}_steplog.txt"); } function pushRawFileLog($path, $lines){ @@ -50,81 +50,81 @@ function eraseTnmtFightLogAll(){ } function eraseTnmtFightLog(int $group){ - $filepath = __dir__."/logs/fight{$group}.txt"; + $filepath = __dir__."/logs/".UniqueConst::$serverID."/fight{$group}.txt"; if(file_exists($filepath)){ @unlink($filepath); } } function pushTnmtFightLog(int $group, $log) { - pushRawFileLog(__dir__."/logs/fight{$group}.txt", $log); + pushRawFileLog(__dir__."/logs/".UniqueConst::$serverID."/fight{$group}.txt", $log); } function getTnmtFightLogAll(int $group) { - return join('
',getFormattedFileLogAll(__dir__."/logs/fight{$group}.txt")); + return join('
',getFormattedFileLogAll(__dir__."/logs/".UniqueConst::$serverID."/fight{$group}.txt")); } function pushSabotageLog($log) { - pushRawFileLog(__dir__."/logs/_sabotagelog.txt", $log); + pushRawFileLog(__dir__."/logs/".UniqueConst::$serverID."/_sabotagelog.txt", $log); } function getSabotageLogRecent($count) { - return join('
', getFormattedFileLogRecent(__dir__."/logs/_sabotagelog.txt", $count, 150)); + return join('
', getFormattedFileLogRecent(__dir__."/logs/".UniqueConst::$serverID."/_sabotagelog.txt", $count, 150)); } function pushProcessLog($log) { $date = date('Y_m_d'); - pushRawFileLog(__dir__."/logs/_{$date}_processlog.txt", $log); + pushRawFileLog(__dir__."/logs/".UniqueConst::$serverID."/_{$date}_processlog.txt", $log); } function pushStepLog($log) { $date = date('Y_m_d'); - pushRawFileLog(__dir__."/logs/_{$date}_steplog.txt", $log); + pushRawFileLog(__dir__."/logs/".UniqueConst::$serverID."/_{$date}_steplog.txt", $log); } function pushLockLog($log) { $date = date('Y_m_d'); - pushRawFileLog(__dir__."/logs/_{$date}_locklog.txt", $log); + pushRawFileLog(__dir__."/logs/".UniqueConst::$serverID."/_{$date}_locklog.txt", $log); } function pushAdminLog($log) { - pushRawFileLog(__dir__."/logs/_adminlog.txt", $log); + pushRawFileLog(__dir__."/logs/".UniqueConst::$serverID."/_adminlog.txt", $log); } function pushAuctionLog($log) { - pushRawFileLog(__dir__."/logs/_auctionlog.txt", $log); + pushRawFileLog(__dir__."/logs/".UniqueConst::$serverID."/_auctionlog.txt", $log); } function getAuctionLogRecent(int $count) { - return join('
', array_reverse(getFormattedFileLogRecent(__dir__."/logs/_auctionlog.txt", $count, 300))); + return join('
', array_reverse(getFormattedFileLogRecent(__dir__."/logs/".UniqueConst::$serverID."/_auctionlog.txt", $count, 300))); } function pushGenLog($general, $log) { $no = Util::toInt($general['no']); - pushRawFileLog(__dir__."/logs/gen{$no}.txt", $log); + pushRawFileLog(__dir__."/logs/".UniqueConst::$serverID."/gen{$no}.txt", $log); } function getGenLogRecent(int $no, int $count) { - return join('
', array_reverse(getFormattedFileLogRecent(__dir__."/logs/gen{$no}.txt", $count, 300))); + return join('
', array_reverse(getFormattedFileLogRecent(__dir__."/logs/".UniqueConst::$serverID."/gen{$no}.txt", $count, 300))); } function pushBatRes($general, $log) { $no = Util::toInt($general['no']); - pushRawFileLog(__dir__."/logs/batres{$no}.txt", $log); + pushRawFileLog(__dir__."/logs/".UniqueConst::$serverID."/batres{$no}.txt", $log); } function getBatResRecent(int $no, int $count) { - return join('
', array_reverse(getFormattedFileLogRecent(__dir__."/logs/batres{$no}.txt", $count, 300))); + return join('
', array_reverse(getFormattedFileLogRecent(__dir__."/logs/".UniqueConst::$serverID."/batres{$no}.txt", $count, 300))); } function pushBatLog($general, $log) { $no = Util::toInt($general['no']); - pushRawFileLog(__dir__."/logs/batlog{$no}.txt", $log); + pushRawFileLog(__dir__."/logs/".UniqueConst::$serverID."/batlog{$no}.txt", $log); } function getBatLogRecent(int $no, int $count) { - return join('
', array_reverse(getFormattedFileLogRecent(__dir__."/logs/batlog{$no}.txt", $count, 300))); + return join('
', array_reverse(getFormattedFileLogRecent(__dir__."/logs/".UniqueConst::$serverID."/batlog{$no}.txt", $count, 300))); } //DB-based diff --git a/hwe/lib.php b/hwe/lib.php index d5e0d5ea..3fa6a8de 100644 --- a/hwe/lib.php +++ b/hwe/lib.php @@ -35,7 +35,7 @@ function Error($message='', $url="") if (!$url) { $url = $_SERVER['REQUEST_URI']; } - file_put_contents(__dir__."/logs/_db_bug.txt", "{\"url\":\"$url\",\"msg\":\"$message\"}\n", FILE_APPEND); + file_put_contents(__dir__."/logs/".UniqueConst::$serverID."/_db_bug.txt", "{\"url\":\"$url\",\"msg\":\"$message\"}\n", FILE_APPEND); $templates = new \League\Plates\Engine(__dir__.'/templates'); diff --git a/hwe/sammo/ResetHelper.php b/hwe/sammo/ResetHelper.php index ccf7c07d..03e5fdb7 100644 --- a/hwe/sammo/ResetHelper.php +++ b/hwe/sammo/ResetHelper.php @@ -55,13 +55,15 @@ class ResetHelper{ $db = DB::db(); $mysqli_obj = $db->get(); - FileUtil::delInDir($servRoot."/logs"); - FileUtil::delInDir($servRoot."/data"); + $serverID = DB::prefix().'_'.Util::randomStr(12); + + mkdir($servRoot.'/logs/'.$serverID, 0644); + mkdir($servRoot.'/data/'.$serverID, 0644); $result = Util::generateFileUsingSimpleTemplate( $servRoot.'/d_setting/UniqueConst.orig.php', $servRoot.'/d_setting/UniqueConst.php',[ - 'serverID'=>DB::prefix().'_'.Util::randomStr(8), + 'serverID'=>$serverID, 'serverName'=>AppConf::getList()[$prefix]->getKorName(), ], true ); diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 4c6b34e9..2946d5e7 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -376,9 +376,40 @@ create table if not exists hall ( UNIQUE INDEX `type` (`type`, `rank`) ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4; +CREATE TABLE if not exists `ng_hall` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `server_id` CHAR(20) NOT NULL, + `scenario` INT(11) NOT NULL, + `general_no` INT(11) NOT NULL, + `type` INT(11) NOT NULL, + `value` INT(11) NOT NULL, + `owner` INT(11) NULL DEFAULT NULL, + `aux` TEXT NOT NULL DEFAULT '{}', + PRIMARY KEY (`id`), + UNIQUE INDEX `server_general` (`server_id`, `type`, `general_no`), + UNIQUE INDEX `owner` (`owner`, `server_id`), + INDEX `server_show` (`server_id`, `type`, `value`), + INDEX `scenario` (`scenario`, `type`, `value`) +) +ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + + ########################################################################### ## 왕조 테이블 ########################################################################### + +CREATE TABLE if not exists `ng_games` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `server_id` CHAR(20) NOT NULL, + `date` DATETIME NOT NULL, + `united` INT(1) NOT NULL DEFAULT '0', + `aux` TEXT NOT NULL COMMENT 'json', + PRIMARY KEY (`id`), + UNIQUE INDEX `server_id` (`server_id`), + INDEX `date` (`date`) +) +ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + create table if not exists emperior ( no int(6) not null auto_increment, phase char(255) default '', -- 2.54.0 From f7b7acc1d5efcf238c085d6d2b3983ac95b3b468 Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 25 Jun 2018 01:53:16 +0900 Subject: [PATCH 204/304] =?UTF-8?q?=EC=99=95=EC=A1=B0=20=EC=9D=BC=EB=9E=8C?= =?UTF-8?q?=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + hwe/sql/schema.sql | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 61a7af99..796e3b87 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ d_log/*.txt d_log/*.zip sess_* */logs/*.txt +*/logs/*/*.txt err.txt che/.htaccess kwe/.htaccess diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 2946d5e7..1bf3bffb 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -398,11 +398,11 @@ ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ## 왕조 테이블 ########################################################################### -CREATE TABLE if not exists `ng_games` ( +CREATE TABLE `ng_games` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `server_id` CHAR(20) NOT NULL, `date` DATETIME NOT NULL, - `united` INT(1) NOT NULL DEFAULT '0', + `winner_nation` INT(11) NULL DEFAULT NULL, `aux` TEXT NOT NULL COMMENT 'json', PRIMARY KEY (`id`), UNIQUE INDEX `server_id` (`server_id`), @@ -410,6 +410,16 @@ CREATE TABLE if not exists `ng_games` ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +CREATE TABLE `ng_old_nations` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `server_id` CHAR(20) NOT NULL DEFAULT '0', + `nation` INT(11) NOT NULL DEFAULT '0', + `data` LONGTEXT NOT NULL DEFAULT '0' COMMENT 'json', + PRIMARY KEY (`id`), + INDEX `server_id` (`server_id`, `nation`) +) +ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + create table if not exists emperior ( no int(6) not null auto_increment, phase char(255) default '', -- 2.54.0 From 356eb8a8b52a935c83b9635c07c04297d6913b68 Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 25 Jun 2018 16:08:19 +0900 Subject: [PATCH 205/304] =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EB=B3=84=EB=AA=85?= =?UTF-8?q?=EC=97=90=20=EC=97=B0=EC=9B=94=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/ResetHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/sammo/ResetHelper.php b/hwe/sammo/ResetHelper.php index 03e5fdb7..90a8f2d8 100644 --- a/hwe/sammo/ResetHelper.php +++ b/hwe/sammo/ResetHelper.php @@ -55,7 +55,7 @@ class ResetHelper{ $db = DB::db(); $mysqli_obj = $db->get(); - $serverID = DB::prefix().'_'.Util::randomStr(12); + $serverID = DB::prefix().'_'.date("ymd").'_'.Util::randomStr(4); mkdir($servRoot.'/logs/'.$serverID, 0644); mkdir($servRoot.'/data/'.$serverID, 0644); -- 2.54.0 From e74851b289d024b3a529b393e0621040d71df885 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 00:32:43 +0900 Subject: [PATCH 206/304] =?UTF-8?q?=EB=AA=85=EC=9E=A5=20=EC=9D=BC=EB=9E=8C?= =?UTF-8?q?,=20=EB=AA=85=EC=98=88=EC=9D=98=20=EC=A0=84=EB=8B=B9=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=AC=EA=B5=AC=ED=98=84.=20=EC=97=B0?= =?UTF-8?q?=EA=B0=90=20=EB=93=B1=EC=97=90=20=EC=9D=B4=EC=A0=84=20=EA=B5=AD?= =?UTF-8?q?=EA=B0=80=20=EA=B8=B0=EB=A1=9D=20=EB=82=A8=EA=B8=B0=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_bestGeneral.php | 418 +++++++++++----------------------- hwe/a_hallOfFame.php | 95 +++----- hwe/a_history.php | 31 +-- hwe/css/hallOfFrame.css | 76 +++++++ hwe/func.php | 87 +++---- hwe/func_history.php | 1 + hwe/func_map.php | 14 +- hwe/func_message.php | 12 - hwe/j_map_history.php | 7 +- hwe/sammo/ResetHelper.php | 13 +- hwe/sql/reset.sql | 3 - hwe/sql/schema.sql | 77 ++----- hwe/templates/hallOfFrame.php | 21 ++ i_entrance/entrance.php | 12 +- 14 files changed, 378 insertions(+), 489 deletions(-) create mode 100644 hwe/css/hallOfFrame.css create mode 100644 hwe/templates/hallOfFrame.php diff --git a/hwe/a_bestGeneral.php b/hwe/a_bestGeneral.php index 19459516..76c4b99a 100644 --- a/hwe/a_bestGeneral.php +++ b/hwe/a_bestGeneral.php @@ -14,19 +14,9 @@ $userID = Session::getUserID(); $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); -$connect=$db->get(); -increaseRefresh("명장일람", 2); +increaseRefresh("명장일람", 1); -$query = "select con,turntime from general where owner='{$userID}'"; -$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect), ""); -$me = MYDB_fetch_array($result); - -$con = checkLimit($me['con']); -if ($con >= 2) { - printLimitMsg($me['turntime']); - exit(); -} ?> @@ -36,14 +26,14 @@ if ($con >= 2) { <?=UniqueConst::$serverName?>: 명장일람 - + -
'.$name.'
'; @@ -279,7 +279,7 @@ function calc(cost, formnum) { form.cost.value = Math.round(crew * cost); } - +
현재 기술력 : @@ -487,7 +487,7 @@ function calc(cost, formnum) { form.cost.value = Math.round(crew * cost * 2); } - +
-- 2.54.0 From b47cbcb925e29b9aa64e7acde084c73fb008172f Mon Sep 17 00:00:00 2001 From: hide_d Date: Thu, 21 Jun 2018 20:16:01 +0900 Subject: [PATCH 177/304] =?UTF-8?q?=ED=99=98=EC=83=81=ED=96=A5=20=EC=8B=9C?= =?UTF-8?q?=EB=82=98=EB=A6=AC=EC=98=A4=20=EC=A0=84=EC=BD=98=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=20=EC=9E=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/scenario/scenario_320.json | 487 +++++++++++++++++---------------- hwe/scenario/scenario_321.json | 487 +++++++++++++++++---------------- 2 files changed, 488 insertions(+), 486 deletions(-) diff --git a/hwe/scenario/scenario_320.json b/hwe/scenario/scenario_320.json index 1c139cf6..1b11e973 100644 --- a/hwe/scenario/scenario_320.json +++ b/hwe/scenario/scenario_320.json @@ -8,254 +8,255 @@ "max":110, "npcMax":100 }, + "iconPath":"환상향", "nation":[ ], "diplomacy":[], "general":[ - [0, "히카리코", null, 0, null, 56, 66, 21, 0, 160, 300, null, null], - [0, "히메미코", null, 0, null, 68, 78, 65, 0, 160, 300, null, null], - [0, "히나", "환상향/히나.jpg", 0, null, 66, 63, 83, 0, 160, 300, null, null, "온갖 재액이 닥쳐올 거야. 인간을 지키기 위해서라도 절대 보내주지 않겠어!"], - [0, "후토", "환상향/후토.png", 0, null, 84, 69, 93, 0, 160, 300, null, null, "불이라도 지를까."], - [0, "홍미령", "환상향/홍미령.jpg", 0, null, 85, 79, 63, 0, 160, 300, null, null, "젠장, 배수진이다!"], - [0, "호쥬누에", "환상향/호쥬누에.jpg", 0, null, 85, 95, 72, 0, 160, 300, null, null, "자아, 나의 정체를 알아버린 당신을 죽여버리고, 정체불명으로 돌아가야겠어."], - [0, "호라이인형", null, 0, null, 66, 71, 73, 0, 160, 300, null, null], - [0, "헤카티아", "환상향/헤카티아.jpg", 0, null, 85, 84, 94, 0, 160, 300, null, null, "꿈의 세계에선 무방비하니까 간단한 일이었지."], - [0, "하타테", "환상향/하타테.jpg", 0, null, 30, 88, 70, 0, 160, 300, null, null, "둘이서 더블스포일러가 되어서, 최강을 노리는 거야!"], - [0, "하츠나", null, 0, null, 63, 61, 65, 0, 160, 300, null, null], - [0, "하니에", null, 0, null, 57, 66, 63, 0, 160, 300, null, null], - [0, "플랑드르", "환상향/플랑드르.jpg", 0, null, 76, 101, 71, 0, 160, 300, null, null, "쿠욱하고 콰광~이네"], - [0, "프랑스인형", null, 0, null, 68, 68, 75, 0, 160, 300, null, null], - [0, "파츄리", "환상향/파츄리.jpg", 0, null, 81, 52, 103, 0, 160, 300, null, null, "무큐-"], - [0, "파스칼", null, 0, null, 73, 70, 60, 0, 160, 300, null, null], - [0, "파브리티스", null, 0, null, 61, 61, 62, 0, 160, 300, null, null], - [0, "파르시", "환상향/파르시.jpg", 0, null, 67, 49, 89, 0, 160, 300, null, null, "너한테 원한은 없다만, 내가 널 공격하는 이유는 얼마든지 만들 수 있어."], - [0, "토키코", null, 0, null, 60, 59, 88, 0, 160, 300, null, null], - [0, "토쿠비", null, 0, null, 77, 77, 72, 0, 160, 300, null, null], - [0, "토지코", "환상향/토지코.png", 0, null, 76, 62, 86, 0, 160, 300, null, null, "아이고 이런. 또 하나요?"], - [0, "토요히메", "환상향/토요히메.jpg", 0, null, 96, 69, 95, 0, 160, 300, null, null], - [0, "토비코", null, 0, null, 72, 58, 56, 0, 160, 300, null, null], - [0, "텐시", "환상향/텐시.jpg", 0, null, 81, 89, 71, 0, 160, 300, null, null, "약해! 약해 빠졌어! 그따위로 하늘에 오를 수 있을 거라 꿈도 꾸지 마!"], - [0, "텐무", null, 0, null, 68, 73, 56, 0, 160, 300, null, null], - [0, "텐구5", null, 0, null, 51, 51, 50, 0, 160, 300, null, null], - [0, "텐구4", null, 0, null, 51, 51, 50, 0, 160, 300, null, null], - [0, "텐구3", null, 0, null, 51, 51, 50, 0, 160, 300, null, null], - [0, "텐구2", null, 0, null, 51, 51, 50, 0, 160, 300, null, null], - [0, "텐구1", null, 0, null, 51, 51, 50, 0, 160, 300, null, null], - [0, "테이레이다", "환상향/테이레이다.png", 0, null, 78, 75, 53, 0, 160, 300, null, null, "갑작스럽겠지만, 테스트를 시작하겠어!]"], - [0, "테위", "환상향/테위.jpg", 0, null, 82, 32, 79, 0, 160, 300, null, null, "대숲은 사람을 헤매게 하는 대나무의 미로. 헤매는 것은 산사람다운 행위인 거야."], - [0, "키쿠리", "환상향/키쿠리.jpg", 0, null, 56, 72, 76, 0, 160, 300, null, null], - [0, "키요히메", null, 0, null, 66, 76, 76, 0, 160, 300, null, null], - [0, "키스메", "환상향/키스메.jpg", 0, null, 73, 58, 59, 0, 160, 300, null, null, "너희가 떨어뜨린 시체는 이거냐?"], - [0, "키나코", null, 0, null, 63, 68, 67, 0, 160, 300, null, null], - [0, "클라운피스", "환상향/클라운피스.jpg", 0, null, 89, 80, 77, 0, 160, 300, null, null, "잇츠, 루나틱 타임! 광기의 세계로 어서오세요!"], - [0, "쿠스코", null, 0, null, 70, 57, 53, 0, 160, 300, null, null], - [0, "쿠루미", "환상향/쿠루미.jpg", 0, null, 70, 65, 68, 0, 160, 300, null, null, "나는 쿠루미. 이 호수의 문지기야."], - [0, "쿄코", "환상향/쿄코.jpg", 0, null, 37, 57, 80, 0, 160, 300, null, null, "목소리가 작다!"], - [0, "콘가라", "환상향/콘가라.jpg", 0, null, 91, 96, 60, 0, 160, 300, null, null], - [0, "코토히메", "환상향/코토히메.jpg", 0, null, 54, 36, 90, 0, 160, 300, null, null, "충격의 새로운 사실! 고대인은 전기를 쓰고 있었다!"], - [0, "코코로", "환상향/코코로.jpg", 0, null, 80, 91, 60, 0, 160, 300, null, null, "나와 최강의 칭호를 걸고 싸우자!"], - [0, "코카이", null, 0, null, 67, 78, 61, 0, 160, 300, null, null], - [0, "코이시", "환상향/코이시.jpg", 0, null, 88, 71, 93, 0, 160, 300, null, null, "네 시체는 지령전 입구에 장식해줄게!"], - [0, "코스즈", "환상향/코스즈.jpg", 0, null, 61, 63, 70, 0, 160, 300, null, null, "요괴가 하는 말을 믿으시는 거예요?!"], - [0, "코마치", "환상향/코마치.jpg", 0, null, 80, 97, 75, 0, 160, 300, null, null, "더는 어찌할 도리가 없어. 나는 그런 인간은 버릴 거야."], - [0, "코노하", null, 0, null, 62, 58, 67, 0, 160, 300, null, null], - [0, "코가사", "환상향/코가사.jpg", 0, null, 79, 76, 56, 0, 160, 300, null, null, "됐어. 어차피 요괴는 이렇게 쓸쓸하게 사라져나가는 거니까."], - [0, "케이네", "환상향/케이네.jpg", 0, null, 77, 71, 104, 0, 160, 300, null, null, "여기엔 원래부터 아무것도 없었어, 인간도 인간의 마을도 말이야."], - [0, "캇파5", null, 0, null, 40, 40, 65, 0, 160, 300, null, null], - [0, "캇파4", null, 0, null, 40, 40, 64, 0, 160, 300, null, null], - [0, "캇파3", null, 0, null, 40, 40, 63, 0, 160, 300, null, null], - [0, "캇파2", null, 0, null, 40, 40, 62, 0, 160, 300, null, null], - [0, "캇파1", null, 0, null, 40, 40, 61, 0, 160, 300, null, null], - [0, "카스미", null, 0, null, 67, 63, 82, 0, 160, 300, null, null], - [0, "카센", "환상향/카센.jpg", 0, null, 72, 103, 71, 0, 160, 300, null, null, "이런 곳에서 전력을 다할 이유가없지."], - [0, "카나코", "환상향/카나코.jpg", 0, null, 84, 97, 77, 0, 160, 300, null, null, "나를 부른 이는 누구인가?"], - [0, "카나", "환상향/카나.jpg", 0, null, 64, 72, 57, 0, 160, 300, null, null, "아, 아니, 그러니까, 우주인?]"], - [0, "카구야", "환상향/카구야.jpg", 0, null, 98, 71, 82, 0, 160, 300, null, null, "내 영원을 다루는 술법으로 모두 깨뜨려 보이겠어."], - [0, "카게로", "환상향/카게로.png", 0, null, 78, 88, 68, 0, 160, 300, null, null, "무서워~ 인간 무서워~"], - [0, "치유리", "환상향/치유리.jpg", 0, null, 45, 77, 78, 0, 160, 300, null, null, "싸워서 강한 쪽이 이 세계의 치유리라구."], - [0, "치르노", "환상향/치르노.jpg", 0, null, 79, 89, 49, 0, 160, 300, null, null, "눈앞에 강적이 있잖아?"], - [0, "츠바메", null, 0, null, 71, 80, 58, 0, 160, 300, null, null], - [0, "첸", "환상향/첸.jpg", 0, null, 48, 70, 70, 0, 160, 300, null, null, "그건 그렇고, 마요이가에 어서와"], - [0, "지벤", null, 0, null, 85, 67, 66, 0, 160, 300, null, null], - [0, "지귀", null, 0, null, 79, 67, 69, 0, 160, 300, null, null], - [0, "조온", null, 0, null, 83, 74, 96, 0, 160, 300, null, null, "우리가 왜 최흉최악의 자매인지 그 몸으로 이해해라!"], - [0, "자넷", null, 0, null, 60, 59, 62, 0, 160, 300, null, null], - [0, "이포니아", null, 0, null, 61, 62, 61, 0, 160, 300, null, null], - [0, "이쿠", "환상향/이쿠.jpg", 0, null, 72, 72, 94, 0, 160, 300, null, null, "조금 벌을 받으실 필요가 있을지도 모르겠군요."], - [0, "이치요우", null, 0, null, 70, 73, 58, 0, 160, 300, null, null], - [0, "이치린", "환상향/이치린.jpg", 0, null, 86, 55, 88, 0, 160, 300, null, null, "평화롭게 살고있으니까 괜찮잖아. 너, 요괴를 죽여도 아무렇지 않아?"], - [0, "이슈아", null, 0, null, 67, 76, 78, 0, 160, 300, null, null], - [0, "이나코", null, 0, null, 56, 35, 63, 0, 160, 300, null, null], - [0, "유키", "환상향/유키.jpg", 0, null, 72, 77, 61, 0, 160, 300, null, null, "우리 둘을 쓰러뜨릴 수 있을 거라고 생각해?"], - [0, "유카리", "환상향/유카리.jpg", 0, null, 87, 76, 96, 0, 160, 300, null, null, "아름답고 잔혹하게 이 대지에서 사라져라!"], - [0, "유카", "환상향/유카.jpg", 0, null, 94, 88, 70, 0, 160, 300, null, null, "아 그래, 마침 60년 전이었네. 아아...... 그래, 벌써 그런 시기구나...... 60년인가. 회귀할 때구나."], - [0, "유유코", "환상향/유유코.jpg", 0, null, 97, 80, 81, 0, 160, 300, null, null, "새는 잔뼈가 많아서 싫어."], - [0, "유메코", "환상향/유메코.jpg", 0, null, 72, 103, 73, 0, 160, 300, null, null, "우리들을 모욕한 죄는 죽음으로 밖에 갚을 수 없다!!"], - [0, "유메미", "환상향/유메미.jpg", 0, null, 93, 72, 91, 0, 160, 300, null, null, "기합 넣어! 죽어 버렸다간 데리고 돌아갈 수 없으니까."], - [0, "유기", "환상향/유기.jpg", 0, null, 71, 104, 72, 0, 160, 300, null, null, "네가 강하기만 하다면 나야 아무 군소리도 안해. 어떤 목적으로 여기에 왔건 간에."], - [0, "유겐마간", "환상향/유겐마간.jpg", 0, null, 80, 80, 50, 0, 160, 300, null, null], - [0, "우카", null, 0, null, 60, 73, 82, 0, 160, 300, null, null], - [0, "우츠호", "환상향/우츠호.jpg", 0, null, 73, 105, 68, 0, 160, 300, null, null, "이물질 발견! 핵융합로의 이물질 혼입은 일단 반응을 정지하고, 즉각 이물질을 제거하라."], - [0, "요정5", null, 0, null, 55, 60, 35, 0, 160, 300, null, null], - [0, "요정4", null, 0, null, 55, 59, 34, 0, 160, 300, null, null], - [0, "요정3", null, 0, null, 55, 58, 33, 0, 160, 300, null, null], - [0, "요정2", null, 0, null, 55, 57, 32, 0, 160, 300, null, null], - [0, "요정1", null, 0, null, 55, 56, 31, 0, 160, 300, null, null], - [0, "요우무", "환상향/요우무.jpg", 0, null, 81, 89, 71, 0, 160, 300, null, null, "...요괴가 담금질한 이 누관검에, 벨 수 없는 것 따위, 별로 없어!"], - [0, "요시카", "환상향/요시카.jpg", 0, null, 84, 78, 61, 0, 160, 300, null, null, "오~지~마~!"], - [0, "요리히메", "환상향/요리히메.jpg", 0, null, 90, 98, 74, 0, 160, 300, null, null], - [0, "와카챠나두", null, 0, null, 62, 61, 68, 0, 160, 300, null, null], - [0, "와카사기", "환상향/와카사기.png", 0, null, 68, 57, 79, 0, 160, 300, null, null, "슬슬 우리요괴가 활약할 차례야!"], - [0, "오토히메", null, 0, null, 80, 60, 76, 0, 160, 300, null, null], - [0, "오키나", "환상향/오키나.png", 0, null, 87, 87, 86, 0, 160, 300, null, null, "요괴가 아니라 신이에요~"], - [0, "오모이카네", null, 0, null, 66, 52, 86, 0, 160, 300, null, null], - [0, "오린", "환상향/오린.jpg", 0, null, 87, 84, 77, 0, 160, 300, null, null, "시체가 갖고 싶으니까, 역시 내가 언니를 처리해야겠네!"], - [0, "오렌지", "환상향/오렌지.jpg", 0, null, 72, 52, 68, 0, 160, 300, null, null, "아하하, 1면 보스니까라고 얕보지 말라구!"], - [0, "오니5", null, 0, null, 55, 65, 40, 0, 160, 300, null, null], - [0, "오니4", null, 0, null, 54, 64, 40, 0, 160, 300, null, null], - [0, "오니3", null, 0, null, 53, 63, 40, 0, 160, 300, null, null], - [0, "오니2", null, 0, null, 52, 62, 40, 0, 160, 300, null, null], - [0, "오니1", null, 0, null, 51, 61, 40, 0, 160, 300, null, null], - [0, "엘핀민트", null, 0, null, 71, 71, 61, 0, 160, 300, null, null], - [0, "엘리스", "환상향/엘리스.jpg", 0, null, 55, 69, 88, 0, 160, 300, null, null], - [0, "엘리", "환상향/엘리.jpg", 0, null, 86, 83, 66, 0, 160, 300, null, null, "싸운 거 오랜만인걸 뭐."], - [0, "엘렌", "환상향/엘렌.jpg", 0, null, 67, 62, 71, 0, 160, 300, null, null, "나는, 엘렌. 폭신폭신 엘렌이야."], - [0, "에이린", "환상향/에이린.jpg", 0, null, 72, 76, 105, 0, 160, 300, null, null, "아하하. 너희는 어리석구나."], - [0, "야토", null, 0, null, 72, 83, 66, 0, 160, 300, null, null], - [0, "야츠하시", "환상향/야츠하시.png", 0, null, 69, 58, 73, 0, 160, 300, null, null, "훗훗후, 반역자에겐 어떤 수단이든 써도 된다면서?"], - [0, "야마메", "환상향/야마메.jpg", 0, null, 75, 65, 77, 0, 160, 300, null, null, "지저에 떨어진 요괴의 힘을 보고 싶은 모양이구나."], - [0, "앨리스", "환상향/앨리스.jpg", 0, null, 81, 63, 99, 0, 160, 300, null, null, "그런 소리 하니까 바보 취급당하는 거야. 탄막은 브레인. 상식이야."], - [0, "안자", null, 0, null, 67, 61, 83, 0, 160, 300, null, null], - [0, "안", null, 0, null, 68, 51, 67, 0, 160, 300, null, null], - [0, "아큐", "환상향/아큐.jpg", 0, null, 72, 30, 82, 0, 160, 300, null, null, "나는 이래뵈도 히에다가의 당주로써 환상향의 서기라는 역할을 지닌 유서깊은 집안의 사람이야."], - [0, "아즈마", null, 0, null, 78, 81, 64, 0, 160, 300, null, null], - [0, "아운", "환상향/아운.png", 0, null, 78, 87, 65, 0, 160, 300, null, null, "신사를 지키는 수호신으로서 말이야!"], - [0, "아야", "환상향/아야.jpg", 0, null, 73, 93, 71, 0, 160, 300, null, null, "깨끗하고 올바른 샤메이마루입니다."], - [0, "아리오슈", null, 0, null, 56, 70, 67, 0, 160, 300, null, null], - [0, "써니", "환상향/써니.jpg", 0, null, 75, 75, 60, 0, 160, 300, null, null, "어라, 혼자서도 최강이라며?"], - [0, "쌍자", null, 0, null, 63, 82, 71, 0, 160, 300, null, null], - [0, "쌍사", null, 0, null, 62, 73, 81, 0, 160, 300, null, null], - [0, "신키", "환상향/신키.jpg", 0, null, 96, 92, 73, 0, 160, 300, null, null, "나의 세계에서 저지른 죄값은 크지. 용서할 리가 없잖아."], - [0, "신묘마루", "환상향/신묘마루.png", 0, null, 85, 68, 88, 0, 160, 300, null, null, "누─구?"], - [0, "시키에이키", "환상향/시키에이키.jpg", 0, null, 84, 77, 98, 0, 160, 300, null, null, "보랏빛 벚꽃은 죄 깊은 인간의 영혼이 깃드는 꽃. 너는 그 보랏빛 벚꽃이 떨어져내리는 밑에서, 단죄하거라!"], - [0, "시키에이카", null, 0, null, 71, 60, 77, 0, 160, 300, null, null], - [0, "시즈하", "환상향/시즈하.jpg", 0, null, 79, 55, 62, 0, 160, 300, null, null], - [0, "시온", null, 0, null, 80, 97, 76, 0, 160, 300, null, null, "나도 좋아서 가난하게 살고 있는 게 아닌데~"], - [0, "스타", "환상향/스타.jpg", 0, null, 70, 60, 75, 0, 160, 300, null, null, "그래, 이 비겁한 녀석아!"], - [0, "스이카", "환상향/스이카.jpg", 0, null, 74, 102, 66, 0, 160, 300, null, null, "거짓말은 인간만 하는 거야!"], - [0, "스와코", "환상향/스와코.jpg", 0, null, 95, 81, 81, 0, 160, 300, null, null, "아~우~~"], - [0, "스씨", null, 0, null, 66, 63, 66, 0, 160, 300, null, null], - [0, "스미레코", "환상향/스미레코.jpg", 0, null, 81, 63, 88, 0, 160, 300, null, null, "히가시후카미 고등학교 1학년, 우사미 스미레코. 우는 아이도 뚝 그치는 진짜 초능력자야."], - [0, "순호", "환상향/순호.jpg", 0, null, 77, 102, 79, 0, 160, 300, null, null, "지금의 너로썬 내 힘 앞에선 무력, 절대적으로 무력하도다!"], - [0, "쇼", "환상향/쇼.jpg", 0, null, 75, 75, 95, 0, 160, 300, null, null, "그것이 당신의 정의라는 거라면, 그것도 좋지요."], - [0, "소악마", "환상향/소악마.jpg", 0, null, 50, 72, 67, 0, 160, 300, null, null], - [0, "셰퍼드", null, 0, null, 61, 58, 56, 0, 160, 300, null, null], - [0, "센고", null, 0, null, 83, 56, 79, 0, 160, 300, null, null], - [0, "센", null, 0, null, 67, 66, 28, 0, 160, 300, null, null], - [0, "세키반키", "환상향/세키반키.png", 0, null, 68, 75, 71, 0, 160, 300, null, null, "인간은 누구든지 나를 무서워하지!"], - [0, "세츠조", null, 0, null, 58, 80, 58, 0, 160, 300, null, null], - [0, "세이자", "환상향/세이자.png", 0, null, 82, 94, 69, 0, 160, 300, null, null, "내 이름은 세이자. 뼛속까지 아마노자쿠다!"], - [0, "세이란", "환상향/세이란.jpg", 0, null, 71, 77, 51, 0, 160, 300, null, null, "뭐, 이것도 일이야, 알지? 그러니 내게 여기서 얌전히 당해 줘. 응?"], - [0, "샤리트", null, 0, null, 68, 62, 62, 0, 160, 300, null, null], - [0, "상해인형", null, 0, null, 74, 69, 69, 0, 160, 300, null, null], - [0, "상아", null, 0, null, 84, 84, 84, 0, 160, 300, null, null], - [0, "사토리", "환상향/사토리.jpg", 0, null, 81, 57, 84, 0, 160, 300, null, null, "제게는 보여요. 당신이 마음속에 그리고 있는 탄막이!"], - [0, "사토노", "환상향/사토노.png", 0, null, 71, 76, 77, 0, 160, 300, null, null, "봐 주진 않겠지만 죽으면 죽일꺼니까 말야―!"], - [0, "사쿠야", "환상향/사쿠야.jpg", 0, null, 87, 89, 78, 0, 160, 300, null, null, "당신의 시간 또한 나의 것"], - [0, "사샤", null, 0, null, 74, 48, 78, 0, 160, 300, null, null], - [0, "사리엘", "환상향/사리엘.jpg", 0, null, 85, 68, 102, 0, 160, 300, null, null, "죽는다면 모두 함께"], - [0, "사라카", null, 0, null, 60, 74, 63, 0, 160, 300, null, null], - [0, "사라", "환상향/사라.jpg", 0, null, 76, 78, 36, 0, 160, 300, null, null, "싸우는 건 재미있지만, 지는 건 재미없어."], - [0, "사나에", "환상향/사나에.jpg", 0, null, 87, 71, 85, 0, 160, 300, null, null, "지금은, 바깥 세계는 환경을 지키라든가 경기 대책이 어떻다든가, 바보 같이 뭘 위해 태어난 건지 생각할 수 없는 사람 뿐."], - [0, "사구메", "환상향/사구메.jpg", 0, null, 84, 97, 71, 0, 160, 300, null, null, "......그런건 아냐."], - [0, "벤벤", "환상향/벤벤.png", 0, null, 69, 59, 75, 0, 160, 300, null, null, "어딜 봐도 츠쿠모가미잖아."], - [0, "뱌쿠렌", "환상향/뱌쿠렌.jpg", 0, null, 83, 90, 82, 0, 160, 300, null, null, "아아, 법의 세계에 빛이 가득해."], - [0, "미호", null, 0, null, 85, 86, 62, 0, 160, 300, null, null], - [0, "미하나", null, 0, null, 76, 67, 84, 0, 160, 300, null, null], - [0, "미토리", null, 0, null, 57, 56, 62, 0, 160, 300, null, null], - [0, "미코토", null, 0, null, 73, 82, 82, 0, 160, 300, null, null], - [0, "미코", "환상향/미코.png", 0, null, 87, 72, 97, 0, 160, 300, null, null, "장난은 끝이다!"], - [0, "미요", null, 0, null, 73, 66, 58, 0, 160, 300, null, null], - [0, "미스티아", "환상향/미스티아.jpg", 0, null, 65, 82, 12, 0, 160, 300, null, null, "자, 잠깐 기다려~!"], - [0, "미셸", null, 0, null, 70, 69, 72, 0, 160, 300, null, null], - [0, "미샨드라", null, 0, null, 80, 63, 82, 0, 160, 300, null, null], - [0, "미샤구지", null, 0, null, 48, 99, 59, 0, 160, 300, null, null], - [0, "미마", "환상향/미마.jpg", 0, null, 75, 90, 93, 0, 160, 300, null, null, "나도 인간계의 신이야. (거짓말)"], - [0, "미노리코", "환상향/미노리코.jpg", 0, null, 71, 58, 74, 0, 160, 300, null, null, "갓 수확한 고구마는 나의 향수. 무녀가 먹게 놔둘 줄 알고!"], - [0, "미나유", null, 0, null, 72, 49, 84, 0, 160, 300, null, null], - [0, "무무무", null, 0, null, 70, 77, 57, 0, 160, 300, null, null], - [0, "무라사", "환상향/무라사.jpg", 0, null, 76, 82, 65, 0, 160, 300, null, null, "이 세상에 미련이 남으신 분은 승선할 수 없습니다만 당신은 어떠신가요?"], - [0, "무게츠", "환상향/무게츠.jpg", 0, null, 76, 95, 68, 0, 160, 300, null, null, "난 무게츠. 그리고, 여긴 나의 세계."], - [0, "묘렌", null, 0, null, 77, 78, 79, 0, 160, 300, null, null], - [0, "모코우", "환상향/모코우.jpg", 0, null, 83, 99, 69, 0, 160, 300, null, null, "너희들도 한번 영원의 고륜을 고민해 보는 게 좋아!"], - [0, "모미지", "환상향/모미지.jpg", 0, null, 83, 68, 66, 0, 160, 300, null, null, "높은 분들은 저희 일반 텐구보다 훨씬 강하니까, 경비 같은 건 전혀 필요하지 않을 것 같은데."], - [0, "모모히메", null, 0, null, 75, 68, 72, 0, 160, 300, null, null], - [0, "모모", null, 0, null, 62, 62, 71, 0, 160, 300, null, null], - [0, "메이즈", null, 0, null, 70, 40, 76, 0, 160, 300, null, null], - [0, "메이벨", null, 0, null, 71, 74, 56, 0, 160, 300, null, null], - [0, "메이라", "환상향/메이라.jpg", 0, null, 65, 82, 36, 0, 160, 300, null, null, "내가 이기면 하쿠레이의 힘은 내가 가져가겠어!"], - [0, "메를린", "환상향/메를린.jpg", 0, null, 73, 67, 70, 0, 160, 300, null, null, "내 연주를 듣고서, 무사했던 식량은 없지."], - [0, "메디슨", "환상향/메디슨.jpg", 0, null, 73, 58, 71, 0, 160, 300, null, null, "콘파로, 콘파로, 독이여 모여라~"], - [0, "메구미", null, 0, null, 83, 77, 74, 0, 160, 300, null, null], - [0, "마이", "환상향/마이.jpg", 0, null, 72, 60, 75, 0, 160, 300, null, null, "내가 안내할게..."], - [0, "마을사람5", null, 0, null, 65, 35, 50, 0, 160, 300, null, null], - [0, "마을사람4", null, 0, null, 64, 34, 50, 0, 160, 300, null, null], - [0, "마을사람3", null, 0, null, 63, 33, 50, 0, 160, 300, null, null], - [0, "마을사람2", null, 0, null, 62, 32, 50, 0, 160, 300, null, null], - [0, "마을사람1", null, 0, null, 61, 31, 50, 0, 160, 300, null, null], - [0, "마미조", "환상향/마미조.png", 0, null, 77, 77, 97, 0, 160, 300, null, null, "뭔가 고민이 있는 모양이구먼. 야그나 함 들어 보자."], - [0, "마리우스", null, 0, null, 67, 87, 66, 0, 160, 300, null, null], - [0, "마리사", "환상향/마리사.jpg", 0, null, 83, 91, 78, 0, 160, 300, null, null, "아~? 탄막에 두뇌? 바보 아냐? 탄막은 파워야."], - [0, "마나", null, 0, null, 73, 75, 72, 0, 160, 300, null, null], - [0, "링고", "환상향/링고.jpg", 0, null, 54, 76, 71, 0, 160, 300, null, null, "옛날의 극락정토는 이제 보이는 그림자도 없다고 생각해."], - [0, "릴리", "환상향/릴리.jpg", 0, null, 37, 63, 68, 0, 160, 300, null, null, "봄이에요~"], - [0, "린네", null, 0, null, 83, 66, 83, 0, 160, 300, null, null], - [0, "리코", null, 0, null, 68, 60, 76, 0, 160, 300, null, null], - [0, "리카코", "환상향/리카코.jpg", 0, null, 76, 71, 100, 0, 160, 300, null, null, "왜 마법 따위에 흥미를 가지기나 하지?"], - [0, "리카", "환상향/리카.jpg", 0, null, 61, 63, 75, 0, 160, 300, null, null, "가는 거예요!!"], - [0, "리리카", "환상향/리리카.jpg", 0, null, 57, 66, 83, 0, 160, 300, null, null, "당신은 노래를 부르지. 난 손을 쓰지 않고 연주를 해. 장기전으로 가면 내 쪽이 세다고?"], - [0, "리락", null, 0, null, 74, 54, 62, 0, 160, 300, null, null], - [0, "리글", "환상향/리글.jpg", 0, null, 85, 64, 51, 0, 160, 300, null, null, "달이 보이는 밤에는, 즐기지 않아선 안 되는 거 아닐까."], - [0, "루이즈", "환상향/루이즈.jpg", 0, null, 59, 57, 72, 0, 160, 300, null, null, "뭐, 마계는 좋은 곳이니, 느긋하게 관광이라도 하도록 해☆"], - [0, "루미아", "환상향/루미아.jpg", 0, null, 79, 70, 56, 0, 160, 300, null, null, "그런 건가~"], - [0, "루리", null, 0, null, 66, 66, 62, 0, 160, 300, null, null], - [0, "루나사", "환상향/루나사.jpg", 0, null, 76, 77, 50, 0, 160, 300, null, null, "넌 슬픈 처지에서 태어난 경우야. 들떠있을 때가 아니지."], - [0, "루나", "환상향/루나.jpg", 0, null, 67, 74, 74, 0, 160, 300, null, null, "빨라고? 네녀석이 빨아라!"], - [0, "레티", "환상향/레티.jpg", 0, null, 60, 79, 49, 0, 160, 300, null, null, "흑막~"], - [0, "레이센", "환상향/레이센.jpg", 0, null, 79, 78, 89, 0, 160, 300, null, null, "이 앞에 기다리고 있는 게 악몽이라 해도, 그래도 나는 가야만 해."], - [0, "레이무", "환상향/레이무.jpg", 0, null, 90, 83, 82, 0, 160, 300, null, null, "인간을 쉽사리 죽게 내버려 둘까 보냐!"], - [0, "레이라", null, 0, null, 66, 67, 51, 0, 160, 300, null, null], - [0, "레밀리아", "환상향/레밀리아.jpg", 0, null, 93, 94, 70, 0, 160, 300, null, null, "당신은 이제껏 먹어 온 빵이 몇 개인지 알고 있어?"], - [0, "런던인형", null, 0, null, 64, 78, 62, 0, 160, 300, null, null], - [0, "란", "환상향/란.jpg", 0, null, 71, 79, 101, 0, 160, 300, null, null, "흠. 첸을 괴롭힌건 너지?"], - [0, "라쿠", null, 0, null, 71, 52, 69, 0, 160, 300, null, null], - [0, "라이코", "환상향/라이코.png", 0, null, 80, 66, 91, 0, 160, 300, null, null, "누구라도 자기 목숨을 부지하고 싶으면 노력을 아끼지 않잖아?"], - [0, "라바", "환상향/라바.png", 0, null, 69, 74, 54, 0, 160, 300, null, null, "지금이라면 신세계의 신이라도 될 것 같은 기분이야!"], - [0, "도레미", "환상향/도레미.jpg", 0, null, 67, 63, 88, 0, 160, 300, null, null, "맨몸!? 혹시 맨몸!?"], - [0, "대요정", "환상향/대요정.jpg", 0, null, 60, 56, 79, 0, 160, 300, null, null], - [0, "달토끼5", null, 0, null, 35, 55, 55, 0, 160, 300, null, null], - [0, "달토끼4", null, 0, null, 34, 54, 55, 0, 160, 300, null, null], - [0, "달토끼3", null, 0, null, 33, 53, 55, 0, 160, 300, null, null], - [0, "달토끼2", null, 0, null, 32, 52, 55, 0, 160, 300, null, null], - [0, "달토끼1", null, 0, null, 31, 51, 55, 0, 160, 300, null, null], - [0, "니토리", "환상향/니토리.jpg", 0, null, 68, 68, 76, 0, 160, 300, null, null, "축제 땐 생선이 잘 팔리지. 인간에게 어업권을 뺏어서 독점하면 떼돈 벌겠네!"], - [0, "노바", null, 0, null, 74, 56, 82, 0, 160, 300, null, null], - [0, "노로이코", "환상향/노로이코.jpg", 0, null, 56, 61, 65, 0, 160, 300, null, null], - [0, "네즈", null, 0, null, 76, 71, 40, 0, 160, 300, null, null], - [0, "네무노", "환상향/네무노.png", 0, null, 78, 40, 67, 0, 160, 300, null, null, "자자, 차라도 마시고 가라."], - [0, "나즈린", "환상향/나즈린.jpg", 0, null, 76, 63, 64, 0, 160, 300, null, null, "미안한데, 쥐를 얕보다간 죽어."], - [0, "나루코", null, 0, null, 68, 40, 70, 0, 160, 300, null, null], - [0, "나루미", "환상향/나루미.png", 0, null, 84, 72, 78, 0, 160, 300, null, null, "에? 뭐야 그거 무서워"], - [0, "구유카", "환상향/구유카.jpg", 0, null, 84, 85, 85, 0, 160, 300, null, null, "신이라 해도, 힘이야말로 전부야!"], - [0, "구앨리스", "환상향/구앨리스.jpg", 0, null, 80, 80, 90, 0, 160, 300, null, null, "궁극의 마법으로 널 해치워 주겠어!!"], - [0, "구마리사", "환상향/구마리사.jpg", 0, null, 80, 90, 80, 0, 160, 300, null, null, "오케이~, 여기는 마리사에게 맡겨줘."], - [0, "구레이무", "환상향/구레이무.jpg", 0, null, 90, 80, 80, 0, 160, 300, null, null, "문답무용이야!!"], - [0, "곽청아", "환상향/곽청아.png", 0, null, 72, 77, 87, 0, 160, 300, null, null, "네~ 썩어서 귀엽죠?"], - [0, "골리앗", null, 0, null, 72, 78, 58, 0, 160, 300, null, null], - [0, "겐지", null, 0, null, 70, 70, 70, 0, 160, 300, null, null], - [0, "겐게츠", "환상향/겐게츠.jpg", 0, null, 83, 70, 94, 0, 160, 300, null, null, "얕보지 마. 우린, 둘이서 한 사람 몫이니까.…"], - [0, "강림", null, 0, null, 84, 76, 57, 0, 160, 300, null, null] + [0,"히카리코",null,0,null,56,66,21,0,160,300,null,null], + [0,"히메미코",null,0,null,68,78,65,0,160,300,null,null], + [0,"히나",null,0,null,66,63,83,0,160,300,null,null,"온갖 재액이 닥쳐올 거야. 인간을 지키기 위해서라도 절대 보내주지 않겠어!"], + [0,"후토",null,0,null,84,69,93,0,160,300,null,null,"불이라도 지를까."], + [0,"홍미령",null,0,null,85,79,63,0,160,300,null,null,"젠장, 배수진이다!"], + [0,"호쥬누에",null,0,null,85,95,72,0,160,300,null,null,"자아, 나의 정체를 알아버린 당신을 죽여버리고, 정체불명으로 돌아가야겠어."], + [0,"호라이인형",null,0,null,66,71,73,0,160,300,null,null], + [0,"헤카티아",null,0,null,85,84,94,0,160,300,null,null,"꿈의 세계에선 무방비하니까 간단한 일이었지."], + [0,"하타테",null,0,null,30,88,70,0,160,300,null,null,"둘이서 더블스포일러가 되어서, 최강을 노리는 거야!"], + [0,"하츠나",null,0,null,63,61,65,0,160,300,null,null], + [0,"하니에",null,0,null,57,66,63,0,160,300,null,null], + [0,"플랑드르",null,0,null,76,101,71,0,160,300,null,null,"쿠욱하고 콰광~이네"], + [0,"프랑스인형",null,0,null,68,68,75,0,160,300,null,null], + [0,"파츄리",null,0,null,81,52,103,0,160,300,null,null,"무큐-"], + [0,"파스칼",null,0,null,73,70,60,0,160,300,null,null], + [0,"파브리티스",null,0,null,61,61,62,0,160,300,null,null], + [0,"파르시",null,0,null,67,49,89,0,160,300,null,null,"너한테 원한은 없다만, 내가 널 공격하는 이유는 얼마든지 만들 수 있어."], + [0,"토키코",null,0,null,60,59,88,0,160,300,null,null], + [0,"토쿠비",null,0,null,77,77,72,0,160,300,null,null], + [0,"토지코",null,0,null,76,62,86,0,160,300,null,null,"아이고 이런. 또 하나요?"], + [0,"토요히메",null,0,null,96,69,95,0,160,300,null,null], + [0,"토비코",null,0,null,72,58,56,0,160,300,null,null], + [0,"텐시",null,0,null,81,89,71,0,160,300,null,null,"약해! 약해 빠졌어! 그따위로 하늘에 오를 수 있을 거라 꿈도 꾸지 마!"], + [0,"텐무",null,0,null,68,73,56,0,160,300,null,null], + [0,"텐구5",null,0,null,51,51,50,0,160,300,null,null], + [0,"텐구4",null,0,null,51,51,50,0,160,300,null,null], + [0,"텐구3",null,0,null,51,51,50,0,160,300,null,null], + [0,"텐구2",null,0,null,51,51,50,0,160,300,null,null], + [0,"텐구1",null,0,null,51,51,50,0,160,300,null,null], + [0,"테이레이다",null,0,null,78,75,53,0,160,300,null,null,"갑작스럽겠지만, 테스트를 시작하겠어!]"], + [0,"테위",null,0,null,82,32,79,0,160,300,null,null,"대숲은 사람을 헤매게 하는 대나무의 미로. 헤매는 것은 산사람다운 행위인 거야."], + [0,"키쿠리",null,0,null,56,72,76,0,160,300,null,null], + [0,"키요히메",null,0,null,66,76,76,0,160,300,null,null], + [0,"키스메",null,0,null,73,58,59,0,160,300,null,null,"너희가 떨어뜨린 시체는 이거냐?"], + [0,"키나코",null,0,null,63,68,67,0,160,300,null,null], + [0,"클라운피스",null,0,null,89,80,77,0,160,300,null,null,"잇츠, 루나틱 타임! 광기의 세계로 어서오세요!"], + [0,"쿠스코",null,0,null,70,57,53,0,160,300,null,null], + [0,"쿠루미",null,0,null,70,65,68,0,160,300,null,null,"나는 쿠루미. 이 호수의 문지기야."], + [0,"쿄코",null,0,null,37,57,80,0,160,300,null,null,"목소리가 작다!"], + [0,"콘가라",null,0,null,91,96,60,0,160,300,null,null], + [0,"코토히메",null,0,null,54,36,90,0,160,300,null,null,"충격의 새로운 사실! 고대인은 전기를 쓰고 있었다!"], + [0,"코코로",null,0,null,80,91,60,0,160,300,null,null,"나와 최강의 칭호를 걸고 싸우자!"], + [0,"코카이",null,0,null,67,78,61,0,160,300,null,null], + [0,"코이시",null,0,null,88,71,93,0,160,300,null,null,"네 시체는 지령전 입구에 장식해줄게!"], + [0,"코스즈",null,0,null,61,63,70,0,160,300,null,null,"요괴가 하는 말을 믿으시는 거예요?!"], + [0,"코마치",null,0,null,80,97,75,0,160,300,null,null,"더는 어찌할 도리가 없어. 나는 그런 인간은 버릴 거야."], + [0,"코노하",null,0,null,62,58,67,0,160,300,null,null], + [0,"코가사",null,0,null,79,76,56,0,160,300,null,null,"됐어. 어차피 요괴는 이렇게 쓸쓸하게 사라져나가는 거니까."], + [0,"케이네",null,0,null,77,71,104,0,160,300,null,null,"여기엔 원래부터 아무것도 없었어, 인간도 인간의 마을도 말이야."], + [0,"캇파5",null,0,null,40,40,65,0,160,300,null,null], + [0,"캇파4",null,0,null,40,40,64,0,160,300,null,null], + [0,"캇파3",null,0,null,40,40,63,0,160,300,null,null], + [0,"캇파2",null,0,null,40,40,62,0,160,300,null,null], + [0,"캇파1",null,0,null,40,40,61,0,160,300,null,null], + [0,"카스미",null,0,null,67,63,82,0,160,300,null,null], + [0,"카센",null,0,null,72,103,71,0,160,300,null,null,"이런 곳에서 전력을 다할 이유가없지."], + [0,"카나코",null,0,null,84,97,77,0,160,300,null,null,"나를 부른 이는 누구인가?"], + [0,"카나",null,0,null,64,72,57,0,160,300,null,null,"아, 아니, 그러니까, 우주인?]"], + [0,"카구야",null,0,null,98,71,82,0,160,300,null,null,"내 영원을 다루는 술법으로 모두 깨뜨려 보이겠어."], + [0,"카게로",null,0,null,78,88,68,0,160,300,null,null,"무서워~ 인간 무서워~"], + [0,"치유리",null,0,null,45,77,78,0,160,300,null,null,"싸워서 강한 쪽이 이 세계의 치유리라구."], + [0,"치르노",null,0,null,79,89,49,0,160,300,null,null,"눈앞에 강적이 있잖아?"], + [0,"츠바메",null,0,null,71,80,58,0,160,300,null,null], + [0,"첸",null,0,null,48,70,70,0,160,300,null,null,"그건 그렇고, 마요이가에 어서와"], + [0,"지벤",null,0,null,85,67,66,0,160,300,null,null], + [0,"지귀",null,0,null,79,67,69,0,160,300,null,null], + [0,"조온",null,0,null,83,74,96,0,160,300,null,null,"우리가 왜 최흉최악의 자매인지 그 몸으로 이해해라!"], + [0,"자넷",null,0,null,60,59,62,0,160,300,null,null], + [0,"이포니아",null,0,null,61,62,61,0,160,300,null,null], + [0,"이쿠",null,0,null,72,72,94,0,160,300,null,null,"조금 벌을 받으실 필요가 있을지도 모르겠군요."], + [0,"이치요우",null,0,null,70,73,58,0,160,300,null,null], + [0,"이치린",null,0,null,86,55,88,0,160,300,null,null,"평화롭게 살고있으니까 괜찮잖아. 너, 요괴를 죽여도 아무렇지 않아?"], + [0,"이슈아",null,0,null,67,76,78,0,160,300,null,null], + [0,"이나코",null,0,null,56,35,63,0,160,300,null,null], + [0,"유키",null,0,null,72,77,61,0,160,300,null,null,"우리 둘을 쓰러뜨릴 수 있을 거라고 생각해?"], + [0,"유카리",null,0,null,87,76,96,0,160,300,null,null,"아름답고 잔혹하게 이 대지에서 사라져라!"], + [0,"유카",null,0,null,94,88,70,0,160,300,null,null,"아 그래, 마침 60년 전이었네. 아아...... 그래, 벌써 그런 시기구나...... 60년인가. 회귀할 때구나."], + [0,"유유코",null,0,null,97,80,81,0,160,300,null,null,"새는 잔뼈가 많아서 싫어."], + [0,"유메코",null,0,null,72,103,73,0,160,300,null,null,"우리들을 모욕한 죄는 죽음으로 밖에 갚을 수 없다!!"], + [0,"유메미",null,0,null,93,72,91,0,160,300,null,null,"기합 넣어! 죽어 버렸다간 데리고 돌아갈 수 없으니까."], + [0,"유기",null,0,null,71,104,72,0,160,300,null,null,"네가 강하기만 하다면 나야 아무 군소리도 안해. 어떤 목적으로 여기에 왔건 간에."], + [0,"유겐마간",null,0,null,80,80,50,0,160,300,null,null], + [0,"우카",null,0,null,60,73,82,0,160,300,null,null], + [0,"우츠호",null,0,null,73,105,68,0,160,300,null,null,"이물질 발견! 핵융합로의 이물질 혼입은 일단 반응을 정지하고, 즉각 이물질을 제거하라."], + [0,"요정5",null,0,null,55,60,35,0,160,300,null,null], + [0,"요정4",null,0,null,55,59,34,0,160,300,null,null], + [0,"요정3",null,0,null,55,58,33,0,160,300,null,null], + [0,"요정2",null,0,null,55,57,32,0,160,300,null,null], + [0,"요정1",null,0,null,55,56,31,0,160,300,null,null], + [0,"요우무",null,0,null,81,89,71,0,160,300,null,null,"...요괴가 담금질한 이 누관검에, 벨 수 없는 것 따위, 별로 없어!"], + [0,"요시카",null,0,null,84,78,61,0,160,300,null,null,"오~지~마~!"], + [0,"요리히메",null,0,null,90,98,74,0,160,300,null,null], + [0,"와카챠나두",null,0,null,62,61,68,0,160,300,null,null], + [0,"와카사기",null,0,null,68,57,79,0,160,300,null,null,"슬슬 우리요괴가 활약할 차례야!"], + [0,"오토히메",null,0,null,80,60,76,0,160,300,null,null], + [0,"오키나",null,0,null,87,87,86,0,160,300,null,null,"요괴가 아니라 신이에요~"], + [0,"오모이카네",null,0,null,66,52,86,0,160,300,null,null], + [0,"오린",null,0,null,87,84,77,0,160,300,null,null,"시체가 갖고 싶으니까, 역시 내가 언니를 처리해야겠네!"], + [0,"오렌지",null,0,null,72,52,68,0,160,300,null,null,"아하하, 1면 보스니까라고 얕보지 말라구!"], + [0,"오니5",null,0,null,55,65,40,0,160,300,null,null], + [0,"오니4",null,0,null,54,64,40,0,160,300,null,null], + [0,"오니3",null,0,null,53,63,40,0,160,300,null,null], + [0,"오니2",null,0,null,52,62,40,0,160,300,null,null], + [0,"오니1",null,0,null,51,61,40,0,160,300,null,null], + [0,"엘핀민트",null,0,null,71,71,61,0,160,300,null,null], + [0,"엘리스",null,0,null,55,69,88,0,160,300,null,null], + [0,"엘리",null,0,null,86,83,66,0,160,300,null,null,"싸운 거 오랜만인걸 뭐."], + [0,"엘렌",null,0,null,67,62,71,0,160,300,null,null,"나는, 엘렌. 폭신폭신 엘렌이야."], + [0,"에이린",null,0,null,72,76,105,0,160,300,null,null,"아하하. 너희는 어리석구나."], + [0,"야토",null,0,null,72,83,66,0,160,300,null,null], + [0,"야츠하시",null,0,null,69,58,73,0,160,300,null,null,"훗훗후, 반역자에겐 어떤 수단이든 써도 된다면서?"], + [0,"야마메",null,0,null,75,65,77,0,160,300,null,null,"지저에 떨어진 요괴의 힘을 보고 싶은 모양이구나."], + [0,"앨리스",null,0,null,81,63,99,0,160,300,null,null,"그런 소리 하니까 바보 취급당하는 거야. 탄막은 브레인. 상식이야."], + [0,"안자",null,0,null,67,61,83,0,160,300,null,null], + [0,"안",null,0,null,68,51,67,0,160,300,null,null], + [0,"아큐",null,0,null,72,30,82,0,160,300,null,null,"나는 이래뵈도 히에다가의 당주로써 환상향의 서기라는 역할을 지닌 유서깊은 집안의 사람이야."], + [0,"아즈마",null,0,null,78,81,64,0,160,300,null,null], + [0,"아운",null,0,null,78,87,65,0,160,300,null,null,"신사를 지키는 수호신으로서 말이야!"], + [0,"아야",null,0,null,73,93,71,0,160,300,null,null,"깨끗하고 올바른 샤메이마루입니다."], + [0,"아리오슈",null,0,null,56,70,67,0,160,300,null,null], + [0,"써니",null,0,null,75,75,60,0,160,300,null,null,"어라, 혼자서도 최강이라며?"], + [0,"쌍자",null,0,null,63,82,71,0,160,300,null,null], + [0,"쌍사",null,0,null,62,73,81,0,160,300,null,null], + [0,"신키",null,0,null,96,92,73,0,160,300,null,null,"나의 세계에서 저지른 죄값은 크지. 용서할 리가 없잖아."], + [0,"신묘마루",null,0,null,85,68,88,0,160,300,null,null,"누─구?"], + [0,"시키에이키",null,0,null,84,77,98,0,160,300,null,null,"보랏빛 벚꽃은 죄 깊은 인간의 영혼이 깃드는 꽃. 너는 그 보랏빛 벚꽃이 떨어져내리는 밑에서, 단죄하거라!"], + [0,"시키에이카",null,0,null,71,60,77,0,160,300,null,null], + [0,"시즈하",null,0,null,79,55,62,0,160,300,null,null], + [0,"시온",null,0,null,80,97,76,0,160,300,null,null,"나도 좋아서 가난하게 살고 있는 게 아닌데~"], + [0,"스타",null,0,null,70,60,75,0,160,300,null,null,"그래, 이 비겁한 녀석아!"], + [0,"스이카",null,0,null,74,102,66,0,160,300,null,null,"거짓말은 인간만 하는 거야!"], + [0,"스와코",null,0,null,95,81,81,0,160,300,null,null,"아~우~~"], + [0,"스씨",null,0,null,66,63,66,0,160,300,null,null], + [0,"스미레코",null,0,null,81,63,88,0,160,300,null,null,"히가시후카미 고등학교 1학년, 우사미 스미레코. 우는 아이도 뚝 그치는 진짜 초능력자야."], + [0,"순호",null,0,null,77,102,79,0,160,300,null,null,"지금의 너로썬 내 힘 앞에선 무력, 절대적으로 무력하도다!"], + [0,"쇼",null,0,null,75,75,95,0,160,300,null,null,"그것이 당신의 정의라는 거라면, 그것도 좋지요."], + [0,"소악마",null,0,null,50,72,67,0,160,300,null,null], + [0,"셰퍼드",null,0,null,61,58,56,0,160,300,null,null], + [0,"센고",null,0,null,83,56,79,0,160,300,null,null], + [0,"센",null,0,null,67,66,28,0,160,300,null,null], + [0,"세키반키",null,0,null,68,75,71,0,160,300,null,null,"인간은 누구든지 나를 무서워하지!"], + [0,"세츠조",null,0,null,58,80,58,0,160,300,null,null], + [0,"세이자",null,0,null,82,94,69,0,160,300,null,null,"내 이름은 세이자. 뼛속까지 아마노자쿠다!"], + [0,"세이란",null,0,null,71,77,51,0,160,300,null,null,"뭐, 이것도 일이야, 알지? 그러니 내게 여기서 얌전히 당해 줘. 응?"], + [0,"샤리트",null,0,null,68,62,62,0,160,300,null,null], + [0,"상해인형",null,0,null,74,69,69,0,160,300,null,null], + [0,"상아",null,0,null,84,84,84,0,160,300,null,null], + [0,"사토리",null,0,null,81,57,84,0,160,300,null,null,"제게는 보여요. 당신이 마음속에 그리고 있는 탄막이!"], + [0,"사토노",null,0,null,71,76,77,0,160,300,null,null,"봐 주진 않겠지만 죽으면 죽일꺼니까 말야―!"], + [0,"사쿠야",null,0,null,87,89,78,0,160,300,null,null,"당신의 시간 또한 나의 것"], + [0,"사샤",null,0,null,74,48,78,0,160,300,null,null], + [0,"사리엘",null,0,null,85,68,102,0,160,300,null,null,"죽는다면 모두 함께"], + [0,"사라카",null,0,null,60,74,63,0,160,300,null,null], + [0,"사라",null,0,null,76,78,36,0,160,300,null,null,"싸우는 건 재미있지만, 지는 건 재미없어."], + [0,"사나에",null,0,null,87,71,85,0,160,300,null,null,"지금은, 바깥 세계는 환경을 지키라든가 경기 대책이 어떻다든가, 바보 같이 뭘 위해 태어난 건지 생각할 수 없는 사람 뿐."], + [0,"사구메",null,0,null,84,97,71,0,160,300,null,null,"......그런건 아냐."], + [0,"벤벤",null,0,null,69,59,75,0,160,300,null,null,"어딜 봐도 츠쿠모가미잖아."], + [0,"뱌쿠렌",null,0,null,83,90,82,0,160,300,null,null,"아아, 법의 세계에 빛이 가득해."], + [0,"미호",null,0,null,85,86,62,0,160,300,null,null], + [0,"미하나",null,0,null,76,67,84,0,160,300,null,null], + [0,"미토리",null,0,null,57,56,62,0,160,300,null,null], + [0,"미코토",null,0,null,73,82,82,0,160,300,null,null], + [0,"미코",null,0,null,87,72,97,0,160,300,null,null,"장난은 끝이다!"], + [0,"미요",null,0,null,73,66,58,0,160,300,null,null], + [0,"미스티아",null,0,null,65,82,12,0,160,300,null,null,"자, 잠깐 기다려~!"], + [0,"미셸",null,0,null,70,69,72,0,160,300,null,null], + [0,"미샨드라",null,0,null,80,63,82,0,160,300,null,null], + [0,"미샤구지",null,0,null,48,99,59,0,160,300,null,null], + [0,"미마",null,0,null,75,90,93,0,160,300,null,null,"나도 인간계의 신이야. (거짓말)"], + [0,"미노리코",null,0,null,71,58,74,0,160,300,null,null,"갓 수확한 고구마는 나의 향수. 무녀가 먹게 놔둘 줄 알고!"], + [0,"미나유",null,0,null,72,49,84,0,160,300,null,null], + [0,"무무무",null,0,null,70,77,57,0,160,300,null,null], + [0,"무라사",null,0,null,76,82,65,0,160,300,null,null,"이 세상에 미련이 남으신 분은 승선할 수 없습니다만 당신은 어떠신가요?"], + [0,"무게츠",null,0,null,76,95,68,0,160,300,null,null,"난 무게츠. 그리고, 여긴 나의 세계."], + [0,"묘렌",null,0,null,77,78,79,0,160,300,null,null], + [0,"모코우",null,0,null,83,99,69,0,160,300,null,null,"너희들도 한번 영원의 고륜을 고민해 보는 게 좋아!"], + [0,"모미지",null,0,null,83,68,66,0,160,300,null,null,"높은 분들은 저희 일반 텐구보다 훨씬 강하니까, 경비 같은 건 전혀 필요하지 않을 것 같은데."], + [0,"모모히메",null,0,null,75,68,72,0,160,300,null,null], + [0,"모모",null,0,null,62,62,71,0,160,300,null,null], + [0,"메이즈",null,0,null,70,40,76,0,160,300,null,null], + [0,"메이벨",null,0,null,71,74,56,0,160,300,null,null], + [0,"메이라",null,0,null,65,82,36,0,160,300,null,null,"내가 이기면 하쿠레이의 힘은 내가 가져가겠어!"], + [0,"메를린",null,0,null,73,67,70,0,160,300,null,null,"내 연주를 듣고서, 무사했던 식량은 없지."], + [0,"메디슨",null,0,null,73,58,71,0,160,300,null,null,"콘파로, 콘파로, 독이여 모여라~"], + [0,"메구미",null,0,null,83,77,74,0,160,300,null,null], + [0,"마이",null,0,null,72,60,75,0,160,300,null,null,"내가 안내할게..."], + [0,"마을사람5",null,0,null,65,35,50,0,160,300,null,null], + [0,"마을사람4",null,0,null,64,34,50,0,160,300,null,null], + [0,"마을사람3",null,0,null,63,33,50,0,160,300,null,null], + [0,"마을사람2",null,0,null,62,32,50,0,160,300,null,null], + [0,"마을사람1",null,0,null,61,31,50,0,160,300,null,null], + [0,"마미조",null,0,null,77,77,97,0,160,300,null,null,"뭔가 고민이 있는 모양이구먼. 야그나 함 들어 보자."], + [0,"마리우스",null,0,null,67,87,66,0,160,300,null,null], + [0,"마리사",null,0,null,83,91,78,0,160,300,null,null,"아~? 탄막에 두뇌? 바보 아냐? 탄막은 파워야."], + [0,"마나",null,0,null,73,75,72,0,160,300,null,null], + [0,"링고",null,0,null,54,76,71,0,160,300,null,null,"옛날의 극락정토는 이제 보이는 그림자도 없다고 생각해."], + [0,"릴리",null,0,null,37,63,68,0,160,300,null,null,"봄이에요~"], + [0,"린네",null,0,null,83,66,83,0,160,300,null,null], + [0,"리코",null,0,null,68,60,76,0,160,300,null,null], + [0,"리카코",null,0,null,76,71,100,0,160,300,null,null,"왜 마법 따위에 흥미를 가지기나 하지?"], + [0,"리카",null,0,null,61,63,75,0,160,300,null,null,"가는 거예요!!"], + [0,"리리카",null,0,null,57,66,83,0,160,300,null,null,"당신은 노래를 부르지. 난 손을 쓰지 않고 연주를 해. 장기전으로 가면 내 쪽이 세다고?"], + [0,"리락",null,0,null,74,54,62,0,160,300,null,null], + [0,"리글",null,0,null,85,64,51,0,160,300,null,null,"달이 보이는 밤에는, 즐기지 않아선 안 되는 거 아닐까."], + [0,"루이즈",null,0,null,59,57,72,0,160,300,null,null,"뭐, 마계는 좋은 곳이니, 느긋하게 관광이라도 하도록 해☆"], + [0,"루미아",null,0,null,79,70,56,0,160,300,null,null,"그런 건가~"], + [0,"루리",null,0,null,66,66,62,0,160,300,null,null], + [0,"루나사",null,0,null,76,77,50,0,160,300,null,null,"넌 슬픈 처지에서 태어난 경우야. 들떠있을 때가 아니지."], + [0,"루나",null,0,null,67,74,74,0,160,300,null,null,"빨라고? 네녀석이 빨아라!"], + [0,"레티",null,0,null,60,79,49,0,160,300,null,null,"흑막~"], + [0,"레이센",null,0,null,79,78,89,0,160,300,null,null,"이 앞에 기다리고 있는 게 악몽이라 해도, 그래도 나는 가야만 해."], + [0,"레이무",null,0,null,90,83,82,0,160,300,null,null,"인간을 쉽사리 죽게 내버려 둘까 보냐!"], + [0,"레이라",null,0,null,66,67,51,0,160,300,null,null], + [0,"레밀리아",null,0,null,93,94,70,0,160,300,null,null,"당신은 이제껏 먹어 온 빵이 몇 개인지 알고 있어?"], + [0,"런던인형",null,0,null,64,78,62,0,160,300,null,null], + [0,"란",null,0,null,71,79,101,0,160,300,null,null,"흠. 첸을 괴롭힌건 너지?"], + [0,"라쿠",null,0,null,71,52,69,0,160,300,null,null], + [0,"라이코",null,0,null,80,66,91,0,160,300,null,null,"누구라도 자기 목숨을 부지하고 싶으면 노력을 아끼지 않잖아?"], + [0,"라바",null,0,null,69,74,54,0,160,300,null,null,"지금이라면 신세계의 신이라도 될 것 같은 기분이야!"], + [0,"도레미",null,0,null,67,63,88,0,160,300,null,null,"맨몸!? 혹시 맨몸!?"], + [0,"대요정",null,0,null,60,56,79,0,160,300,null,null], + [0,"달토끼5",null,0,null,35,55,55,0,160,300,null,null], + [0,"달토끼4",null,0,null,34,54,55,0,160,300,null,null], + [0,"달토끼3",null,0,null,33,53,55,0,160,300,null,null], + [0,"달토끼2",null,0,null,32,52,55,0,160,300,null,null], + [0,"달토끼1",null,0,null,31,51,55,0,160,300,null,null], + [0,"니토리",null,0,null,68,68,76,0,160,300,null,null,"축제 땐 생선이 잘 팔리지. 인간에게 어업권을 뺏어서 독점하면 떼돈 벌겠네!"], + [0,"노바",null,0,null,74,56,82,0,160,300,null,null], + [0,"노로이코",null,0,null,56,61,65,0,160,300,null,null], + [0,"네즈",null,0,null,76,71,40,0,160,300,null,null], + [0,"네무노",null,0,null,78,40,67,0,160,300,null,null,"자자, 차라도 마시고 가라."], + [0,"나즈린",null,0,null,76,63,64,0,160,300,null,null,"미안한데, 쥐를 얕보다간 죽어."], + [0,"나루코",null,0,null,68,40,70,0,160,300,null,null], + [0,"나루미",null,0,null,84,72,78,0,160,300,null,null,"에? 뭐야 그거 무서워"], + [0,"구유카",null,0,null,84,85,85,0,160,300,null,null,"신이라 해도, 힘이야말로 전부야!"], + [0,"구앨리스",null,0,null,80,80,90,0,160,300,null,null,"궁극의 마법으로 널 해치워 주겠어!!"], + [0,"구마리사",null,0,null,80,90,80,0,160,300,null,null,"오케이~, 여기는 마리사에게 맡겨줘."], + [0,"구레이무",null,0,null,90,80,80,0,160,300,null,null,"문답무용이야!!"], + [0,"곽청아",null,0,null,72,77,87,0,160,300,null,null,"네~ 썩어서 귀엽죠?"], + [0,"골리앗",null,0,null,72,78,58,0,160,300,null,null], + [0,"겐지",null,0,null,70,70,70,0,160,300,null,null], + [0,"겐게츠",null,0,null,83,70,94,0,160,300,null,null,"얕보지 마. 우린, 둘이서 한 사람 몫이니까.…"], + [0,"강림",null,0,null,84,76,57,0,160,300,null,null] ], "cities":[ diff --git a/hwe/scenario/scenario_321.json b/hwe/scenario/scenario_321.json index f7e4758c..e2a8c78b 100644 --- a/hwe/scenario/scenario_321.json +++ b/hwe/scenario/scenario_321.json @@ -8,6 +8,7 @@ "max":110, "npcMax":100 }, + "iconPath":"환상향", "nation":[ ["영이", "#A9A9A9", 3000, 3000, "The Highly Responsive to Prayers.", 0, "묵가", 1, ["상용"]], ["봉마", "#483D8B", 3000, 3000, "The Story of Eastern Wonderland.", 0, "종횡가", 1, ["이도"]], @@ -30,249 +31,249 @@ ], "diplomacy":[], "general":[ - [0, "히카리코", null, "환상", null, 56, 66, 21, 0, 160, 300, null, null], - [0, "히메미코", null, 0, null, 68, 78, 65, 0, 160, 300, null, null], - [0, "히나", "환상향/히나.jpg", "풍신", null, 66, 63, 83, 0, 160, 300, null, null, "온갖 재액이 닥쳐올 거야. 인간을 지키기 위해서라도 절대 보내주지 않겠어!"], - [0, "후토", "환상향/후토.png", "신령", null, 84, 69, 93, 0, 160, 300, null, null, "불이라도 지를까."], - [0, "홍미령", "환상향/홍미령.jpg", "홍마", null, 85, 79, 63, 0, 160, 300, null, null, "젠장, 배수진이다!"], - [0, "호쥬누에", "환상향/호쥬누에.jpg", "성령", null, 85, 95, 72, 0, 160, 300, null, null, "자아, 나의 정체를 알아버린 당신을 죽여버리고, 정체불명으로 돌아가야겠어."], - [0, "호라이인형", null, 0, null, 66, 71, 73, 0, 160, 300, null, null], - [0, "헤카티아", "환상향/헤카티아.jpg", "감주", null, 85, 84, 94, 0, 160, 300, null, null, "꿈의 세계에선 무방비하니까 간단한 일이었지."], - [0, "하타테", "환상향/하타테.jpg", "외전", null, 30, 88, 70, 0, 160, 300, null, null, "둘이서 더블스포일러가 되어서, 최강을 노리는 거야!"], - [0, "하츠나", null, 0, null, 63, 61, 65, 0, 160, 300, null, null], - [0, "하니에", null, 0, null, 57, 66, 63, 0, 160, 300, null, null], - [0, "플랑드르", "환상향/플랑드르.jpg", "홍마", null, 76, 101, 71, 0, 160, 300, null, null, "쿠욱하고 콰광~이네"], - [0, "프랑스인형", null, 0, null, 68, 68, 75, 0, 160, 300, null, null], - [0, "파츄리", "환상향/파츄리.jpg", "홍마", null, 81, 52, 103, 0, 160, 300, null, null, "무큐-"], - [0, "파스칼", null, 0, null, 73, 70, 60, 0, 160, 300, null, null], - [0, "파브리티스", null, 0, null, 61, 61, 62, 0, 160, 300, null, null], - [0, "파르시", "환상향/파르시.jpg", "지령", null, 67, 49, 89, 0, 160, 300, null, null, "너한테 원한은 없다만, 내가 널 공격하는 이유는 얼마든지 만들 수 있어."], - [0, "토키코", null, "구문", null, 60, 59, 88, 0, 160, 300, null, null], - [0, "토쿠비", null, 0, null, 77, 77, 72, 0, 160, 300, null, null], - [0, "토지코", "환상향/토지코.png", "신령", null, 76, 62, 86, 0, 160, 300, null, null, "아이고 이런. 또 하나요?"], - [0, "토요히메", "환상향/토요히메.jpg", "구문", null, 96, 69, 95, 0, 160, 300, null, null], - [0, "토비코", null, 0, null, 72, 58, 56, 0, 160, 300, null, null], - [0, "텐시", "환상향/텐시.jpg", "외전", null, 81, 89, 71, 0, 160, 300, null, null, "약해! 약해 빠졌어! 그따위로 하늘에 오를 수 있을 거라 꿈도 꾸지 마!"], - [0, "텐무", null, 0, null, 68, 73, 56, 0, 160, 300, null, null], - [0, "텐구5", null, 0, null, 51, 51, 50, 0, 160, 300, null, null], - [0, "텐구4", null, 0, null, 51, 51, 50, 0, 160, 300, null, null], - [0, "텐구3", null, 0, null, 51, 51, 50, 0, 160, 300, null, null], - [0, "텐구2", null, 0, null, 51, 51, 50, 0, 160, 300, null, null], - [0, "텐구1", null, 0, null, 51, 51, 50, 0, 160, 300, null, null], - [0, "테이레이다", "환상향/테이레이다.png", "천공", null, 78, 75, 53, 0, 160, 300, null, null, "갑작스럽겠지만, 테스트를 시작하겠어!]"], - [0, "테위", "환상향/테위.jpg", "영야", null, 82, 32, 79, 0, 160, 300, null, null, "대숲은 사람을 헤매게 하는 대나무의 미로. 헤매는 것은 산사람다운 행위인 거야."], - [0, "키쿠리", "환상향/키쿠리.jpg", "영이", null, 56, 72, 76, 0, 160, 300, null, null], - [0, "키요히메", null, 0, null, 66, 76, 76, 0, 160, 300, null, null], - [0, "키스메", "환상향/키스메.jpg", "지령", null, 73, 58, 59, 0, 160, 300, null, null, "너희가 떨어뜨린 시체는 이거냐?"], - [0, "키나코", null, 0, null, 63, 68, 67, 0, 160, 300, null, null], - [0, "클라운피스", "환상향/클라운피스.jpg", "감주", null, 89, 80, 77, 0, 160, 300, null, null, "잇츠, 루나틱 타임! 광기의 세계로 어서오세요!"], - [0, "쿠스코", null, 0, null, 70, 57, 53, 0, 160, 300, null, null], - [0, "쿠루미", "환상향/쿠루미.jpg", "환상", null, 70, 65, 68, 0, 160, 300, null, null, "나는 쿠루미. 이 호수의 문지기야."], - [0, "쿄코", "환상향/쿄코.jpg", "신령", null, 37, 57, 80, 0, 160, 300, null, null, "목소리가 작다!"], - [0, "콘가라", "환상향/콘가라.jpg", "영이", null, 91, 96, 60, 0, 160, 300, null, null], - [0, "코토히메", "환상향/코토히메.jpg", "몽시", null, 54, 36, 90, 0, 160, 300, null, null, "충격의 새로운 사실! 고대인은 전기를 쓰고 있었다!"], - [0, "코코로", "환상향/코코로.jpg", "외전", null, 80, 91, 60, 0, 160, 300, null, null, "나와 최강의 칭호를 걸고 싸우자!"], - [0, "코카이", null, 0, null, 67, 78, 61, 0, 160, 300, null, null], - [0, "코이시", "환상향/코이시.jpg", "지령", null, 88, 71, 93, 0, 160, 300, null, null, "네 시체는 지령전 입구에 장식해줄게!"], - [0, "코스즈", "환상향/코스즈.jpg", "구문", null, 61, 63, 70, 0, 160, 300, null, null, "요괴가 하는 말을 믿으시는 거예요?!"], - [0, "코마치", "환상향/코마치.jpg", "화영", null, 80, 97, 75, 0, 160, 300, null, null, "더는 어찌할 도리가 없어. 나는 그런 인간은 버릴 거야."], - [0, "코노하", null, 0, null, 62, 58, 67, 0, 160, 300, null, null], - [0, "코가사", "환상향/코가사.jpg", "성령", null, 79, 76, 56, 0, 160, 300, null, null, "됐어. 어차피 요괴는 이렇게 쓸쓸하게 사라져나가는 거니까."], - [0, "케이네", "환상향/케이네.jpg", "영야", null, 77, 71, 104, 0, 160, 300, null, null, "여기엔 원래부터 아무것도 없었어, 인간도 인간의 마을도 말이야."], - [0, "캇파5", null, 0, null, 40, 40, 65, 0, 160, 300, null, null], - [0, "캇파4", null, 0, null, 40, 40, 64, 0, 160, 300, null, null], - [0, "캇파3", null, 0, null, 40, 40, 63, 0, 160, 300, null, null], - [0, "캇파2", null, 0, null, 40, 40, 62, 0, 160, 300, null, null], - [0, "캇파1", null, 0, null, 40, 40, 61, 0, 160, 300, null, null], - [0, "카스미", null, 0, null, 67, 63, 82, 0, 160, 300, null, null], - [0, "카센", "환상향/카센.jpg", "외전", null, 72, 103, 71, 0, 160, 300, null, null, "이런 곳에서 전력을 다할 이유가없지."], - [0, "카나코", "환상향/카나코.jpg", "풍신", null, 84, 97, 77, 0, 160, 300, null, null, "나를 부른 이는 누구인가?"], - [0, "카나", "환상향/카나.jpg", "몽시", null, 64, 72, 57, 0, 160, 300, null, null, "아, 아니, 그러니까, 우주인?]"], - [0, "카구야", "환상향/카구야.jpg", "영야", null, 98, 71, 82, 0, 160, 300, null, null, "내 영원을 다루는 술법으로 모두 깨뜨려 보이겠어."], - [0, "카게로", "환상향/카게로.png", "휘침", null, 78, 88, 68, 0, 160, 300, null, null, "무서워~ 인간 무서워~"], - [0, "치유리", "환상향/치유리.jpg", "몽시", null, 45, 77, 78, 0, 160, 300, null, null, "싸워서 강한 쪽이 이 세계의 치유리라구."], - [0, "치르노", "환상향/치르노.jpg", 0, null, 79, 89, 49, 0, 160, 300, null, null, "눈앞에 강적이 있잖아?"], - [0, "츠바메", null, 0, null, 71, 80, 58, 0, 160, 300, null, null], - [0, "첸", "환상향/첸.jpg", "요요", null, 48, 70, 70, 0, 160, 300, null, null, "그건 그렇고, 마요이가에 어서와"], - [0, "지벤", null, 0, null, 85, 67, 66, 0, 160, 300, null, null], - [0, "지귀", null, 0, null, 79, 67, 69, 0, 160, 300, null, null], - [0, "조온", null, "외전", null, 83, 74, 96, 0, 160, 300, null, null, "우리가 왜 최흉최악의 자매인지 그 몸으로 이해해라!"], - [0, "자넷", null, 0, null, 60, 59, 62, 0, 160, 300, null, null], - [0, "이포니아", null, 0, null, 61, 62, 61, 0, 160, 300, null, null], - [0, "이쿠", "환상향/이쿠.jpg", "외전", null, 72, 72, 94, 0, 160, 300, null, null, "조금 벌을 받으실 필요가 있을지도 모르겠군요."], - [0, "이치요우", null, 0, null, 70, 73, 58, 0, 160, 300, null, null], - [0, "이치린", "환상향/이치린.jpg", "성령", null, 86, 55, 88, 0, 160, 300, null, null, "평화롭게 살고있으니까 괜찮잖아. 너, 요괴를 죽여도 아무렇지 않아?"], - [0, "이슈아", null, 0, null, 67, 76, 78, 0, 160, 300, null, null], - [0, "이나코", null, 0, null, 56, 35, 63, 0, 160, 300, null, null], - [0, "유키", "환상향/유키.jpg", "괴기", null, 72, 77, 61, 0, 160, 300, null, null, "우리 둘을 쓰러뜨릴 수 있을 거라고 생각해?"], - [0, "유카리", "환상향/유카리.jpg", "요요", null, 87, 76, 96, 0, 160, 300, null, null, "아름답고 잔혹하게 이 대지에서 사라져라!"], - [0, "유카", "환상향/유카.jpg", "화영", null, 94, 88, 70, 0, 160, 300, null, null, "아 그래, 마침 60년 전이었네. 아아...... 그래, 벌써 그런 시기구나...... 60년인가. 회귀할 때구나."], - [0, "유유코", "환상향/유유코.jpg", "요요", null, 97, 80, 81, 0, 160, 300, null, null, "새는 잔뼈가 많아서 싫어."], - [0, "유메코", "환상향/유메코.jpg", "괴기", null, 72, 103, 73, 0, 160, 300, null, null, "우리들을 모욕한 죄는 죽음으로 밖에 갚을 수 없다!!"], - [0, "유메미", "환상향/유메미.jpg", "몽시", null, 93, 72, 91, 0, 160, 300, null, null, "기합 넣어! 죽어 버렸다간 데리고 돌아갈 수 없으니까."], - [0, "유기", "환상향/유기.jpg", "지령", null, 71, 104, 72, 0, 160, 300, null, null, "네가 강하기만 하다면 나야 아무 군소리도 안해. 어떤 목적으로 여기에 왔건 간에."], - [0, "유겐마간", "환상향/유겐마간.jpg", "영이", null, 80, 80, 50, 0, 160, 300, null, null], - [0, "우카", null, 0, null, 60, 73, 82, 0, 160, 300, null, null], - [0, "우츠호", "환상향/우츠호.jpg", "지령", null, 73, 105, 68, 0, 160, 300, null, null, "이물질 발견! 핵융합로의 이물질 혼입은 일단 반응을 정지하고, 즉각 이물질을 제거하라."], - [0, "요정5", null, 0, null, 55, 60, 35, 0, 160, 300, null, null], - [0, "요정4", null, 0, null, 55, 59, 34, 0, 160, 300, null, null], - [0, "요정3", null, 0, null, 55, 58, 33, 0, 160, 300, null, null], - [0, "요정2", null, 0, null, 55, 57, 32, 0, 160, 300, null, null], - [0, "요정1", null, 0, null, 55, 56, 31, 0, 160, 300, null, null], - [0, "요우무", "환상향/요우무.jpg", "요요", null, 81, 89, 71, 0, 160, 300, null, null, "...요괴가 담금질한 이 누관검에, 벨 수 없는 것 따위, 별로 없어!"], - [0, "요시카", "환상향/요시카.jpg", "신령", null, 84, 78, 61, 0, 160, 300, null, null, "오~지~마~!"], - [0, "요리히메", "환상향/요리히메.jpg", "구문", null, 90, 98, 74, 0, 160, 300, null, null], - [0, "와카챠나두", null, 0, null, 62, 61, 68, 0, 160, 300, null, null], - [0, "와카사기", "환상향/와카사기.png", "휘침", null, 68, 57, 79, 0, 160, 300, null, null, "슬슬 우리요괴가 활약할 차례야!"], - [0, "오토히메", null, 0, null, 80, 60, 76, 0, 160, 300, null, null], - [0, "오키나", "환상향/오키나.png", "천공", null, 87, 87, 86, 0, 160, 300, null, null, "요괴가 아니라 신이에요~"], - [0, "오모이카네", null, 0, null, 66, 52, 86, 0, 160, 300, null, null], - [0, "오린", "환상향/오린.jpg", "지령", null, 87, 84, 77, 0, 160, 300, null, null, "시체가 갖고 싶으니까, 역시 내가 언니를 처리해야겠네!"], - [0, "오렌지", "환상향/오렌지.jpg", "환상", null, 72, 52, 68, 0, 160, 300, null, null, "아하하, 1면 보스니까라고 얕보지 말라구!"], - [0, "오니5", null, 0, null, 55, 65, 40, 0, 160, 300, null, null], - [0, "오니4", null, 0, null, 54, 64, 40, 0, 160, 300, null, null], - [0, "오니3", null, 0, null, 53, 63, 40, 0, 160, 300, null, null], - [0, "오니2", null, 0, null, 52, 62, 40, 0, 160, 300, null, null], - [0, "오니1", null, 0, null, 51, 61, 40, 0, 160, 300, null, null], - [0, "엘핀민트", null, 0, null, 71, 71, 61, 0, 160, 300, null, null], - [0, "엘리스", "환상향/엘리스.jpg", "영이", null, 55, 69, 88, 0, 160, 300, null, null], - [0, "엘리", "환상향/엘리.jpg", "환상", null, 86, 83, 66, 0, 160, 300, null, null, "싸운 거 오랜만인걸 뭐."], - [0, "엘렌", "환상향/엘렌.jpg", "몽시", null, 67, 62, 71, 0, 160, 300, null, null, "나는, 엘렌. 폭신폭신 엘렌이야."], - [0, "에이린", "환상향/에이린.jpg", "영야", null, 72, 76, 105, 0, 160, 300, null, null, "아하하. 너희는 어리석구나."], - [0, "야토", null, 0, null, 72, 83, 66, 0, 160, 300, null, null], - [0, "야츠하시", "환상향/야츠하시.png", "휘침", null, 69, 58, 73, 0, 160, 300, null, null, "훗훗후, 반역자에겐 어떤 수단이든 써도 된다면서?"], - [0, "야마메", "환상향/야마메.jpg", "지령", null, 75, 65, 77, 0, 160, 300, null, null, "지저에 떨어진 요괴의 힘을 보고 싶은 모양이구나."], - [0, "앨리스", "환상향/앨리스.jpg", "요요", null, 81, 63, 99, 0, 160, 300, null, null, "그런 소리 하니까 바보 취급당하는 거야. 탄막은 브레인. 상식이야."], - [0, "안자", null, 0, null, 67, 61, 83, 0, 160, 300, null, null], - [0, "안", null, 0, null, 68, 51, 67, 0, 160, 300, null, null], - [0, "아큐", "환상향/아큐.jpg", "구문", null, 72, 30, 82, 0, 160, 300, null, null, "나는 이래뵈도 히에다가의 당주로써 환상향의 서기라는 역할을 지닌 유서깊은 집안의 사람이야."], - [0, "아즈마", null, 0, null, 78, 81, 64, 0, 160, 300, null, null], - [0, "아운", "환상향/아운.png", "천공", null, 78, 87, 65, 0, 160, 300, null, null, "신사를 지키는 수호신으로서 말이야!"], - [0, "아야", "환상향/아야.jpg", "화영", null, 73, 93, 71, 0, 160, 300, null, null, "깨끗하고 올바른 샤메이마루입니다."], - [0, "아리오슈", null, 0, null, 56, 70, 67, 0, 160, 300, null, null], - [0, "써니", "환상향/써니.jpg", "구문", null, 75, 75, 60, 0, 160, 300, null, null, "어라, 혼자서도 최강이라며?"], - [0, "쌍자", null, 0, null, 63, 82, 71, 0, 160, 300, null, null], - [0, "쌍사", null, 0, null, 62, 73, 81, 0, 160, 300, null, null], - [0, "신키", "환상향/신키.jpg", "괴기", null, 96, 92, 73, 0, 160, 300, null, null, "나의 세계에서 저지른 죄값은 크지. 용서할 리가 없잖아."], - [0, "신묘마루", "환상향/신묘마루.png", "휘침", null, 85, 68, 88, 0, 160, 300, null, null, "누─구?"], - [0, "시키에이키", "환상향/시키에이키.jpg", "화영", null, 84, 77, 98, 0, 160, 300, null, null, "보랏빛 벚꽃은 죄 깊은 인간의 영혼이 깃드는 꽃. 너는 그 보랏빛 벚꽃이 떨어져내리는 밑에서, 단죄하거라!"], - [0, "시키에이카", null, 0, null, 71, 60, 77, 0, 160, 300, null, null], - [0, "시즈하", "환상향/시즈하.jpg", "풍신", null, 79, 55, 62, 0, 160, 300, null, null], - [0, "시온", null, "외전", null, 80, 97, 76, 0, 160, 300, null, null, "나도 좋아서 가난하게 살고 있는 게 아닌데~"], - [0, "스타", "환상향/스타.jpg", "구문", null, 70, 60, 75, 0, 160, 300, null, null, "그래, 이 비겁한 녀석아!"], - [0, "스이카", "환상향/스이카.jpg", "외전", null, 74, 102, 66, 0, 160, 300, null, null, "거짓말은 인간만 하는 거야!"], - [0, "스와코", "환상향/스와코.jpg", "풍신", null, 95, 81, 81, 0, 160, 300, null, null, "아~우~~"], - [0, "스씨", null, 0, null, 66, 63, 66, 0, 160, 300, null, null], - [0, "스미레코", "환상향/스미레코.jpg", "외전", null, 81, 63, 88, 0, 160, 300, null, null, "히가시후카미 고등학교 1학년, 우사미 스미레코. 우는 아이도 뚝 그치는 진짜 초능력자야."], - [0, "순호", "환상향/순호.jpg", "감주", null, 77, 102, 79, 0, 160, 300, null, null, "지금의 너로썬 내 힘 앞에선 무력, 절대적으로 무력하도다!"], - [0, "쇼", "환상향/쇼.jpg", "성령", null, 75, 75, 95, 0, 160, 300, null, null, "그것이 당신의 정의라는 거라면, 그것도 좋지요."], - [0, "소악마", "환상향/소악마.jpg", "홍마", null, 50, 72, 67, 0, 160, 300, null, null], - [0, "셰퍼드", null, 0, null, 61, 58, 56, 0, 160, 300, null, null], - [0, "센고", null, 0, null, 83, 56, 79, 0, 160, 300, null, null], - [0, "센", null, 0, null, 67, 66, 28, 0, 160, 300, null, null], - [0, "세키반키", "환상향/세키반키.png", "휘침", null, 68, 75, 71, 0, 160, 300, null, null, "인간은 누구든지 나를 무서워하지!"], - [0, "세츠조", null, 0, null, 58, 80, 58, 0, 160, 300, null, null], - [0, "세이자", "환상향/세이자.png", "휘침", null, 82, 94, 69, 0, 160, 300, null, null, "내 이름은 세이자. 뼛속까지 아마노자쿠다!"], - [0, "세이란", "환상향/세이란.jpg", "감주", null, 71, 77, 51, 0, 160, 300, null, null, "뭐, 이것도 일이야, 알지? 그러니 내게 여기서 얌전히 당해 줘. 응?"], - [0, "샤리트", null, 0, null, 68, 62, 62, 0, 160, 300, null, null], - [0, "상해인형", null, 0, null, 74, 69, 69, 0, 160, 300, null, null], - [0, "상아", null, 0, null, 84, 84, 84, 0, 160, 300, null, null], - [0, "사토리", "환상향/사토리.jpg", "지령", null, 81, 57, 84, 0, 160, 300, null, null, "제게는 보여요. 당신이 마음속에 그리고 있는 탄막이!"], - [0, "사토노", "환상향/사토노.png", "천공", null, 71, 76, 77, 0, 160, 300, null, null, "봐 주진 않겠지만 죽으면 죽일꺼니까 말야―!"], - [0, "사쿠야", "환상향/사쿠야.jpg", "홍마", null, 87, 89, 78, 0, 160, 300, null, null, "당신의 시간 또한 나의 것"], - [0, "사샤", null, 0, null, 74, 48, 78, 0, 160, 300, null, null], - [0, "사리엘", "환상향/사리엘.jpg", "영이", null, 85, 68, 102, 0, 160, 300, null, null, "죽는다면 모두 함께"], - [0, "사라카", null, 0, null, 60, 74, 63, 0, 160, 300, null, null], - [0, "사라", "환상향/사라.jpg", "괴기", null, 76, 78, 36, 0, 160, 300, null, null, "싸우는 건 재미있지만, 지는 건 재미없어."], - [0, "사나에", "환상향/사나에.jpg", "풍신", null, 87, 71, 85, 0, 160, 300, null, null, "지금은, 바깥 세계는 환경을 지키라든가 경기 대책이 어떻다든가, 바보 같이 뭘 위해 태어난 건지 생각할 수 없는 사람 뿐."], - [0, "사구메", "환상향/사구메.jpg", "감주", null, 84, 97, 71, 0, 160, 300, null, null, "......그런건 아냐."], - [0, "벤벤", "환상향/벤벤.png", "휘침", null, 69, 59, 75, 0, 160, 300, null, null, "어딜 봐도 츠쿠모가미잖아."], - [0, "뱌쿠렌", "환상향/뱌쿠렌.jpg", "성령", null, 83, 90, 82, 0, 160, 300, null, null, "아아, 법의 세계에 빛이 가득해."], - [0, "미호", null, 0, null, 85, 86, 62, 0, 160, 300, null, null], - [0, "미하나", null, 0, null, 76, 67, 84, 0, 160, 300, null, null], - [0, "미토리", null, 0, null, 57, 56, 62, 0, 160, 300, null, null], - [0, "미코토", null, 0, null, 73, 82, 82, 0, 160, 300, null, null], - [0, "미코", "환상향/미코.png", "신령", null, 87, 72, 97, 0, 160, 300, null, null, "장난은 끝이다!"], - [0, "미요", null, 0, null, 73, 66, 58, 0, 160, 300, null, null], - [0, "미스티아", "환상향/미스티아.jpg", "영야", null, 65, 82, 12, 0, 160, 300, null, null, "자, 잠깐 기다려~!"], - [0, "미셸", null, 0, null, 70, 69, 72, 0, 160, 300, null, null], - [0, "미샨드라", null, 0, null, 80, 63, 82, 0, 160, 300, null, null], - [0, "미샤구지", null, "풍신", null, 48, 99, 59, 0, 160, 300, null, null], - [0, "미마", "환상향/미마.jpg", "봉마", null, 75, 90, 93, 0, 160, 300, null, null, "나도 인간계의 신이야. (거짓말)"], - [0, "미노리코", "환상향/미노리코.jpg", "풍신", null, 71, 58, 74, 0, 160, 300, null, null, "갓 수확한 고구마는 나의 향수. 무녀가 먹게 놔둘 줄 알고!"], - [0, "미나유", null, 0, null, 72, 49, 84, 0, 160, 300, null, null], - [0, "무무무", null, 0, null, 70, 77, 57, 0, 160, 300, null, null], - [0, "무라사", "환상향/무라사.jpg", "성령", null, 76, 82, 65, 0, 160, 300, null, null, "이 세상에 미련이 남으신 분은 승선할 수 없습니다만 당신은 어떠신가요?"], - [0, "무게츠", "환상향/무게츠.jpg", "환상", null, 76, 95, 68, 0, 160, 300, null, null, "난 무게츠. 그리고, 여긴 나의 세계."], - [0, "묘렌", null, 0, null, 77, 78, 79, 0, 160, 300, null, null], - [0, "모코우", "환상향/모코우.jpg", "영야", null, 83, 99, 69, 0, 160, 300, null, null, "너희들도 한번 영원의 고륜을 고민해 보는 게 좋아!"], - [0, "모미지", "환상향/모미지.jpg", "풍신", null, 83, 68, 66, 0, 160, 300, null, null, "높은 분들은 저희 일반 텐구보다 훨씬 강하니까, 경비 같은 건 전혀 필요하지 않을 것 같은데."], - [0, "모모히메", null, 0, null, 75, 68, 72, 0, 160, 300, null, null], - [0, "모모", null, 0, null, 62, 62, 71, 0, 160, 300, null, null], - [0, "메이즈", null, 0, null, 70, 40, 76, 0, 160, 300, null, null], - [0, "메이벨", null, 0, null, 71, 74, 56, 0, 160, 300, null, null], - [0, "메이라", "환상향/메이라.jpg", "봉마", null, 65, 82, 36, 0, 160, 300, null, null, "내가 이기면 하쿠레이의 힘은 내가 가져가겠어!"], - [0, "메를린", "환상향/메를린.jpg", "화영", null, 73, 67, 70, 0, 160, 300, null, null, "내 연주를 듣고서, 무사했던 식량은 없지."], - [0, "메디슨", "환상향/메디슨.jpg", "화영", null, 73, 58, 71, 0, 160, 300, null, null, "콘파로, 콘파로, 독이여 모여라~"], - [0, "메구미", null, 0, null, 83, 77, 74, 0, 160, 300, null, null], - [0, "마이", "환상향/마이.jpg", "괴기", null, 72, 60, 75, 0, 160, 300, null, null, "내가 안내할게..."], - [0, "마을사람5", null, 0, null, 65, 35, 50, 0, 160, 300, null, null], - [0, "마을사람4", null, 0, null, 64, 34, 50, 0, 160, 300, null, null], - [0, "마을사람3", null, 0, null, 63, 33, 50, 0, 160, 300, null, null], - [0, "마을사람2", null, 0, null, 62, 32, 50, 0, 160, 300, null, null], - [0, "마을사람1", null, 0, null, 61, 31, 50, 0, 160, 300, null, null], - [0, "마미조", "환상향/마미조.png", "신령", null, 77, 77, 97, 0, 160, 300, null, null, "뭔가 고민이 있는 모양이구먼. 야그나 함 들어 보자."], - [0, "마리우스", null, 0, null, 67, 87, 66, 0, 160, 300, null, null], - [0, "마리사", "환상향/마리사.jpg", 0, null, 83, 91, 78, 0, 160, 300, null, null, "아~? 탄막에 두뇌? 바보 아냐? 탄막은 파워야."], - [0, "마나", null, 0, null, 73, 75, 72, 0, 160, 300, null, null], - [0, "링고", "환상향/링고.jpg", "감주", null, 54, 76, 71, 0, 160, 300, null, null, "옛날의 극락정토는 이제 보이는 그림자도 없다고 생각해."], - [0, "릴리", "환상향/릴리.jpg", "요요", null, 37, 63, 68, 0, 160, 300, null, null, "봄이에요~"], - [0, "린네", null, 0, null, 83, 66, 83, 0, 160, 300, null, null], - [0, "리코", null, 0, null, 68, 60, 76, 0, 160, 300, null, null], - [0, "리카코", "환상향/리카코.jpg", "몽시", null, 76, 71, 100, 0, 160, 300, null, null, "왜 마법 따위에 흥미를 가지기나 하지?"], - [0, "리카", "환상향/리카.jpg", "봉마", null, 61, 63, 75, 0, 160, 300, null, null, "가는 거예요!!"], - [0, "리리카", "환상향/리리카.jpg", "화영", null, 57, 66, 83, 0, 160, 300, null, null, "당신은 노래를 부르지. 난 손을 쓰지 않고 연주를 해. 장기전으로 가면 내 쪽이 세다고?"], - [0, "리락", null, 0, null, 74, 54, 62, 0, 160, 300, null, null], - [0, "리글", "환상향/리글.jpg", "영야", null, 85, 64, 51, 0, 160, 300, null, null, "달이 보이는 밤에는, 즐기지 않아선 안 되는 거 아닐까."], - [0, "루이즈", "환상향/루이즈.jpg", "괴기", null, 59, 57, 72, 0, 160, 300, null, null, "뭐, 마계는 좋은 곳이니, 느긋하게 관광이라도 하도록 해☆"], - [0, "루미아", "환상향/루미아.jpg", "홍마", null, 79, 70, 56, 0, 160, 300, null, null, "그런 건가~"], - [0, "루리", null, 0, null, 66, 66, 62, 0, 160, 300, null, null], - [0, "루나사", "환상향/루나사.jpg", "화영", null, 76, 77, 50, 0, 160, 300, null, null, "넌 슬픈 처지에서 태어난 경우야. 들떠있을 때가 아니지."], - [0, "루나", "환상향/루나.jpg", "구문", null, 67, 74, 74, 0, 160, 300, null, null, "빨라고? 네녀석이 빨아라!"], - [0, "레티", "환상향/레티.jpg", "요요", null, 60, 79, 49, 0, 160, 300, null, null, "흑막~"], - [0, "레이센", "환상향/레이센.jpg", "영야", null, 79, 78, 89, 0, 160, 300, null, null, "이 앞에 기다리고 있는 게 악몽이라 해도, 그래도 나는 가야만 해."], - [0, "레이무", "환상향/레이무.jpg", 0, null, 90, 83, 82, 0, 160, 300, null, null, "인간을 쉽사리 죽게 내버려 둘까 보냐!"], - [0, "레이라", null, 0, null, 66, 67, 51, 0, 160, 300, null, null], - [0, "레밀리아", "환상향/레밀리아.jpg", "홍마", null, 93, 94, 70, 0, 160, 300, null, null, "당신은 이제껏 먹어 온 빵이 몇 개인지 알고 있어?"], - [0, "런던인형", null, 0, null, 64, 78, 62, 0, 160, 300, null, null], - [0, "란", "환상향/란.jpg", "요요", null, 71, 79, 101, 0, 160, 300, null, null, "흠. 첸을 괴롭힌건 너지?"], - [0, "라쿠", null, 0, null, 71, 52, 69, 0, 160, 300, null, null], - [0, "라이코", "환상향/라이코.png", "휘침", null, 80, 66, 91, 0, 160, 300, null, null, "누구라도 자기 목숨을 부지하고 싶으면 노력을 아끼지 않잖아?"], - [0, "라바", "환상향/라바.png", "천공", null, 69, 74, 54, 0, 160, 300, null, null, "지금이라면 신세계의 신이라도 될 것 같은 기분이야!"], - [0, "도레미", "환상향/도레미.jpg", "감주", null, 67, 63, 88, 0, 160, 300, null, null, "맨몸!? 혹시 맨몸!?"], - [0, "대요정", "환상향/대요정.jpg", "홍마", null, 60, 56, 79, 0, 160, 300, null, null], - [0, "달토끼5", null, 0, null, 35, 55, 55, 0, 160, 300, null, null], - [0, "달토끼4", null, 0, null, 34, 54, 55, 0, 160, 300, null, null], - [0, "달토끼3", null, 0, null, 33, 53, 55, 0, 160, 300, null, null], - [0, "달토끼2", null, 0, null, 32, 52, 55, 0, 160, 300, null, null], - [0, "달토끼1", null, 0, null, 31, 51, 55, 0, 160, 300, null, null], - [0, "니토리", "환상향/니토리.jpg", "풍신", null, 68, 68, 76, 0, 160, 300, null, null, "축제 땐 생선이 잘 팔리지. 인간에게 어업권을 뺏어서 독점하면 떼돈 벌겠네!"], - [0, "노바", null, 0, null, 74, 56, 82, 0, 160, 300, null, null], - [0, "노로이코", "환상향/노로이코.jpg", "봉마", null, 56, 61, 65, 0, 160, 300, null, null], - [0, "네즈", null, 0, null, 76, 71, 40, 0, 160, 300, null, null], - [0, "네무노", "환상향/네무노.png", "천공", null, 78, 40, 67, 0, 160, 300, null, null, "자자, 차라도 마시고 가라."], - [0, "나즈린", "환상향/나즈린.jpg", "성령", null, 76, 63, 64, 0, 160, 300, null, null, "미안한데, 쥐를 얕보다간 죽어."], - [0, "나루코", null, 0, null, 68, 40, 70, 0, 160, 300, null, null], - [0, "나루미", "환상향/나루미.png", "천공", null, 84, 72, 78, 0, 160, 300, null, null, "에? 뭐야 그거 무서워"], - [0, "구유카", "환상향/구유카.jpg", "환상", null, 84, 85, 85, 0, 160, 300, null, null, "신이라 해도, 힘이야말로 전부야!"], - [0, "구앨리스", "환상향/구앨리스.jpg", "괴기", null, 80, 80, 90, 0, 160, 300, null, null, "궁극의 마법으로 널 해치워 주겠어!!"], - [0, "구마리사", "환상향/구마리사.jpg", "봉마", null, 80, 90, 80, 0, 160, 300, null, null, "오케이~, 여기는 마리사에게 맡겨줘."], - [0, "구레이무", "환상향/구레이무.jpg", "영이", null, 90, 80, 80, 0, 160, 300, null, null, "문답무용이야!!"], - [0, "곽청아", "환상향/곽청아.png", "신령", null, 72, 77, 87, 0, 160, 300, null, null, "네~ 썩어서 귀엽죠?"], - [0, "골리앗", null, 0, null, 72, 78, 58, 0, 160, 300, null, null], - [0, "겐지", null, "봉마", null, 70, 70, 70, 0, 160, 300, null, null], - [0, "겐게츠", "환상향/겐게츠.jpg", "환상", null, 83, 70, 94, 0, 160, 300, null, null, "얕보지 마. 우린, 둘이서 한 사람 몫이니까.…"], - [0, "강림", null, 0, null, 84, 76, 57, 0, 160, 300, null, null] + [0,"히카리코",null,"환상",null,56,66,21,0,160,300,null,null], + [0,"히메미코",null,0,null,68,78,65,0,160,300,null,null], + [0,"히나",null,"풍신",null,66,63,83,0,160,300,null,null,"온갖 재액이 닥쳐올 거야. 인간을 지키기 위해서라도 절대 보내주지 않겠어!"], + [0,"후토",null,"신령",null,84,69,93,0,160,300,null,null,"불이라도 지를까."], + [0,"홍미령",null,"홍마",null,85,79,63,0,160,300,null,null,"젠장, 배수진이다!"], + [0,"호쥬누에",null,"성령",null,85,95,72,0,160,300,null,null,"자아, 나의 정체를 알아버린 당신을 죽여버리고, 정체불명으로 돌아가야겠어."], + [0,"호라이인형",null,0,null,66,71,73,0,160,300,null,null], + [0,"헤카티아",null,"감주",null,85,84,94,0,160,300,null,null,"꿈의 세계에선 무방비하니까 간단한 일이었지."], + [0,"하타테",null,"외전",null,30,88,70,0,160,300,null,null,"둘이서 더블스포일러가 되어서, 최강을 노리는 거야!"], + [0,"하츠나",null,0,null,63,61,65,0,160,300,null,null], + [0,"하니에",null,0,null,57,66,63,0,160,300,null,null], + [0,"플랑드르",null,"홍마",null,76,101,71,0,160,300,null,null,"쿠욱하고 콰광~이네"], + [0,"프랑스인형",null,0,null,68,68,75,0,160,300,null,null], + [0,"파츄리",null,"홍마",null,81,52,103,0,160,300,null,null,"무큐-"], + [0,"파스칼",null,0,null,73,70,60,0,160,300,null,null], + [0,"파브리티스",null,0,null,61,61,62,0,160,300,null,null], + [0,"파르시",null,"지령",null,67,49,89,0,160,300,null,null,"너한테 원한은 없다만, 내가 널 공격하는 이유는 얼마든지 만들 수 있어."], + [0,"토키코",null,"구문",null,60,59,88,0,160,300,null,null], + [0,"토쿠비",null,0,null,77,77,72,0,160,300,null,null], + [0,"토지코",null,"신령",null,76,62,86,0,160,300,null,null,"아이고 이런. 또 하나요?"], + [0,"토요히메",null,"구문",null,96,69,95,0,160,300,null,null], + [0,"토비코",null,0,null,72,58,56,0,160,300,null,null], + [0,"텐시",null,"외전",null,81,89,71,0,160,300,null,null,"약해! 약해 빠졌어! 그따위로 하늘에 오를 수 있을 거라 꿈도 꾸지 마!"], + [0,"텐무",null,0,null,68,73,56,0,160,300,null,null], + [0,"텐구5",null,0,null,51,51,50,0,160,300,null,null], + [0,"텐구4",null,0,null,51,51,50,0,160,300,null,null], + [0,"텐구3",null,0,null,51,51,50,0,160,300,null,null], + [0,"텐구2",null,0,null,51,51,50,0,160,300,null,null], + [0,"텐구1",null,0,null,51,51,50,0,160,300,null,null], + [0,"테이레이다",null,"천공",null,78,75,53,0,160,300,null,null,"갑작스럽겠지만, 테스트를 시작하겠어!]"], + [0,"테위",null,"영야",null,82,32,79,0,160,300,null,null,"대숲은 사람을 헤매게 하는 대나무의 미로. 헤매는 것은 산사람다운 행위인 거야."], + [0,"키쿠리",null,"영이",null,56,72,76,0,160,300,null,null], + [0,"키요히메",null,0,null,66,76,76,0,160,300,null,null], + [0,"키스메",null,"지령",null,73,58,59,0,160,300,null,null,"너희가 떨어뜨린 시체는 이거냐?"], + [0,"키나코",null,0,null,63,68,67,0,160,300,null,null], + [0,"클라운피스",null,"감주",null,89,80,77,0,160,300,null,null,"잇츠, 루나틱 타임! 광기의 세계로 어서오세요!"], + [0,"쿠스코",null,0,null,70,57,53,0,160,300,null,null], + [0,"쿠루미",null,"환상",null,70,65,68,0,160,300,null,null,"나는 쿠루미. 이 호수의 문지기야."], + [0,"쿄코",null,"신령",null,37,57,80,0,160,300,null,null,"목소리가 작다!"], + [0,"콘가라",null,"영이",null,91,96,60,0,160,300,null,null], + [0,"코토히메",null,"몽시",null,54,36,90,0,160,300,null,null,"충격의 새로운 사실! 고대인은 전기를 쓰고 있었다!"], + [0,"코코로",null,"외전",null,80,91,60,0,160,300,null,null,"나와 최강의 칭호를 걸고 싸우자!"], + [0,"코카이",null,0,null,67,78,61,0,160,300,null,null], + [0,"코이시",null,"지령",null,88,71,93,0,160,300,null,null,"네 시체는 지령전 입구에 장식해줄게!"], + [0,"코스즈",null,"구문",null,61,63,70,0,160,300,null,null,"요괴가 하는 말을 믿으시는 거예요?!"], + [0,"코마치",null,"화영",null,80,97,75,0,160,300,null,null,"더는 어찌할 도리가 없어. 나는 그런 인간은 버릴 거야."], + [0,"코노하",null,0,null,62,58,67,0,160,300,null,null], + [0,"코가사",null,"성령",null,79,76,56,0,160,300,null,null,"됐어. 어차피 요괴는 이렇게 쓸쓸하게 사라져나가는 거니까."], + [0,"케이네",null,"영야",null,77,71,104,0,160,300,null,null,"여기엔 원래부터 아무것도 없었어, 인간도 인간의 마을도 말이야."], + [0,"캇파5",null,0,null,40,40,65,0,160,300,null,null], + [0,"캇파4",null,0,null,40,40,64,0,160,300,null,null], + [0,"캇파3",null,0,null,40,40,63,0,160,300,null,null], + [0,"캇파2",null,0,null,40,40,62,0,160,300,null,null], + [0,"캇파1",null,0,null,40,40,61,0,160,300,null,null], + [0,"카스미",null,0,null,67,63,82,0,160,300,null,null], + [0,"카센",null,"외전",null,72,103,71,0,160,300,null,null,"이런 곳에서 전력을 다할 이유가없지."], + [0,"카나코",null,"풍신",null,84,97,77,0,160,300,null,null,"나를 부른 이는 누구인가?"], + [0,"카나",null,"몽시",null,64,72,57,0,160,300,null,null,"아, 아니, 그러니까, 우주인?]"], + [0,"카구야",null,"영야",null,98,71,82,0,160,300,null,null,"내 영원을 다루는 술법으로 모두 깨뜨려 보이겠어."], + [0,"카게로",null,"휘침",null,78,88,68,0,160,300,null,null,"무서워~ 인간 무서워~"], + [0,"치유리",null,"몽시",null,45,77,78,0,160,300,null,null,"싸워서 강한 쪽이 이 세계의 치유리라구."], + [0,"치르노",null,0,null,79,89,49,0,160,300,null,null,"눈앞에 강적이 있잖아?"], + [0,"츠바메",null,0,null,71,80,58,0,160,300,null,null], + [0,"첸",null,"요요",null,48,70,70,0,160,300,null,null,"그건 그렇고, 마요이가에 어서와"], + [0,"지벤",null,0,null,85,67,66,0,160,300,null,null], + [0,"지귀",null,0,null,79,67,69,0,160,300,null,null], + [0,"조온",null,"외전",null,83,74,96,0,160,300,null,null,"우리가 왜 최흉최악의 자매인지 그 몸으로 이해해라!"], + [0,"자넷",null,0,null,60,59,62,0,160,300,null,null], + [0,"이포니아",null,0,null,61,62,61,0,160,300,null,null], + [0,"이쿠",null,"외전",null,72,72,94,0,160,300,null,null,"조금 벌을 받으실 필요가 있을지도 모르겠군요."], + [0,"이치요우",null,0,null,70,73,58,0,160,300,null,null], + [0,"이치린",null,"성령",null,86,55,88,0,160,300,null,null,"평화롭게 살고있으니까 괜찮잖아. 너, 요괴를 죽여도 아무렇지 않아?"], + [0,"이슈아",null,0,null,67,76,78,0,160,300,null,null], + [0,"이나코",null,0,null,56,35,63,0,160,300,null,null], + [0,"유키",null,"괴기",null,72,77,61,0,160,300,null,null,"우리 둘을 쓰러뜨릴 수 있을 거라고 생각해?"], + [0,"유카리",null,"요요",null,87,76,96,0,160,300,null,null,"아름답고 잔혹하게 이 대지에서 사라져라!"], + [0,"유카",null,"화영",null,94,88,70,0,160,300,null,null,"아 그래, 마침 60년 전이었네. 아아...... 그래, 벌써 그런 시기구나...... 60년인가. 회귀할 때구나."], + [0,"유유코",null,"요요",null,97,80,81,0,160,300,null,null,"새는 잔뼈가 많아서 싫어."], + [0,"유메코",null,"괴기",null,72,103,73,0,160,300,null,null,"우리들을 모욕한 죄는 죽음으로 밖에 갚을 수 없다!!"], + [0,"유메미",null,"몽시",null,93,72,91,0,160,300,null,null,"기합 넣어! 죽어 버렸다간 데리고 돌아갈 수 없으니까."], + [0,"유기",null,"지령",null,71,104,72,0,160,300,null,null,"네가 강하기만 하다면 나야 아무 군소리도 안해. 어떤 목적으로 여기에 왔건 간에."], + [0,"유겐마간",null,"영이",null,80,80,50,0,160,300,null,null], + [0,"우카",null,0,null,60,73,82,0,160,300,null,null], + [0,"우츠호",null,"지령",null,73,105,68,0,160,300,null,null,"이물질 발견! 핵융합로의 이물질 혼입은 일단 반응을 정지하고, 즉각 이물질을 제거하라."], + [0,"요정5",null,0,null,55,60,35,0,160,300,null,null], + [0,"요정4",null,0,null,55,59,34,0,160,300,null,null], + [0,"요정3",null,0,null,55,58,33,0,160,300,null,null], + [0,"요정2",null,0,null,55,57,32,0,160,300,null,null], + [0,"요정1",null,0,null,55,56,31,0,160,300,null,null], + [0,"요우무",null,"요요",null,81,89,71,0,160,300,null,null,"...요괴가 담금질한 이 누관검에, 벨 수 없는 것 따위, 별로 없어!"], + [0,"요시카",null,"신령",null,84,78,61,0,160,300,null,null,"오~지~마~!"], + [0,"요리히메",null,"구문",null,90,98,74,0,160,300,null,null], + [0,"와카챠나두",null,0,null,62,61,68,0,160,300,null,null], + [0,"와카사기",null,"휘침",null,68,57,79,0,160,300,null,null,"슬슬 우리요괴가 활약할 차례야!"], + [0,"오토히메",null,0,null,80,60,76,0,160,300,null,null], + [0,"오키나",null,"천공",null,87,87,86,0,160,300,null,null,"요괴가 아니라 신이에요~"], + [0,"오모이카네",null,0,null,66,52,86,0,160,300,null,null], + [0,"오린",null,"지령",null,87,84,77,0,160,300,null,null,"시체가 갖고 싶으니까, 역시 내가 언니를 처리해야겠네!"], + [0,"오렌지",null,"환상",null,72,52,68,0,160,300,null,null,"아하하, 1면 보스니까라고 얕보지 말라구!"], + [0,"오니5",null,0,null,55,65,40,0,160,300,null,null], + [0,"오니4",null,0,null,54,64,40,0,160,300,null,null], + [0,"오니3",null,0,null,53,63,40,0,160,300,null,null], + [0,"오니2",null,0,null,52,62,40,0,160,300,null,null], + [0,"오니1",null,0,null,51,61,40,0,160,300,null,null], + [0,"엘핀민트",null,0,null,71,71,61,0,160,300,null,null], + [0,"엘리스",null,"영이",null,55,69,88,0,160,300,null,null], + [0,"엘리",null,"환상",null,86,83,66,0,160,300,null,null,"싸운 거 오랜만인걸 뭐."], + [0,"엘렌",null,"몽시",null,67,62,71,0,160,300,null,null,"나는, 엘렌. 폭신폭신 엘렌이야."], + [0,"에이린",null,"영야",null,72,76,105,0,160,300,null,null,"아하하. 너희는 어리석구나."], + [0,"야토",null,0,null,72,83,66,0,160,300,null,null], + [0,"야츠하시",null,"휘침",null,69,58,73,0,160,300,null,null,"훗훗후, 반역자에겐 어떤 수단이든 써도 된다면서?"], + [0,"야마메",null,"지령",null,75,65,77,0,160,300,null,null,"지저에 떨어진 요괴의 힘을 보고 싶은 모양이구나."], + [0,"앨리스",null,"요요",null,81,63,99,0,160,300,null,null,"그런 소리 하니까 바보 취급당하는 거야. 탄막은 브레인. 상식이야."], + [0,"안자",null,0,null,67,61,83,0,160,300,null,null], + [0,"안",null,0,null,68,51,67,0,160,300,null,null], + [0,"아큐",null,"구문",null,72,30,82,0,160,300,null,null,"나는 이래뵈도 히에다가의 당주로써 환상향의 서기라는 역할을 지닌 유서깊은 집안의 사람이야."], + [0,"아즈마",null,0,null,78,81,64,0,160,300,null,null], + [0,"아운",null,"천공",null,78,87,65,0,160,300,null,null,"신사를 지키는 수호신으로서 말이야!"], + [0,"아야",null,"화영",null,73,93,71,0,160,300,null,null,"깨끗하고 올바른 샤메이마루입니다."], + [0,"아리오슈",null,0,null,56,70,67,0,160,300,null,null], + [0,"써니",null,"구문",null,75,75,60,0,160,300,null,null,"어라, 혼자서도 최강이라며?"], + [0,"쌍자",null,0,null,63,82,71,0,160,300,null,null], + [0,"쌍사",null,0,null,62,73,81,0,160,300,null,null], + [0,"신키",null,"괴기",null,96,92,73,0,160,300,null,null,"나의 세계에서 저지른 죄값은 크지. 용서할 리가 없잖아."], + [0,"신묘마루",null,"휘침",null,85,68,88,0,160,300,null,null,"누─구?"], + [0,"시키에이키",null,"화영",null,84,77,98,0,160,300,null,null,"보랏빛 벚꽃은 죄 깊은 인간의 영혼이 깃드는 꽃. 너는 그 보랏빛 벚꽃이 떨어져내리는 밑에서, 단죄하거라!"], + [0,"시키에이카",null,0,null,71,60,77,0,160,300,null,null], + [0,"시즈하",null,"풍신",null,79,55,62,0,160,300,null,null], + [0,"시온",null,"외전",null,80,97,76,0,160,300,null,null,"나도 좋아서 가난하게 살고 있는 게 아닌데~"], + [0,"스타",null,"구문",null,70,60,75,0,160,300,null,null,"그래, 이 비겁한 녀석아!"], + [0,"스이카",null,"외전",null,74,102,66,0,160,300,null,null,"거짓말은 인간만 하는 거야!"], + [0,"스와코",null,"풍신",null,95,81,81,0,160,300,null,null,"아~우~~"], + [0,"스씨",null,0,null,66,63,66,0,160,300,null,null], + [0,"스미레코",null,"외전",null,81,63,88,0,160,300,null,null,"히가시후카미 고등학교 1학년, 우사미 스미레코. 우는 아이도 뚝 그치는 진짜 초능력자야."], + [0,"순호",null,"감주",null,77,102,79,0,160,300,null,null,"지금의 너로썬 내 힘 앞에선 무력, 절대적으로 무력하도다!"], + [0,"쇼",null,"성령",null,75,75,95,0,160,300,null,null,"그것이 당신의 정의라는 거라면, 그것도 좋지요."], + [0,"소악마",null,"홍마",null,50,72,67,0,160,300,null,null], + [0,"셰퍼드",null,0,null,61,58,56,0,160,300,null,null], + [0,"센고",null,0,null,83,56,79,0,160,300,null,null], + [0,"센",null,0,null,67,66,28,0,160,300,null,null], + [0,"세키반키",null,"휘침",null,68,75,71,0,160,300,null,null,"인간은 누구든지 나를 무서워하지!"], + [0,"세츠조",null,0,null,58,80,58,0,160,300,null,null], + [0,"세이자",null,"휘침",null,82,94,69,0,160,300,null,null,"내 이름은 세이자. 뼛속까지 아마노자쿠다!"], + [0,"세이란",null,"감주",null,71,77,51,0,160,300,null,null,"뭐, 이것도 일이야, 알지? 그러니 내게 여기서 얌전히 당해 줘. 응?"], + [0,"샤리트",null,0,null,68,62,62,0,160,300,null,null], + [0,"상해인형",null,0,null,74,69,69,0,160,300,null,null], + [0,"상아",null,0,null,84,84,84,0,160,300,null,null], + [0,"사토리",null,"지령",null,81,57,84,0,160,300,null,null,"제게는 보여요. 당신이 마음속에 그리고 있는 탄막이!"], + [0,"사토노",null,"천공",null,71,76,77,0,160,300,null,null,"봐 주진 않겠지만 죽으면 죽일꺼니까 말야―!"], + [0,"사쿠야",null,"홍마",null,87,89,78,0,160,300,null,null,"당신의 시간 또한 나의 것"], + [0,"사샤",null,0,null,74,48,78,0,160,300,null,null], + [0,"사리엘",null,"영이",null,85,68,102,0,160,300,null,null,"죽는다면 모두 함께"], + [0,"사라카",null,0,null,60,74,63,0,160,300,null,null], + [0,"사라",null,"괴기",null,76,78,36,0,160,300,null,null,"싸우는 건 재미있지만, 지는 건 재미없어."], + [0,"사나에",null,"풍신",null,87,71,85,0,160,300,null,null,"지금은, 바깥 세계는 환경을 지키라든가 경기 대책이 어떻다든가, 바보 같이 뭘 위해 태어난 건지 생각할 수 없는 사람 뿐."], + [0,"사구메",null,"감주",null,84,97,71,0,160,300,null,null,"......그런건 아냐."], + [0,"벤벤",null,"휘침",null,69,59,75,0,160,300,null,null,"어딜 봐도 츠쿠모가미잖아."], + [0,"뱌쿠렌",null,"성령",null,83,90,82,0,160,300,null,null,"아아, 법의 세계에 빛이 가득해."], + [0,"미호",null,0,null,85,86,62,0,160,300,null,null], + [0,"미하나",null,0,null,76,67,84,0,160,300,null,null], + [0,"미토리",null,0,null,57,56,62,0,160,300,null,null], + [0,"미코토",null,0,null,73,82,82,0,160,300,null,null], + [0,"미코",null,"신령",null,87,72,97,0,160,300,null,null,"장난은 끝이다!"], + [0,"미요",null,0,null,73,66,58,0,160,300,null,null], + [0,"미스티아",null,"영야",null,65,82,12,0,160,300,null,null,"자, 잠깐 기다려~!"], + [0,"미셸",null,0,null,70,69,72,0,160,300,null,null], + [0,"미샨드라",null,0,null,80,63,82,0,160,300,null,null], + [0,"미샤구지",null,"풍신",null,48,99,59,0,160,300,null,null], + [0,"미마",null,"봉마",null,75,90,93,0,160,300,null,null,"나도 인간계의 신이야. (거짓말)"], + [0,"미노리코",null,"풍신",null,71,58,74,0,160,300,null,null,"갓 수확한 고구마는 나의 향수. 무녀가 먹게 놔둘 줄 알고!"], + [0,"미나유",null,0,null,72,49,84,0,160,300,null,null], + [0,"무무무",null,0,null,70,77,57,0,160,300,null,null], + [0,"무라사",null,"성령",null,76,82,65,0,160,300,null,null,"이 세상에 미련이 남으신 분은 승선할 수 없습니다만 당신은 어떠신가요?"], + [0,"무게츠",null,"환상",null,76,95,68,0,160,300,null,null,"난 무게츠. 그리고, 여긴 나의 세계."], + [0,"묘렌",null,0,null,77,78,79,0,160,300,null,null], + [0,"모코우",null,"영야",null,83,99,69,0,160,300,null,null,"너희들도 한번 영원의 고륜을 고민해 보는 게 좋아!"], + [0,"모미지",null,"풍신",null,83,68,66,0,160,300,null,null,"높은 분들은 저희 일반 텐구보다 훨씬 강하니까, 경비 같은 건 전혀 필요하지 않을 것 같은데."], + [0,"모모히메",null,0,null,75,68,72,0,160,300,null,null], + [0,"모모",null,0,null,62,62,71,0,160,300,null,null], + [0,"메이즈",null,0,null,70,40,76,0,160,300,null,null], + [0,"메이벨",null,0,null,71,74,56,0,160,300,null,null], + [0,"메이라",null,"봉마",null,65,82,36,0,160,300,null,null,"내가 이기면 하쿠레이의 힘은 내가 가져가겠어!"], + [0,"메를린",null,"화영",null,73,67,70,0,160,300,null,null,"내 연주를 듣고서, 무사했던 식량은 없지."], + [0,"메디슨",null,"화영",null,73,58,71,0,160,300,null,null,"콘파로, 콘파로, 독이여 모여라~"], + [0,"메구미",null,0,null,83,77,74,0,160,300,null,null], + [0,"마이",null,"괴기",null,72,60,75,0,160,300,null,null,"내가 안내할게..."], + [0,"마을사람5",null,0,null,65,35,50,0,160,300,null,null], + [0,"마을사람4",null,0,null,64,34,50,0,160,300,null,null], + [0,"마을사람3",null,0,null,63,33,50,0,160,300,null,null], + [0,"마을사람2",null,0,null,62,32,50,0,160,300,null,null], + [0,"마을사람1",null,0,null,61,31,50,0,160,300,null,null], + [0,"마미조",null,"신령",null,77,77,97,0,160,300,null,null,"뭔가 고민이 있는 모양이구먼. 야그나 함 들어 보자."], + [0,"마리우스",null,0,null,67,87,66,0,160,300,null,null], + [0,"마리사",null,0,null,83,91,78,0,160,300,null,null,"아~? 탄막에 두뇌? 바보 아냐? 탄막은 파워야."], + [0,"마나",null,0,null,73,75,72,0,160,300,null,null], + [0,"링고",null,"감주",null,54,76,71,0,160,300,null,null,"옛날의 극락정토는 이제 보이는 그림자도 없다고 생각해."], + [0,"릴리",null,"요요",null,37,63,68,0,160,300,null,null,"봄이에요~"], + [0,"린네",null,0,null,83,66,83,0,160,300,null,null], + [0,"리코",null,0,null,68,60,76,0,160,300,null,null], + [0,"리카코",null,"몽시",null,76,71,100,0,160,300,null,null,"왜 마법 따위에 흥미를 가지기나 하지?"], + [0,"리카",null,"봉마",null,61,63,75,0,160,300,null,null,"가는 거예요!!"], + [0,"리리카",null,"화영",null,57,66,83,0,160,300,null,null,"당신은 노래를 부르지. 난 손을 쓰지 않고 연주를 해. 장기전으로 가면 내 쪽이 세다고?"], + [0,"리락",null,0,null,74,54,62,0,160,300,null,null], + [0,"리글",null,"영야",null,85,64,51,0,160,300,null,null,"달이 보이는 밤에는, 즐기지 않아선 안 되는 거 아닐까."], + [0,"루이즈",null,"괴기",null,59,57,72,0,160,300,null,null,"뭐, 마계는 좋은 곳이니, 느긋하게 관광이라도 하도록 해☆"], + [0,"루미아",null,"홍마",null,79,70,56,0,160,300,null,null,"그런 건가~"], + [0,"루리",null,0,null,66,66,62,0,160,300,null,null], + [0,"루나사",null,"화영",null,76,77,50,0,160,300,null,null,"넌 슬픈 처지에서 태어난 경우야. 들떠있을 때가 아니지."], + [0,"루나",null,"구문",null,67,74,74,0,160,300,null,null,"빨라고? 네녀석이 빨아라!"], + [0,"레티",null,"요요",null,60,79,49,0,160,300,null,null,"흑막~"], + [0,"레이센",null,"영야",null,79,78,89,0,160,300,null,null,"이 앞에 기다리고 있는 게 악몽이라 해도, 그래도 나는 가야만 해."], + [0,"레이무",null,0,null,90,83,82,0,160,300,null,null,"인간을 쉽사리 죽게 내버려 둘까 보냐!"], + [0,"레이라",null,0,null,66,67,51,0,160,300,null,null], + [0,"레밀리아",null,"홍마",null,93,94,70,0,160,300,null,null,"당신은 이제껏 먹어 온 빵이 몇 개인지 알고 있어?"], + [0,"런던인형",null,0,null,64,78,62,0,160,300,null,null], + [0,"란",null,"요요",null,71,79,101,0,160,300,null,null,"흠. 첸을 괴롭힌건 너지?"], + [0,"라쿠",null,0,null,71,52,69,0,160,300,null,null], + [0,"라이코",null,"휘침",null,80,66,91,0,160,300,null,null,"누구라도 자기 목숨을 부지하고 싶으면 노력을 아끼지 않잖아?"], + [0,"라바",null,"천공",null,69,74,54,0,160,300,null,null,"지금이라면 신세계의 신이라도 될 것 같은 기분이야!"], + [0,"도레미",null,"감주",null,67,63,88,0,160,300,null,null,"맨몸!? 혹시 맨몸!?"], + [0,"대요정",null,"홍마",null,60,56,79,0,160,300,null,null], + [0,"달토끼5",null,0,null,35,55,55,0,160,300,null,null], + [0,"달토끼4",null,0,null,34,54,55,0,160,300,null,null], + [0,"달토끼3",null,0,null,33,53,55,0,160,300,null,null], + [0,"달토끼2",null,0,null,32,52,55,0,160,300,null,null], + [0,"달토끼1",null,0,null,31,51,55,0,160,300,null,null], + [0,"니토리",null,"풍신",null,68,68,76,0,160,300,null,null,"축제 땐 생선이 잘 팔리지. 인간에게 어업권을 뺏어서 독점하면 떼돈 벌겠네!"], + [0,"노바",null,0,null,74,56,82,0,160,300,null,null], + [0,"노로이코",null,"봉마",null,56,61,65,0,160,300,null,null], + [0,"네즈",null,0,null,76,71,40,0,160,300,null,null], + [0,"네무노",null,"천공",null,78,40,67,0,160,300,null,null,"자자, 차라도 마시고 가라."], + [0,"나즈린",null,"성령",null,76,63,64,0,160,300,null,null,"미안한데, 쥐를 얕보다간 죽어."], + [0,"나루코",null,0,null,68,40,70,0,160,300,null,null], + [0,"나루미",null,"천공",null,84,72,78,0,160,300,null,null,"에? 뭐야 그거 무서워"], + [0,"구유카",null,"환상",null,84,85,85,0,160,300,null,null,"신이라 해도, 힘이야말로 전부야!"], + [0,"구앨리스",null,"괴기",null,80,80,90,0,160,300,null,null,"궁극의 마법으로 널 해치워 주겠어!!"], + [0,"구마리사",null,"봉마",null,80,90,80,0,160,300,null,null,"오케이~, 여기는 마리사에게 맡겨줘."], + [0,"구레이무",null,"영이",null,90,80,80,0,160,300,null,null,"문답무용이야!!"], + [0,"곽청아",null,"신령",null,72,77,87,0,160,300,null,null,"네~ 썩어서 귀엽죠?"], + [0,"골리앗",null,0,null,72,78,58,0,160,300,null,null], + [0,"겐지",null,"봉마",null,70,70,70,0,160,300,null,null], + [0,"겐게츠",null,"환상",null,83,70,94,0,160,300,null,null,"얕보지 마. 우린, 둘이서 한 사람 몫이니까.…"], + [0,"강림",null,0,null,84,76,57,0,160,300,null,null] ], "cities":[ -- 2.54.0 From 85b7e6ad17b12a9e8ae89bfd838eb4d22d04fc72 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 02:22:32 +0900 Subject: [PATCH 178/304] =?UTF-8?q?=EC=A0=80=EA=B2=A9=20=ED=8A=B9=EA=B8=B0?= =?UTF-8?q?,=20=ED=99=9C=20=EC=9C=A0=EB=8B=88=ED=81=AC,=20=EC=88=98?= =?UTF-8?q?=EA=B7=B9=20=ED=99=95=EB=A5=A0=EC=9D=B4=20=EA=B0=81=EA=B0=81=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/process_war.php | 70 +++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/hwe/process_war.php b/hwe/process_war.php index cda4335f..b2e10e49 100644 --- a/hwe/process_war.php +++ b/hwe/process_war.php @@ -599,19 +599,30 @@ function processWar($general, $city) { $query = "update general set recwar='{$general['turntime']}',train='{$oppose['train']}',warnum=warnum+1 where no='{$oppose['no']}'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $ratio = rand() % 100; //특기보정 : 저격(수극), 활무기저격 - if(($general['special2'] == 70 && $ratio <= 33) || ($general['item'] == 2 && $ratio <= 20) || (($general['weap'] == 10 || $general['weap'] == 14 || $general['weap'] == 18 || $general['weap'] == 22) && $ratio <= 20)) { + $snipe = false; + $snipeItem = false; + if(!$snipe && $general['special2'] == 70 && Util::randF(1/3)){ + $snipe = true; + } + if(!$snipe && in_array($general['weap'], [10, 14, 18, 22]) && Util::randF(1/5)){ + $snipe = true; + } + if(!$snipe && $general['item'] == 2){ + if(Util::randF(1/5)){ + $snipe = true; + $snipeItem = true; + } + //수극을 사용했지만 저격 실패한 케이스도 '필요하면' 넣을 것.(밸런스) + + } + if($snipe) { //수극 사용 - if($general['item'] == 2) { - $query = "update general set item=0 where no='{$general['no']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + if($snipeItem) { $josaUl = JosaUtil::pick($general['item'], '을'); $log[] = "".getItemName($general['item'])."{$josaUl} 사용!"; $general['item'] = 0; - } elseif($general['weap'] == 10 || $general['weap'] == 14 || $general['weap'] == 18 || $general['weap'] == 22) { -// $josaUl = JosaUtil::pick($general['weap'], '을'); -// $log[] = "".getWeapName($general['weap'])."{$josaUl} 사용!"; + $db->update('general', ['item'=>0], 'no=%i', $general['no']); } $log[] = "●상대를 저격했다!"; $batlog[] = "●상대를 저격했다!"; @@ -620,26 +631,37 @@ function processWar($general, $city) { // 부상 $oppose['injury'] += rand() % 41 + 20; // 20 ~ 60 if($oppose['injury'] > 80) { $oppose['injury'] = 80; } - } else if($general['item'] == 2 && $ratio <= 40) { - $query = "update general set item=0 where no='{$general['no']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + } + else if($snipeItem) { + $db->update('general', ['item'=>0], 'no=%i', $general['no']); $josaYi = JosaUtil::pick($general['item'], '이'); $batlog[] = "".getItemName($general['item'])."{$josaYi} 빗나갑니다!"; $general['item'] = 0; } - $ratio = rand() % 100; + //특기보정 : 저격(수극), 활무기저격 - if(($oppose['special2'] == 70 && $ratio <= 33) || ($oppose['item'] == 2 && $ratio <= 20) || (($oppose['weap'] == 10 || $oppose['weap'] == 14 || $oppose['weap'] == 18 || $oppose['weap'] == 22) && $ratio <= 20)) { + $snipe = false; + $snipeItem = false; + if(!$snipe && $oppose['special2'] == 70 && Util::randF(1/3)){ + $snipe = true; + } + if(!$snipe && in_array($oppose['weap'], [10, 14, 18, 22]) && Util::randF(1/5)){ + $snipe = true; + } + if(!$snipe && $oppose['item'] == 2){ + if(Util::randF(1/5)){ + $snipe = true; + $snipeItem = true; + } + //수극을 사용했지만 저격 실패한 케이스도 '필요하면' 넣을 것.(밸런스) + } + if($snipe) { //수극 사용 - if($oppose['item'] == 2) { - $query = "update general set item=0 where no='{$oppose['no']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + if($snipeItem) { $josaUl = JosaUtil::pick($oppose['item'], '을'); - $opplog[] = "".getItemName($oppose['item'])."{$josaUl} 사용!"; + $log[] = "".getItemName($oppose['item'])."{$josaUl} 사용!"; $oppose['item'] = 0; - } elseif($oppose['weap'] == 10 || $oppose['weap'] == 14 || $oppose['weap'] == 18 || $oppose['weap'] == 22) { -// $josaUl = JosaUtil::pick($oppose['weap'], '을'); -// $opplog[] = "".getWeapName($oppose['weap'])."{$josaUl} 사용!"; + $db->update('general', ['item'=>0], 'no=%i', $oppose['no']); } $oppbatlog[] = "●상대를 저격했다!"; $opplog[] = "●상대를 저격했다!"; @@ -648,11 +670,11 @@ function processWar($general, $city) { // 부상 $general['injury'] += rand() % 41 + 20; // 20 ~ 60 if($general['injury'] > 80) { $general['injury'] = 80; } - } else if($oppose['item'] == 2 && $ratio <= 40) { - $query = "update general set item=0 where no='{$oppose['no']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + } + else if($snipeItem) { + $db->update('general', ['item'=>0], 'no=%i', $oppose['no']); $josaYi = JosaUtil::pick($oppose['item'], '이'); - $oppbatlog[] = "".getItemName($oppose['item'])."{$josaYi} 빗나갑니다!"; + $batlog[] = "".getItemName($oppose['item'])."{$josaYi} 빗나갑니다!"; $oppose['item'] = 0; } -- 2.54.0 From 469fea321d60fc40c3be590245be68854e2c415f Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 02:22:49 +0900 Subject: [PATCH 179/304] =?UTF-8?q?key,value=20pair=EB=A5=BC=20=EB=B3=B4?= =?UTF-8?q?=EC=A1=B4=ED=95=9C=20=EC=84=9E=EA=B8=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sammo/Util.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sammo/Util.php b/src/sammo/Util.php index 2b0a3bce..86c944c2 100644 --- a/src/sammo/Util.php +++ b/src/sammo/Util.php @@ -281,6 +281,23 @@ class Util extends \utilphp\util return $dict; } + /** + * key=>value pair를 보존한 섞기 + */ + public static function shuffle_assoc(&$array) { + $keys = array_keys($array); + + shuffle($keys); + + foreach($keys as $key) { + $new[$key] = $array[$key]; + } + + $array = $new; + + return true; + } + /** * 0.0~1.0 사이의 랜덤 float -- 2.54.0 From 6089c449bf4aba1e464b5f7dd5e22d2b07d15ab1 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 02:23:06 +0900 Subject: [PATCH 180/304] =?UTF-8?q?=EC=83=81=EC=9D=B8=20=EA=B1=B0=EB=9E=98?= =?UTF-8?q?=EC=8B=9C=20110%=20=EA=B0=80=20=EC=9E=98=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD.=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=EB=90=9C=20=EA=B1=B0=EC=83=81=20=EB=AC=B8=EA=B5=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/b_auction.php | 2 +- hwe/c_auction.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hwe/b_auction.php b/hwe/b_auction.php index cd88224a..43548d94 100644 --- a/hwe/b_auction.php +++ b/hwe/b_auction.php @@ -280,7 +280,7 @@ for ($i=0; $i < $count; $i++) { ㆍ악용 방지를 위해 50% ~ 200%의 가격에서 거래시작이 가능합니다.
ㆍ악용 방지를 위해 즉시판매가는 110% 이상, 즉시구매가는 90% 이하의 시세로 가능합니다.
ㆍ악용 방지를 위해 즉시판매가는 시작판매가의 110% 이상, 즉시구매가는 시작구매가의 90% 이하로 가능합니다.
-ㆍ1인당 도합 1건의 거래와 입찰이 가능합니다. 거상 특기 소유자는 1인당 도합 3건입니다.
+ㆍ1인당 도합 1건의 거래와 입찰이 가능합니다.
ㆍ기본금쌀 1000은 거래에 사용되지 못합니다.
ㆍ유찰될 때는 거래 과실자에게 거래금의 1%가 벌금으로 부과됩니다.
10단위로 거래가 가능합니다. 1자리는 반올림 처리 됩니다.
diff --git a/hwe/c_auction.php b/hwe/c_auction.php index b5648b74..3aa120b8 100644 --- a/hwe/c_auction.php +++ b/hwe/c_auction.php @@ -91,11 +91,11 @@ if ($btn == "판매") { $msg = "ㆍ시작판매가는 50% ~ 200% 이어야 합니다."; $valid = 0; } - if ($topv < $amount*1.1 || $topv > $amount * 2) { + if ($topv * 10 < $amount * 11 || $topv > $amount * 2) { $msg = "ㆍ즉시판매가는 110% ~ 200% 이어야 합니다."; $valid = 0; } - if ($topv < $cost*1.1) { + if ($topv * 10 < $cost * 11) { $msg = "ㆍ즉시판매가는 시작판매가의 110% 이상이어야 합니다."; $valid = 0; } -- 2.54.0 From 69031f6da5059dcde72d4c8d8cc0ffca22e2a27d Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 04:45:27 +0900 Subject: [PATCH 181/304] =?UTF-8?q?NPC=20=EC=82=AC=EB=A0=B9=ED=84=B4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=83=88=EB=A1=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 570 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 391 insertions(+), 179 deletions(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 0e103437..30afb75b 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -228,7 +228,7 @@ function processAI($no) { // 전쟁준비 선포중이면 2상태 if($dipCount > 0) { $dipState = 2; } - $query = "select no from diplomacy where me='{$general['nation']}' and state=1 and term<=3"; + $query = "select no from diplomacy where me='{$general['nation']}' and state=1 and term<=5"; $result = MYDB_query($query, $connect) or Error("processAI04 ".MYDB_error($connect),""); $dipCount = MYDB_num_rows($result); // 교전 직전이면 3상태 @@ -484,184 +484,11 @@ function processAI($no) { //방랑군 아니고, 입력된 턴이 없을때 수뇌부가 할일 if($nation['level'] != 0 && $general['level'] >= 5 && $rulerCommand == 0) { - $query = "select A.no,A.name,A.nation,B.nation from general A, city B where A.city=B.city and A.nation='{$general['nation']}' and B.nation!='{$general['nation']}' and A.no!='{$general['no']}' order by rand() limit 0,1"; - $result = MYDB_query($query, $connect) or Error("processAI11 ".MYDB_error($connect),""); - $curGen = MYDB_fetch_array($result); - - if($curGen['no'] != 0) { // 타도시에 있는 경우 국내로 발령 - if($dipState >= 3) { - $query = "select city from city where nation='{$general['nation']}' and front=1 and supply=1 order by rand() limit 0,1"; - $result = MYDB_query($query, $connect) or Error("processAI10 ".MYDB_error($connect),""); - $selCity = MYDB_fetch_array($result); - if($selCity['city'] > 0) { - // 발령 - $command = EncodeCommand(0, $curGen['no'], $selCity['city'], 27); - } else { - // 발령 - $command = EncodeCommand(0, $curGen['no'], $city['city'], 27); - } - } else { - // 발령 - $command = EncodeCommand(0, $curGen['no'], $city['city'], 27); - } - $query = "update nation set l{$general['level']}turn0='$command' where nation='{$general['nation']}'"; - MYDB_query($query, $connect) or Error("processAI09 ".MYDB_error($connect),""); - } elseif($dipState <= 1) { // 평시엔 균등 발령만 - //발령, 최소장수 도시 선택, 최다장수도시의 장수 선택 - $query = "select B.city,count(*) as cnt,((B.agri+B.comm+B.secu+B.def+B.wall)/(B.agri2+B.comm2+B.secu2+B.def2+B.wall2)+(B.pop/B.pop2))/2*100 as dev from general A, city B where A.city=B.city and A.nation='{$general['nation']}' and B.nation='{$general['nation']}' and B.supply=1 group by A.city"; - $result = MYDB_query($query, $connect) or Error("processAI10 ".MYDB_error($connect),""); - $cityCount = MYDB_num_rows($result); - //도시 2개 이상일때만 - if($cityCount > 1) { - $min = 500; $minCity = 0; - $max = 0; $maxCity = 0; - $devCity = 0; - for($i=0; $i < $cityCount; $i++) { - $curCity = MYDB_fetch_array($result); - if($curCity['cnt'] >= $max) { $max = $curCity['cnt']; $maxCity = $curCity['city']; } - if($curCity['cnt'] <= $min) { $min = $curCity['cnt']; $minCity = $curCity['city']; } - if($curCity['dev'] < 70) { $devCity = $curCity['city']; } // 개발이 안된 곳 우선 - } - if($devCity != 0) { $minCity = $devCity; } - if($maxCity != $minCity) { - $query = "select no from general where city='$maxCity' and nation='{$general['nation']}' and no!='{$general['no']}' and npc>=2 limit 0,1"; - $result = MYDB_query($query, $connect) or Error("processAI11 ".MYDB_error($connect),""); - $curGen = MYDB_fetch_array($result); - - if($curGen['no'] != 0) { - // 발령 - $command = EncodeCommand(0, $curGen['no'], $minCity, 27); - $query = "update nation set l{$general['level']}turn0='$command' where nation='{$general['nation']}'"; - MYDB_query($query, $connect) or Error("processAI09 ".MYDB_error($connect),""); - } - //계속 진행 - } - } - } else { - // 병사있고 쌀있고 후방에 있는 장수 - $query = "select A.no from general A, city B where A.city=B.city and A.nation='{$general['nation']}' and B.nation='{$general['nation']}' and B.front=0 and A.crew>700 and A.rice>700*{$tech} order by A.npc,A.crew desc limit 0,1"; - $result = MYDB_query($query, $connect) or Error("processAI10 ".MYDB_error($connect),""); - $selGen = MYDB_fetch_array($result); - // 전방 도시, 30% 확률로 태수 있는 전방으로 발령 - if(rand()%100 < 30) { - $query = "select city from city where nation='{$general['nation']}' and front=1 and supply=1 order by gen1 desc,rand() limit 0,1"; - } else { - $query = "select city from city where nation='{$general['nation']}' and front=1 and supply=1 order by rand() limit 0,1"; - } - $result = MYDB_query($query, $connect) or Error("processAI10 ".MYDB_error($connect),""); - $selCity = MYDB_fetch_array($result); - if($selGen['no'] > 0 && $selCity['city'] > 0 && rand() % 100 < 80) { // 80% 확률 - // 발령 - $command = EncodeCommand(0, $selGen['no'], $selCity['city'], 27); - } else { - //병사 없고 인구없는 전방에 있는 장수 - $query = "select A.no from general A, city B where A.city=B.city and A.nation='{$general['nation']}' and B.nation='{$general['nation']}' and B.pop<40000 and B.front=1 and A.crew<700 order by A.npc,A.crew limit 0,1"; - $result = MYDB_query($query, $connect) or Error("processAI10 ".MYDB_error($connect),""); - $selGen = MYDB_fetch_array($result); - // 인구많은도시 - $query = "select city from city where nation='{$general['nation']}' and supply=1 order by pop desc limit 0,1"; - $result = MYDB_query($query, $connect) or Error("processAI10 ".MYDB_error($connect),""); - $selCity = MYDB_fetch_array($result); - if($selGen['no'] > 0 && $selCity['city'] > 0 && rand() % 100 < 80) { // 80% 확률 - // 발령 - $command = EncodeCommand(0, $selGen['no'], $selCity['city'], 27); - } else { - // 발령할 장수 없으면 몰포 - if(rand() % 2 == 0) { $type = "gold"; $type2 = 1; } - else { $type = "rice"; $type2 = 2; } - - if($nation[$type] < $type2*3000) { // 몰수 - // 몰수 대상 - list($npcGenID, $npcGenValue) = $db->queryFirstList( - 'SELECT `no`, %b FROM general WHERE nation=%i AND `no`!=%i AND %b>3000 AND npc >= 2 ORDER BY %b DESC LIMIT 1', - $type, - $general['nation'], - $general['no'], - $type, - $type - ); - - list($userGenID, $userGenValue) = $db->queryFirstList( - 'SELECT `no`, %b FROM general WHERE nation=%i AND `no`!=%i AND %b>3000 AND npc < 2 ORDER BY %b DESC LIMIT 1', - $type, - $general['nation'], - $general['no'], - $type, - $type - ); - - if($npcGenID === null && $userGenID === null){ - $genID = 0; - $genValue = 0; - } - else if($npcGenID === null || $userGenValue > $npcGenValue * 4){ - $genID = $userGenID; - $genValue = $userGenValue; - } - else{ - $genID = $npcGenID; - $genValue = $npcGenValue; - } - - if($genID){ - $amount = min(100, intdiv($genValue, 5000)*10 + 10); - // 몰수 - $command = EncodeCommand($type2, $genID, $amount, 24); // 금,쌀 1000단위 몰수 - } - } else { // 포상 - // 포상 대상 - list($npcGenID, $npcGenValue, $npcLeadership) = $db->queryFirstList( - 'SELECT `no`, %b, leader FROM general WHERE nation=%i AND `no`!=%i AND npc >= 2 AND killturn > 5 AND (leader >= 40 OR %b < %i) ORDER BY %b ASC LIMIT 1', - $type, - $general['nation'], - $general['no'], - $type, - $resrc, - $type - ); - - list($userGenID, $userGenValue) = $db->queryFirstList( - 'SELECT `no`, %b FROM general WHERE nation=%i AND `no`!=%i AND npc < 2 AND killturn > 5 AND (leader >= 40 OR %b < %i) ORDER BY %b ASC LIMIT 1', - $type, - $general['nation'], - $general['no'], - $type, - ($type=='gold')?21000:3000, - $type - ); - - if($npcGenID === null && $userGenID === null){ - $genID = 0; - } - else if($npcGenID === null || ($userGenValue !== null && $userGenValue < $npcGenValue * 3)){ - $genID = $userGenID; - } - else{ - $genID = $npcGenID; - } - - if ($genID) { - if($genID === $npcGenID){ - $amount = min(100, intdiv(($nation[$type]-($type=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 5000)*10 + 10); - if($npcLeadership < 40){ - $amount = min($amount, intdiv($resrc, 1000)*10 + 10); - } - - } - else{ - $amount = min(100, intdiv(($nation[$type]-($type=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 2000)*10 + 10); - } - - // 포상 - $command = EncodeCommand($type2, $genID, $amount, 23); // 금,쌀 1000단위 포상 - } - } - } - } - if(isset($command)){ - $query = "update nation set l{$general['level']}turn0='$command' where nation='{$general['nation']}'"; - MYDB_query($query, $connect) or Error("processAI09 ".MYDB_error($connect),""); - } + $command = NPCStaffWork($general, $nation, $dipState); + if($command){ + $db->update('nation', [ + "l{$general['level']}turn0"=>$command + ], 'nation=%i', $general['nation']); } } @@ -1024,6 +851,391 @@ function processAI($no) { return; } } + + +function NPCStaffWork($general, $nation, $dipState){ + $db = DB::db(); + $gameStor = KVStorage::getStorage($db, 'game_env'); + $connect=$db->get(); + + $admin = $gameStor->getValues(['startyear','year','month','turnterm','scenario','gold_rate','rice_rate', 'develcost']); + + $nationCities = []; + $frontCitiesID = []; + $frontImportantCitiesID = []; + $supplyCitiesID = []; + $backupCitiesID = []; + + $tech = getTechCost($nation['tech']); + + foreach ($db->query('SELECT * FROM city WHERE nation = %i', $general['nation']) as $nationCity) { + $nationCity['generals'] = []; + $cityID = $nationCity['city']; + $dev = + ($nationCity['agri'] + $nationCity['comm'] + $nationCity['secu'] + $nationCity['def'] + $nationCity['wall'])/ + ($nationCity['agri'] + $nationCity['comm'] + $nationCity['secu'] + $nationCity['def'] + $nationCity['wall']); + $dev += $nationCity['pop'] / $nationCity['pop2']; + $dev /= 50; + + $nationCity['dev'] = $dev; + + $nationCities[$cityID] = $nationCity; + + if($nationCity['supply']){ + $supplyCitiesID[] = $cityID; + if($nationCity['front']){ + $frontCitiesID[] = $cityID; + if($nationCity['gen1']){ + $frontImportantCitiesID[] = $cityID; + } + } + else{ + $backupCitiesID[] = $cityID; + } + } + } + Util::shuffle_assoc($nationCities); + shuffle($frontCitiesID); + shuffle($supplyCitiesID); + + $nationGenerals = []; + $lostGeneralsID = []; + + $userGeneralsID = []; + $npcWarGeneralsID = []; + $npcCivilGeneralsID = []; + + + $commandList = []; + + foreach($db->query('SELECT * FROM general WHERE nation = %i', $general['nation']) as $nationGeneral) { + $cityID = $nationGeneral['city']; + $generalID = $nationGeneral['no']; + + if($generalID == $general['no']){ + continue; + } + + if(key_exists($cityID, $nationCities)){ + $nationCities[$cityID]['generals'][] = $generalID; + if(!$nationCities[$cityID]['supply']){ + $lostGeneralsID[] = $generalID; + } + } + else{ + $lostGeneralsID[] = $generalID; + } + + if($nationGeneral['npc']<2 && $nationGeneral['killturn'] >= 5){ + $userGeneralsID[] = $generalID; + } + else if($nationGeneral['leader']>=40 && $nationGeneral['killturn'] >= 5){ + $npcWarGeneralsID[] = $generalID; + } + else{ + //삭턴이 몇 안남은 장수는 '내정장 npc'로 처리 + $npcCivilGeneralsID[] = $generalID; + } + + $nationGenerals[$generalID] = $nationGeneral; + } + Util::shuffle_assoc($nationGenerals); + shuffle($lostGeneralsID); + + uasort($nationCities, function($lhs, $rhs){ + //키 순서를 지키지 않지만, 원래부터 random order를 목표로 하므로 크게 신경쓰지 않는다. + return count($lhs['generals']) - count($rhs['generals']); + }); + + + //타 도시에 있는 '유저장' 발령 + foreach($lostGeneralsID as $lostGeneralID){ + $lostGeneral = $nationGenerals[$lostGeneralID]; + if($lostGeneral['npc'] < 2){ + if($dipState >= 3 && $frontCitiesID){ + $selCityID = Util::choiceRandom($frontCitiesID); + } + else{ + $selCityID = Util::choiceRandom($supplyCitiesID); + } + $commandList[EncodeCommand(0, $lostGeneralID, $selCityID, 27)] = 200; + } + } + + + $resBaseType = [['gold', 1], ['rice', 2]]; + [$resName, $resType] = Util::choiceRandom($resBaseType); + + usort($userGeneralsID, function($lhs, $rhs) use ($nationGenerals, $resName){ + return $nationGenerals[$lhs][$resName] - $nationGenerals[$rhs][$resName]; + }); + + usort($npcWarGeneralsID, function($lhs, $rhs) use ($nationGenerals, $resName){ + return $nationGenerals[$lhs][$resName] - $nationGenerals[$rhs][$resName]; + }); + + usort($npcCivilGeneralsID, function($lhs, $rhs) use ($nationGenerals, $resName){ + return $nationGenerals[$lhs][$resName] - $nationGenerals[$rhs][$resName]; + }); + + $avgUserRes = 0; + foreach ($userGeneralsID as $id){ + $avgUserRes += $nationGenerals[$id][$resName]; + } + $avgUserRes /= max(1, count($userGeneralsID)); + + $avgNpcWarRes = 0; + foreach ($npcWarGeneralsID as $id){ + $avgNpcWarRes += $nationGenerals[$id][$resName]; + } + $avgNpcWarRes /= max(1, count($npcWarGeneralsID)); + + $avgNpcCivilRes = 0; + foreach ($npcCivilGeneralsID as $id){ + $avgNpcCivilRes += $nationGenerals[$id][$resName]; + } + if($npcCivilGeneralsID){ + $avgNpcCivilRes /= max(1, count($npcCivilGeneralsID)); + } + + + + //금쌀이 부족한 '유저장' 먼저 포상 + if ($nation[$resName] > ($resName=='gold'?1:2)*3000 && $userGeneralsID) { + $compUser = $nationGenerals[$userGeneralsID[0]]; + $compRes = $compUser[$resName]; + + $work = false; + if ($compRes < $avgNpcWarRes*3) { + $work = true; + } elseif ($compRes < $avgNpcCivilRes * 4) { + $work = true; + } + + if($compUser[$resName] < 21000){ + if($work){ + $amount = min(100, intdiv(($nation[$resName]-($resName=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 2000)*10 + 10); + $commandList[EncodeCommand($resType, $userGeneralsID[0], $amount, 23)] = 40; // 금,쌀 1000단위 포상 + } + else{ + $amount = min(100, intdiv(($nation[$resName]-($resName=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 5000)*10 + 10); + $commandList[EncodeCommand($resType, $userGeneralsID[0], $amount, 23)] = 1; // 금,쌀 1000단위 포상 + } + + } + } + + $minRes = $admin['develcost'] * 24 * $tech; + + if($nation[$resName] < ($resName=='gold'?1:2)*3000) { // 몰수 + // 몰수 대상 + $compUser = $userGeneralsID?$nationGenerals[end($userGeneralsID)]:null; + $compNpcWar = $npcWarGeneralsID?$nationGenerals[end($npcWarGeneralsID)]:null; + $compNpcCivil = $npcCivilGeneralsID?$nationGenerals[end($npcCivilGeneralsID)]:null; + + $compUserRes = $compUser[$resName]??0; + $compNpcWarRes = $compNpcWar[$resName]*5??0; + $compNpcCivilRes = $compNpcCivil[$resName]*10??0; + + [$compRes, $compGenID] = max( + [$compNpcCivilRes, $compNpcCivil['no']??null], + [$compNpcWarRes, $compNpcWar['no']??null], + [$compUserRes, $compUser['no']??null] + ); + + if($compGenID){ + $targetGeneral = $nationGenerals[$compGenID]; + if($compGenID === ($compNpcCivil['no']??null)){ + $amount = intdiv($targetGeneral[$resName] - $minRes * 3, 100); + } + else{ + $amount = min(100, intdiv($targetGeneral[$resName], 5000)*10 + 10); + } + + if($amount > 0){ + $commandList[EncodeCommand($resType, $compGenID, $amount, 24)] = 3; + } + + } + } else{ // 포상 + $compNpcWar = $npcWarGeneralsID?$nationGenerals[$npcWarGeneralsID[0]]:null; + $compNpcCivil = $npcCivilGeneralsID?$nationGenerals[$npcCivilGeneralsID[0]]:null; + + if($compNpcWar && $compNpcWar[$resName] < 21000){ + $amount = min(100, intdiv(($nation[$resName]-($resName=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 5000)*10 + 10); + $commandList[EncodeCommand($resType, $compNpcWar['no'], $amount, 23)] = 2; + } + if($compNpcCivil && $compNpcCivil[$resName] < $minRes){ + $amount = intdiv($minRes+99, 100); + $commandList[EncodeCommand($resType, $compNpcCivil['no'], $amount, 23)] = 2; + } + } + + //고립 도시 장수 발령 + foreach($lostGeneralsID as $lostGeneralID){ + $lostGeneral = $nationGenerals[$lostGeneralID]; + if($lostGeneral['npc']<2){ + //고립 유저 장수는 이미 세팅했음 + continue; + } + if($dipState >= 3 && $frontCitiesID){ + $selCityID = Util::choiceRandom($frontCitiesID); + } + else{ + $selCityID = Util::choiceRandom($supplyCitiesID); + } + //고립된 장수가 많을 수록 발령 확률 증가 + $commandList[EncodeCommand(0, $lostGeneralID, $selCityID, 27)] = sqrt(count($lostGeneralsID)) * 10; + } + + // 평시엔 균등 발령만 + if($dipState <= 1 && count($supplyCitiesID) > 1) { + $targetCity = null; + $minCity = null; + $maxCity = null; + foreach($nationCities as $nationCity){ + if($nationCity['supply']){ + $minCity = $nationCity; + break; + } + } + + //reverse_order T_T + $maxCity = end($nationCities); + while($maxCity['city'] !== $minCity['city']){ + if($nationCity['supply']){ + break; + } + $maxCity = prev($nationCities); + } + + foreach($nationCities as $nationCity){ + if($nationCity['city'] == $maxCity['city']){ + break; + } + if(!$nationCity['supply']){ + continue; + } + if($nationCity['dev'] < 70){ + $targetCity = $nationCity; + break; + } + } + + if($targetCity === null || (count($targetCity['generals']) >= count($maxCity['generals']) - 1)){ + $targetCity = $minCity; + } + + if(count($targetCity['generals']) < count($maxCity['generals']) - 2){ + //세명 이상 차이나야 함 + $targetGeneral = $nationGenerals[Util::choiceRandom($maxCity['generals'])]; + if($targetGeneral['npc']>=2 || $maxCity['dev'] >= 95){ + //유저장은 의도가 있을 것이므로 삽나지 않는 이상 발령 안함! + $commandList[EncodeCommand(0, $targetGeneral['no'], $targetCity['city'], 27)] = 5; + } + + } + } + + // 병사있고 쌀있고 후방에 있는 장수 + if($frontCitiesID){ + $workRemain = 5; + foreach($nationGenerals as $nationGeneral){ + $generalCity = $nationCities[$nationGeneral['city']]??null; + if(!$generalCity){ + continue; + } + if($nationGeneral['crew'] < 2000){ + continue; + } + if($nationGeneral['rice'] < 700 * $tech){ + continue; + } + if($generalCity['front']){ + continue; + } + + $score = 5; + if($nationGeneral['npc']<2){ + $score *= 8; + } + + if(Util::randF(0.3) && $frontImportantCitiesID){ + $targetCityID = Util::choiceRandom($frontImportantCitiesID); + } + else{ + $targetCityID = Util::choiceRandom($frontCitiesID); + } + + $commandList[EncodeCommand(0, $nationGeneral['no'], $targetCityID, 27)] = $score; + + if($nationGeneral['npc']<2){ + $workRemain -= 5; + } + else{ + $workRemain--; + } + + if($workRemain <= 0){ + break; + } + } + } + + //병사 없고 인구없는 전방에 있는 장수 + if($frontCitiesID && $backupCitiesID){ + $workRemain = 5; + foreach($nationGenerals as $nationGeneral){ + $generalCity = $nationCities[$nationGeneral['city']]??null; + if(!$generalCity){ + continue; + } + if($nationGeneral['crew'] >= 1000){ + continue; + } + if($nationGeneral['rice'] < 700 * $tech){ + continue; + } + if(!$generalCity['front']){ + continue; + } + + $score = 5; + if($nationGeneral['npc']<2){ + $score *= 8; + } + + $popTrial = 5; + for($popTrial = 0; $popTrial < 5; $popTrial++){ + $targetCity = $nationCities[Util::choiceRandom($backupCitiesID)]; + if($targetCity['pop'] < 33000 + $nationGeneral['leader']){ + continue; + } + if (Util::randF($targetCity['pop'] / $targetCity['pop2'])) { + break; + } + } + + + $commandList[EncodeCommand(0, $nationGeneral['no'], $targetCity['city'], 27)] = $score; + + if($nationGeneral['npc']<2){ + $workRemain -= 5; + } + else{ + $workRemain--; + } + + if($workRemain <= 0){ + break; + } + } + } + + if(!$commandList)return 0; + return Util::choiceRandomUsingWeight($commandList); +} + //종전하기, 지급율 //$command = $fourth * 100000000 + $type * 100000 + $crew * 100 + 11; -- 2.54.0 From d9e5a8224d534403ce5b64c7fa18568489a27858 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 04:51:07 +0900 Subject: [PATCH 182/304] =?UTF-8?q?=EC=A0=84=EB=B0=A9=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=EC=9D=84=205=EA=B0=9C=EC=9B=94=20=EB=82=B4=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_gamerule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/func_gamerule.php b/hwe/func_gamerule.php index 6622e207..39fad85f 100644 --- a/hwe/func_gamerule.php +++ b/hwe/func_gamerule.php @@ -144,7 +144,7 @@ function SetNationFront($nationNo) { $db = DB::db(); $enemyCities = $db->queryFirstColumn( 'SELECT city from city where nation IN - (SELECT you from diplomacy where me = %i and (state=0 or (state=1 and term<=3)))' + (SELECT you from diplomacy where me = %i and (state=0 or (state=1 and term<=5)))' , $nationNo ); if($enemyCities) { -- 2.54.0 From df7d568c2e85986e176dcc9795b30bf0765b6456 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 05:00:57 +0900 Subject: [PATCH 183/304] =?UTF-8?q?npc=20=EB=B0=9C=EB=A0=B9=20=EC=A0=84?= =?UTF-8?q?=EB=9E=B5=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 30afb75b..4b514ab6 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -1139,10 +1139,13 @@ function NPCStaffWork($general, $nation, $dipState){ // 병사있고 쌀있고 후방에 있는 장수 if($frontCitiesID){ - $workRemain = 5; + $workRemain = 3; foreach($nationGenerals as $nationGeneral){ $generalCity = $nationCities[$nationGeneral['city']]??null; if(!$generalCity){ + if(!in_array($nationGeneral['no'], $lostGeneralsID)){ + trigger_error('쓔?'); + } continue; } if($nationGeneral['crew'] < 2000){ @@ -1157,7 +1160,7 @@ function NPCStaffWork($general, $nation, $dipState){ $score = 5; if($nationGeneral['npc']<2){ - $score *= 8; + $score *= 4; } if(Util::randF(0.3) && $frontImportantCitiesID){ @@ -1167,13 +1170,15 @@ function NPCStaffWork($general, $nation, $dipState){ $targetCityID = Util::choiceRandom($frontCitiesID); } - $commandList[EncodeCommand(0, $nationGeneral['no'], $targetCityID, 27)] = $score; + $command = EncodeCommand(0, $nationGeneral['no'], $targetCityID, 27); - if($nationGeneral['npc']<2){ - $workRemain -= 5; + if($nationGeneral['npc']<2 && ($workRemain&2)){ + $workRemain ^= 2; + $commandList[$command] = $score; } - else{ - $workRemain--; + else if($nationGeneral['npc']>=2 && ($workRemain&1)){ + $workRemain ^= 1; + $commandList[$command] = $score; } if($workRemain <= 0){ @@ -1184,7 +1189,7 @@ function NPCStaffWork($general, $nation, $dipState){ //병사 없고 인구없는 전방에 있는 장수 if($frontCitiesID && $backupCitiesID){ - $workRemain = 5; + $workRemain = 3; foreach($nationGenerals as $nationGeneral){ $generalCity = $nationCities[$nationGeneral['city']]??null; if(!$generalCity){ @@ -1202,7 +1207,7 @@ function NPCStaffWork($general, $nation, $dipState){ $score = 5; if($nationGeneral['npc']<2){ - $score *= 8; + $score *= 4; } $popTrial = 5; @@ -1217,13 +1222,15 @@ function NPCStaffWork($general, $nation, $dipState){ } - $commandList[EncodeCommand(0, $nationGeneral['no'], $targetCity['city'], 27)] = $score; + $command = EncodeCommand(0, $nationGeneral['no'], $targetCity['city'], 27); - if($nationGeneral['npc']<2){ - $workRemain -= 5; + if($nationGeneral['npc']<2 && ($workRemain&2)){ + $workRemain ^= 2; + $commandList[$command] = $score; } - else{ - $workRemain--; + else if($nationGeneral['npc']>=2 && ($workRemain&1)){ + $workRemain ^= 1; + $commandList[$command] = $score; } if($workRemain <= 0){ -- 2.54.0 From 3a37b35ceec995a721e12fd55b2df324ac4d77da Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 05:06:10 +0900 Subject: [PATCH 184/304] =?UTF-8?q?=EB=B0=9C=EB=A0=B9=20=EC=A0=84=EB=9E=B5?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 4b514ab6..adac5bb8 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -1143,9 +1143,6 @@ function NPCStaffWork($general, $nation, $dipState){ foreach($nationGenerals as $nationGeneral){ $generalCity = $nationCities[$nationGeneral['city']]??null; if(!$generalCity){ - if(!in_array($nationGeneral['no'], $lostGeneralsID)){ - trigger_error('쓔?'); - } continue; } if($nationGeneral['crew'] < 2000){ @@ -1157,6 +1154,9 @@ function NPCStaffWork($general, $nation, $dipState){ if($generalCity['front']){ continue; } + if($nationGeneral['train'] * $nationGeneral['atmos'] < 75 * 75){ + continue; + } $score = 5; if($nationGeneral['npc']<2){ @@ -1204,6 +1204,9 @@ function NPCStaffWork($general, $nation, $dipState){ if(!$generalCity['front']){ continue; } + if($generalCity['pop'] - 33000 > $nationGeneral['leader']){ + continue; + } $score = 5; if($nationGeneral['npc']<2){ -- 2.54.0 From bed4b87a540b10e975514808d54f402e45b9b204 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 05:22:49 +0900 Subject: [PATCH 185/304] =?UTF-8?q?=ED=8F=AC=EC=83=81=20=EC=A0=84=EB=9E=B5?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index adac5bb8..676de481 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -994,9 +994,7 @@ function NPCStaffWork($general, $nation, $dipState){ foreach ($npcCivilGeneralsID as $id){ $avgNpcCivilRes += $nationGenerals[$id][$resName]; } - if($npcCivilGeneralsID){ - $avgNpcCivilRes /= max(1, count($npcCivilGeneralsID)); - } + $avgNpcCivilRes /= max(1, count($npcCivilGeneralsID)); @@ -1063,7 +1061,7 @@ function NPCStaffWork($general, $nation, $dipState){ if($compNpcWar && $compNpcWar[$resName] < 21000){ $amount = min(100, intdiv(($nation[$resName]-($resName=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 5000)*10 + 10); - $commandList[EncodeCommand($resType, $compNpcWar['no'], $amount, 23)] = 2; + $commandList[EncodeCommand($resType, $compNpcWar['no'], $amount, 23)] = 3; } if($compNpcCivil && $compNpcCivil[$resName] < $minRes){ $amount = intdiv($minRes+99, 100); -- 2.54.0 From 61911bdd287d1e3b3d0fd8c70951070602f12a88 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 05:37:34 +0900 Subject: [PATCH 186/304] =?UTF-8?q?query=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 676de481..7af75e93 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -908,7 +908,7 @@ function NPCStaffWork($general, $nation, $dipState){ $commandList = []; - foreach($db->query('SELECT * FROM general WHERE nation = %i', $general['nation']) as $nationGeneral) { + foreach($db->query('SELECT `no`, nation, city, npc, `gold`, `rice`, leader, `power`, intel, killturn, crew, train, atmos, `level` FROM general WHERE nation = %i', $general['nation']) as $nationGeneral) { $cityID = $nationGeneral['city']; $generalID = $nationGeneral['no']; -- 2.54.0 From 3a33d0cbc2c964c2043a2f0a7cbabcec7037ef92 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 12:57:11 +0900 Subject: [PATCH 187/304] =?UTF-8?q?=EB=82=B4=EC=A0=95=20=EB=B0=9C=EB=A0=B9?= =?UTF-8?q?=EC=8B=9C=20=EB=82=B4=EC=A0=95=20=EC=B0=AC=EA=B3=B3=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=EB=8A=94=20=EC=95=88=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 7af75e93..395e5d17 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -1092,14 +1092,21 @@ function NPCStaffWork($general, $nation, $dipState){ $minCity = null; $maxCity = null; foreach($nationCities as $nationCity){ + if($nationCity['dev']>=95){ + continue; + } if($nationCity['supply']){ $minCity = $nationCity; break; } + } //reverse_order T_T $maxCity = end($nationCities); + if(!$minCity){ + $minCity = $maxCity; + } while($maxCity['city'] !== $minCity['city']){ if($nationCity['supply']){ break; -- 2.54.0 From 230df2cec0a06340ded7023d2074b76eff3f2dbd Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 13:08:16 +0900 Subject: [PATCH 188/304] =?UTF-8?q?=EB=82=B4=EC=A0=95,=20=EC=A0=84?= =?UTF-8?q?=ED=88=AC=20=EA=B8=B0=EC=A4=80=20=EC=9D=BC=EB=B6=80=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 395e5d17..eac2d980 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -268,7 +268,7 @@ function processAI($no) { } $tech = getTechCost($nation['tech']); - $resrc = $tech * 700;//XXX: 왜 700이지? + $resrc = $tech * 1000;//XXX: 왜 1000이지? if($general['atmos'] >= 90 && $general['train'] >= 90) { if($general['mode'] == 0) { @@ -536,7 +536,7 @@ function processAI($no) { ){ //쌀을 많이 들고 있다면 $amount = $general['rice'] * 0.9; - $amount = intdiv(Util::valueFit($amount, $resrc + 700, 10000), 100); + $amount = intdiv(Util::valueFit($amount, $resrc + 1000, 10000), 100); $command = EncodeCommand(0, 2, $amount, 44); //헌납 $db->update('general', [ @@ -551,7 +551,7 @@ function processAI($no) { ){ //금을 많이 들고 있다면 $amount = $general['gold'] * 0.9; - $amount = intdiv(Util::valueFit($amount, $resrc + 700, 10000), 100); + $amount = intdiv(Util::valueFit($amount, $resrc + 1000, 10000), 100); $command = EncodeCommand(0, 1, $amount, 44); //헌납 $db->update('general', [ @@ -573,12 +573,12 @@ function processAI($no) { // R // ┃ ┃ 공격/내정 -// 700┃쌀┣━━━━┓ +// 500┃쌀┣━━━━┓ // ┃팜┃ ┃ -// 100┣━┫ 내정 ┣━━━ +// 100┣━┫ 내정 ┣━━━ // ┃조┃ ┃쌀삼 // 0┗━┻━━━━┻━━━> G -// 100 700 +// 100 500 $target = array(); // 평시거나 초반아니면서 공격가능 없으면서 병사 있으면 해제(25%) @@ -591,7 +591,7 @@ function processAI($no) { elseif($general['gold'] < 100) { //금없으면 쌀팜 $amount = intdiv(($general['rice'] - $general['gold'])/2, 100); // 100단위 $command = EncodeCommand(0, 1, $amount, 49); //팜 - } elseif($general['gold'] < 700 && $general['rice'] < 700) { $command = EncodeCommand(0, 0, 0, 1); } //금쌀되면 내정 + } elseif($general['gold'] < 500 && $general['rice'] < 500) { $command = EncodeCommand(0, 0, 0, 1); } //금쌀되면 내정 elseif($general['rice'] < 100) { //쌀없으면 쌀삼 $amount = intdiv(($general['gold'] - $general['rice'])/2, 100); // 100단위 $command = EncodeCommand(0, 2, $amount, 49); //삼 @@ -655,16 +655,16 @@ function processAI($no) { // ┃ ┃ // ┃ 쌀팜 ┃ 공격 // ┃ ┃ -// 700t┣━━━━╋━━━━━━━━━ +//1000t┣━━━━╋━━━━━━━━━ // ┃내조 ↗┃ // ┃ ↗ ┃ // ┣━┓내조┃ 쌀삼 // ┃**┃ ┃ // 0 ┗━┻━━━━━━━> G -// 700t +// 1000t } else { // 공격인 경우 - if($general['crew'] < 700 && $general['gold'] >= $resrc && $general['rice'] >= $resrc) { //자원되고, 병사없을때 + if($general['crew'] < 1000 && $general['gold'] >= $resrc && $general['rice'] >= $resrc) { //자원되고, 병사없을때 if($city['pop'] > 40000) { $command = EncodeCommand(0, 0, 0, 11); } else { $command = EncodeCommand(0, 0, 0, 1); } } elseif($general['rice'] < $resrc && $general['rice'] <= $general['gold']) { @@ -678,9 +678,9 @@ function processAI($no) { if($amount > 0) { $command = EncodeCommand(0, 1, $amount, 49); }//팜 else { $command = EncodeCommand(0, 0, 0, (rand()%2)*8 + 1); } // 내정, 조달 //자원, 병사 모두 충족 - } elseif($general['crew'] >= 700 && $general['train'] < 90) { + } elseif($general['crew'] >= 1000 && $general['train'] < 90) { $command = EncodeCommand(0, 0, 0, 13); //훈련 - } elseif($general['crew'] >= 700 && $general['atmos'] < 90) { + } elseif($general['crew'] >= 1000 && $general['atmos'] < 90) { $command = EncodeCommand(0, 0, 0, 14); //사기진작 } else { //공격 @@ -693,10 +693,10 @@ function processAI($no) { // ┃ ┃ // ┃쌀┃ // ┃팜┃ 공격 -// 700t┣━╋━━━━━ +//1000t┣━╋━━━━━ // ┃조┃ 쌀삼 // 0┗━┻━━━━━> G -// 700t +// 1000t //전시일때 if($general['gold'] + $general['rice'] < $resrc*2) { $command = EncodeCommand(0, 0, 0, 9); } //금쌀없으면 조달 @@ -711,7 +711,7 @@ function processAI($no) { if($amount > 0) { $command = EncodeCommand(0, 2, $amount, 49); }// 팜 else { $command = EncodeCommand(0, 0, 0, 9); } // 조달 } elseif($genType >= 2) { $command = EncodeCommand(0, 0, 0, 1); } //내정장일때 내정 - elseif($general['crew'] < 700 && $general['gold'] >= $resrc && $general['rice'] >= $resrc) { + elseif($general['crew'] < 1000 && $general['gold'] >= $resrc && $general['rice'] >= $resrc) { $query = "select no from general where nation='{$general['nation']}'"; $result = MYDB_query($query, $connect) or Error("processAI16 ".MYDB_error($connect),""); $genCount = MYDB_num_rows($result); @@ -750,13 +750,13 @@ function processAI($no) { $command = EncodeCommand(0, 0, 0, 7); //인구 안되면 정장 } } - } elseif($general['crew'] >= 700 && $general['train'] < 90) { + } elseif($general['crew'] >= 1000 && $general['train'] < 90) { if($general['atmos'] >= 90 && $general['train'] >= 60 && $general['mode'] == 0) { $query = "update general set mode=1 where no='{$general['no']}'"; MYDB_query($query, $connect) or Error("processAI05 ".MYDB_error($connect),""); } $command = EncodeCommand(0, 0, 0, 13); //훈련 - } elseif($general['crew'] >= 700 && $general['atmos'] < 90) { + } elseif($general['crew'] >= 1000 && $general['atmos'] < 90) { if($general['atmos'] >= 60 && $general['train'] >= 90 && $general['mode'] == 0) { $query = "update general set mode=1 where no='{$general['no']}'"; MYDB_query($query, $connect) or Error("processAI05 ".MYDB_error($connect),""); @@ -1153,7 +1153,7 @@ function NPCStaffWork($general, $nation, $dipState){ if($nationGeneral['crew'] < 2000){ continue; } - if($nationGeneral['rice'] < 700 * $tech){ + if($nationGeneral['rice'] < 1000 * $tech){ continue; } if($generalCity['front']){ @@ -1203,7 +1203,7 @@ function NPCStaffWork($general, $nation, $dipState){ if($nationGeneral['crew'] >= 1000){ continue; } - if($nationGeneral['rice'] < 700 * $tech){ + if($nationGeneral['rice'] < 1000 * $tech){ continue; } if(!$generalCity['front']){ -- 2.54.0 From 931d291e654838d7a8ab2130a36e7da6350154cb Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 13:28:36 +0900 Subject: [PATCH 189/304] =?UTF-8?q?=EC=9D=B8=EC=82=AC=EB=B6=80=20=EC=9E=84?= =?UTF-8?q?=EB=AA=85=20=ED=95=B4=EC=A0=9C=20=EB=B6=88=EA=B0=80=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/c_myBossInfo.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/hwe/c_myBossInfo.php b/hwe/c_myBossInfo.php index ef3f394c..12ba86db 100644 --- a/hwe/c_myBossInfo.php +++ b/hwe/c_myBossInfo.php @@ -39,16 +39,14 @@ if($meLevel < 5){ } if($btn == "임명") { - if(!$genlist){ - header('location:b_myBossInfo.php'); - exit(); + if($genlist==0){ + $general = []; + } + else{ + $general = $db->queryFirstRow('SELECT `no`,nation,`level`,leader,`power`,intel FROM general WHERE no = %i', $genlist); } - $query = "select no,nation,level,leader,power,intel from general where no='$genlist'"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $general = MYDB_fetch_array($result); - - if(!$general){ + if($genlist != 0 && !$general){ header('location:b_myBossInfo.php'); exit(); } @@ -241,7 +239,7 @@ if($btn == "임명" && $level >= 5 && $level <= 11) { $nation['chemi'] -= 1; if($nation['chemi'] < 0) { $nation['chemi'] = 0; } - $query = "update nation set chemi='{$nation['chemi']}' where nation='{$general['nation']}'"; + $query = "update nation set chemi='{$nation['chemi']}' where nation='{$me['nation']}'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); //기존 장수 일반으로 -- 2.54.0 From e4f53b50fdebeec8d353357bdf5c02c36bb80fa6 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 14:10:52 +0900 Subject: [PATCH 190/304] =?UTF-8?q?=EA=B8=88=EC=8C=80=20=EA=B3=84=EC=88=98?= =?UTF-8?q?=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index eac2d980..2ae08b31 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -268,7 +268,7 @@ function processAI($no) { } $tech = getTechCost($nation['tech']); - $resrc = $tech * 1000;//XXX: 왜 1000이지? + $resrc = $tech * 700;//XXX: 왜 700이지? if($general['atmos'] >= 90 && $general['train'] >= 90) { if($general['mode'] == 0) { @@ -655,13 +655,13 @@ function processAI($no) { // ┃ ┃ // ┃ 쌀팜 ┃ 공격 // ┃ ┃ -//1000t┣━━━━╋━━━━━━━━━ +// 700t┣━━━━╋━━━━━━━━━ // ┃내조 ↗┃ // ┃ ↗ ┃ // ┣━┓내조┃ 쌀삼 // ┃**┃ ┃ // 0 ┗━┻━━━━━━━> G -// 1000t +// 700t } else { // 공격인 경우 if($general['crew'] < 1000 && $general['gold'] >= $resrc && $general['rice'] >= $resrc) { //자원되고, 병사없을때 @@ -1153,7 +1153,7 @@ function NPCStaffWork($general, $nation, $dipState){ if($nationGeneral['crew'] < 2000){ continue; } - if($nationGeneral['rice'] < 1000 * $tech){ + if($nationGeneral['rice'] < 700 * $tech){ continue; } if($generalCity['front']){ @@ -1203,7 +1203,7 @@ function NPCStaffWork($general, $nation, $dipState){ if($nationGeneral['crew'] >= 1000){ continue; } - if($nationGeneral['rice'] < 1000 * $tech){ + if($nationGeneral['rice'] < 700 * $tech){ continue; } if(!$generalCity['front']){ -- 2.54.0 From ead76e2486450ad175b9a441ce487d01c2f7f08c Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 14:22:41 +0900 Subject: [PATCH 191/304] =?UTF-8?q?=EC=A0=81=EA=B7=B9=EC=A0=81=20=EA=B8=88?= =?UTF-8?q?=EC=8C=80=20=EA=B5=90=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 2ae08b31..723b34ee 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -702,11 +702,11 @@ function processAI($no) { if($general['gold'] + $general['rice'] < $resrc*2) { $command = EncodeCommand(0, 0, 0, 9); } //금쌀없으면 조달 elseif($general['rice'] > $resrc && $city['rate'] < 95 && $city['front'] == 0) { $command = EncodeCommand(0, 0, 0, 4); } // 우선 선정 elseif($general['rice'] > $resrc && $city['rate'] < 50 && $city['front'] == 1) { $command = EncodeCommand(0, 0, 0, 4); } // 우선 선정 - elseif($general['gold'] < $resrc) { // 금없으면 쌀팜 + elseif($general['gold'] < $resrc || ($general['gold'] < $resrc *2 && $general['rice'] > $resrc * 6)) { // 금없으면 쌀팜 $amount = intdiv(($general['rice'] - $general['gold'])/2, 100); // 100단위 if($amount > 0) { $command = EncodeCommand(0, 1, $amount, 49); }// 팜 else { $command = EncodeCommand(0, 0, 0, 9); } // 조달 - } elseif($general['rice'] < $resrc) { // 쌀없으면 쌀삼 + } elseif($general['rice'] < $resrc || ($general['rice'] < $resrc *2 && $general['gold'] > $resrc * 6)) { // 쌀없으면 쌀삼 $amount = intdiv(($general['gold'] - $general['rice'])/2, 100); // 100단위 if($amount > 0) { $command = EncodeCommand(0, 2, $amount, 49); }// 팜 else { $command = EncodeCommand(0, 0, 0, 9); } // 조달 -- 2.54.0 From 4645679d47d1de14916e113c27f938451b09715a Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 14:36:05 +0900 Subject: [PATCH 192/304] =?UTF-8?q?=EC=9D=B8=EC=82=AC=EB=B6=80=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EA=B2=80=EC=82=AC=20=EC=88=9C=EC=84=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/c_myBossInfo.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hwe/c_myBossInfo.php b/hwe/c_myBossInfo.php index 12ba86db..d03dddc1 100644 --- a/hwe/c_myBossInfo.php +++ b/hwe/c_myBossInfo.php @@ -52,8 +52,7 @@ if($btn == "임명") { } //임명할사람이 군주이면 불가, 내가 수뇌부이어야함, 공석아닌때는 국가가 같아야함 - if($meLevel < 5 || ($general['nation'] != $me['nation'] && $genlist != 0) || ($general['level'] == 12 && $genlist != 0)) { - + if($meLevel < 5 || ($genlist != 0 && $general['nation'] != $me['nation']) || ($genlist != 0 && $general['level'] == 12)) { header('location:b_myBossInfo.php'); exit(); } -- 2.54.0 From 3998bddacac95b56257224097cdd91011307a28e Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 22:52:43 +0900 Subject: [PATCH 193/304] =?UTF-8?q?npc=20=EB=B0=9C=EB=A0=B9=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD.=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EB=AC=B4=EC=A7=80=EC=9E=A5=20=ED=8F=AC=EC=83=81=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 723b34ee..512622b0 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -999,17 +999,36 @@ function NPCStaffWork($general, $nation, $dipState){ //금쌀이 부족한 '유저장' 먼저 포상 - if ($nation[$resName] > ($resName=='gold'?1:2)*3000 && $userGeneralsID) { - $compUser = $nationGenerals[$userGeneralsID[0]]; + while ($nation[$resName] > ($resName=='gold'?1:2)*3000 && $userGeneralsID) { + $isWarUser = null; + + foreach($userGeneralsID as $userGeneralID){ + $compUser = $nationGenerals[$userGeneralID]; + if($compUser['leader'] >= 50){ + $isWarUser = true; + break; + } + if(Util::randF(0.2)){ + $isWarUser = false; + break; + } + } + + if($isWarUser === null){ + break; + } + $compRes = $compUser[$resName]; $work = false; - if ($compRes < $avgNpcWarRes*3) { + if(!$isWarUser){ + $work = false; + } else if ($compRes < $avgNpcWarRes*3) { $work = true; } elseif ($compRes < $avgNpcCivilRes * 4) { $work = true; } - + if($compUser[$resName] < 21000){ if($work){ $amount = min(100, intdiv(($nation[$resName]-($resName=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 2000)*10 + 10); @@ -1021,6 +1040,7 @@ function NPCStaffWork($general, $nation, $dipState){ } } + break; } $minRes = $admin['develcost'] * 24 * $tech; @@ -1091,6 +1111,7 @@ function NPCStaffWork($general, $nation, $dipState){ $targetCity = null; $minCity = null; $maxCity = null; + $maxDevCity = null; foreach($nationCities as $nationCity){ if($nationCity['dev']>=95){ continue; @@ -1127,10 +1148,27 @@ function NPCStaffWork($general, $nation, $dipState){ } } + foreach ($nationCities as $nationCity) { + if(!$nationCity['supply']){ + continue; + } + if(count($nationCity['generals']) == 0){ + continue; + } + if($maxDevCity === null || $maxDevCity['dev'] < $nationCity['dev']){ + $maxDevCity = $nationCity; + } + } + if($targetCity === null || (count($targetCity['generals']) >= count($maxCity['generals']) - 1)){ $targetCity = $minCity; } + if($maxDevCity['dev'] >= 95 && $targetCity['city'] != $maxDevCity['city'] && $targetCity['dev'] <= 70){ + $targetGeneral = $nationGenerals[Util::choiceRandom($maxDevCity['generals'])]; + $commandList[EncodeCommand(0, $targetGeneral['no'], $targetCity['city'], 27)] = 2; + } + if(count($targetCity['generals']) < count($maxCity['generals']) - 2){ //세명 이상 차이나야 함 $targetGeneral = $nationGenerals[Util::choiceRandom($maxCity['generals'])]; -- 2.54.0 From fba0f67181617fec03b52f28877cb992ba5a3935 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 22:55:59 +0900 Subject: [PATCH 194/304] =?UTF-8?q?=EB=B2=A0=ED=8C=85=20=ED=91=9C=EA=B8=B0?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/b_betting.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hwe/b_betting.php b/hwe/b_betting.php index 5e17c32b..97fd71ed 100644 --- a/hwe/b_betting.php +++ b/hwe/b_betting.php @@ -297,7 +297,13 @@ for ($i=0; $i < 16; $i++) { } for ($i=0; $i < 16; $i++) { - $gold[$i] = Util::round($myBet[$i] * $bet[$i]); + if(!is_numeric($bet[$i])){ + $gold[$i] = 0; + } + else{ + $gold[$i] = Util::round($myBet[$i] * $bet[$i]); + } + } ?> -- 2.54.0 From 69ff5393ef46d175216e2cffc46f94184059e067 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 22:58:11 +0900 Subject: [PATCH 195/304] =?UTF-8?q?=EB=AC=B4=EC=A7=80=EC=9E=A5=EC=9D=98=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=EC=97=90=EB=8A=94=20=EC=8C=80=20=EC=A0=81?= =?UTF-8?q?=EA=B2=8C=20=EC=A3=BC=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 512622b0..f58ad664 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -1029,7 +1029,7 @@ function NPCStaffWork($general, $nation, $dipState){ $work = true; } - if($compUser[$resName] < 21000){ + if((($isWarUser || $resName == 'gold') && $compUser[$resName] < 21000) || ($compUser[$resName] < 5000)){ if($work){ $amount = min(100, intdiv(($nation[$resName]-($resName=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 2000)*10 + 10); $commandList[EncodeCommand($resType, $userGeneralsID[0], $amount, 23)] = 40; // 금,쌀 1000단위 포상 -- 2.54.0 From 6031ec2578968faab11c012ac63bc073ef2021b7 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 23 Jun 2018 23:49:22 +0900 Subject: [PATCH 196/304] =?UTF-8?q?=ED=8F=AC=EC=83=81=20=EA=B3=84=EC=88=98?= =?UTF-8?q?=20=EC=A1=B0=EC=A0=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index f58ad664..2e6c169d 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -1031,8 +1031,8 @@ function NPCStaffWork($general, $nation, $dipState){ if((($isWarUser || $resName == 'gold') && $compUser[$resName] < 21000) || ($compUser[$resName] < 5000)){ if($work){ - $amount = min(100, intdiv(($nation[$resName]-($resName=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 2000)*10 + 10); - $commandList[EncodeCommand($resType, $userGeneralsID[0], $amount, 23)] = 40; // 금,쌀 1000단위 포상 + $amount = min(100, intdiv(($nation[$resName]-($resName=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 3000)*10 + 10); + $commandList[EncodeCommand($resType, $userGeneralsID[0], $amount, 23)] = 10; // 금,쌀 1000단위 포상 } else{ $amount = min(100, intdiv(($nation[$resName]-($resName=='rice'?(GameConst::$baserice):(GameConst::$basegold))), 5000)*10 + 10); -- 2.54.0 From 2add3944d3bf7dedbf506dcf0c3fd4b4472b4567 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 24 Jun 2018 02:56:38 +0900 Subject: [PATCH 197/304] =?UTF-8?q?html=20purifier=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 3 +- composer.lock | 42 ++++++- vendor/composer/autoload_classmap.php | 8 ++ vendor/composer/autoload_files.php | 2 +- vendor/composer/autoload_static.php | 10 +- vendor/composer/installed.json | 42 +++++++ vendor/xemlock/htmlpurifier-html5/LICENSE | 22 ++++ vendor/xemlock/htmlpurifier-html5/README.md | 62 +++++++++ .../xemlock/htmlpurifier-html5/composer.json | 25 ++++ .../HTMLPurifier/AttrDef/HTML/Bool2.php | 38 ++++++ .../library/HTMLPurifier/AttrDef/Regexp.php | 48 +++++++ .../library/HTMLPurifier/ChildDef/Details.php | 84 +++++++++++++ .../library/HTMLPurifier/ChildDef/Figure.php | 89 +++++++++++++ .../library/HTMLPurifier/ChildDef/Media.php | 94 ++++++++++++++ .../library/HTMLPurifier/ChildDef/Picture.php | 54 ++++++++ .../library/HTMLPurifier/HTML5Config.php | 75 +++++++++++ .../library/HTMLPurifier/HTML5Definition.php | 118 ++++++++++++++++++ 17 files changed, 812 insertions(+), 4 deletions(-) create mode 100644 vendor/xemlock/htmlpurifier-html5/LICENSE create mode 100644 vendor/xemlock/htmlpurifier-html5/README.md create mode 100644 vendor/xemlock/htmlpurifier-html5/composer.json create mode 100644 vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/HTML/Bool2.php create mode 100644 vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/Regexp.php create mode 100644 vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Details.php create mode 100644 vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Figure.php create mode 100644 vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Media.php create mode 100644 vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Picture.php create mode 100644 vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Config.php create mode 100644 vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Definition.php diff --git a/composer.json b/composer.json index 218dbccd..7158ebdf 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "php-extended/php-tail": "^2.1", "pguardiario/phpuri": "^1.0", "symfony/lock": "^4.1", - "ezyang/htmlpurifier": "^4.10" + "ezyang/htmlpurifier": "^4.10", + "xemlock/htmlpurifier-html5": "^0.1.7" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 5e70d1a5..3c556fb3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "cdf923ea7b2d505b83ec019db08b2f9b", + "content-hash": "43da7d5e9d8441faa4311f32b16ee1c0", "packages": [ { "name": "brandonwamboldt/utilphp", @@ -858,6 +858,46 @@ "validator" ], "time": "2018-05-06T10:45:26+00:00" + }, + { + "name": "xemlock/htmlpurifier-html5", + "version": "v0.1.7", + "source": { + "type": "git", + "url": "https://github.com/xemlock/htmlpurifier-html5.git", + "reference": "b4b35e51ec75e0207c23035f7d9a354bdcdac359" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/xemlock/htmlpurifier-html5/zipball/b4b35e51ec75e0207c23035f7d9a354bdcdac359", + "reference": "b4b35e51ec75e0207c23035f7d9a354bdcdac359", + "shasum": "" + }, + "require": { + "ezyang/htmlpurifier": "^4.7", + "php": ">=5.2" + }, + "require-dev": { + "phpunit/phpunit": "4.7.*|5.7.*" + }, + "type": "library", + "autoload": { + "classmap": [ + "library/HTMLPurifier/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "xemlock", + "email": "xemlock@gmail.com" + } + ], + "description": "HTML Purifier definitions for HTML5 elements", + "time": "2018-06-07T14:30:17+00:00" } ], "packages-dev": [], diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 0f375530..f698c3fd 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -11,6 +11,14 @@ return array( 'DBTransaction' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', 'FB' => $vendorDir . '/firephp/firephp-core/lib/FirePHPCore/fb.php', 'FirePHP' => $vendorDir . '/firephp/firephp-core/lib/FirePHPCore/FirePHP.class.php', + 'HTMLPurifier_AttrDef_HTML_Bool2' => $vendorDir . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/HTML/Bool2.php', + 'HTMLPurifier_AttrDef_Regexp' => $vendorDir . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/Regexp.php', + 'HTMLPurifier_ChildDef_Details' => $vendorDir . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Details.php', + 'HTMLPurifier_ChildDef_Figure' => $vendorDir . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Figure.php', + 'HTMLPurifier_ChildDef_Media' => $vendorDir . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Media.php', + 'HTMLPurifier_ChildDef_Picture' => $vendorDir . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Picture.php', + 'HTMLPurifier_HTML5Config' => $vendorDir . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Config.php', + 'HTMLPurifier_HTML5Definition' => $vendorDir . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Definition.php', 'MeekroDB' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', 'MeekroDBEval' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', 'MeekroDBException' => $vendorDir . '/sergeytsalkov/meekrodb/db.class.php', diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index db419d6f..c61c1246 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -6,9 +6,9 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', - '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '5b154887902198b16314243c6e0e3e19' => $vendorDir . '/pguardiario/phpuri/phpuri.php', '870dc64919afa8b0f700701bb2c6a783' => $baseDir . '/f_config/config.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index b1fe0e54..1762a4fd 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -7,9 +7,9 @@ namespace Composer\Autoload; class ComposerStaticInit67b09c83b85c3bc0027caefe5bba171a { public static $files = array ( + '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', - '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '5b154887902198b16314243c6e0e3e19' => __DIR__ . '/..' . '/pguardiario/phpuri/phpuri.php', '870dc64919afa8b0f700701bb2c6a783' => __DIR__ . '/../..' . '/f_config/config.php', @@ -138,6 +138,14 @@ class ComposerStaticInit67b09c83b85c3bc0027caefe5bba171a 'DBTransaction' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', 'FB' => __DIR__ . '/..' . '/firephp/firephp-core/lib/FirePHPCore/fb.php', 'FirePHP' => __DIR__ . '/..' . '/firephp/firephp-core/lib/FirePHPCore/FirePHP.class.php', + 'HTMLPurifier_AttrDef_HTML_Bool2' => __DIR__ . '/..' . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/HTML/Bool2.php', + 'HTMLPurifier_AttrDef_Regexp' => __DIR__ . '/..' . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/Regexp.php', + 'HTMLPurifier_ChildDef_Details' => __DIR__ . '/..' . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Details.php', + 'HTMLPurifier_ChildDef_Figure' => __DIR__ . '/..' . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Figure.php', + 'HTMLPurifier_ChildDef_Media' => __DIR__ . '/..' . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Media.php', + 'HTMLPurifier_ChildDef_Picture' => __DIR__ . '/..' . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Picture.php', + 'HTMLPurifier_HTML5Config' => __DIR__ . '/..' . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Config.php', + 'HTMLPurifier_HTML5Definition' => __DIR__ . '/..' . '/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Definition.php', 'MeekroDB' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', 'MeekroDBEval' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', 'MeekroDBException' => __DIR__ . '/..' . '/sergeytsalkov/meekrodb/db.class.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 544eced6..1829b0a3 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -885,5 +885,47 @@ "validation", "validator" ] + }, + { + "name": "xemlock/htmlpurifier-html5", + "version": "v0.1.7", + "version_normalized": "0.1.7.0", + "source": { + "type": "git", + "url": "https://github.com/xemlock/htmlpurifier-html5.git", + "reference": "b4b35e51ec75e0207c23035f7d9a354bdcdac359" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/xemlock/htmlpurifier-html5/zipball/b4b35e51ec75e0207c23035f7d9a354bdcdac359", + "reference": "b4b35e51ec75e0207c23035f7d9a354bdcdac359", + "shasum": "" + }, + "require": { + "ezyang/htmlpurifier": "^4.7", + "php": ">=5.2" + }, + "require-dev": { + "phpunit/phpunit": "4.7.*|5.7.*" + }, + "time": "2018-06-07T14:30:17+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "library/HTMLPurifier/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "xemlock", + "email": "xemlock@gmail.com" + } + ], + "description": "HTML Purifier definitions for HTML5 elements" } ] diff --git a/vendor/xemlock/htmlpurifier-html5/LICENSE b/vendor/xemlock/htmlpurifier-html5/LICENSE new file mode 100644 index 00000000..cf02cf13 --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Xemlock + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/xemlock/htmlpurifier-html5/README.md b/vendor/xemlock/htmlpurifier-html5/README.md new file mode 100644 index 00000000..aea27ebd --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/README.md @@ -0,0 +1,62 @@ +# HTML5 Definitions for HTML Purifier + +[![Build Status](https://travis-ci.org/xemlock/htmlpurifier-html5.svg?branch=master)](https://travis-ci.org/xemlock/htmlpurifier-html5) +[![Latest Stable Version](https://img.shields.io/packagist/v/xemlock/htmlpurifier-html5.svg)](https://packagist.org/packages/xemlock/htmlpurifier-html5) +[![Total Downloads](https://img.shields.io/packagist/dt/xemlock/htmlpurifier-html5.svg)](https://packagist.org/packages/xemlock/htmlpurifier-html5) +[![License](https://img.shields.io/packagist/l/xemlock/htmlpurifier-html5.svg)](https://packagist.org/packages/xemlock/htmlpurifier-html5) + +This library provides HTML5 element definitions for [HTMLPurifier](http://htmlpurifier.org/). + +## Installation + +Install with [Composer](https://getcomposer.org/) by running the following command: + +``` +composer require xemlock/htmlpurifier-html5 +``` + +## Usage + +The most basic usage is similar to the original HTML Purifier. Create a HTML5-compatible config +using `HTMLPurifier_HTML5Config::createDefault()` factory method, and then pass it to an `HTMLPurifier` instance: + +```php +$config = HTMLPurifier_HTML5Config::createDefault(); +$purifier = new HTMLPurifier($config); +$clean_html5 = $purifier->purify($dirty_html5); +``` + +To modify the config you can either instantiate the config with a configuration array passed to +`HTMLPurifier_HTML5Config::create()`, or by calling `set` method on an already existing config instance. + +For example, to allow `IFRAME`s with Youtube videos you can do the following: + +```php +$config = HTMLPurifier_HTML5Config::create(array( + 'HTML.SafeIframe' => true, + 'URI.SafeIframeRegexp' => '%^//www\.youtube\.com/embed/%', +)); +``` + +or equivalently: + +```php +$config = HTMLPurifier_HTML5Config::createDefault(); +$config->set('HTML.SafeIframe', true); +$config->set('URI.SafeIframeRegexp', '#^//www\.youtube\.com/embed/#'); +``` + +## Supported HTML5 elements + +Aside from HTML elements supported originally by HTML Purifier, this library +adds support for the following HTML5 elements: + +article, aside, audio, details, figcaption, figure, footer, header, hgroup, main, nav, picture, section, source, summary, time, track, video + +### Elements not (yet) supported + +dialog, menu, menuitem, progress + +## License + +The MIT License (MIT). See the LICENSE file. diff --git a/vendor/xemlock/htmlpurifier-html5/composer.json b/vendor/xemlock/htmlpurifier-html5/composer.json new file mode 100644 index 00000000..3bdbe952 --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/composer.json @@ -0,0 +1,25 @@ +{ + "name": "xemlock/htmlpurifier-html5", + "description": "HTML Purifier definitions for HTML5 elements", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "xemlock", + "email": "xemlock@gmail.com" + } + ], + "require": { + "php": ">=5.2", + "ezyang/htmlpurifier": "^4.7" + }, + "require-dev": { + "phpunit/phpunit": "4.7.*|5.7.*" + }, + "autoload": { + "classmap": ["library/HTMLPurifier/"] + }, + "scripts": { + "test": "phpunit" + } +} diff --git a/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/HTML/Bool2.php b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/HTML/Bool2.php new file mode 100644 index 00000000..b063a9c6 --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/HTML/Bool2.php @@ -0,0 +1,38 @@ +name) ? $this->name : $context->get('CurrentAttr'); + // boolean attribute validates if its value is either empty + // or case-insensitively equal to attribute name + return $string === '' || strcasecmp($name, $string) === 0; + } + + /** + * @param string $string Name of attribute + * @return HTMLPurifier_AttrDef_HTML_Bool2 + */ + public function make($string) + { + return new self($string); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/Regexp.php b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/Regexp.php new file mode 100644 index 00000000..edcfc338 --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/AttrDef/Regexp.php @@ -0,0 +1,48 @@ +pattern = $pattern; + } + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function validate($string, $config, $context) + { + if ($this->pattern) { + return (bool) preg_match($this->pattern, $string); + } + return false; + } + + /** + * @param string $string + * @return HTMLPurifier_AttrDef_Regexp + */ + public function make($string) + { + return new self($string); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Details.php b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Details.php new file mode 100644 index 00000000..fce75bd7 --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Details.php @@ -0,0 +1,84 @@ + true, + ); + + protected $allowedElements; + + /** + * @param HTMLPurifier_Config $config + * @return array + */ + public function getAllowedElements($config) + { + if (null === $this->allowedElements) { + // Add Flow content to allowed elements to prevent MakeWellFormed + // strategy moving them outside details element + $def = $config->getHTMLDefinition(); + + $this->allowedElements = array_merge( + $def->info_content_sets['Flow'], + $this->elements + ); + } + return $this->allowedElements; + } + + /** + * @param array $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function validateChildren($children, $config, $context) + { + if (empty($children)) { + return false; + } + + if (!isset($config->getHTMLDefinition()->info['summary'])) { + trigger_error("Cannot allow details without allowing summary", E_USER_WARNING); + return false; + } + + $summary = null; + $result = array(); + + // Content model: + // One summary element followed by flow content + foreach ($children as $node) { + if (!$summary && $node->name === 'summary') { + $summary = $node; + continue; + } + if ($node->name === 'summary') { + // duplicated summary, add only its children + $result = array_merge($result, (array) $node->children); + } else { + $result[] = $node; + } + } + + $whitespaceOnly = true; + foreach ($result as $node) { + $whitespaceOnly = $whitespaceOnly && !empty($node->is_whitespace); + } + + if (!$summary) { + // remove parent node if there are no children or all children are whitespace-only + if ($whitespaceOnly) { + return false; + } + $summary = new HTMLPurifier_Node_Element('summary'); + } + + array_unshift($result, $summary); + + return $result; + } +} diff --git a/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Figure.php b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Figure.php new file mode 100644 index 00000000..b59f4041 --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Figure.php @@ -0,0 +1,89 @@ + true, + ); + + protected $allowedElements; + + /** + * @param HTMLPurifier_Config $config + * @return array + */ + public function getAllowedElements($config) + { + if (null === $this->allowedElements) { + // Add Flow content to allowed elements to prevent MakeWellFormed + // strategy moving them outside 'figure' element + $def = $config->getHTMLDefinition(); + + $this->allowedElements = array_merge( + $def->info_content_sets['Flow'], + $this->elements + ); + } + return $this->allowedElements; + } + + /** + * @param array $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function validateChildren($children, $config, $context) + { + $allowFigcaption = isset($config->getHTMLDefinition()->info['figcaption']); + $hasFigcaption = false; + $figcaptionPos = -1; + + $result = array(); + + // Content model: + // Either: one figcaption element followed by flow content. + // Or: flow content followed by one figcaption element. + // Or: flow content. + + // Scan through children, accept at most one figcaption. If additional + // figcaption appears replace it with div + foreach ($children as $node) { + if ($node->name === 'figcaption') { + if ($allowFigcaption && !$hasFigcaption) { + $hasFigcaption = true; + $figcaptionPos = count($result); + $result[] = $node; + continue; + } + + $div = new HTMLPurifier_Node_Element('div', $node->attr); + $div->children = $node->children; + $result[] = $div; + continue; + } + + // Figcaption must be a first or last child of a figure element. + // If it's not first, then we ignore all siblings that come after. + if ($hasFigcaption && $figcaptionPos > 0) { + break; + } + + $result[] = $node; + } + + $whitespaceOnly = true; + foreach ($result as $node) { + $whitespaceOnly = $whitespaceOnly && !empty($node->is_whitespace); + } + + // remove parent node if there are no children or all children are whitespace-only + if (empty($result) || $whitespaceOnly) { + return false; + } + + return $result; + } +} diff --git a/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Media.php b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Media.php new file mode 100644 index 00000000..d77a13da --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Media.php @@ -0,0 +1,94 @@ + true, + 'track' => true, + ); + + protected $allowedElements; + + /** + * @param HTMLPurifier_Config $config + * @return array + */ + public function getAllowedElements($config) + { + if (null === $this->allowedElements) { + // Add Flow content to allowed elements to prevent MakeWellFormed + // strategy moving them outside details element + $def = $config->getHTMLDefinition(); + + $this->allowedElements = array_merge( + $def->info_content_sets['Flow'], + $this->elements + ); + unset( + $this->allowedElements['audio'], + $this->allowedElements['video'] + ); + } + return $this->allowedElements; + } + + /** + * @param array $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function validateChildren($children, $config, $context) + { + // Content model: + // If the element has a src attribute: zero or more track elements, + // then transparent, but with no media element descendants. + // If the element does not have a src attribute: zero or more source + // elements, then zero or more track elements, then transparent, but + // with no media element descendants. + + $allowSource = isset($config->getHTMLDefinition()->info['source']); + $allowTrack = isset($config->getHTMLDefinition()->info['track']); + + $sources = array(); + $tracks = array(); + $content = array(); + + foreach ($children as $node) { + switch ($node->name) { + case 'source': + if ($allowSource) { + $sources[] = $node; + } + break; + + case 'track': + if ($allowTrack) { + $tracks[] = $node; + } + break; + + default: + $content[] = $node; + break; + } + } + + $currentNode = $context->get('CurrentNode'); + $hasSrcAttr = $currentNode instanceof HTMLPurifier_Node_Element && isset($currentNode->attr['src']); + + if ($hasSrcAttr) { + $result = array_merge($tracks, $content); + } else { + $result = array_merge($sources, $tracks, $content); + } + + if (empty($result) && !$hasSrcAttr) { + return false; + } + + return $result; + } +} diff --git a/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Picture.php b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Picture.php new file mode 100644 index 00000000..192571f6 --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/ChildDef/Picture.php @@ -0,0 +1,54 @@ + true, + 'source' => true, + ); + + /** + * @param array $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function validateChildren($children, $config, $context) + { + // if there are no tokens, delete parent node + if (empty($children)) { + return false; + } + + if (!isset($config->getHTMLDefinition()->info['img'])) { + trigger_error("Cannot allow picture without allowing img", E_USER_WARNING); + return false; + } + + $allowSource = isset($config->getHTMLDefinition()->info['source']); + $hasImg = false; + + $result = array(); + + // Content model: + // Zero or more source elements, followed by one img element, optionally intermixed with script-supporting elements. + // https://html.spec.whatwg.org/multipage/embedded-content.html#the-picture-element + foreach ($children as $node) { + if (($allowSource && $node->name === 'source') || $node->name === 'img') { + $result[] = $node; + } + if ($node->name === 'img') { + $hasImg = true; + break; + } + } + + if (!$hasImg || empty($result)) { + return false; + } + + return $result; + } +} diff --git a/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Config.php b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Config.php new file mode 100644 index 00000000..bbc9028d --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Config.php @@ -0,0 +1,75 @@ +def; + $config = null; + } + + if (!$schema instanceof HTMLPurifier_ConfigSchema) { + $schema = HTMLPurifier_ConfigSchema::instance(); + } + + $configObj = new self($schema); + $configObj->set('Core.Encoding', 'UTF-8'); + $configObj->set('HTML.Doctype', 'HTML 4.01 Transitional'); + + $configObj->set('HTML.DefinitionID', __CLASS__); + $configObj->set('HTML.DefinitionRev', self::REVISION); + + if (is_string($config)) { + $configObj->loadIni($config); + + } elseif (is_array($config)) { + $configObj->loadArray($config); + } + + return $configObj; + } + + /** + * Creates a configuration object using the default config schema instance + * + * @return HTMLPurifier_Config + */ + public static function createDefault() + { + $schema = HTMLPurifier_ConfigSchema::instance(); + $config = self::create(null, $schema); + return $config; + } + + /** + * Creates a new config object that inherits from a previous one + * + * @param HTMLPurifier_Config $config + * @return HTMLPurifier_Config + */ + public static function inherit(HTMLPurifier_Config $config) + { + return new self($config->def, $config->plist); + } + + public function getDefinition($type, $raw = false, $optimized = false) + { + // Setting HTML.* keys removes any previously instantiated HTML + // definition object, so set up HTML5 definition as late as possible + $needSetup = $type === 'HTML' && !isset($this->definitions[$type]); + if ($needSetup) { + if ($def = parent::getDefinition($type, true, true)) { + HTMLPurifier_HTML5Definition::setup($def); + } + } + return parent::getDefinition($type, $raw, $optimized); + } +} diff --git a/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Definition.php b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Definition.php new file mode 100644 index 00000000..90dded61 --- /dev/null +++ b/vendor/xemlock/htmlpurifier-html5/library/HTMLPurifier/HTML5Definition.php @@ -0,0 +1,118 @@ +manager->attrTypes->set('Bool', new HTMLPurifier_AttrDef_HTML_Bool2()); + + // http://developers.whatwg.org/sections.html + $def->addElement('section', 'Block', 'Flow', 'Common'); + $def->addElement('nav', 'Block', 'Flow', 'Common'); + $def->addElement('article', 'Block', 'Flow', 'Common'); + $def->addElement('aside', 'Block', 'Flow', 'Common'); + $def->addElement('header', 'Block', 'Flow', 'Common'); + $def->addElement('footer', 'Block', 'Flow', 'Common'); + $def->addElement('main', 'Block', 'Flow', 'Common'); + + // Content model actually excludes several tags, not modelled here + $def->addElement('address', 'Block', 'Flow', 'Common'); + $def->addElement('hgroup', 'Block', 'Required: h1 | h2 | h3 | h4 | h5 | h6', 'Common'); + + // https://html.spec.whatwg.org/dev/grouping-content.html#the-figure-element + $def->addElement('figure', 'Block', new HTMLPurifier_ChildDef_Figure(), 'Common'); + $def->addElement('figcaption', false, 'Flow', 'Common'); + + $mediaContent = new HTMLPurifier_ChildDef_Media(); + + // https://html.spec.whatwg.org/dev/media.html#the-video-element + $def->addElement('video', 'Flow', $mediaContent, 'Common', array( + 'controls' => 'Bool', + 'height' => 'Length', + 'poster' => 'URI', + 'preload' => 'Enum#auto,metadata,none', + 'src' => 'URI', + 'width' => 'Length', + )); + $def->getAnonymousModule()->addElementToContentSet('video', 'Inline'); + + // https://html.spec.whatwg.org/dev/media.html#the-audio-element + $def->addElement('audio', 'Flow', $mediaContent, 'Common', array( + 'controls' => 'Bool', + 'preload' => 'Enum#auto,metadata,none', + 'src' => 'URI', + )); + $def->getAnonymousModule()->addElementToContentSet('audio', 'Inline'); + + // https://html.spec.whatwg.org/dev/embedded-content.html#the-source-element + $def->addElement('source', false, 'Empty', 'Common', array( + 'media' => 'Text', + 'sizes' => 'Text', + 'src' => 'URI', + 'srcset' => 'Text', + 'type' => 'Text', + )); + + // https://html.spec.whatwg.org/dev/media.html#the-track-element + $def->addElement('track', false, 'Empty', 'Common', array( + 'kind' => 'Enum#captions,chapters,descriptions,metadata,subtitles', + 'src' => 'URI', + 'srclang' => 'Text', + 'label' => 'Text', + 'default' => 'Bool', + )); + + // https://html.spec.whatwg.org/dev/embedded-content.html#the-picture-element + $def->addElement('picture', 'Flow', new HTMLPurifier_ChildDef_Picture(), 'Common'); + $def->getAnonymousModule()->addElementToContentSet('picture', 'Inline'); + + // http://developers.whatwg.org/text-level-semantics.html + $def->addElement('s', 'Inline', 'Inline', 'Common'); + $def->addElement('var', 'Inline', 'Inline', 'Common'); + $def->addElement('sub', 'Inline', 'Inline', 'Common'); + $def->addElement('sup', 'Inline', 'Inline', 'Common'); + $def->addElement('mark', 'Inline', 'Inline', 'Common'); + $def->addElement('wbr', 'Inline', 'Empty', 'Core'); + + // http://developers.whatwg.org/edits.html + $def->addElement('ins', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text')); + $def->addElement('del', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text')); + + // TIME + $time = $def->addElement('time', 'Inline', 'Inline', 'Common', array('datetime' => 'Text', 'pubdate' => 'Bool')); + $time->excludes = array('time' => true); + + // https://html.spec.whatwg.org/dev/text-level-semantics.html#the-a-element + $def->addElement('a', 'Flow', 'Flow', 'Common', array( + 'download' => 'Text', + 'hreflang' => 'Text', + 'rel' => 'Text', + 'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget(), + 'type' => 'Text', + )); + + // IMG + $def->addAttribute('img', 'srcset', 'Text'); + $def->addAttribute('img', 'sizes', 'Text'); + + // IFRAME + $def->addAttribute('iframe', 'allowfullscreen', 'Bool'); + + // Interactive elements + // https://html.spec.whatwg.org/dev/interactive-elements.html#the-details-element + $def->addElement('details', 'Block', new HTMLPurifier_ChildDef_Details(), 'Common', array( + 'open' => 'Bool', + )); + $def->addElement('summary', false, 'Flow', 'Common'); + + return $def; + } +} -- 2.54.0 From 8e26910343a52217fa01574a4f6115263fd3fc55 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 24 Jun 2018 03:47:44 +0900 Subject: [PATCH 198/304] =?UTF-8?q?purifier=201=EC=B0=A8=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + hwe/b_dipcenter.php | 4 ++-- hwe/c_dipcenter.php | 26 ++++++++++++-------------- hwe/c_vote.php | 4 ++-- hwe/func_string.php | 7 ------- hwe/processing.php | 13 ++++++++----- src/sammo/WebUtil.php | 10 ++++++++++ 7 files changed, 35 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 1cfa8731..61a7af99 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ test.php /twe phpinfo.php +vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/* diff --git a/hwe/b_dipcenter.php b/hwe/b_dipcenter.php index ad0810f0..1dd949c9 100644 --- a/hwe/b_dipcenter.php +++ b/hwe/b_dipcenter.php @@ -217,8 +217,8 @@ if ($budgetricediff > 0) {
- - + + diff --git a/hwe/c_dipcenter.php b/hwe/c_dipcenter.php index 929543ee..07c8424c 100644 --- a/hwe/c_dipcenter.php +++ b/hwe/c_dipcenter.php @@ -19,11 +19,8 @@ $session = Session::requireGameLogin()->setReadOnly(); $userID = Session::getUserID(); $db = DB::db(); -$connect=$db->get(); -$query = "select no,nation,level from general where owner='{$userID}'"; -$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); -$me = MYDB_fetch_array($result); +$me = $db->queryFirstRow('SELECT `no`,nation,`level` FROM general WHERE `owner`=%i', $userID); //내가 수뇌부이어야함 if($me['level'] < 5) { @@ -32,21 +29,21 @@ if($me['level'] < 5) { } if($btn == "국가방침") { - $msg == mb_substr($msg, 0, 1000); + $msg = mb_substr($msg, 0, 16384); //$msg = StringUtil:: $db->update('nation', [ - 'msg'=>BadTag2Code($msg) + 'msg'=>WebUtil::htmlPurify($msg) ], 'nation=%i',$me['nation']); } elseif($btn == "임관권유") { - $scoutmsg == mb_substr($msg, 0, 500); + $scoutmsg = mb_substr($scoutmsg, 0, 1000); $db->update('nation', [ - 'scoutmsg'=>BadTag2Code($scoutmsg) + 'scoutmsg'=>WebUtil::htmlPurify($scoutmsg) ], 'nation=%i',$me['nation']); } elseif($btn == "세율") { - if($rate < 5) { $rate = 5; } - if($rate > 30) { $rate = 30; } - $query = "update nation set rate='$rate' where nation='{$me['nation']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + $rate = Util::valueFit($rate, 5, 30); + $db->update('nation', [ + 'rate'=>$rate, + ], 'nation=%i', $me['nation']); } elseif($btn == "지급율") { $bill = Util::valueFit($bill, 20, 200); $db->update('nation', [ @@ -70,8 +67,9 @@ if($btn == "국가방침") { 'war'=>1 ], 'nation=%i',$me['nation']); } elseif($btn == "전쟁 허가") { - $query = "update nation set war='0' where nation='{$me['nation']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + $db->update('nation', [ + 'war'=>0 + ], 'nation=%i',$me['nation']); } header('location:b_dipcenter.php'); diff --git a/hwe/c_vote.php b/hwe/c_vote.php index 7653edb5..7224f476 100644 --- a/hwe/c_vote.php +++ b/hwe/c_vote.php @@ -58,14 +58,14 @@ if(!$isVoteAdmin){ if($btn == "수정") { if($title != "") { - $gameStor->vote_title = $title; + $gameStor->vote_title = WebUtil::htmlPurify($title); } } elseif($btn == "추가") { if($str != "") { if(!$admin['vote']){ $admin['vote'] = []; } - $admin['vote'][] = $str; + $admin['vote'][] = WebUtil::htmlPurify($str); $gameStor->vote=$admin['vote']; } } elseif($btn == "리셋") { diff --git a/hwe/func_string.php b/hwe/func_string.php index fb50e16e..6f6c93a2 100644 --- a/hwe/func_string.php +++ b/hwe/func_string.php @@ -24,13 +24,6 @@ function Tag2Code(string $str) { return nl2br($str); } -function BadTag2Code(string $str) { - /* FIXME: 제대로된 tag 변환 코드 사용 */ - $str = str_replace("{$nation['name']}"; - } else { - $scoutStr .= ""; - } + $scoutStr .= + "" + ."" + ."'; if($gameStor->year < $gameStor->startyear+3 && $nation['gennum'] >= GameConst::$initialNationGenLimit) { echo " diff --git a/src/sammo/WebUtil.php b/src/sammo/WebUtil.php index 969b2046..05f77318 100644 --- a/src/sammo/WebUtil.php +++ b/src/sammo/WebUtil.php @@ -125,4 +125,14 @@ class WebUtil } return "\n"; } + + public static function htmlPurify(?string $text): string{ + if(!$text){ + return ''; + } + + $config = \HTMLPurifier_HTML5Config::createDefault(); + $purifier = new \HTMLPurifier($config); + return $purifier->purify($text); + } } -- 2.54.0 From 330d9650c537ec382f039435cf4abb17b84f4cf0 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 24 Jun 2018 04:12:19 +0900 Subject: [PATCH 199/304] =?UTF-8?q?=EC=9E=84=EA=B4=80=20=EA=B6=8C=EC=9C=A0?= =?UTF-8?q?=20=EB=A9=94=EC=8B=9C=EC=A7=80=EB=A5=BC=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/b_dipcenter.php | 2 +- hwe/func_template.php | 11 +++++++++++ hwe/join.php | 20 ++------------------ hwe/processing.php | 22 +++++----------------- hwe/select_npc.php | 18 ++---------------- hwe/templates/invitationList.php | 18 ++++++++++++++++++ 6 files changed, 39 insertions(+), 52 deletions(-) create mode 100644 hwe/templates/invitationList.php diff --git a/hwe/b_dipcenter.php b/hwe/b_dipcenter.php index 1dd949c9..7e1cc3e2 100644 --- a/hwe/b_dipcenter.php +++ b/hwe/b_dipcenter.php @@ -216,7 +216,7 @@ if ($budgetricediff > 0) {
모병은 가격 2배의 자금이 소요됩니다.
@@ -1031,7 +1031,7 @@ function command_25($turn, $command) { echo " - +
{$scoutStr}
임관 권유 메세지
"; diff --git a/hwe/select_npc.php b/hwe/select_npc.php index 6de57857..8e5a32f2 100644 --- a/hwe/select_npc.php +++ b/hwe/select_npc.php @@ -27,7 +27,7 @@ $nations = $db->queryAllLists('SELECT `name`, scoutmsg, color FROM nation'); - + diff --git a/hwe/templates/msg_plate.php b/hwe/templates/msg_plate.php deleted file mode 100644 index a2272296..00000000 --- a/hwe/templates/msg_plate.php +++ /dev/null @@ -1,53 +0,0 @@ - - - bgcolor="#CC6600" - - bgcolor="#336600" - - bgcolor="#000055" - - style="font-size:13px;table-layout:fixed;word-break:break-all;" - data-id="" -> - - - - -
- - - - - - - - [ - e($src['name'])?>:e($src['nation'])?> - ▶ - e($dest['name'])?>:e($dest['nation'])?> - ] - - [ - e($src['name'])?>:e($src['nation'])?> - ▶ - e($dest['nation'])?> - ] - - [ - e($src['name'])?>:e($src['nation'])?> - ] - - <> -
- e($message)?> -
\ No newline at end of file diff --git a/i_other/help.php b/i_other/help.php index 68ca000e..1245a04a 100644 --- a/i_other/help.php +++ b/i_other/help.php @@ -13,16 +13,10 @@ $category = Util::getReq('category', 'int', 0); 튜토리얼 - + + Some text'; + + $config = HTMLPurifier_Config::createDefault(); + $config->set('Filter', 'ExtractStyleBlocks', true); + $purifier = new HTMLPurifier($config); + + $html = $purifier->purify($dirty); + + // This implementation writes the stylesheets to the styles/ directory. + // You can also echo the styles inside the document, but it's a bit + // more difficult to make sure they get interpreted properly by + // browsers; try the usual CSS armoring techniques. + $styles = $purifier->context->get('StyleBlocks'); + $dir = 'styles/'; + if (!is_dir($dir)) mkdir($dir); + $hash = sha1($_GET['html']); + foreach ($styles as $i => $style) { + file_put_contents($name = $dir . $hash . "_$i"); + echo ''; + } +?> + + +
+ +
+ + +]]> +

+ Warning: It is possible for a user to mount an + imagecrash attack using this CSS. Counter-measures are difficult; + it is not simply enough to limit the range of CSS lengths (using + relative lengths with many nesting levels allows for large values + to be attained without actually specifying them in the stylesheet), + and the flexible nature of selectors makes it difficult to selectively + disable lengths on image tags (HTML Purifier, however, does disable + CSS width and height in inline styling). There are probably two effective + counter measures: an explicit width and height set to auto in all + images in your document (unlikely) or the disabling of width and + height (somewhat reasonable). Whether or not these measures should be + used is left to the reader. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt new file mode 100644 index 00000000..321eaa2d --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt @@ -0,0 +1,16 @@ +Filter.YouTube +TYPE: bool +VERSION: 3.1.0 +DEFAULT: false +--DESCRIPTION-- +

+ Warning: Deprecated in favor of %HTML.SafeObject and + %Output.FlashCompat (turn both on to allow YouTube videos and other + Flash content). +

+

+ This directive enables YouTube video embedding in HTML Purifier. Check + this document + on embedding videos for more information on what this filter does. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt new file mode 100644 index 00000000..0b2c106d --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt @@ -0,0 +1,25 @@ +HTML.Allowed +TYPE: itext/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ This is a preferred convenience directive that combines + %HTML.AllowedElements and %HTML.AllowedAttributes. + Specify elements and attributes that are allowed using: + element1[attr1|attr2],element2.... For example, + if you would like to only allow paragraphs and links, specify + a[href],p. You can specify attributes that apply + to all elements using an asterisk, e.g. *[lang]. + You can also use newlines instead of commas to separate elements. +

+

+ Warning: + All of the constraints on the component directives are still enforced. + The syntax is a subset of TinyMCE's valid_elements + whitelist: directly copy-pasting it here will probably result in + broken whitelists. If %HTML.AllowedElements or %HTML.AllowedAttributes + are set, this directive has no effect. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt new file mode 100644 index 00000000..fcf093f1 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt @@ -0,0 +1,19 @@ +HTML.AllowedAttributes +TYPE: lookup/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ If HTML Purifier's attribute set is unsatisfactory, overload it! + The syntax is "tag.attr" or "*.attr" for the global attributes + (style, id, class, dir, lang, xml:lang). +

+

+ Warning: If another directive conflicts with the + elements here, that directive will win and override. For + example, %HTML.EnableAttrID will take precedence over *.id in this + directive. You must set that directive to true before you can use + IDs at all. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt new file mode 100644 index 00000000..140e2142 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt @@ -0,0 +1,10 @@ +HTML.AllowedComments +TYPE: lookup +VERSION: 4.4.0 +DEFAULT: array() +--DESCRIPTION-- +A whitelist which indicates what explicit comment bodies should be +allowed, modulo leading and trailing whitespace. See also %HTML.AllowedCommentsRegexp +(these directives are union'ed together, so a comment is considered +valid if any directive deems it valid.) +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt new file mode 100644 index 00000000..f22e977d --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt @@ -0,0 +1,15 @@ +HTML.AllowedCommentsRegexp +TYPE: string/null +VERSION: 4.4.0 +DEFAULT: NULL +--DESCRIPTION-- +A regexp, which if it matches the body of a comment, indicates that +it should be allowed. Trailing and leading spaces are removed prior +to running this regular expression. +Warning: Make sure you specify +correct anchor metacharacters ^regex$, otherwise you may accept +comments that you did not mean to! In particular, the regex /foo|bar/ +is probably not sufficiently strict, since it also allows foobar. +See also %HTML.AllowedComments (these directives are union'ed together, +so a comment is considered valid if any directive deems it valid.) +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt new file mode 100644 index 00000000..1d3fa790 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt @@ -0,0 +1,23 @@ +HTML.AllowedElements +TYPE: lookup/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- +

+ If HTML Purifier's tag set is unsatisfactory for your needs, you can + overload it with your own list of tags to allow. If you change + this, you probably also want to change %HTML.AllowedAttributes; see + also %HTML.Allowed which lets you set allowed elements and + attributes at the same time. +

+

+ If you attempt to allow an element that HTML Purifier does not know + about, HTML Purifier will raise an error. You will need to manually + tell HTML Purifier about this element by using the + advanced customization features. +

+

+ Warning: If another directive conflicts with the + elements here, that directive will win and override. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt new file mode 100644 index 00000000..5a59a55c --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt @@ -0,0 +1,20 @@ +HTML.AllowedModules +TYPE: lookup/null +VERSION: 2.0.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ A doctype comes with a set of usual modules to use. Without having + to mucking about with the doctypes, you can quickly activate or + disable these modules by specifying which modules you wish to allow + with this directive. This is most useful for unit testing specific + modules, although end users may find it useful for their own ends. +

+

+ If you specify a module that does not exist, the manager will silently + fail to use it, so be careful! User-defined modules are not affected + by this directive. Modules defined in %HTML.CoreModules are not + affected by this directive. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt new file mode 100644 index 00000000..151fb7b8 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt @@ -0,0 +1,11 @@ +HTML.Attr.Name.UseCDATA +TYPE: bool +DEFAULT: false +VERSION: 4.0.0 +--DESCRIPTION-- +The W3C specification DTD defines the name attribute to be CDATA, not ID, due +to limitations of DTD. In certain documents, this relaxed behavior is desired, +whether it is to specify duplicate names, or to specify names that would be +illegal IDs (for example, names that begin with a digit.) Set this configuration +directive to true to use the relaxed parsing rules. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt new file mode 100644 index 00000000..45ae469e --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt @@ -0,0 +1,18 @@ +HTML.BlockWrapper +TYPE: string +VERSION: 1.3.0 +DEFAULT: 'p' +--DESCRIPTION-- + +

+ String name of element to wrap inline elements that are inside a block + context. This only occurs in the children of blockquote in strict mode. +

+

+ Example: by default value, + <blockquote>Foo</blockquote> would become + <blockquote><p>Foo</p></blockquote>. + The <p> tags can be replaced with whatever you desire, + as long as it is a block level element. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt new file mode 100644 index 00000000..52461887 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt @@ -0,0 +1,23 @@ +HTML.CoreModules +TYPE: lookup +VERSION: 2.0.0 +--DEFAULT-- +array ( + 'Structure' => true, + 'Text' => true, + 'Hypertext' => true, + 'List' => true, + 'NonXMLCommonAttributes' => true, + 'XMLCommonAttributes' => true, + 'CommonAttributes' => true, +) +--DESCRIPTION-- + +

+ Certain modularized doctypes (XHTML, namely), have certain modules + that must be included for the doctype to be an conforming document + type: put those modules here. By default, XHTML's core modules + are used. You can set this to a blank array to disable core module + protection, but this is not recommended. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt new file mode 100644 index 00000000..6ed70b59 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt @@ -0,0 +1,9 @@ +HTML.CustomDoctype +TYPE: string/null +VERSION: 2.0.1 +DEFAULT: NULL +--DESCRIPTION-- + +A custom doctype for power-users who defined their own document +type. This directive only applies when %HTML.Doctype is blank. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt new file mode 100644 index 00000000..103db754 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt @@ -0,0 +1,33 @@ +HTML.DefinitionID +TYPE: string/null +DEFAULT: NULL +VERSION: 2.0.0 +--DESCRIPTION-- + +

+ Unique identifier for a custom-built HTML definition. If you edit + the raw version of the HTMLDefinition, introducing changes that the + configuration object does not reflect, you must specify this variable. + If you change your custom edits, you should change this directive, or + clear your cache. Example: +

+
+$config = HTMLPurifier_Config::createDefault();
+$config->set('HTML', 'DefinitionID', '1');
+$def = $config->getHTMLDefinition();
+$def->addAttribute('a', 'tabindex', 'Number');
+
+

+ In the above example, the configuration is still at the defaults, but + using the advanced API, an extra attribute has been added. The + configuration object normally has no way of knowing that this change + has taken place, so it needs an extra directive: %HTML.DefinitionID. + If someone else attempts to use the default configuration, these two + pieces of code will not clobber each other in the cache, since one has + an extra directive attached to it. +

+

+ You must specify a value to this directive to use the + advanced API features. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt new file mode 100644 index 00000000..229ae026 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt @@ -0,0 +1,16 @@ +HTML.DefinitionRev +TYPE: int +VERSION: 2.0.0 +DEFAULT: 1 +--DESCRIPTION-- + +

+ Revision identifier for your custom definition specified in + %HTML.DefinitionID. This serves the same purpose: uniquely identifying + your custom definition, but this one does so in a chronological + context: revision 3 is more up-to-date then revision 2. Thus, when + this gets incremented, the cache handling is smart enough to clean + up any older revisions of your definition as well as flush the + cache. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt new file mode 100644 index 00000000..9dab497f --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt @@ -0,0 +1,11 @@ +HTML.Doctype +TYPE: string/null +DEFAULT: NULL +--DESCRIPTION-- +Doctype to use during filtering. Technically speaking this is not actually +a doctype (as it does not identify a corresponding DTD), but we are using +this name for sake of simplicity. When non-blank, this will override any +older directives like %HTML.XHTML or %HTML.Strict. +--ALLOWED-- +'HTML 4.01 Transitional', 'HTML 4.01 Strict', 'XHTML 1.0 Transitional', 'XHTML 1.0 Strict', 'XHTML 1.1' +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt new file mode 100644 index 00000000..7878dc0b --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt @@ -0,0 +1,11 @@ +HTML.FlashAllowFullScreen +TYPE: bool +VERSION: 4.2.0 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to permit embedded Flash content from + %HTML.SafeObject to expand to the full screen. Corresponds to + the allowFullScreen parameter. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt new file mode 100644 index 00000000..57358f9b --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt @@ -0,0 +1,21 @@ +HTML.ForbiddenAttributes +TYPE: lookup +VERSION: 3.1.0 +DEFAULT: array() +--DESCRIPTION-- +

+ While this directive is similar to %HTML.AllowedAttributes, for + forwards-compatibility with XML, this attribute has a different syntax. Instead of + tag.attr, use tag@attr. To disallow href + attributes in a tags, set this directive to + a@href. You can also disallow an attribute globally with + attr or *@attr (either syntax is fine; the latter + is provided for consistency with %HTML.AllowedAttributes). +

+

+ Warning: This directive complements %HTML.ForbiddenElements, + accordingly, check + out that directive for a discussion of why you + should think twice before using this directive. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt new file mode 100644 index 00000000..93a53e14 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt @@ -0,0 +1,20 @@ +HTML.ForbiddenElements +TYPE: lookup +VERSION: 3.1.0 +DEFAULT: array() +--DESCRIPTION-- +

+ This was, perhaps, the most requested feature ever in HTML + Purifier. Please don't abuse it! This is the logical inverse of + %HTML.AllowedElements, and it will override that directive, or any + other directive. +

+

+ If possible, %HTML.Allowed is recommended over this directive, because it + can sometimes be difficult to tell whether or not you've forbidden all of + the behavior you would like to disallow. If you forbid img + with the expectation of preventing images on your site, you'll be in for + a nasty surprise when people start using the background-image + CSS property. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt new file mode 100644 index 00000000..e424c386 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt @@ -0,0 +1,14 @@ +HTML.MaxImgLength +TYPE: int/null +DEFAULT: 1200 +VERSION: 3.1.1 +--DESCRIPTION-- +

+ This directive controls the maximum number of pixels in the width and + height attributes in img tags. This is + in place to prevent imagecrash attacks, disable with null at your own risk. + This directive is similar to %CSS.MaxImgLength, and both should be + concurrently edited, although there are + subtle differences in the input format (the HTML max is an integer). +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt new file mode 100644 index 00000000..700b3092 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt @@ -0,0 +1,7 @@ +HTML.Nofollow +TYPE: bool +VERSION: 4.3.0 +DEFAULT: FALSE +--DESCRIPTION-- +If enabled, nofollow rel attributes are added to all outgoing links. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt new file mode 100644 index 00000000..62e8e160 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt @@ -0,0 +1,12 @@ +HTML.Parent +TYPE: string +VERSION: 1.3.0 +DEFAULT: 'div' +--DESCRIPTION-- + +

+ String name of element that HTML fragment passed to library will be + inserted in. An interesting variation would be using span as the + parent element, meaning that only inline tags would be allowed. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt new file mode 100644 index 00000000..dfb72049 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt @@ -0,0 +1,12 @@ +HTML.Proprietary +TYPE: bool +VERSION: 3.1.0 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to allow proprietary elements and attributes in your + documents, as per HTMLPurifier_HTMLModule_Proprietary. + Warning: This can cause your documents to stop + validating! +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt new file mode 100644 index 00000000..cdda09a4 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt @@ -0,0 +1,13 @@ +HTML.SafeEmbed +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to permit embed tags in documents, with a number of extra + security features added to prevent script execution. This is similar to + what websites like MySpace do to embed tags. Embed is a proprietary + element and will cause your website to stop validating; you should + see if you can use %Output.FlashCompat with %HTML.SafeObject instead + first.

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt new file mode 100644 index 00000000..5eb6ec2b --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt @@ -0,0 +1,13 @@ +HTML.SafeIframe +TYPE: bool +VERSION: 4.4.0 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to permit iframe tags in untrusted documents. This + directive must be accompanied by a whitelist of permitted iframes, + such as %URI.SafeIframeRegexp, otherwise it will fatally error. + This directive has no effect on strict doctypes, as iframes are not + valid. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt new file mode 100644 index 00000000..ceb342e2 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt @@ -0,0 +1,13 @@ +HTML.SafeObject +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ Whether or not to permit object tags in documents, with a number of extra + security features added to prevent script execution. This is similar to + what websites like MySpace do to object tags. You should also enable + %Output.FlashCompat in order to generate Internet Explorer + compatibility code for your object tags. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt new file mode 100644 index 00000000..5ebc7a19 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt @@ -0,0 +1,10 @@ +HTML.SafeScripting +TYPE: lookup +VERSION: 4.5.0 +DEFAULT: array() +--DESCRIPTION-- +

+ Whether or not to permit script tags to external scripts in documents. + Inline scripting is not allowed, and the script must match an explicit whitelist. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt new file mode 100644 index 00000000..a8b1de56 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt @@ -0,0 +1,9 @@ +HTML.Strict +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +DEPRECATED-VERSION: 1.7.0 +DEPRECATED-USE: HTML.Doctype +--DESCRIPTION-- +Determines whether or not to use Transitional (loose) or Strict rulesets. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt new file mode 100644 index 00000000..587a1677 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt @@ -0,0 +1,8 @@ +HTML.TargetBlank +TYPE: bool +VERSION: 4.4.0 +DEFAULT: FALSE +--DESCRIPTION-- +If enabled, target=blank attributes are added to all outgoing links. +(This includes links from an HTTPS version of a page to an HTTP version.) +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt new file mode 100644 index 00000000..dd514c0d --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt @@ -0,0 +1,10 @@ +--# vim: et sw=4 sts=4 +HTML.TargetNoopener +TYPE: bool +VERSION: 4.8.0 +DEFAULT: TRUE +--DESCRIPTION-- +If enabled, noopener rel attributes are added to links which have +a target attribute associated with them. This prevents malicious +destinations from overwriting the original window. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt new file mode 100644 index 00000000..cb5a0b0e --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt @@ -0,0 +1,9 @@ +HTML.TargetNoreferrer +TYPE: bool +VERSION: 4.8.0 +DEFAULT: TRUE +--DESCRIPTION-- +If enabled, noreferrer rel attributes are added to links which have +a target attribute associated with them. This prevents malicious +destinations from overwriting the original window. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt new file mode 100644 index 00000000..b4c271b7 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt @@ -0,0 +1,8 @@ +HTML.TidyAdd +TYPE: lookup +VERSION: 2.0.0 +DEFAULT: array() +--DESCRIPTION-- + +Fixes to add to the default set of Tidy fixes as per your level. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt new file mode 100644 index 00000000..4186ccd0 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt @@ -0,0 +1,24 @@ +HTML.TidyLevel +TYPE: string +VERSION: 2.0.0 +DEFAULT: 'medium' +--DESCRIPTION-- + +

General level of cleanliness the Tidy module should enforce. +There are four allowed values:

+
+
none
+
No extra tidying should be done
+
light
+
Only fix elements that would be discarded otherwise due to + lack of support in doctype
+
medium
+
Enforce best practices
+
heavy
+
Transform all deprecated elements and attributes to standards + compliant equivalents
+
+ +--ALLOWED-- +'none', 'light', 'medium', 'heavy' +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt new file mode 100644 index 00000000..996762bd --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt @@ -0,0 +1,8 @@ +HTML.TidyRemove +TYPE: lookup +VERSION: 2.0.0 +DEFAULT: array() +--DESCRIPTION-- + +Fixes to remove from the default set of Tidy fixes as per your level. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt new file mode 100644 index 00000000..1db9237e --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt @@ -0,0 +1,9 @@ +HTML.Trusted +TYPE: bool +VERSION: 2.0.0 +DEFAULT: false +--DESCRIPTION-- +Indicates whether or not the user input is trusted or not. If the input is +trusted, a more expansive set of allowed tags and attributes will be used. +See also %CSS.Trusted. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt new file mode 100644 index 00000000..2a47e384 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt @@ -0,0 +1,11 @@ +HTML.XHTML +TYPE: bool +DEFAULT: true +VERSION: 1.1.0 +DEPRECATED-VERSION: 1.7.0 +DEPRECATED-USE: HTML.Doctype +--DESCRIPTION-- +Determines whether or not output is XHTML 1.0 or HTML 4.01 flavor. +--ALIASES-- +Core.XHTML +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt new file mode 100644 index 00000000..08921fde --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt @@ -0,0 +1,10 @@ +Output.CommentScriptContents +TYPE: bool +VERSION: 2.0.0 +DEFAULT: true +--DESCRIPTION-- +Determines whether or not HTML Purifier should attempt to fix up the +contents of script tags for legacy browsers with comments. +--ALIASES-- +Core.CommentScriptContents +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt new file mode 100644 index 00000000..d6f0d9f2 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt @@ -0,0 +1,15 @@ +Output.FixInnerHTML +TYPE: bool +VERSION: 4.3.0 +DEFAULT: true +--DESCRIPTION-- +

+ If true, HTML Purifier will protect against Internet Explorer's + mishandling of the innerHTML attribute by appending + a space to any attribute that does not contain angled brackets, spaces + or quotes, but contains a backtick. This slightly changes the + semantics of any given attribute, so if this is unacceptable and + you do not use innerHTML on any of your pages, you can + turn this directive off. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt new file mode 100644 index 00000000..93398e85 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt @@ -0,0 +1,11 @@ +Output.FlashCompat +TYPE: bool +VERSION: 4.1.0 +DEFAULT: false +--DESCRIPTION-- +

+ If true, HTML Purifier will generate Internet Explorer compatibility + code for all object code. This is highly recommended if you enable + %HTML.SafeObject. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt new file mode 100644 index 00000000..79f8ad82 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt @@ -0,0 +1,13 @@ +Output.Newline +TYPE: string/null +VERSION: 2.0.1 +DEFAULT: NULL +--DESCRIPTION-- + +

+ Newline string to format final output with. If left null, HTML Purifier + will auto-detect the default newline type of the system and use that; + you can manually override it here. Remember, \r\n is Windows, \r + is Mac, and \n is Unix. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt new file mode 100644 index 00000000..232b0236 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt @@ -0,0 +1,14 @@ +Output.SortAttr +TYPE: bool +VERSION: 3.2.0 +DEFAULT: false +--DESCRIPTION-- +

+ If true, HTML Purifier will sort attributes by name before writing them back + to the document, converting a tag like: <el b="" a="" c="" /> + to <el a="" b="" c="" />. This is a workaround for + a bug in FCKeditor which causes it to swap attributes order, adding noise + to text diffs. If you're not seeing this bug, chances are, you don't need + this directive. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt new file mode 100644 index 00000000..06bab00a --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt @@ -0,0 +1,25 @@ +Output.TidyFormat +TYPE: bool +VERSION: 1.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ Determines whether or not to run Tidy on the final output for pretty + formatting reasons, such as indentation and wrap. +

+

+ This can greatly improve readability for editors who are hand-editing + the HTML, but is by no means necessary as HTML Purifier has already + fixed all major errors the HTML may have had. Tidy is a non-default + extension, and this directive will silently fail if Tidy is not + available. +

+

+ If you are looking to make the overall look of your page's source + better, I recommend running Tidy on the entire page rather than just + user-content (after all, the indentation relative to the containing + blocks will be incorrect). +

+--ALIASES-- +Core.TidyFormat +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt new file mode 100644 index 00000000..071bc029 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt @@ -0,0 +1,7 @@ +Test.ForceNoIconv +TYPE: bool +DEFAULT: false +--DESCRIPTION-- +When set to true, HTMLPurifier_Encoder will act as if iconv does not exist +and use only pure PHP implementations. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt new file mode 100644 index 00000000..eb97307e --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt @@ -0,0 +1,18 @@ +URI.AllowedSchemes +TYPE: lookup +--DEFAULT-- +array ( + 'http' => true, + 'https' => true, + 'mailto' => true, + 'ftp' => true, + 'nntp' => true, + 'news' => true, + 'tel' => true, +) +--DESCRIPTION-- +Whitelist that defines the schemes that a URI is allowed to have. This +prevents XSS attacks from using pseudo-schemes like javascript or mocha. +There is also support for the data and file +URI schemes, but they are not enabled by default. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt new file mode 100644 index 00000000..876f0680 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt @@ -0,0 +1,17 @@ +URI.Base +TYPE: string/null +VERSION: 2.1.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ The base URI is the URI of the document this purified HTML will be + inserted into. This information is important if HTML Purifier needs + to calculate absolute URIs from relative URIs, such as when %URI.MakeAbsolute + is on. You may use a non-absolute URI for this value, but behavior + may vary (%URI.MakeAbsolute deals nicely with both absolute and + relative paths, but forwards-compatibility is not guaranteed). + Warning: If set, the scheme on this URI + overrides the one specified by %URI.DefaultScheme. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt new file mode 100644 index 00000000..834bc08c --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt @@ -0,0 +1,15 @@ +URI.DefaultScheme +TYPE: string/null +DEFAULT: 'http' +--DESCRIPTION-- + +

+ Defines through what scheme the output will be served, in order to + select the proper object validator when no scheme information is present. +

+ +

+ Starting with HTML Purifier 4.9.0, the default scheme can be null, in + which case we reject all URIs which do not have explicit schemes. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt new file mode 100644 index 00000000..f05312ba --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt @@ -0,0 +1,11 @@ +URI.DefinitionID +TYPE: string/null +VERSION: 2.1.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ Unique identifier for a custom-built URI definition. If you want + to add custom URIFilters, you must specify this value. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt new file mode 100644 index 00000000..80cfea93 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt @@ -0,0 +1,11 @@ +URI.DefinitionRev +TYPE: int +VERSION: 2.1.0 +DEFAULT: 1 +--DESCRIPTION-- + +

+ Revision identifier for your custom definition. See + %HTML.DefinitionRev for details. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt new file mode 100644 index 00000000..71ce025a --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt @@ -0,0 +1,14 @@ +URI.Disable +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +--DESCRIPTION-- + +

+ Disables all URIs in all forms. Not sure why you'd want to do that + (after all, the Internet's founded on the notion of a hyperlink). +

+ +--ALIASES-- +Attr.DisableURI +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt new file mode 100644 index 00000000..13c122c8 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt @@ -0,0 +1,11 @@ +URI.DisableExternal +TYPE: bool +VERSION: 1.2.0 +DEFAULT: false +--DESCRIPTION-- +Disables links to external websites. This is a highly effective anti-spam +and anti-pagerank-leech measure, but comes at a hefty price: nolinks or +images outside of your domain will be allowed. Non-linkified URIs will +still be preserved. If you want to be able to link to subdomains or use +absolute URIs, specify %URI.Host for your website. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt new file mode 100644 index 00000000..abcc1efd --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt @@ -0,0 +1,13 @@ +URI.DisableExternalResources +TYPE: bool +VERSION: 1.3.0 +DEFAULT: false +--DESCRIPTION-- +Disables the embedding of external resources, preventing users from +embedding things like images from other hosts. This prevents access +tracking (good for email viewers), bandwidth leeching, cross-site request +forging, goatse.cx posting, and other nasties, but also results in a loss +of end-user functionality (they can't directly post a pic they posted from +Flickr anymore). Use it if you don't have a robust user-content moderation +team. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt new file mode 100644 index 00000000..f891de49 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt @@ -0,0 +1,15 @@ +URI.DisableResources +TYPE: bool +VERSION: 4.2.0 +DEFAULT: false +--DESCRIPTION-- +

+ Disables embedding resources, essentially meaning no pictures. You can + still link to them though. See %URI.DisableExternalResources for why + this might be a good idea. +

+

+ Note: While this directive has been available since 1.3.0, + it didn't actually start doing anything until 4.2.0. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt new file mode 100644 index 00000000..ee83b121 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt @@ -0,0 +1,19 @@ +URI.Host +TYPE: string/null +VERSION: 1.2.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ Defines the domain name of the server, so we can determine whether or + an absolute URI is from your website or not. Not strictly necessary, + as users should be using relative URIs to reference resources on your + website. It will, however, let you use absolute URIs to link to + subdomains of the domain you post here: i.e. example.com will allow + sub.example.com. However, higher up domains will still be excluded: + if you set %URI.Host to sub.example.com, example.com will be blocked. + Note: This directive overrides %URI.Base because + a given page may be on a sub-domain, but you wish HTML Purifier to be + more relaxed and allow some of the parent domains too. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt new file mode 100644 index 00000000..0b6df762 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt @@ -0,0 +1,9 @@ +URI.HostBlacklist +TYPE: list +VERSION: 1.3.0 +DEFAULT: array() +--DESCRIPTION-- +List of strings that are forbidden in the host of any URI. Use it to kill +domain names of spam, etc. Note that it will catch anything in the domain, +so moo.com will catch moo.com.example.com. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt new file mode 100644 index 00000000..4214900a --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt @@ -0,0 +1,13 @@ +URI.MakeAbsolute +TYPE: bool +VERSION: 2.1.0 +DEFAULT: false +--DESCRIPTION-- + +

+ Converts all URIs into absolute forms. This is useful when the HTML + being filtered assumes a specific base path, but will actually be + viewed in a different context (and setting an alternate base URI is + not possible). %URI.Base must be set for this directive to work. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt new file mode 100644 index 00000000..58c81dcc --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt @@ -0,0 +1,83 @@ +URI.Munge +TYPE: string/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- + +

+ Munges all browsable (usually http, https and ftp) + absolute URIs into another URI, usually a URI redirection service. + This directive accepts a URI, formatted with a %s where + the url-encoded original URI should be inserted (sample: + http://www.google.com/url?q=%s). +

+

+ Uses for this directive: +

+
    +
  • + Prevent PageRank leaks, while being fairly transparent + to users (you may also want to add some client side JavaScript to + override the text in the statusbar). Notice: + Many security experts believe that this form of protection does not deter spam-bots. +
  • +
  • + Redirect users to a splash page telling them they are leaving your + website. While this is poor usability practice, it is often mandated + in corporate environments. +
  • +
+

+ Prior to HTML Purifier 3.1.1, this directive also enabled the munging + of browsable external resources, which could break things if your redirection + script was a splash page or used meta tags. To revert to + previous behavior, please use %URI.MungeResources. +

+

+ You may want to also use %URI.MungeSecretKey along with this directive + in order to enforce what URIs your redirector script allows. Open + redirector scripts can be a security risk and negatively affect the + reputation of your domain name. +

+

+ Starting with HTML Purifier 3.1.1, there is also these substitutions: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyDescriptionExample <a href="">
%r1 - The URI embeds a resource
(blank) - The URI is merely a link
%nThe name of the tag this URI came froma
%mThe name of the attribute this URI came fromhref
%pThe name of the CSS property this URI came from, or blank if irrelevant
+

+ Admittedly, these letters are somewhat arbitrary; the only stipulation + was that they couldn't be a through f. r is for resource (I would have preferred + e, but you take what you can get), n is for name, m + was picked because it came after n (and I couldn't use a), p is for + property. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt new file mode 100644 index 00000000..6fce0fdc --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt @@ -0,0 +1,17 @@ +URI.MungeResources +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +

+ If true, any URI munging directives like %URI.Munge + will also apply to embedded resources, such as <img src="">. + Be careful enabling this directive if you have a redirector script + that does not use the Location HTTP header; all of your images + and other embedded resources will break. +

+

+ Warning: It is strongly advised you use this in conjunction + %URI.MungeSecretKey to mitigate the security risk of an open redirector. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt new file mode 100644 index 00000000..1e17c1d4 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt @@ -0,0 +1,30 @@ +URI.MungeSecretKey +TYPE: string/null +VERSION: 3.1.1 +DEFAULT: NULL +--DESCRIPTION-- +

+ This directive enables secure checksum generation along with %URI.Munge. + It should be set to a secure key that is not shared with anyone else. + The checksum can be placed in the URI using %t. Use of this checksum + affords an additional level of protection by allowing a redirector + to check if a URI has passed through HTML Purifier with this line: +

+ +
$checksum === hash_hmac("sha256", $url, $secret_key)
+ +

+ If the output is TRUE, the redirector script should accept the URI. +

+ +

+ Please note that it would still be possible for an attacker to procure + secure hashes en-mass by abusing your website's Preview feature or the + like, but this service affords an additional level of protection + that should be combined with website blacklisting. +

+ +

+ Remember this has no effect if %URI.Munge is not on. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt new file mode 100644 index 00000000..23331a4e --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt @@ -0,0 +1,9 @@ +URI.OverrideAllowedSchemes +TYPE: bool +DEFAULT: true +--DESCRIPTION-- +If this is set to true (which it is by default), you can override +%URI.AllowedSchemes by simply registering a HTMLPurifier_URIScheme to the +registry. If false, you will also have to update that directive in order +to add more schemes. +--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt new file mode 100644 index 00000000..79084832 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt @@ -0,0 +1,22 @@ +URI.SafeIframeRegexp +TYPE: string/null +VERSION: 4.4.0 +DEFAULT: NULL +--DESCRIPTION-- +

+ A PCRE regular expression that will be matched against an iframe URI. This is + a relatively inflexible scheme, but works well enough for the most common + use-case of iframes: embedded video. This directive only has an effect if + %HTML.SafeIframe is enabled. Here are some example values: +

+
    +
  • %^http://www.youtube.com/embed/% - Allow YouTube videos
  • +
  • %^http://player.vimeo.com/video/% - Allow Vimeo videos
  • +
  • %^http://(www.youtube.com/embed/|player.vimeo.com/video/)% - Allow both
  • +
+

+ Note that this directive does not give you enough granularity to, say, disable + all autoplay videos. Pipe up on the HTML Purifier forums if this + is a capability you want. +

+--# vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini new file mode 100644 index 00000000..5de4505e --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini @@ -0,0 +1,3 @@ +name = "HTML Purifier" + +; vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php new file mode 100644 index 00000000..543e3f8f --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php @@ -0,0 +1,170 @@ + true) indexed by name. + * @type array + * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets + */ + public $lookup = array(); + + /** + * Synchronized list of defined content sets (keys of info). + * @type array + */ + protected $keys = array(); + /** + * Synchronized list of defined content values (values of info). + * @type array + */ + protected $values = array(); + + /** + * Merges in module's content sets, expands identifiers in the content + * sets and populates the keys, values and lookup member variables. + * @param HTMLPurifier_HTMLModule[] $modules List of HTMLPurifier_HTMLModule + */ + public function __construct($modules) + { + if (!is_array($modules)) { + $modules = array($modules); + } + // populate content_sets based on module hints + // sorry, no way of overloading + foreach ($modules as $module) { + foreach ($module->content_sets as $key => $value) { + $temp = $this->convertToLookup($value); + if (isset($this->lookup[$key])) { + // add it into the existing content set + $this->lookup[$key] = array_merge($this->lookup[$key], $temp); + } else { + $this->lookup[$key] = $temp; + } + } + } + $old_lookup = false; + while ($old_lookup !== $this->lookup) { + $old_lookup = $this->lookup; + foreach ($this->lookup as $i => $set) { + $add = array(); + foreach ($set as $element => $x) { + if (isset($this->lookup[$element])) { + $add += $this->lookup[$element]; + unset($this->lookup[$i][$element]); + } + } + $this->lookup[$i] += $add; + } + } + + foreach ($this->lookup as $key => $lookup) { + $this->info[$key] = implode(' | ', array_keys($lookup)); + } + $this->keys = array_keys($this->info); + $this->values = array_values($this->info); + } + + /** + * Accepts a definition; generates and assigns a ChildDef for it + * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef reference + * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef + */ + public function generateChildDef(&$def, $module) + { + if (!empty($def->child)) { // already done! + return; + } + $content_model = $def->content_model; + if (is_string($content_model)) { + // Assume that $this->keys is alphanumeric + $def->content_model = preg_replace_callback( + '/\b(' . implode('|', $this->keys) . ')\b/', + array($this, 'generateChildDefCallback'), + $content_model + ); + //$def->content_model = str_replace( + // $this->keys, $this->values, $content_model); + } + $def->child = $this->getChildDef($def, $module); + } + + public function generateChildDefCallback($matches) + { + return $this->info[$matches[0]]; + } + + /** + * Instantiates a ChildDef based on content_model and content_model_type + * member variables in HTMLPurifier_ElementDef + * @note This will also defer to modules for custom HTMLPurifier_ChildDef + * subclasses that need content set expansion + * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef to have ChildDef extracted + * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef + * @return HTMLPurifier_ChildDef corresponding to ElementDef + */ + public function getChildDef($def, $module) + { + $value = $def->content_model; + if (is_object($value)) { + trigger_error( + 'Literal object child definitions should be stored in '. + 'ElementDef->child not ElementDef->content_model', + E_USER_NOTICE + ); + return $value; + } + switch ($def->content_model_type) { + case 'required': + return new HTMLPurifier_ChildDef_Required($value); + case 'optional': + return new HTMLPurifier_ChildDef_Optional($value); + case 'empty': + return new HTMLPurifier_ChildDef_Empty(); + case 'custom': + return new HTMLPurifier_ChildDef_Custom($value); + } + // defer to its module + $return = false; + if ($module->defines_child_def) { // save a func call + $return = $module->getChildDef($def); + } + if ($return !== false) { + return $return; + } + // error-out + trigger_error( + 'Could not determine which ChildDef class to instantiate', + E_USER_ERROR + ); + return false; + } + + /** + * Converts a string list of elements separated by pipes into + * a lookup array. + * @param string $string List of elements + * @return array Lookup array of elements + */ + protected function convertToLookup($string) + { + $array = explode('|', str_replace(' ', '', $string)); + $ret = array(); + foreach ($array as $k) { + $ret[$k] = true; + } + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Context.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Context.php new file mode 100644 index 00000000..00e509c8 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Context.php @@ -0,0 +1,95 @@ +_storage)) { + trigger_error( + "Name $name produces collision, cannot re-register", + E_USER_ERROR + ); + return; + } + $this->_storage[$name] =& $ref; + } + + /** + * Retrieves a variable reference from the context. + * @param string $name String name + * @param bool $ignore_error Boolean whether or not to ignore error + * @return mixed + */ + public function &get($name, $ignore_error = false) + { + if (!array_key_exists($name, $this->_storage)) { + if (!$ignore_error) { + trigger_error( + "Attempted to retrieve non-existent variable $name", + E_USER_ERROR + ); + } + $var = null; // so we can return by reference + return $var; + } + return $this->_storage[$name]; + } + + /** + * Destroys a variable in the context. + * @param string $name String name + */ + public function destroy($name) + { + if (!array_key_exists($name, $this->_storage)) { + trigger_error( + "Attempted to destroy non-existent variable $name", + E_USER_ERROR + ); + return; + } + unset($this->_storage[$name]); + } + + /** + * Checks whether or not the variable exists. + * @param string $name String name + * @return bool + */ + public function exists($name) + { + return array_key_exists($name, $this->_storage); + } + + /** + * Loads a series of variables from an associative array + * @param array $context_array Assoc array of variables to load + */ + public function loadArray($context_array) + { + foreach ($context_array as $key => $discard) { + $this->register($key, $context_array[$key]); + } + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php new file mode 100644 index 00000000..bc6d4336 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php @@ -0,0 +1,55 @@ +setup) { + return; + } + $this->setup = true; + $this->doSetup($config); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php new file mode 100644 index 00000000..9aa8ff35 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php @@ -0,0 +1,129 @@ +type = $type; + } + + /** + * Generates a unique identifier for a particular configuration + * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config + * @return string + */ + public function generateKey($config) + { + return $config->version . ',' . // possibly replace with function calls + $config->getBatchSerial($this->type) . ',' . + $config->get($this->type . '.DefinitionRev'); + } + + /** + * Tests whether or not a key is old with respect to the configuration's + * version and revision number. + * @param string $key Key to test + * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config to test against + * @return bool + */ + public function isOld($key, $config) + { + if (substr_count($key, ',') < 2) { + return true; + } + list($version, $hash, $revision) = explode(',', $key, 3); + $compare = version_compare($version, $config->version); + // version mismatch, is always old + if ($compare != 0) { + return true; + } + // versions match, ids match, check revision number + if ($hash == $config->getBatchSerial($this->type) && + $revision < $config->get($this->type . '.DefinitionRev')) { + return true; + } + return false; + } + + /** + * Checks if a definition's type jives with the cache's type + * @note Throws an error on failure + * @param HTMLPurifier_Definition $def Definition object to check + * @return bool true if good, false if not + */ + public function checkDefType($def) + { + if ($def->type !== $this->type) { + trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}"); + return false; + } + return true; + } + + /** + * Adds a definition object to the cache + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + */ + abstract public function add($def, $config); + + /** + * Unconditionally saves a definition object to the cache + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + */ + abstract public function set($def, $config); + + /** + * Replace an object in the cache + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + */ + abstract public function replace($def, $config); + + /** + * Retrieves a definition object from the cache + * @param HTMLPurifier_Config $config + */ + abstract public function get($config); + + /** + * Removes a definition object to the cache + * @param HTMLPurifier_Config $config + */ + abstract public function remove($config); + + /** + * Clears all objects from cache + * @param HTMLPurifier_Config $config + */ + abstract public function flush($config); + + /** + * Clears all expired (older version or revision) objects from cache + * @note Be careful implementing this method as flush. Flush must + * not interfere with other Definition types, and cleanup() + * should not be repeatedly called by userland code. + * @param HTMLPurifier_Config $config + */ + abstract public function cleanup($config); +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php new file mode 100644 index 00000000..b57a51b6 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php @@ -0,0 +1,112 @@ +copy(); + // reference is necessary for mocks in PHP 4 + $decorator->cache =& $cache; + $decorator->type = $cache->type; + return $decorator; + } + + /** + * Cross-compatible clone substitute + * @return HTMLPurifier_DefinitionCache_Decorator + */ + public function copy() + { + return new HTMLPurifier_DefinitionCache_Decorator(); + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function add($def, $config) + { + return $this->cache->add($def, $config); + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function set($def, $config) + { + return $this->cache->set($def, $config); + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function replace($def, $config) + { + return $this->cache->replace($def, $config); + } + + /** + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function get($config) + { + return $this->cache->get($config); + } + + /** + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function remove($config) + { + return $this->cache->remove($config); + } + + /** + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function flush($config) + { + return $this->cache->flush($config); + } + + /** + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function cleanup($config) + { + return $this->cache->cleanup($config); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php new file mode 100644 index 00000000..4991777c --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php @@ -0,0 +1,78 @@ +definitions[$this->generateKey($config)] = $def; + } + return $status; + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function set($def, $config) + { + $status = parent::set($def, $config); + if ($status) { + $this->definitions[$this->generateKey($config)] = $def; + } + return $status; + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function replace($def, $config) + { + $status = parent::replace($def, $config); + if ($status) { + $this->definitions[$this->generateKey($config)] = $def; + } + return $status; + } + + /** + * @param HTMLPurifier_Config $config + * @return mixed + */ + public function get($config) + { + $key = $this->generateKey($config); + if (isset($this->definitions[$key])) { + return $this->definitions[$key]; + } + $this->definitions[$key] = parent::get($config); + return $this->definitions[$key]; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in new file mode 100644 index 00000000..b1fec8d3 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in @@ -0,0 +1,82 @@ +checkDefType($def)) { + return; + } + $file = $this->generateFilePath($config); + if (file_exists($file)) { + return false; + } + if (!$this->_prepareDir($config)) { + return false; + } + return $this->_write($file, serialize($def), $config); + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return int|bool + */ + public function set($def, $config) + { + if (!$this->checkDefType($def)) { + return; + } + $file = $this->generateFilePath($config); + if (!$this->_prepareDir($config)) { + return false; + } + return $this->_write($file, serialize($def), $config); + } + + /** + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config + * @return int|bool + */ + public function replace($def, $config) + { + if (!$this->checkDefType($def)) { + return; + } + $file = $this->generateFilePath($config); + if (!file_exists($file)) { + return false; + } + if (!$this->_prepareDir($config)) { + return false; + } + return $this->_write($file, serialize($def), $config); + } + + /** + * @param HTMLPurifier_Config $config + * @return bool|HTMLPurifier_Config + */ + public function get($config) + { + $file = $this->generateFilePath($config); + if (!file_exists($file)) { + return false; + } + return unserialize(file_get_contents($file)); + } + + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function remove($config) + { + $file = $this->generateFilePath($config); + if (!file_exists($file)) { + return false; + } + return unlink($file); + } + + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function flush($config) + { + if (!$this->_prepareDir($config)) { + return false; + } + $dir = $this->generateDirectoryPath($config); + $dh = opendir($dir); + // Apparently, on some versions of PHP, readdir will return + // an empty string if you pass an invalid argument to readdir. + // So you need this test. See #49. + if (false === $dh) { + return false; + } + while (false !== ($filename = readdir($dh))) { + if (empty($filename)) { + continue; + } + if ($filename[0] === '.') { + continue; + } + unlink($dir . '/' . $filename); + } + closedir($dh); + return true; + } + + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function cleanup($config) + { + if (!$this->_prepareDir($config)) { + return false; + } + $dir = $this->generateDirectoryPath($config); + $dh = opendir($dir); + // See #49 (and above). + if (false === $dh) { + return false; + } + while (false !== ($filename = readdir($dh))) { + if (empty($filename)) { + continue; + } + if ($filename[0] === '.') { + continue; + } + $key = substr($filename, 0, strlen($filename) - 4); + if ($this->isOld($key, $config)) { + unlink($dir . '/' . $filename); + } + } + closedir($dh); + return true; + } + + /** + * Generates the file path to the serial file corresponding to + * the configuration and definition name + * @param HTMLPurifier_Config $config + * @return string + * @todo Make protected + */ + public function generateFilePath($config) + { + $key = $this->generateKey($config); + return $this->generateDirectoryPath($config) . '/' . $key . '.ser'; + } + + /** + * Generates the path to the directory contain this cache's serial files + * @param HTMLPurifier_Config $config + * @return string + * @note No trailing slash + * @todo Make protected + */ + public function generateDirectoryPath($config) + { + $base = $this->generateBaseDirectoryPath($config); + return $base . '/' . $this->type; + } + + /** + * Generates path to base directory that contains all definition type + * serials + * @param HTMLPurifier_Config $config + * @return mixed|string + * @todo Make protected + */ + public function generateBaseDirectoryPath($config) + { + $base = $config->get('Cache.SerializerPath'); + $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base; + return $base; + } + + /** + * Convenience wrapper function for file_put_contents + * @param string $file File name to write to + * @param string $data Data to write into file + * @param HTMLPurifier_Config $config + * @return int|bool Number of bytes written if success, or false if failure. + */ + private function _write($file, $data, $config) + { + $result = file_put_contents($file, $data); + if ($result !== false) { + // set permissions of the new file (no execute) + $chmod = $config->get('Cache.SerializerPermissions'); + if ($chmod !== null) { + chmod($file, $chmod & 0666); + } + } + return $result; + } + + /** + * Prepares the directory that this type stores the serials in + * @param HTMLPurifier_Config $config + * @return bool True if successful + */ + private function _prepareDir($config) + { + $directory = $this->generateDirectoryPath($config); + $chmod = $config->get('Cache.SerializerPermissions'); + if ($chmod === null) { + if (!@mkdir($directory) && !is_dir($directory)) { + trigger_error( + 'Could not create directory ' . $directory . '', + E_USER_WARNING + ); + return false; + } + return true; + } + if (!is_dir($directory)) { + $base = $this->generateBaseDirectoryPath($config); + if (!is_dir($base)) { + trigger_error( + 'Base directory ' . $base . ' does not exist, + please create or change using %Cache.SerializerPath', + E_USER_WARNING + ); + return false; + } elseif (!$this->_testPermissions($base, $chmod)) { + return false; + } + if (!@mkdir($directory, $chmod) && !is_dir($directory)) { + trigger_error( + 'Could not create directory ' . $directory . '', + E_USER_WARNING + ); + return false; + } + if (!$this->_testPermissions($directory, $chmod)) { + return false; + } + } elseif (!$this->_testPermissions($directory, $chmod)) { + return false; + } + return true; + } + + /** + * Tests permissions on a directory and throws out friendly + * error messages and attempts to chmod it itself if possible + * @param string $dir Directory path + * @param int $chmod Permissions + * @return bool True if directory is writable + */ + private function _testPermissions($dir, $chmod) + { + // early abort, if it is writable, everything is hunky-dory + if (is_writable($dir)) { + return true; + } + if (!is_dir($dir)) { + // generally, you'll want to handle this beforehand + // so a more specific error message can be given + trigger_error( + 'Directory ' . $dir . ' does not exist', + E_USER_WARNING + ); + return false; + } + if (function_exists('posix_getuid') && $chmod !== null) { + // POSIX system, we can give more specific advice + if (fileowner($dir) === posix_getuid()) { + // we can chmod it ourselves + $chmod = $chmod | 0700; + if (chmod($dir, $chmod)) { + return true; + } + } elseif (filegroup($dir) === posix_getgid()) { + $chmod = $chmod | 0070; + } else { + // PHP's probably running as nobody, so we'll + // need to give global permissions + $chmod = $chmod | 0777; + } + trigger_error( + 'Directory ' . $dir . ' not writable, ' . + 'please chmod to ' . decoct($chmod), + E_USER_WARNING + ); + } else { + // generic error message + trigger_error( + 'Directory ' . $dir . ' not writable, ' . + 'please alter file permissions', + E_USER_WARNING + ); + } + return false; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README new file mode 100644 index 00000000..2e35c1c3 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README @@ -0,0 +1,3 @@ +This is a dummy file to prevent Git from ignoring this empty directory. + + vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php new file mode 100644 index 00000000..fd1cc9be --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php @@ -0,0 +1,106 @@ + array()); + + /** + * @type array + */ + protected $implementations = array(); + + /** + * @type HTMLPurifier_DefinitionCache_Decorator[] + */ + protected $decorators = array(); + + /** + * Initialize default decorators + */ + public function setup() + { + $this->addDecorator('Cleanup'); + } + + /** + * Retrieves an instance of global definition cache factory. + * @param HTMLPurifier_DefinitionCacheFactory $prototype + * @return HTMLPurifier_DefinitionCacheFactory + */ + public static function instance($prototype = null) + { + static $instance; + if ($prototype !== null) { + $instance = $prototype; + } elseif ($instance === null || $prototype === true) { + $instance = new HTMLPurifier_DefinitionCacheFactory(); + $instance->setup(); + } + return $instance; + } + + /** + * Registers a new definition cache object + * @param string $short Short name of cache object, for reference + * @param string $long Full class name of cache object, for construction + */ + public function register($short, $long) + { + $this->implementations[$short] = $long; + } + + /** + * Factory method that creates a cache object based on configuration + * @param string $type Name of definitions handled by cache + * @param HTMLPurifier_Config $config Config instance + * @return mixed + */ + public function create($type, $config) + { + $method = $config->get('Cache.DefinitionImpl'); + if ($method === null) { + return new HTMLPurifier_DefinitionCache_Null($type); + } + if (!empty($this->caches[$method][$type])) { + return $this->caches[$method][$type]; + } + if (isset($this->implementations[$method]) && + class_exists($class = $this->implementations[$method], false)) { + $cache = new $class($type); + } else { + if ($method != 'Serializer') { + trigger_error("Unrecognized DefinitionCache $method, using Serializer instead", E_USER_WARNING); + } + $cache = new HTMLPurifier_DefinitionCache_Serializer($type); + } + foreach ($this->decorators as $decorator) { + $new_cache = $decorator->decorate($cache); + // prevent infinite recursion in PHP 4 + unset($cache); + $cache = $new_cache; + } + $this->caches[$method][$type] = $cache; + return $this->caches[$method][$type]; + } + + /** + * Registers a decorator to add to all new cache objects + * @param HTMLPurifier_DefinitionCache_Decorator|string $decorator An instance or the name of a decorator + */ + public function addDecorator($decorator) + { + if (is_string($decorator)) { + $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator"; + $decorator = new $class; + } + $this->decorators[$decorator->name] = $decorator; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php new file mode 100644 index 00000000..4acd06e5 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php @@ -0,0 +1,73 @@ +renderDoctype. + * If structure changes, please update that function. + */ +class HTMLPurifier_Doctype +{ + /** + * Full name of doctype + * @type string + */ + public $name; + + /** + * List of standard modules (string identifiers or literal objects) + * that this doctype uses + * @type array + */ + public $modules = array(); + + /** + * List of modules to use for tidying up code + * @type array + */ + public $tidyModules = array(); + + /** + * Is the language derived from XML (i.e. XHTML)? + * @type bool + */ + public $xml = true; + + /** + * List of aliases for this doctype + * @type array + */ + public $aliases = array(); + + /** + * Public DTD identifier + * @type string + */ + public $dtdPublic; + + /** + * System DTD identifier + * @type string + */ + public $dtdSystem; + + public function __construct( + $name = null, + $xml = true, + $modules = array(), + $tidyModules = array(), + $aliases = array(), + $dtd_public = null, + $dtd_system = null + ) { + $this->name = $name; + $this->xml = $xml; + $this->modules = $modules; + $this->tidyModules = $tidyModules; + $this->aliases = $aliases; + $this->dtdPublic = $dtd_public; + $this->dtdSystem = $dtd_system; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php new file mode 100644 index 00000000..acc1d64a --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php @@ -0,0 +1,142 @@ +doctypes[$doctype->name] = $doctype; + $name = $doctype->name; + // hookup aliases + foreach ($doctype->aliases as $alias) { + if (isset($this->doctypes[$alias])) { + continue; + } + $this->aliases[$alias] = $name; + } + // remove old aliases + if (isset($this->aliases[$name])) { + unset($this->aliases[$name]); + } + return $doctype; + } + + /** + * Retrieves reference to a doctype of a certain name + * @note This function resolves aliases + * @note When possible, use the more fully-featured make() + * @param string $doctype Name of doctype + * @return HTMLPurifier_Doctype Editable doctype object + */ + public function get($doctype) + { + if (isset($this->aliases[$doctype])) { + $doctype = $this->aliases[$doctype]; + } + if (!isset($this->doctypes[$doctype])) { + trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR); + $anon = new HTMLPurifier_Doctype($doctype); + return $anon; + } + return $this->doctypes[$doctype]; + } + + /** + * Creates a doctype based on a configuration object, + * will perform initialization on the doctype + * @note Use this function to get a copy of doctype that config + * can hold on to (this is necessary in order to tell + * Generator whether or not the current document is XML + * based or not). + * @param HTMLPurifier_Config $config + * @return HTMLPurifier_Doctype + */ + public function make($config) + { + return clone $this->get($this->getDoctypeFromConfig($config)); + } + + /** + * Retrieves the doctype from the configuration object + * @param HTMLPurifier_Config $config + * @return string + */ + public function getDoctypeFromConfig($config) + { + // recommended test + $doctype = $config->get('HTML.Doctype'); + if (!empty($doctype)) { + return $doctype; + } + $doctype = $config->get('HTML.CustomDoctype'); + if (!empty($doctype)) { + return $doctype; + } + // backwards-compatibility + if ($config->get('HTML.XHTML')) { + $doctype = 'XHTML 1.0'; + } else { + $doctype = 'HTML 4.01'; + } + if ($config->get('HTML.Strict')) { + $doctype .= ' Strict'; + } else { + $doctype .= ' Transitional'; + } + return $doctype; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php new file mode 100644 index 00000000..d5311ced --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php @@ -0,0 +1,216 @@ +setup(), this array may also + * contain an array at index 0 that indicates which attribute + * collections to load into the full array. It may also + * contain string indentifiers in lieu of HTMLPurifier_AttrDef, + * see HTMLPurifier_AttrTypes on how they are expanded during + * HTMLPurifier_HTMLDefinition->setup() processing. + */ + public $attr = array(); + + // XXX: Design note: currently, it's not possible to override + // previously defined AttrTransforms without messing around with + // the final generated config. This is by design; a previous version + // used an associated list of attr_transform, but it was extremely + // easy to accidentally override other attribute transforms by + // forgetting to specify an index (and just using 0.) While we + // could check this by checking the index number and complaining, + // there is a second problem which is that it is not at all easy to + // tell when something is getting overridden. Combine this with a + // codebase where this isn't really being used, and it's perfect for + // nuking. + + /** + * List of tags HTMLPurifier_AttrTransform to be done before validation. + * @type array + */ + public $attr_transform_pre = array(); + + /** + * List of tags HTMLPurifier_AttrTransform to be done after validation. + * @type array + */ + public $attr_transform_post = array(); + + /** + * HTMLPurifier_ChildDef of this tag. + * @type HTMLPurifier_ChildDef + */ + public $child; + + /** + * Abstract string representation of internal ChildDef rules. + * @see HTMLPurifier_ContentSets for how this is parsed and then transformed + * into an HTMLPurifier_ChildDef. + * @warning This is a temporary variable that is not available after + * being processed by HTMLDefinition + * @type string + */ + public $content_model; + + /** + * Value of $child->type, used to determine which ChildDef to use, + * used in combination with $content_model. + * @warning This must be lowercase + * @warning This is a temporary variable that is not available after + * being processed by HTMLDefinition + * @type string + */ + public $content_model_type; + + /** + * Does the element have a content model (#PCDATA | Inline)*? This + * is important for chameleon ins and del processing in + * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't + * have to worry about this one. + * @type bool + */ + public $descendants_are_inline = false; + + /** + * List of the names of required attributes this element has. + * Dynamically populated by HTMLPurifier_HTMLDefinition::getElement() + * @type array + */ + public $required_attr = array(); + + /** + * Lookup table of tags excluded from all descendants of this tag. + * @type array + * @note SGML permits exclusions for all descendants, but this is + * not possible with DTDs or XML Schemas. W3C has elected to + * use complicated compositions of content_models to simulate + * exclusion for children, but we go the simpler, SGML-style + * route of flat-out exclusions, which correctly apply to + * all descendants and not just children. Note that the XHTML + * Modularization Abstract Modules are blithely unaware of such + * distinctions. + */ + public $excludes = array(); + + /** + * This tag is explicitly auto-closed by the following tags. + * @type array + */ + public $autoclose = array(); + + /** + * If a foreign element is found in this element, test if it is + * allowed by this sub-element; if it is, instead of closing the + * current element, place it inside this element. + * @type string + */ + public $wrap; + + /** + * Whether or not this is a formatting element affected by the + * "Active Formatting Elements" algorithm. + * @type bool + */ + public $formatting; + + /** + * Low-level factory constructor for creating new standalone element defs + */ + public static function create($content_model, $content_model_type, $attr) + { + $def = new HTMLPurifier_ElementDef(); + $def->content_model = $content_model; + $def->content_model_type = $content_model_type; + $def->attr = $attr; + return $def; + } + + /** + * Merges the values of another element definition into this one. + * Values from the new element def take precedence if a value is + * not mergeable. + * @param HTMLPurifier_ElementDef $def + */ + public function mergeIn($def) + { + // later keys takes precedence + foreach ($def->attr as $k => $v) { + if ($k === 0) { + // merge in the includes + // sorry, no way to override an include + foreach ($v as $v2) { + $this->attr[0][] = $v2; + } + continue; + } + if ($v === false) { + if (isset($this->attr[$k])) { + unset($this->attr[$k]); + } + continue; + } + $this->attr[$k] = $v; + } + $this->_mergeAssocArray($this->excludes, $def->excludes); + $this->attr_transform_pre = array_merge($this->attr_transform_pre, $def->attr_transform_pre); + $this->attr_transform_post = array_merge($this->attr_transform_post, $def->attr_transform_post); + + if (!empty($def->content_model)) { + $this->content_model = + str_replace("#SUPER", $this->content_model, $def->content_model); + $this->child = false; + } + if (!empty($def->content_model_type)) { + $this->content_model_type = $def->content_model_type; + $this->child = false; + } + if (!is_null($def->child)) { + $this->child = $def->child; + } + if (!is_null($def->formatting)) { + $this->formatting = $def->formatting; + } + if ($def->descendants_are_inline) { + $this->descendants_are_inline = $def->descendants_are_inline; + } + } + + /** + * Merges one array into another, removes values which equal false + * @param $a1 Array by reference that is merged into + * @param $a2 Array that merges into $a1 + */ + private function _mergeAssocArray(&$a1, $a2) + { + foreach ($a2 as $k => $v) { + if ($v === false) { + if (isset($a1[$k])) { + unset($a1[$k]); + } + continue; + } + $a1[$k] = $v; + } + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php new file mode 100644 index 00000000..b94f1754 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php @@ -0,0 +1,617 @@ += $c) { + $r .= self::unsafeIconv($in, $out, substr($text, $i)); + break; + } + // wibble the boundary + if (0x80 != (0xC0 & ord($text[$i + $max_chunk_size]))) { + $chunk_size = $max_chunk_size; + } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 1]))) { + $chunk_size = $max_chunk_size - 1; + } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 2]))) { + $chunk_size = $max_chunk_size - 2; + } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 3]))) { + $chunk_size = $max_chunk_size - 3; + } else { + return false; // rather confusing UTF-8... + } + $chunk = substr($text, $i, $chunk_size); // substr doesn't mind overlong lengths + $r .= self::unsafeIconv($in, $out, $chunk); + $i += $chunk_size; + } + return $r; + } else { + return false; + } + } else { + return false; + } + } + + /** + * Cleans a UTF-8 string for well-formedness and SGML validity + * + * It will parse according to UTF-8 and return a valid UTF8 string, with + * non-SGML codepoints excluded. + * + * Specifically, it will permit: + * \x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF} + * Source: https://www.w3.org/TR/REC-xml/#NT-Char + * Arguably this function should be modernized to the HTML5 set + * of allowed characters: + * https://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream + * which simultaneously expand and restrict the set of allowed characters. + * + * @param string $str The string to clean + * @param bool $force_php + * @return string + * + * @note Just for reference, the non-SGML code points are 0 to 31 and + * 127 to 159, inclusive. However, we allow code points 9, 10 + * and 13, which are the tab, line feed and carriage return + * respectively. 128 and above the code points map to multibyte + * UTF-8 representations. + * + * @note Fallback code adapted from utf8ToUnicode by Henri Sivonen and + * hsivonen@iki.fi at under the + * LGPL license. Notes on what changed are inside, but in general, + * the original code transformed UTF-8 text into an array of integer + * Unicode codepoints. Understandably, transforming that back to + * a string would be somewhat expensive, so the function was modded to + * directly operate on the string. However, this discourages code + * reuse, and the logic enumerated here would be useful for any + * function that needs to be able to understand UTF-8 characters. + * As of right now, only smart lossless character encoding converters + * would need that, and I'm probably not going to implement them. + */ + public static function cleanUTF8($str, $force_php = false) + { + // UTF-8 validity is checked since PHP 4.3.5 + // This is an optimization: if the string is already valid UTF-8, no + // need to do PHP stuff. 99% of the time, this will be the case. + if (preg_match( + '/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du', + $str + )) { + return $str; + } + + $mState = 0; // cached expected number of octets after the current octet + // until the beginning of the next UTF8 character sequence + $mUcs4 = 0; // cached Unicode character + $mBytes = 1; // cached expected number of octets in the current sequence + + // original code involved an $out that was an array of Unicode + // codepoints. Instead of having to convert back into UTF-8, we've + // decided to directly append valid UTF-8 characters onto a string + // $out once they're done. $char accumulates raw bytes, while $mUcs4 + // turns into the Unicode code point, so there's some redundancy. + + $out = ''; + $char = ''; + + $len = strlen($str); + for ($i = 0; $i < $len; $i++) { + $in = ord($str{$i}); + $char .= $str[$i]; // append byte to char + if (0 == $mState) { + // When mState is zero we expect either a US-ASCII character + // or a multi-octet sequence. + if (0 == (0x80 & ($in))) { + // US-ASCII, pass straight through. + if (($in <= 31 || $in == 127) && + !($in == 9 || $in == 13 || $in == 10) // save \r\t\n + ) { + // control characters, remove + } else { + $out .= $char; + } + // reset + $char = ''; + $mBytes = 1; + } elseif (0xC0 == (0xE0 & ($in))) { + // First octet of 2 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x1F) << 6; + $mState = 1; + $mBytes = 2; + } elseif (0xE0 == (0xF0 & ($in))) { + // First octet of 3 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x0F) << 12; + $mState = 2; + $mBytes = 3; + } elseif (0xF0 == (0xF8 & ($in))) { + // First octet of 4 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x07) << 18; + $mState = 3; + $mBytes = 4; + } elseif (0xF8 == (0xFC & ($in))) { + // First octet of 5 octet sequence. + // + // This is illegal because the encoded codepoint must be + // either: + // (a) not the shortest form or + // (b) outside the Unicode range of 0-0x10FFFF. + // Rather than trying to resynchronize, we will carry on + // until the end of the sequence and let the later error + // handling code catch it. + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x03) << 24; + $mState = 4; + $mBytes = 5; + } elseif (0xFC == (0xFE & ($in))) { + // First octet of 6 octet sequence, see comments for 5 + // octet sequence. + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 1) << 30; + $mState = 5; + $mBytes = 6; + } else { + // Current octet is neither in the US-ASCII range nor a + // legal first octet of a multi-octet sequence. + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + $char = ''; + } + } else { + // When mState is non-zero, we expect a continuation of the + // multi-octet sequence + if (0x80 == (0xC0 & ($in))) { + // Legal continuation. + $shift = ($mState - 1) * 6; + $tmp = $in; + $tmp = ($tmp & 0x0000003F) << $shift; + $mUcs4 |= $tmp; + + if (0 == --$mState) { + // End of the multi-octet sequence. mUcs4 now contains + // the final Unicode codepoint to be output + + // Check for illegal sequences and codepoints. + + // From Unicode 3.1, non-shortest form is illegal + if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || + ((3 == $mBytes) && ($mUcs4 < 0x0800)) || + ((4 == $mBytes) && ($mUcs4 < 0x10000)) || + (4 < $mBytes) || + // From Unicode 3.2, surrogate characters = illegal + (($mUcs4 & 0xFFFFF800) == 0xD800) || + // Codepoints outside the Unicode range are illegal + ($mUcs4 > 0x10FFFF) + ) { + + } elseif (0xFEFF != $mUcs4 && // omit BOM + // check for valid Char unicode codepoints + ( + 0x9 == $mUcs4 || + 0xA == $mUcs4 || + 0xD == $mUcs4 || + (0x20 <= $mUcs4 && 0x7E >= $mUcs4) || + // 7F-9F is not strictly prohibited by XML, + // but it is non-SGML, and thus we don't allow it + (0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) || + (0xE000 <= $mUcs4 && 0xFFFD >= $mUcs4) || + (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4) + ) + ) { + $out .= $char; + } + // initialize UTF8 cache (reset) + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + $char = ''; + } + } else { + // ((0xC0 & (*in) != 0x80) && (mState != 0)) + // Incomplete multi-octet sequence. + // used to result in complete fail, but we'll reset + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + $char =''; + } + } + } + return $out; + } + + /** + * Translates a Unicode codepoint into its corresponding UTF-8 character. + * @note Based on Feyd's function at + * , + * which is in public domain. + * @note While we're going to do code point parsing anyway, a good + * optimization would be to refuse to translate code points that + * are non-SGML characters. However, this could lead to duplication. + * @note This is very similar to the unichr function in + * maintenance/generate-entity-file.php (although this is superior, + * due to its sanity checks). + */ + + // +----------+----------+----------+----------+ + // | 33222222 | 22221111 | 111111 | | + // | 10987654 | 32109876 | 54321098 | 76543210 | bit + // +----------+----------+----------+----------+ + // | | | | 0xxxxxxx | 1 byte 0x00000000..0x0000007F + // | | | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF + // | | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF + // | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF + // +----------+----------+----------+----------+ + // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF) + // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes + // +----------+----------+----------+----------+ + + public static function unichr($code) + { + if ($code > 1114111 or $code < 0 or + ($code >= 55296 and $code <= 57343) ) { + // bits are set outside the "valid" range as defined + // by UNICODE 4.1.0 + return ''; + } + + $x = $y = $z = $w = 0; + if ($code < 128) { + // regular ASCII character + $x = $code; + } else { + // set up bits for UTF-8 + $x = ($code & 63) | 128; + if ($code < 2048) { + $y = (($code & 2047) >> 6) | 192; + } else { + $y = (($code & 4032) >> 6) | 128; + if ($code < 65536) { + $z = (($code >> 12) & 15) | 224; + } else { + $z = (($code >> 12) & 63) | 128; + $w = (($code >> 18) & 7) | 240; + } + } + } + // set up the actual character + $ret = ''; + if ($w) { + $ret .= chr($w); + } + if ($z) { + $ret .= chr($z); + } + if ($y) { + $ret .= chr($y); + } + $ret .= chr($x); + + return $ret; + } + + /** + * @return bool + */ + public static function iconvAvailable() + { + static $iconv = null; + if ($iconv === null) { + $iconv = function_exists('iconv') && self::testIconvTruncateBug() != self::ICONV_UNUSABLE; + } + return $iconv; + } + + /** + * Convert a string to UTF-8 based on configuration. + * @param string $str The string to convert + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public static function convertToUTF8($str, $config, $context) + { + $encoding = $config->get('Core.Encoding'); + if ($encoding === 'utf-8') { + return $str; + } + static $iconv = null; + if ($iconv === null) { + $iconv = self::iconvAvailable(); + } + if ($iconv && !$config->get('Test.ForceNoIconv')) { + // unaffected by bugs, since UTF-8 support all characters + $str = self::unsafeIconv($encoding, 'utf-8//IGNORE', $str); + if ($str === false) { + // $encoding is not a valid encoding + trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR); + return ''; + } + // If the string is bjorked by Shift_JIS or a similar encoding + // that doesn't support all of ASCII, convert the naughty + // characters to their true byte-wise ASCII/UTF-8 equivalents. + $str = strtr($str, self::testEncodingSupportsASCII($encoding)); + return $str; + } elseif ($encoding === 'iso-8859-1') { + $str = utf8_encode($str); + return $str; + } + $bug = HTMLPurifier_Encoder::testIconvTruncateBug(); + if ($bug == self::ICONV_OK) { + trigger_error('Encoding not supported, please install iconv', E_USER_ERROR); + } else { + trigger_error( + 'You have a buggy version of iconv, see https://bugs.php.net/bug.php?id=48147 ' . + 'and http://sourceware.org/bugzilla/show_bug.cgi?id=13541', + E_USER_ERROR + ); + } + } + + /** + * Converts a string from UTF-8 based on configuration. + * @param string $str The string to convert + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + * @note Currently, this is a lossy conversion, with unexpressable + * characters being omitted. + */ + public static function convertFromUTF8($str, $config, $context) + { + $encoding = $config->get('Core.Encoding'); + if ($escape = $config->get('Core.EscapeNonASCIICharacters')) { + $str = self::convertToASCIIDumbLossless($str); + } + if ($encoding === 'utf-8') { + return $str; + } + static $iconv = null; + if ($iconv === null) { + $iconv = self::iconvAvailable(); + } + if ($iconv && !$config->get('Test.ForceNoIconv')) { + // Undo our previous fix in convertToUTF8, otherwise iconv will barf + $ascii_fix = self::testEncodingSupportsASCII($encoding); + if (!$escape && !empty($ascii_fix)) { + $clear_fix = array(); + foreach ($ascii_fix as $utf8 => $native) { + $clear_fix[$utf8] = ''; + } + $str = strtr($str, $clear_fix); + } + $str = strtr($str, array_flip($ascii_fix)); + // Normal stuff + $str = self::iconv('utf-8', $encoding . '//IGNORE', $str); + return $str; + } elseif ($encoding === 'iso-8859-1') { + $str = utf8_decode($str); + return $str; + } + trigger_error('Encoding not supported', E_USER_ERROR); + // You might be tempted to assume that the ASCII representation + // might be OK, however, this is *not* universally true over all + // encodings. So we take the conservative route here, rather + // than forcibly turn on %Core.EscapeNonASCIICharacters + } + + /** + * Lossless (character-wise) conversion of HTML to ASCII + * @param string $str UTF-8 string to be converted to ASCII + * @return string ASCII encoded string with non-ASCII character entity-ized + * @warning Adapted from MediaWiki, claiming fair use: this is a common + * algorithm. If you disagree with this license fudgery, + * implement it yourself. + * @note Uses decimal numeric entities since they are best supported. + * @note This is a DUMB function: it has no concept of keeping + * character entities that the projected character encoding + * can allow. We could possibly implement a smart version + * but that would require it to also know which Unicode + * codepoints the charset supported (not an easy task). + * @note Sort of with cleanUTF8() but it assumes that $str is + * well-formed UTF-8 + */ + public static function convertToASCIIDumbLossless($str) + { + $bytesleft = 0; + $result = ''; + $working = 0; + $len = strlen($str); + for ($i = 0; $i < $len; $i++) { + $bytevalue = ord($str[$i]); + if ($bytevalue <= 0x7F) { //0xxx xxxx + $result .= chr($bytevalue); + $bytesleft = 0; + } elseif ($bytevalue <= 0xBF) { //10xx xxxx + $working = $working << 6; + $working += ($bytevalue & 0x3F); + $bytesleft--; + if ($bytesleft <= 0) { + $result .= "&#" . $working . ";"; + } + } elseif ($bytevalue <= 0xDF) { //110x xxxx + $working = $bytevalue & 0x1F; + $bytesleft = 1; + } elseif ($bytevalue <= 0xEF) { //1110 xxxx + $working = $bytevalue & 0x0F; + $bytesleft = 2; + } else { //1111 0xxx + $working = $bytevalue & 0x07; + $bytesleft = 3; + } + } + return $result; + } + + /** No bugs detected in iconv. */ + const ICONV_OK = 0; + + /** Iconv truncates output if converting from UTF-8 to another + * character set with //IGNORE, and a non-encodable character is found */ + const ICONV_TRUNCATES = 1; + + /** Iconv does not support //IGNORE, making it unusable for + * transcoding purposes */ + const ICONV_UNUSABLE = 2; + + /** + * glibc iconv has a known bug where it doesn't handle the magic + * //IGNORE stanza correctly. In particular, rather than ignore + * characters, it will return an EILSEQ after consuming some number + * of characters, and expect you to restart iconv as if it were + * an E2BIG. Old versions of PHP did not respect the errno, and + * returned the fragment, so as a result you would see iconv + * mysteriously truncating output. We can work around this by + * manually chopping our input into segments of about 8000 + * characters, as long as PHP ignores the error code. If PHP starts + * paying attention to the error code, iconv becomes unusable. + * + * @return int Error code indicating severity of bug. + */ + public static function testIconvTruncateBug() + { + static $code = null; + if ($code === null) { + // better not use iconv, otherwise infinite loop! + $r = self::unsafeIconv('utf-8', 'ascii//IGNORE', "\xCE\xB1" . str_repeat('a', 9000)); + if ($r === false) { + $code = self::ICONV_UNUSABLE; + } elseif (($c = strlen($r)) < 9000) { + $code = self::ICONV_TRUNCATES; + } elseif ($c > 9000) { + trigger_error( + 'Your copy of iconv is extremely buggy. Please notify HTML Purifier maintainers: ' . + 'include your iconv version as per phpversion()', + E_USER_ERROR + ); + } else { + $code = self::ICONV_OK; + } + } + return $code; + } + + /** + * This expensive function tests whether or not a given character + * encoding supports ASCII. 7/8-bit encodings like Shift_JIS will + * fail this test, and require special processing. Variable width + * encodings shouldn't ever fail. + * + * @param string $encoding Encoding name to test, as per iconv format + * @param bool $bypass Whether or not to bypass the precompiled arrays. + * @return Array of UTF-8 characters to their corresponding ASCII, + * which can be used to "undo" any overzealous iconv action. + */ + public static function testEncodingSupportsASCII($encoding, $bypass = false) + { + // All calls to iconv here are unsafe, proof by case analysis: + // If ICONV_OK, no difference. + // If ICONV_TRUNCATE, all calls involve one character inputs, + // so bug is not triggered. + // If ICONV_UNUSABLE, this call is irrelevant + static $encodings = array(); + if (!$bypass) { + if (isset($encodings[$encoding])) { + return $encodings[$encoding]; + } + $lenc = strtolower($encoding); + switch ($lenc) { + case 'shift_jis': + return array("\xC2\xA5" => '\\', "\xE2\x80\xBE" => '~'); + case 'johab': + return array("\xE2\x82\xA9" => '\\'); + } + if (strpos($lenc, 'iso-8859-') === 0) { + return array(); + } + } + $ret = array(); + if (self::unsafeIconv('UTF-8', $encoding, 'a') === false) { + return false; + } + for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars + $c = chr($i); // UTF-8 char + $r = self::unsafeIconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion + if ($r === '' || + // This line is needed for iconv implementations that do not + // omit characters that do not exist in the target character set + ($r === $c && self::unsafeIconv($encoding, 'UTF-8//IGNORE', $r) !== $c) + ) { + // Reverse engineer: what's the UTF-8 equiv of this byte + // sequence? This assumes that there's no variable width + // encoding that doesn't support ASCII. + $ret[self::unsafeIconv($encoding, 'UTF-8//IGNORE', $c)] = $c; + } + } + $encodings[$encoding] = $ret; + return $ret; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php new file mode 100644 index 00000000..f12ff13a --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php @@ -0,0 +1,48 @@ +table = unserialize(file_get_contents($file)); + } + + /** + * Retrieves sole instance of the object. + * @param bool|HTMLPurifier_EntityLookup $prototype Optional prototype of custom lookup table to overload with. + * @return HTMLPurifier_EntityLookup + */ + public static function instance($prototype = false) + { + // no references, since PHP doesn't copy unless modified + static $instance = null; + if ($prototype) { + $instance = $prototype; + } elseif (!$instance) { + $instance = new HTMLPurifier_EntityLookup(); + $instance->setup(); + } + return $instance; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser new file mode 100644 index 00000000..e8b08128 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser @@ -0,0 +1 @@ +a:253:{s:4:"fnof";s:2:"ƒ";s:5:"Alpha";s:2:"Α";s:4:"Beta";s:2:"Β";s:5:"Gamma";s:2:"Γ";s:5:"Delta";s:2:"Δ";s:7:"Epsilon";s:2:"Ε";s:4:"Zeta";s:2:"Ζ";s:3:"Eta";s:2:"Η";s:5:"Theta";s:2:"Θ";s:4:"Iota";s:2:"Ι";s:5:"Kappa";s:2:"Κ";s:6:"Lambda";s:2:"Λ";s:2:"Mu";s:2:"Μ";s:2:"Nu";s:2:"Ν";s:2:"Xi";s:2:"Ξ";s:7:"Omicron";s:2:"Ο";s:2:"Pi";s:2:"Π";s:3:"Rho";s:2:"Ρ";s:5:"Sigma";s:2:"Σ";s:3:"Tau";s:2:"Τ";s:7:"Upsilon";s:2:"Υ";s:3:"Phi";s:2:"Φ";s:3:"Chi";s:2:"Χ";s:3:"Psi";s:2:"Ψ";s:5:"Omega";s:2:"Ω";s:5:"alpha";s:2:"α";s:4:"beta";s:2:"β";s:5:"gamma";s:2:"γ";s:5:"delta";s:2:"δ";s:7:"epsilon";s:2:"ε";s:4:"zeta";s:2:"ζ";s:3:"eta";s:2:"η";s:5:"theta";s:2:"θ";s:4:"iota";s:2:"ι";s:5:"kappa";s:2:"κ";s:6:"lambda";s:2:"λ";s:2:"mu";s:2:"μ";s:2:"nu";s:2:"ν";s:2:"xi";s:2:"ξ";s:7:"omicron";s:2:"ο";s:2:"pi";s:2:"π";s:3:"rho";s:2:"ρ";s:6:"sigmaf";s:2:"ς";s:5:"sigma";s:2:"σ";s:3:"tau";s:2:"τ";s:7:"upsilon";s:2:"υ";s:3:"phi";s:2:"φ";s:3:"chi";s:2:"χ";s:3:"psi";s:2:"ψ";s:5:"omega";s:2:"ω";s:8:"thetasym";s:2:"ϑ";s:5:"upsih";s:2:"ϒ";s:3:"piv";s:2:"ϖ";s:4:"bull";s:3:"•";s:6:"hellip";s:3:"…";s:5:"prime";s:3:"′";s:5:"Prime";s:3:"″";s:5:"oline";s:3:"‾";s:5:"frasl";s:3:"⁄";s:6:"weierp";s:3:"℘";s:5:"image";s:3:"ℑ";s:4:"real";s:3:"ℜ";s:5:"trade";s:3:"™";s:7:"alefsym";s:3:"ℵ";s:4:"larr";s:3:"←";s:4:"uarr";s:3:"↑";s:4:"rarr";s:3:"→";s:4:"darr";s:3:"↓";s:4:"harr";s:3:"↔";s:5:"crarr";s:3:"↵";s:4:"lArr";s:3:"⇐";s:4:"uArr";s:3:"⇑";s:4:"rArr";s:3:"⇒";s:4:"dArr";s:3:"⇓";s:4:"hArr";s:3:"⇔";s:6:"forall";s:3:"∀";s:4:"part";s:3:"∂";s:5:"exist";s:3:"∃";s:5:"empty";s:3:"∅";s:5:"nabla";s:3:"∇";s:4:"isin";s:3:"∈";s:5:"notin";s:3:"∉";s:2:"ni";s:3:"∋";s:4:"prod";s:3:"∏";s:3:"sum";s:3:"∑";s:5:"minus";s:3:"−";s:6:"lowast";s:3:"∗";s:5:"radic";s:3:"√";s:4:"prop";s:3:"∝";s:5:"infin";s:3:"∞";s:3:"ang";s:3:"∠";s:3:"and";s:3:"∧";s:2:"or";s:3:"∨";s:3:"cap";s:3:"∩";s:3:"cup";s:3:"∪";s:3:"int";s:3:"∫";s:6:"there4";s:3:"∴";s:3:"sim";s:3:"∼";s:4:"cong";s:3:"≅";s:5:"asymp";s:3:"≈";s:2:"ne";s:3:"≠";s:5:"equiv";s:3:"≡";s:2:"le";s:3:"≤";s:2:"ge";s:3:"≥";s:3:"sub";s:3:"⊂";s:3:"sup";s:3:"⊃";s:4:"nsub";s:3:"⊄";s:4:"sube";s:3:"⊆";s:4:"supe";s:3:"⊇";s:5:"oplus";s:3:"⊕";s:6:"otimes";s:3:"⊗";s:4:"perp";s:3:"⊥";s:4:"sdot";s:3:"⋅";s:5:"lceil";s:3:"⌈";s:5:"rceil";s:3:"⌉";s:6:"lfloor";s:3:"⌊";s:6:"rfloor";s:3:"⌋";s:4:"lang";s:3:"〈";s:4:"rang";s:3:"〉";s:3:"loz";s:3:"◊";s:6:"spades";s:3:"♠";s:5:"clubs";s:3:"♣";s:6:"hearts";s:3:"♥";s:5:"diams";s:3:"♦";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Œ";s:5:"oelig";s:2:"œ";s:6:"Scaron";s:2:"Š";s:6:"scaron";s:2:"š";s:4:"Yuml";s:2:"Ÿ";s:4:"circ";s:2:"ˆ";s:5:"tilde";s:2:"˜";s:4:"ensp";s:3:" ";s:4:"emsp";s:3:" ";s:6:"thinsp";s:3:" ";s:4:"zwnj";s:3:"‌";s:3:"zwj";s:3:"‍";s:3:"lrm";s:3:"‎";s:3:"rlm";s:3:"‏";s:5:"ndash";s:3:"–";s:5:"mdash";s:3:"—";s:5:"lsquo";s:3:"‘";s:5:"rsquo";s:3:"’";s:5:"sbquo";s:3:"‚";s:5:"ldquo";s:3:"“";s:5:"rdquo";s:3:"”";s:5:"bdquo";s:3:"„";s:6:"dagger";s:3:"†";s:6:"Dagger";s:3:"‡";s:6:"permil";s:3:"‰";s:6:"lsaquo";s:3:"‹";s:6:"rsaquo";s:3:"›";s:4:"euro";s:3:"€";s:4:"nbsp";s:2:" ";s:5:"iexcl";s:2:"¡";s:4:"cent";s:2:"¢";s:5:"pound";s:2:"£";s:6:"curren";s:2:"¤";s:3:"yen";s:2:"¥";s:6:"brvbar";s:2:"¦";s:4:"sect";s:2:"§";s:3:"uml";s:2:"¨";s:4:"copy";s:2:"©";s:4:"ordf";s:2:"ª";s:5:"laquo";s:2:"«";s:3:"not";s:2:"¬";s:3:"shy";s:2:"­";s:3:"reg";s:2:"®";s:4:"macr";s:2:"¯";s:3:"deg";s:2:"°";s:6:"plusmn";s:2:"±";s:4:"sup2";s:2:"²";s:4:"sup3";s:2:"³";s:5:"acute";s:2:"´";s:5:"micro";s:2:"µ";s:4:"para";s:2:"¶";s:6:"middot";s:2:"·";s:5:"cedil";s:2:"¸";s:4:"sup1";s:2:"¹";s:4:"ordm";s:2:"º";s:5:"raquo";s:2:"»";s:6:"frac14";s:2:"¼";s:6:"frac12";s:2:"½";s:6:"frac34";s:2:"¾";s:6:"iquest";s:2:"¿";s:6:"Agrave";s:2:"À";s:6:"Aacute";s:2:"Á";s:5:"Acirc";s:2:"Â";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ä";s:5:"Aring";s:2:"Å";s:5:"AElig";s:2:"Æ";s:6:"Ccedil";s:2:"Ç";s:6:"Egrave";s:2:"È";s:6:"Eacute";s:2:"É";s:5:"Ecirc";s:2:"Ê";s:4:"Euml";s:2:"Ë";s:6:"Igrave";s:2:"Ì";s:6:"Iacute";s:2:"Í";s:5:"Icirc";s:2:"Î";s:4:"Iuml";s:2:"Ï";s:3:"ETH";s:2:"Ð";s:6:"Ntilde";s:2:"Ñ";s:6:"Ograve";s:2:"Ò";s:6:"Oacute";s:2:"Ó";s:5:"Ocirc";s:2:"Ô";s:6:"Otilde";s:2:"Õ";s:4:"Ouml";s:2:"Ö";s:5:"times";s:2:"×";s:6:"Oslash";s:2:"Ø";s:6:"Ugrave";s:2:"Ù";s:6:"Uacute";s:2:"Ú";s:5:"Ucirc";s:2:"Û";s:4:"Uuml";s:2:"Ü";s:6:"Yacute";s:2:"Ý";s:5:"THORN";s:2:"Þ";s:5:"szlig";s:2:"ß";s:6:"agrave";s:2:"à";s:6:"aacute";s:2:"á";s:5:"acirc";s:2:"â";s:6:"atilde";s:2:"ã";s:4:"auml";s:2:"ä";s:5:"aring";s:2:"å";s:5:"aelig";s:2:"æ";s:6:"ccedil";s:2:"ç";s:6:"egrave";s:2:"è";s:6:"eacute";s:2:"é";s:5:"ecirc";s:2:"ê";s:4:"euml";s:2:"ë";s:6:"igrave";s:2:"ì";s:6:"iacute";s:2:"í";s:5:"icirc";s:2:"î";s:4:"iuml";s:2:"ï";s:3:"eth";s:2:"ð";s:6:"ntilde";s:2:"ñ";s:6:"ograve";s:2:"ò";s:6:"oacute";s:2:"ó";s:5:"ocirc";s:2:"ô";s:6:"otilde";s:2:"õ";s:4:"ouml";s:2:"ö";s:6:"divide";s:2:"÷";s:6:"oslash";s:2:"ø";s:6:"ugrave";s:2:"ù";s:6:"uacute";s:2:"ú";s:5:"ucirc";s:2:"û";s:4:"uuml";s:2:"ü";s:6:"yacute";s:2:"ý";s:5:"thorn";s:2:"þ";s:4:"yuml";s:2:"ÿ";} \ No newline at end of file diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php new file mode 100644 index 00000000..c372b5a6 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php @@ -0,0 +1,285 @@ +_semiOptionalPrefixRegex = "/&()()()($semi_optional)/"; + + $this->_textEntitiesRegex = + '/&(?:'. + // hex + '[#]x([a-fA-F0-9]+);?|'. + // dec + '[#]0*(\d+);?|'. + // string (mandatory semicolon) + // NB: order matters: match semicolon preferentially + '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'. + // string (optional semicolon) + "($semi_optional)". + ')/'; + + $this->_attrEntitiesRegex = + '/&(?:'. + // hex + '[#]x([a-fA-F0-9]+);?|'. + // dec + '[#]0*(\d+);?|'. + // string (mandatory semicolon) + // NB: order matters: match semicolon preferentially + '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'. + // string (optional semicolon) + // don't match if trailing is equals or alphanumeric (URL + // like) + "($semi_optional)(?![=;A-Za-z0-9])". + ')/'; + + } + + /** + * Substitute entities with the parsed equivalents. Use this on + * textual data in an HTML document (as opposed to attributes.) + * + * @param string $string String to have entities parsed. + * @return string Parsed string. + */ + public function substituteTextEntities($string) + { + return preg_replace_callback( + $this->_textEntitiesRegex, + array($this, 'entityCallback'), + $string + ); + } + + /** + * Substitute entities with the parsed equivalents. Use this on + * attribute contents in documents. + * + * @param string $string String to have entities parsed. + * @return string Parsed string. + */ + public function substituteAttrEntities($string) + { + return preg_replace_callback( + $this->_attrEntitiesRegex, + array($this, 'entityCallback'), + $string + ); + } + + /** + * Callback function for substituteNonSpecialEntities() that does the work. + * + * @param array $matches PCRE matches array, with 0 the entire match, and + * either index 1, 2 or 3 set with a hex value, dec value, + * or string (respectively). + * @return string Replacement string. + */ + + protected function entityCallback($matches) + { + $entity = $matches[0]; + $hex_part = @$matches[1]; + $dec_part = @$matches[2]; + $named_part = empty($matches[3]) ? @$matches[4] : $matches[3]; + if ($hex_part !== NULL && $hex_part !== "") { + return HTMLPurifier_Encoder::unichr(hexdec($hex_part)); + } elseif ($dec_part !== NULL && $dec_part !== "") { + return HTMLPurifier_Encoder::unichr((int) $dec_part); + } else { + if (!$this->_entity_lookup) { + $this->_entity_lookup = HTMLPurifier_EntityLookup::instance(); + } + if (isset($this->_entity_lookup->table[$named_part])) { + return $this->_entity_lookup->table[$named_part]; + } else { + // exact match didn't match anything, so test if + // any of the semicolon optional match the prefix. + // Test that this is an EXACT match is important to + // prevent infinite loop + if (!empty($matches[3])) { + return preg_replace_callback( + $this->_semiOptionalPrefixRegex, + array($this, 'entityCallback'), + $entity + ); + } + return $entity; + } + } + } + + // LEGACY CODE BELOW + + /** + * Callback regex string for parsing entities. + * @type string + */ + protected $_substituteEntitiesRegex = + '/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/'; + // 1. hex 2. dec 3. string (XML style) + + /** + * Decimal to parsed string conversion table for special entities. + * @type array + */ + protected $_special_dec2str = + array( + 34 => '"', + 38 => '&', + 39 => "'", + 60 => '<', + 62 => '>' + ); + + /** + * Stripped entity names to decimal conversion table for special entities. + * @type array + */ + protected $_special_ent2dec = + array( + 'quot' => 34, + 'amp' => 38, + 'lt' => 60, + 'gt' => 62 + ); + + /** + * Substitutes non-special entities with their parsed equivalents. Since + * running this whenever you have parsed character is t3h 5uck, we run + * it before everything else. + * + * @param string $string String to have non-special entities parsed. + * @return string Parsed string. + */ + public function substituteNonSpecialEntities($string) + { + // it will try to detect missing semicolons, but don't rely on it + return preg_replace_callback( + $this->_substituteEntitiesRegex, + array($this, 'nonSpecialEntityCallback'), + $string + ); + } + + /** + * Callback function for substituteNonSpecialEntities() that does the work. + * + * @param array $matches PCRE matches array, with 0 the entire match, and + * either index 1, 2 or 3 set with a hex value, dec value, + * or string (respectively). + * @return string Replacement string. + */ + + protected function nonSpecialEntityCallback($matches) + { + // replaces all but big five + $entity = $matches[0]; + $is_num = (@$matches[0][1] === '#'); + if ($is_num) { + $is_hex = (@$entity[2] === 'x'); + $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2]; + // abort for special characters + if (isset($this->_special_dec2str[$code])) { + return $entity; + } + return HTMLPurifier_Encoder::unichr($code); + } else { + if (isset($this->_special_ent2dec[$matches[3]])) { + return $entity; + } + if (!$this->_entity_lookup) { + $this->_entity_lookup = HTMLPurifier_EntityLookup::instance(); + } + if (isset($this->_entity_lookup->table[$matches[3]])) { + return $this->_entity_lookup->table[$matches[3]]; + } else { + return $entity; + } + } + } + + /** + * Substitutes only special entities with their parsed equivalents. + * + * @notice We try to avoid calling this function because otherwise, it + * would have to be called a lot (for every parsed section). + * + * @param string $string String to have non-special entities parsed. + * @return string Parsed string. + */ + public function substituteSpecialEntities($string) + { + return preg_replace_callback( + $this->_substituteEntitiesRegex, + array($this, 'specialEntityCallback'), + $string + ); + } + + /** + * Callback function for substituteSpecialEntities() that does the work. + * + * This callback has same syntax as nonSpecialEntityCallback(). + * + * @param array $matches PCRE-style matches array, with 0 the entire match, and + * either index 1, 2 or 3 set with a hex value, dec value, + * or string (respectively). + * @return string Replacement string. + */ + protected function specialEntityCallback($matches) + { + $entity = $matches[0]; + $is_num = (@$matches[0][1] === '#'); + if ($is_num) { + $is_hex = (@$entity[2] === 'x'); + $int = $is_hex ? hexdec($matches[1]) : (int) $matches[2]; + return isset($this->_special_dec2str[$int]) ? + $this->_special_dec2str[$int] : + $entity; + } else { + return isset($this->_special_ent2dec[$matches[3]]) ? + $this->_special_dec2str[$this->_special_ent2dec[$matches[3]]] : + $entity; + } + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php new file mode 100644 index 00000000..d47e3f2e --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php @@ -0,0 +1,244 @@ +locale =& $context->get('Locale'); + $this->context = $context; + $this->_current =& $this->_stacks[0]; + $this->errors =& $this->_stacks[0]; + } + + /** + * Sends an error message to the collector for later use + * @param int $severity Error severity, PHP error style (don't use E_USER_) + * @param string $msg Error message text + */ + public function send($severity, $msg) + { + $args = array(); + if (func_num_args() > 2) { + $args = func_get_args(); + array_shift($args); + unset($args[0]); + } + + $token = $this->context->get('CurrentToken', true); + $line = $token ? $token->line : $this->context->get('CurrentLine', true); + $col = $token ? $token->col : $this->context->get('CurrentCol', true); + $attr = $this->context->get('CurrentAttr', true); + + // perform special substitutions, also add custom parameters + $subst = array(); + if (!is_null($token)) { + $args['CurrentToken'] = $token; + } + if (!is_null($attr)) { + $subst['$CurrentAttr.Name'] = $attr; + if (isset($token->attr[$attr])) { + $subst['$CurrentAttr.Value'] = $token->attr[$attr]; + } + } + + if (empty($args)) { + $msg = $this->locale->getMessage($msg); + } else { + $msg = $this->locale->formatMessage($msg, $args); + } + + if (!empty($subst)) { + $msg = strtr($msg, $subst); + } + + // (numerically indexed) + $error = array( + self::LINENO => $line, + self::SEVERITY => $severity, + self::MESSAGE => $msg, + self::CHILDREN => array() + ); + $this->_current[] = $error; + + // NEW CODE BELOW ... + // Top-level errors are either: + // TOKEN type, if $value is set appropriately, or + // "syntax" type, if $value is null + $new_struct = new HTMLPurifier_ErrorStruct(); + $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN; + if ($token) { + $new_struct->value = clone $token; + } + if (is_int($line) && is_int($col)) { + if (isset($this->lines[$line][$col])) { + $struct = $this->lines[$line][$col]; + } else { + $struct = $this->lines[$line][$col] = $new_struct; + } + // These ksorts may present a performance problem + ksort($this->lines[$line], SORT_NUMERIC); + } else { + if (isset($this->lines[-1])) { + $struct = $this->lines[-1]; + } else { + $struct = $this->lines[-1] = $new_struct; + } + } + ksort($this->lines, SORT_NUMERIC); + + // Now, check if we need to operate on a lower structure + if (!empty($attr)) { + $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr); + if (!$struct->value) { + $struct->value = array($attr, 'PUT VALUE HERE'); + } + } + if (!empty($cssprop)) { + $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop); + if (!$struct->value) { + // if we tokenize CSS this might be a little more difficult to do + $struct->value = array($cssprop, 'PUT VALUE HERE'); + } + } + + // Ok, structs are all setup, now time to register the error + $struct->addError($severity, $msg); + } + + /** + * Retrieves raw error data for custom formatter to use + */ + public function getRaw() + { + return $this->errors; + } + + /** + * Default HTML formatting implementation for error messages + * @param HTMLPurifier_Config $config Configuration, vital for HTML output nature + * @param array $errors Errors array to display; used for recursion. + * @return string + */ + public function getHTMLFormatted($config, $errors = null) + { + $ret = array(); + + $this->generator = new HTMLPurifier_Generator($config, $this->context); + if ($errors === null) { + $errors = $this->errors; + } + + // 'At line' message needs to be removed + + // generation code for new structure goes here. It needs to be recursive. + foreach ($this->lines as $line => $col_array) { + if ($line == -1) { + continue; + } + foreach ($col_array as $col => $struct) { + $this->_renderStruct($ret, $struct, $line, $col); + } + } + if (isset($this->lines[-1])) { + $this->_renderStruct($ret, $this->lines[-1]); + } + + if (empty($errors)) { + return '

' . $this->locale->getMessage('ErrorCollector: No errors') . '

'; + } else { + return '
  • ' . implode('
  • ', $ret) . '
'; + } + + } + + private function _renderStruct(&$ret, $struct, $line = null, $col = null) + { + $stack = array($struct); + $context_stack = array(array()); + while ($current = array_pop($stack)) { + $context = array_pop($context_stack); + foreach ($current->errors as $error) { + list($severity, $msg) = $error; + $string = ''; + $string .= '
'; + // W3C uses an icon to indicate the severity of the error. + $error = $this->locale->getErrorName($severity); + $string .= "$error "; + if (!is_null($line) && !is_null($col)) { + $string .= "Line $line, Column $col: "; + } else { + $string .= 'End of Document: '; + } + $string .= '' . $this->generator->escape($msg) . ' '; + $string .= '
'; + // Here, have a marker for the character on the column appropriate. + // Be sure to clip extremely long lines. + //$string .= '
';
+                //$string .= '';
+                //$string .= '
'; + $ret[] = $string; + } + foreach ($current->children as $array) { + $context[] = $current; + $stack = array_merge($stack, array_reverse($array, true)); + for ($i = count($array); $i > 0; $i--) { + $context_stack[] = $context; + } + } + } + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php new file mode 100644 index 00000000..cf869d32 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php @@ -0,0 +1,74 @@ +children[$type][$id])) { + $this->children[$type][$id] = new HTMLPurifier_ErrorStruct(); + $this->children[$type][$id]->type = $type; + } + return $this->children[$type][$id]; + } + + /** + * @param int $severity + * @param string $message + */ + public function addError($severity, $message) + { + $this->errors[] = array($severity, $message); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php new file mode 100644 index 00000000..be85b4c5 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php @@ -0,0 +1,12 @@ +preFilter, + * 2->preFilter, 3->preFilter, purify, 3->postFilter, 2->postFilter, + * 1->postFilter. + * + * @note Methods are not declared abstract as it is perfectly legitimate + * for an implementation not to want anything to happen on a step + */ + +class HTMLPurifier_Filter +{ + + /** + * Name of the filter for identification purposes. + * @type string + */ + public $name; + + /** + * Pre-processor function, handles HTML before HTML Purifier + * @param string $html + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function preFilter($html, $config, $context) + { + return $html; + } + + /** + * Post-processor function, handles HTML after HTML Purifier + * @param string $html + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function postFilter($html, $config, $context) + { + return $html; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php new file mode 100644 index 00000000..66f70b0f --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php @@ -0,0 +1,341 @@ + blocks from input HTML, cleans them up + * using CSSTidy, and then places them in $purifier->context->get('StyleBlocks') + * so they can be used elsewhere in the document. + * + * @note + * See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php for + * sample usage. + * + * @note + * This filter can also be used on stylesheets not included in the + * document--something purists would probably prefer. Just directly + * call HTMLPurifier_Filter_ExtractStyleBlocks->cleanCSS() + */ +class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter +{ + /** + * @type string + */ + public $name = 'ExtractStyleBlocks'; + + /** + * @type array + */ + private $_styleMatches = array(); + + /** + * @type csstidy + */ + private $_tidy; + + /** + * @type HTMLPurifier_AttrDef_HTML_ID + */ + private $_id_attrdef; + + /** + * @type HTMLPurifier_AttrDef_CSS_Ident + */ + private $_class_attrdef; + + /** + * @type HTMLPurifier_AttrDef_Enum + */ + private $_enum_attrdef; + + public function __construct() + { + $this->_tidy = new csstidy(); + $this->_tidy->set_cfg('lowercase_s', false); + $this->_id_attrdef = new HTMLPurifier_AttrDef_HTML_ID(true); + $this->_class_attrdef = new HTMLPurifier_AttrDef_CSS_Ident(); + $this->_enum_attrdef = new HTMLPurifier_AttrDef_Enum( + array( + 'first-child', + 'link', + 'visited', + 'active', + 'hover', + 'focus' + ) + ); + } + + /** + * Save the contents of CSS blocks to style matches + * @param array $matches preg_replace style $matches array + */ + protected function styleCallback($matches) + { + $this->_styleMatches[] = $matches[1]; + } + + /** + * Removes inline + // we must not grab foo in a font-family prop). + if ($config->get('Filter.ExtractStyleBlocks.Escaping')) { + $css = str_replace( + array('<', '>', '&'), + array('\3C ', '\3E ', '\26 '), + $css + ); + } + return $css; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php new file mode 100644 index 00000000..276d8362 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php @@ -0,0 +1,65 @@ +]+>.+?' . + '(?:http:)?//www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s'; + $pre_replace = '\1'; + return preg_replace($pre_regex, $pre_replace, $html); + } + + /** + * @param string $html + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function postFilter($html, $config, $context) + { + $post_regex = '#((?:v|cp)/[A-Za-z0-9\-_=]+)#'; + return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html); + } + + /** + * @param $url + * @return string + */ + protected function armorUrl($url) + { + return str_replace('--', '--', $url); + } + + /** + * @param array $matches + * @return string + */ + protected function postFilterCallback($matches) + { + $url = $this->armorUrl($matches[1]); + return '' . + '' . + '' . + ''; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php new file mode 100644 index 00000000..eb56e2df --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php @@ -0,0 +1,286 @@ + tags. + * @type bool + */ + private $_scriptFix = false; + + /** + * Cache of HTMLDefinition during HTML output to determine whether or + * not attributes should be minimized. + * @type HTMLPurifier_HTMLDefinition + */ + private $_def; + + /** + * Cache of %Output.SortAttr. + * @type bool + */ + private $_sortAttr; + + /** + * Cache of %Output.FlashCompat. + * @type bool + */ + private $_flashCompat; + + /** + * Cache of %Output.FixInnerHTML. + * @type bool + */ + private $_innerHTMLFix; + + /** + * Stack for keeping track of object information when outputting IE + * compatibility code. + * @type array + */ + private $_flashStack = array(); + + /** + * Configuration for the generator + * @type HTMLPurifier_Config + */ + protected $config; + + /** + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + */ + public function __construct($config, $context) + { + $this->config = $config; + $this->_scriptFix = $config->get('Output.CommentScriptContents'); + $this->_innerHTMLFix = $config->get('Output.FixInnerHTML'); + $this->_sortAttr = $config->get('Output.SortAttr'); + $this->_flashCompat = $config->get('Output.FlashCompat'); + $this->_def = $config->getHTMLDefinition(); + $this->_xhtml = $this->_def->doctype->xml; + } + + /** + * Generates HTML from an array of tokens. + * @param HTMLPurifier_Token[] $tokens Array of HTMLPurifier_Token + * @return string Generated HTML + */ + public function generateFromTokens($tokens) + { + if (!$tokens) { + return ''; + } + + // Basic algorithm + $html = ''; + for ($i = 0, $size = count($tokens); $i < $size; $i++) { + if ($this->_scriptFix && $tokens[$i]->name === 'script' + && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) { + // script special case + // the contents of the script block must be ONE token + // for this to work. + $html .= $this->generateFromToken($tokens[$i++]); + $html .= $this->generateScriptFromToken($tokens[$i++]); + } + $html .= $this->generateFromToken($tokens[$i]); + } + + // Tidy cleanup + if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) { + $tidy = new Tidy; + $tidy->parseString( + $html, + array( + 'indent'=> true, + 'output-xhtml' => $this->_xhtml, + 'show-body-only' => true, + 'indent-spaces' => 2, + 'wrap' => 68, + ), + 'utf8' + ); + $tidy->cleanRepair(); + $html = (string) $tidy; // explicit cast necessary + } + + // Normalize newlines to system defined value + if ($this->config->get('Core.NormalizeNewlines')) { + $nl = $this->config->get('Output.Newline'); + if ($nl === null) { + $nl = PHP_EOL; + } + if ($nl !== "\n") { + $html = str_replace("\n", $nl, $html); + } + } + return $html; + } + + /** + * Generates HTML from a single token. + * @param HTMLPurifier_Token $token HTMLPurifier_Token object. + * @return string Generated HTML + */ + public function generateFromToken($token) + { + if (!$token instanceof HTMLPurifier_Token) { + trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING); + return ''; + + } elseif ($token instanceof HTMLPurifier_Token_Start) { + $attr = $this->generateAttributes($token->attr, $token->name); + if ($this->_flashCompat) { + if ($token->name == "object") { + $flash = new stdClass(); + $flash->attr = $token->attr; + $flash->param = array(); + $this->_flashStack[] = $flash; + } + } + return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_End) { + $_extra = ''; + if ($this->_flashCompat) { + if ($token->name == "object" && !empty($this->_flashStack)) { + // doesn't do anything for now + } + } + return $_extra . 'name . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_Empty) { + if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) { + $this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value']; + } + $attr = $this->generateAttributes($token->attr, $token->name); + return '<' . $token->name . ($attr ? ' ' : '') . $attr . + ( $this->_xhtml ? ' /': '' ) //
v.
+ . '>'; + + } elseif ($token instanceof HTMLPurifier_Token_Text) { + return $this->escape($token->data, ENT_NOQUOTES); + + } elseif ($token instanceof HTMLPurifier_Token_Comment) { + return ''; + } else { + return ''; + + } + } + + /** + * Special case processor for the contents of script tags + * @param HTMLPurifier_Token $token HTMLPurifier_Token object. + * @return string + * @warning This runs into problems if there's already a literal + * --> somewhere inside the script contents. + */ + public function generateScriptFromToken($token) + { + if (!$token instanceof HTMLPurifier_Token_Text) { + return $this->generateFromToken($token); + } + // Thanks + $data = preg_replace('#//\s*$#', '', $token->data); + return ''; + } + + /** + * Generates attribute declarations from attribute array. + * @note This does not include the leading or trailing space. + * @param array $assoc_array_of_attributes Attribute array + * @param string $element Name of element attributes are for, used to check + * attribute minimization. + * @return string Generated HTML fragment for insertion. + */ + public function generateAttributes($assoc_array_of_attributes, $element = '') + { + $html = ''; + if ($this->_sortAttr) { + ksort($assoc_array_of_attributes); + } + foreach ($assoc_array_of_attributes as $key => $value) { + if (!$this->_xhtml) { + // Remove namespaced attributes + if (strpos($key, ':') !== false) { + continue; + } + // Check if we should minimize the attribute: val="val" -> val + if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) { + $html .= $key . ' '; + continue; + } + } + // Workaround for Internet Explorer innerHTML bug. + // Essentially, Internet Explorer, when calculating + // innerHTML, omits quotes if there are no instances of + // angled brackets, quotes or spaces. However, when parsing + // HTML (for example, when you assign to innerHTML), it + // treats backticks as quotes. Thus, + // `` + // becomes + // `` + // becomes + // + // Fortunately, all we need to do is trigger an appropriate + // quoting style, which we do by adding an extra space. + // This also is consistent with the W3C spec, which states + // that user agents may ignore leading or trailing + // whitespace (in fact, most don't, at least for attributes + // like alt, but an extra space at the end is barely + // noticeable). Still, we have a configuration knob for + // this, since this transformation is not necesary if you + // don't process user input with innerHTML or you don't plan + // on supporting Internet Explorer. + if ($this->_innerHTMLFix) { + if (strpos($value, '`') !== false) { + // check if correct quoting style would not already be + // triggered + if (strcspn($value, '"\' <>') === strlen($value)) { + // protect! + $value .= ' '; + } + } + } + $html .= $key.'="'.$this->escape($value).'" '; + } + return rtrim($html); + } + + /** + * Escapes raw text data. + * @todo This really ought to be protected, but until we have a facility + * for properly generating HTML here w/o using tokens, it stays + * public. + * @param string $string String data to escape for HTML. + * @param int $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is + * permissible for non-attribute output. + * @return string escaped data. + */ + public function escape($string, $quote = null) + { + // Workaround for APC bug on Mac Leopard reported by sidepodcast + // http://htmlpurifier.org/phorum/read.php?3,4823,4846 + if ($quote === null) { + $quote = ENT_COMPAT; + } + return htmlspecialchars($string, $quote, 'UTF-8'); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php new file mode 100644 index 00000000..9b7b334d --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php @@ -0,0 +1,493 @@ +getAnonymousModule(); + if (!isset($module->info[$element_name])) { + $element = $module->addBlankElement($element_name); + } else { + $element = $module->info[$element_name]; + } + $element->attr[$attr_name] = $def; + } + + /** + * Adds a custom element to your HTML definition + * @see HTMLPurifier_HTMLModule::addElement() for detailed + * parameter and return value descriptions. + */ + public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) + { + $module = $this->getAnonymousModule(); + // assume that if the user is calling this, the element + // is safe. This may not be a good idea + $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes); + return $element; + } + + /** + * Adds a blank element to your HTML definition, for overriding + * existing behavior + * @param string $element_name + * @return HTMLPurifier_ElementDef + * @see HTMLPurifier_HTMLModule::addBlankElement() for detailed + * parameter and return value descriptions. + */ + public function addBlankElement($element_name) + { + $module = $this->getAnonymousModule(); + $element = $module->addBlankElement($element_name); + return $element; + } + + /** + * Retrieves a reference to the anonymous module, so you can + * bust out advanced features without having to make your own + * module. + * @return HTMLPurifier_HTMLModule + */ + public function getAnonymousModule() + { + if (!$this->_anonModule) { + $this->_anonModule = new HTMLPurifier_HTMLModule(); + $this->_anonModule->name = 'Anonymous'; + } + return $this->_anonModule; + } + + private $_anonModule = null; + + // PUBLIC BUT INTERNAL VARIABLES -------------------------------------- + + /** + * @type string + */ + public $type = 'HTML'; + + /** + * @type HTMLPurifier_HTMLModuleManager + */ + public $manager; + + /** + * Performs low-cost, preliminary initialization. + */ + public function __construct() + { + $this->manager = new HTMLPurifier_HTMLModuleManager(); + } + + /** + * @param HTMLPurifier_Config $config + */ + protected function doSetup($config) + { + $this->processModules($config); + $this->setupConfigStuff($config); + unset($this->manager); + + // cleanup some of the element definitions + foreach ($this->info as $k => $v) { + unset($this->info[$k]->content_model); + unset($this->info[$k]->content_model_type); + } + } + + /** + * Extract out the information from the manager + * @param HTMLPurifier_Config $config + */ + protected function processModules($config) + { + if ($this->_anonModule) { + // for user specific changes + // this is late-loaded so we don't have to deal with PHP4 + // reference wonky-ness + $this->manager->addModule($this->_anonModule); + unset($this->_anonModule); + } + + $this->manager->setup($config); + $this->doctype = $this->manager->doctype; + + foreach ($this->manager->modules as $module) { + foreach ($module->info_tag_transform as $k => $v) { + if ($v === false) { + unset($this->info_tag_transform[$k]); + } else { + $this->info_tag_transform[$k] = $v; + } + } + foreach ($module->info_attr_transform_pre as $k => $v) { + if ($v === false) { + unset($this->info_attr_transform_pre[$k]); + } else { + $this->info_attr_transform_pre[$k] = $v; + } + } + foreach ($module->info_attr_transform_post as $k => $v) { + if ($v === false) { + unset($this->info_attr_transform_post[$k]); + } else { + $this->info_attr_transform_post[$k] = $v; + } + } + foreach ($module->info_injector as $k => $v) { + if ($v === false) { + unset($this->info_injector[$k]); + } else { + $this->info_injector[$k] = $v; + } + } + } + $this->info = $this->manager->getElements(); + $this->info_content_sets = $this->manager->contentSets->lookup; + } + + /** + * Sets up stuff based on config. We need a better way of doing this. + * @param HTMLPurifier_Config $config + */ + protected function setupConfigStuff($config) + { + $block_wrapper = $config->get('HTML.BlockWrapper'); + if (isset($this->info_content_sets['Block'][$block_wrapper])) { + $this->info_block_wrapper = $block_wrapper; + } else { + trigger_error( + 'Cannot use non-block element as block wrapper', + E_USER_ERROR + ); + } + + $parent = $config->get('HTML.Parent'); + $def = $this->manager->getElement($parent, true); + if ($def) { + $this->info_parent = $parent; + $this->info_parent_def = $def; + } else { + trigger_error( + 'Cannot use unrecognized element as parent', + E_USER_ERROR + ); + $this->info_parent_def = $this->manager->getElement($this->info_parent, true); + } + + // support template text + $support = "(for information on implementing this, see the support forums) "; + + // setup allowed elements ----------------------------------------- + + $allowed_elements = $config->get('HTML.AllowedElements'); + $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early + + if (!is_array($allowed_elements) && !is_array($allowed_attributes)) { + $allowed = $config->get('HTML.Allowed'); + if (is_string($allowed)) { + list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed); + } + } + + if (is_array($allowed_elements)) { + foreach ($this->info as $name => $d) { + if (!isset($allowed_elements[$name])) { + unset($this->info[$name]); + } + unset($allowed_elements[$name]); + } + // emit errors + foreach ($allowed_elements as $element => $d) { + $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful! + trigger_error("Element '$element' is not supported $support", E_USER_WARNING); + } + } + + // setup allowed attributes --------------------------------------- + + $allowed_attributes_mutable = $allowed_attributes; // by copy! + if (is_array($allowed_attributes)) { + // This actually doesn't do anything, since we went away from + // global attributes. It's possible that userland code uses + // it, but HTMLModuleManager doesn't! + foreach ($this->info_global_attr as $attr => $x) { + $keys = array($attr, "*@$attr", "*.$attr"); + $delete = true; + foreach ($keys as $key) { + if ($delete && isset($allowed_attributes[$key])) { + $delete = false; + } + if (isset($allowed_attributes_mutable[$key])) { + unset($allowed_attributes_mutable[$key]); + } + } + if ($delete) { + unset($this->info_global_attr[$attr]); + } + } + + foreach ($this->info as $tag => $info) { + foreach ($info->attr as $attr => $x) { + $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr"); + $delete = true; + foreach ($keys as $key) { + if ($delete && isset($allowed_attributes[$key])) { + $delete = false; + } + if (isset($allowed_attributes_mutable[$key])) { + unset($allowed_attributes_mutable[$key]); + } + } + if ($delete) { + if ($this->info[$tag]->attr[$attr]->required) { + trigger_error( + "Required attribute '$attr' in element '$tag' " . + "was not allowed, which means '$tag' will not be allowed either", + E_USER_WARNING + ); + } + unset($this->info[$tag]->attr[$attr]); + } + } + } + // emit errors + foreach ($allowed_attributes_mutable as $elattr => $d) { + $bits = preg_split('/[.@]/', $elattr, 2); + $c = count($bits); + switch ($c) { + case 2: + if ($bits[0] !== '*') { + $element = htmlspecialchars($bits[0]); + $attribute = htmlspecialchars($bits[1]); + if (!isset($this->info[$element])) { + trigger_error( + "Cannot allow attribute '$attribute' if element " . + "'$element' is not allowed/supported $support" + ); + } else { + trigger_error( + "Attribute '$attribute' in element '$element' not supported $support", + E_USER_WARNING + ); + } + break; + } + // otherwise fall through + case 1: + $attribute = htmlspecialchars($bits[0]); + trigger_error( + "Global attribute '$attribute' is not ". + "supported in any elements $support", + E_USER_WARNING + ); + break; + } + } + } + + // setup forbidden elements --------------------------------------- + + $forbidden_elements = $config->get('HTML.ForbiddenElements'); + $forbidden_attributes = $config->get('HTML.ForbiddenAttributes'); + + foreach ($this->info as $tag => $info) { + if (isset($forbidden_elements[$tag])) { + unset($this->info[$tag]); + continue; + } + foreach ($info->attr as $attr => $x) { + if (isset($forbidden_attributes["$tag@$attr"]) || + isset($forbidden_attributes["*@$attr"]) || + isset($forbidden_attributes[$attr]) + ) { + unset($this->info[$tag]->attr[$attr]); + continue; + } elseif (isset($forbidden_attributes["$tag.$attr"])) { // this segment might get removed eventually + // $tag.$attr are not user supplied, so no worries! + trigger_error( + "Error with $tag.$attr: tag.attr syntax not supported for " . + "HTML.ForbiddenAttributes; use tag@attr instead", + E_USER_WARNING + ); + } + } + } + foreach ($forbidden_attributes as $key => $v) { + if (strlen($key) < 2) { + continue; + } + if ($key[0] != '*') { + continue; + } + if ($key[1] == '.') { + trigger_error( + "Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", + E_USER_WARNING + ); + } + } + + // setup injectors ----------------------------------------------------- + foreach ($this->info_injector as $i => $injector) { + if ($injector->checkNeeded($config) !== false) { + // remove injector that does not have it's required + // elements/attributes present, and is thus not needed. + unset($this->info_injector[$i]); + } + } + } + + /** + * Parses a TinyMCE-flavored Allowed Elements and Attributes list into + * separate lists for processing. Format is element[attr1|attr2],element2... + * @warning Although it's largely drawn from TinyMCE's implementation, + * it is different, and you'll probably have to modify your lists + * @param array $list String list to parse + * @return array + * @todo Give this its own class, probably static interface + */ + public function parseTinyMCEAllowedList($list) + { + $list = str_replace(array(' ', "\t"), '', $list); + + $elements = array(); + $attributes = array(); + + $chunks = preg_split('/(,|[\n\r]+)/', $list); + foreach ($chunks as $chunk) { + if (empty($chunk)) { + continue; + } + // remove TinyMCE element control characters + if (!strpos($chunk, '[')) { + $element = $chunk; + $attr = false; + } else { + list($element, $attr) = explode('[', $chunk); + } + if ($element !== '*') { + $elements[$element] = true; + } + if (!$attr) { + continue; + } + $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ] + $attr = explode('|', $attr); + foreach ($attr as $key) { + $attributes["$element.$key"] = true; + } + } + return array($elements, $attributes); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php new file mode 100644 index 00000000..bb3a9230 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php @@ -0,0 +1,284 @@ +info, since the object's data is only info, + * with extra behavior associated with it. + * @type array + */ + public $attr_collections = array(); + + /** + * Associative array of deprecated tag name to HTMLPurifier_TagTransform. + * @type array + */ + public $info_tag_transform = array(); + + /** + * List of HTMLPurifier_AttrTransform to be performed before validation. + * @type array + */ + public $info_attr_transform_pre = array(); + + /** + * List of HTMLPurifier_AttrTransform to be performed after validation. + * @type array + */ + public $info_attr_transform_post = array(); + + /** + * List of HTMLPurifier_Injector to be performed during well-formedness fixing. + * An injector will only be invoked if all of it's pre-requisites are met; + * if an injector fails setup, there will be no error; it will simply be + * silently disabled. + * @type array + */ + public $info_injector = array(); + + /** + * Boolean flag that indicates whether or not getChildDef is implemented. + * For optimization reasons: may save a call to a function. Be sure + * to set it if you do implement getChildDef(), otherwise it will have + * no effect! + * @type bool + */ + public $defines_child_def = false; + + /** + * Boolean flag whether or not this module is safe. If it is not safe, all + * of its members are unsafe. Modules are safe by default (this might be + * slightly dangerous, but it doesn't make much sense to force HTML Purifier, + * which is based off of safe HTML, to explicitly say, "This is safe," even + * though there are modules which are "unsafe") + * + * @type bool + * @note Previously, safety could be applied at an element level granularity. + * We've removed this ability, so in order to add "unsafe" elements + * or attributes, a dedicated module with this property set to false + * must be used. + */ + public $safe = true; + + /** + * Retrieves a proper HTMLPurifier_ChildDef subclass based on + * content_model and content_model_type member variables of + * the HTMLPurifier_ElementDef class. There is a similar function + * in HTMLPurifier_HTMLDefinition. + * @param HTMLPurifier_ElementDef $def + * @return HTMLPurifier_ChildDef subclass + */ + public function getChildDef($def) + { + return false; + } + + // -- Convenience ----------------------------------------------------- + + /** + * Convenience function that sets up a new element + * @param string $element Name of element to add + * @param string|bool $type What content set should element be registered to? + * Set as false to skip this step. + * @param string $contents Allowed children in form of: + * "$content_model_type: $content_model" + * @param array $attr_includes What attribute collections to register to + * element? + * @param array $attr What unique attributes does the element define? + * @see HTMLPurifier_ElementDef:: for in-depth descriptions of these parameters. + * @return HTMLPurifier_ElementDef Created element definition object, so you + * can set advanced parameters + */ + public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array()) + { + $this->elements[] = $element; + // parse content_model + list($content_model_type, $content_model) = $this->parseContents($contents); + // merge in attribute inclusions + $this->mergeInAttrIncludes($attr, $attr_includes); + // add element to content sets + if ($type) { + $this->addElementToContentSet($element, $type); + } + // create element + $this->info[$element] = HTMLPurifier_ElementDef::create( + $content_model, + $content_model_type, + $attr + ); + // literal object $contents means direct child manipulation + if (!is_string($contents)) { + $this->info[$element]->child = $contents; + } + return $this->info[$element]; + } + + /** + * Convenience function that creates a totally blank, non-standalone + * element. + * @param string $element Name of element to create + * @return HTMLPurifier_ElementDef Created element + */ + public function addBlankElement($element) + { + if (!isset($this->info[$element])) { + $this->elements[] = $element; + $this->info[$element] = new HTMLPurifier_ElementDef(); + $this->info[$element]->standalone = false; + } else { + trigger_error("Definition for $element already exists in module, cannot redefine"); + } + return $this->info[$element]; + } + + /** + * Convenience function that registers an element to a content set + * @param string $element Element to register + * @param string $type Name content set (warning: case sensitive, usually upper-case + * first letter) + */ + public function addElementToContentSet($element, $type) + { + if (!isset($this->content_sets[$type])) { + $this->content_sets[$type] = ''; + } else { + $this->content_sets[$type] .= ' | '; + } + $this->content_sets[$type] .= $element; + } + + /** + * Convenience function that transforms single-string contents + * into separate content model and content model type + * @param string $contents Allowed children in form of: + * "$content_model_type: $content_model" + * @return array + * @note If contents is an object, an array of two nulls will be + * returned, and the callee needs to take the original $contents + * and use it directly. + */ + public function parseContents($contents) + { + if (!is_string($contents)) { + return array(null, null); + } // defer + switch ($contents) { + // check for shorthand content model forms + case 'Empty': + return array('empty', ''); + case 'Inline': + return array('optional', 'Inline | #PCDATA'); + case 'Flow': + return array('optional', 'Flow | #PCDATA'); + } + list($content_model_type, $content_model) = explode(':', $contents); + $content_model_type = strtolower(trim($content_model_type)); + $content_model = trim($content_model); + return array($content_model_type, $content_model); + } + + /** + * Convenience function that merges a list of attribute includes into + * an attribute array. + * @param array $attr Reference to attr array to modify + * @param array $attr_includes Array of includes / string include to merge in + */ + public function mergeInAttrIncludes(&$attr, $attr_includes) + { + if (!is_array($attr_includes)) { + if (empty($attr_includes)) { + $attr_includes = array(); + } else { + $attr_includes = array($attr_includes); + } + } + $attr[0] = $attr_includes; + } + + /** + * Convenience function that generates a lookup table with boolean + * true as value. + * @param string $list List of values to turn into a lookup + * @note You can also pass an arbitrary number of arguments in + * place of the regular argument + * @return array array equivalent of list + */ + public function makeLookup($list) + { + if (is_string($list)) { + $list = func_get_args(); + } + $ret = array(); + foreach ($list as $value) { + if (is_null($value)) { + continue; + } + $ret[$value] = true; + } + return $ret; + } + + /** + * Lazy load construction of the module after determining whether + * or not it's needed, and also when a finalized configuration object + * is available. + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php new file mode 100644 index 00000000..1e67c790 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php @@ -0,0 +1,44 @@ + array('dir' => false) + ); + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + $bdo = $this->addElement( + 'bdo', + 'Inline', + 'Inline', + array('Core', 'Lang'), + array( + 'dir' => 'Enum#ltr,rtl', // required + // The Abstract Module specification has the attribute + // inclusions wrong for bdo: bdo allows Lang + ) + ); + $bdo->attr_transform_post[] = new HTMLPurifier_AttrTransform_BdoDir(); + + $this->attr_collections['I18N']['dir'] = 'Enum#ltr,rtl'; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php new file mode 100644 index 00000000..a96ab1be --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php @@ -0,0 +1,31 @@ + array( + 0 => array('Style'), + // 'xml:space' => false, + 'class' => 'Class', + 'id' => 'ID', + 'title' => 'CDATA', + ), + 'Lang' => array(), + 'I18N' => array( + 0 => array('Lang'), // proprietary, for xml:lang/lang + ), + 'Common' => array( + 0 => array('Core', 'I18N') + ) + ); +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php new file mode 100644 index 00000000..a9042a35 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php @@ -0,0 +1,55 @@ + 'URI', + // 'datetime' => 'Datetime', // not implemented + ); + $this->addElement('del', 'Inline', $contents, 'Common', $attr); + $this->addElement('ins', 'Inline', $contents, 'Common', $attr); + } + + // HTML 4.01 specifies that ins/del must not contain block + // elements when used in an inline context, chameleon is + // a complicated workaround to acheive this effect + + // Inline context ! Block context (exclamation mark is + // separator, see getChildDef for parsing) + + /** + * @type bool + */ + public $defines_child_def = true; + + /** + * @param HTMLPurifier_ElementDef $def + * @return HTMLPurifier_ChildDef_Chameleon + */ + public function getChildDef($def) + { + if ($def->content_model_type != 'chameleon') { + return false; + } + $value = explode('!', $def->content_model); + return new HTMLPurifier_ChildDef_Chameleon($value[0], $value[1]); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php new file mode 100644 index 00000000..6f7ddbc0 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php @@ -0,0 +1,190 @@ + 'Form', + 'Inline' => 'Formctrl', + ); + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + $form = $this->addElement( + 'form', + 'Form', + 'Required: Heading | List | Block | fieldset', + 'Common', + array( + 'accept' => 'ContentTypes', + 'accept-charset' => 'Charsets', + 'action*' => 'URI', + 'method' => 'Enum#get,post', + // really ContentType, but these two are the only ones used today + 'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data', + ) + ); + $form->excludes = array('form' => true); + + $input = $this->addElement( + 'input', + 'Formctrl', + 'Empty', + 'Common', + array( + 'accept' => 'ContentTypes', + 'accesskey' => 'Character', + 'alt' => 'Text', + 'checked' => 'Bool#checked', + 'disabled' => 'Bool#disabled', + 'maxlength' => 'Number', + 'name' => 'CDATA', + 'readonly' => 'Bool#readonly', + 'size' => 'Number', + 'src' => 'URI#embedded', + 'tabindex' => 'Number', + 'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image', + 'value' => 'CDATA', + ) + ); + $input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input(); + + $this->addElement( + 'select', + 'Formctrl', + 'Required: optgroup | option', + 'Common', + array( + 'disabled' => 'Bool#disabled', + 'multiple' => 'Bool#multiple', + 'name' => 'CDATA', + 'size' => 'Number', + 'tabindex' => 'Number', + ) + ); + + $this->addElement( + 'option', + false, + 'Optional: #PCDATA', + 'Common', + array( + 'disabled' => 'Bool#disabled', + 'label' => 'Text', + 'selected' => 'Bool#selected', + 'value' => 'CDATA', + ) + ); + // It's illegal for there to be more than one selected, but not + // be multiple. Also, no selected means undefined behavior. This might + // be difficult to implement; perhaps an injector, or a context variable. + + $textarea = $this->addElement( + 'textarea', + 'Formctrl', + 'Optional: #PCDATA', + 'Common', + array( + 'accesskey' => 'Character', + 'cols*' => 'Number', + 'disabled' => 'Bool#disabled', + 'name' => 'CDATA', + 'readonly' => 'Bool#readonly', + 'rows*' => 'Number', + 'tabindex' => 'Number', + ) + ); + $textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea(); + + $button = $this->addElement( + 'button', + 'Formctrl', + 'Optional: #PCDATA | Heading | List | Block | Inline', + 'Common', + array( + 'accesskey' => 'Character', + 'disabled' => 'Bool#disabled', + 'name' => 'CDATA', + 'tabindex' => 'Number', + 'type' => 'Enum#button,submit,reset', + 'value' => 'CDATA', + ) + ); + + // For exclusions, ideally we'd specify content sets, not literal elements + $button->excludes = $this->makeLookup( + 'form', + 'fieldset', // Form + 'input', + 'select', + 'textarea', + 'label', + 'button', // Formctrl + 'a', // as per HTML 4.01 spec, this is omitted by modularization + 'isindex', + 'iframe' // legacy items + ); + + // Extra exclusion: img usemap="" is not permitted within this element. + // We'll omit this for now, since we don't have any good way of + // indicating it yet. + + // This is HIGHLY user-unfriendly; we need a custom child-def for this + $this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common'); + + $label = $this->addElement( + 'label', + 'Formctrl', + 'Optional: #PCDATA | Inline', + 'Common', + array( + 'accesskey' => 'Character', + // 'for' => 'IDREF', // IDREF not implemented, cannot allow + ) + ); + $label->excludes = array('label' => true); + + $this->addElement( + 'legend', + false, + 'Optional: #PCDATA | Inline', + 'Common', + array( + 'accesskey' => 'Character', + ) + ); + + $this->addElement( + 'optgroup', + false, + 'Required: option', + 'Common', + array( + 'disabled' => 'Bool#disabled', + 'label*' => 'Text', + ) + ); + // Don't forget an injector for . This one's a little complex + // because it maps to multiple elements. + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php new file mode 100644 index 00000000..72d7a31e --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php @@ -0,0 +1,40 @@ +addElement( + 'a', + 'Inline', + 'Inline', + 'Common', + array( + // 'accesskey' => 'Character', + // 'charset' => 'Charset', + 'href' => 'URI', + // 'hreflang' => 'LanguageCode', + 'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'), + 'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'), + // 'tabindex' => 'Number', + // 'type' => 'ContentType', + ) + ); + $a->formatting = true; + $a->excludes = array('a' => true); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php new file mode 100644 index 00000000..f7e7c91c --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php @@ -0,0 +1,51 @@ +get('HTML.SafeIframe')) { + $this->safe = true; + } + $this->addElement( + 'iframe', + 'Inline', + 'Flow', + 'Common', + array( + 'src' => 'URI#embedded', + 'width' => 'Length', + 'height' => 'Length', + 'name' => 'ID', + 'scrolling' => 'Enum#yes,no,auto', + 'frameborder' => 'Enum#0,1', + 'longdesc' => 'URI', + 'marginheight' => 'Pixels', + 'marginwidth' => 'Pixels', + ) + ); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php new file mode 100644 index 00000000..0f5fdb3b --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php @@ -0,0 +1,49 @@ +get('HTML.MaxImgLength'); + $img = $this->addElement( + 'img', + 'Inline', + 'Empty', + 'Common', + array( + 'alt*' => 'Text', + // According to the spec, it's Length, but percents can + // be abused, so we allow only Pixels. + 'height' => 'Pixels#' . $max, + 'width' => 'Pixels#' . $max, + 'longdesc' => 'URI', + 'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded + ) + ); + if ($max === null || $config->get('HTML.Trusted')) { + $img->attr['height'] = + $img->attr['width'] = 'Length'; + } + + // kind of strange, but splitting things up would be inefficient + $img->attr_transform_pre[] = + $img->attr_transform_post[] = + new HTMLPurifier_AttrTransform_ImgRequired(); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php new file mode 100644 index 00000000..86b52995 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php @@ -0,0 +1,186 @@ +addElement( + 'basefont', + 'Inline', + 'Empty', + null, + array( + 'color' => 'Color', + 'face' => 'Text', // extremely broad, we should + 'size' => 'Text', // tighten it + 'id' => 'ID' + ) + ); + $this->addElement('center', 'Block', 'Flow', 'Common'); + $this->addElement( + 'dir', + 'Block', + 'Required: li', + 'Common', + array( + 'compact' => 'Bool#compact' + ) + ); + $this->addElement( + 'font', + 'Inline', + 'Inline', + array('Core', 'I18N'), + array( + 'color' => 'Color', + 'face' => 'Text', // extremely broad, we should + 'size' => 'Text', // tighten it + ) + ); + $this->addElement( + 'menu', + 'Block', + 'Required: li', + 'Common', + array( + 'compact' => 'Bool#compact' + ) + ); + + $s = $this->addElement('s', 'Inline', 'Inline', 'Common'); + $s->formatting = true; + + $strike = $this->addElement('strike', 'Inline', 'Inline', 'Common'); + $strike->formatting = true; + + $u = $this->addElement('u', 'Inline', 'Inline', 'Common'); + $u->formatting = true; + + // setup modifications to old elements + + $align = 'Enum#left,right,center,justify'; + + $address = $this->addBlankElement('address'); + $address->content_model = 'Inline | #PCDATA | p'; + $address->content_model_type = 'optional'; + $address->child = false; + + $blockquote = $this->addBlankElement('blockquote'); + $blockquote->content_model = 'Flow | #PCDATA'; + $blockquote->content_model_type = 'optional'; + $blockquote->child = false; + + $br = $this->addBlankElement('br'); + $br->attr['clear'] = 'Enum#left,all,right,none'; + + $caption = $this->addBlankElement('caption'); + $caption->attr['align'] = 'Enum#top,bottom,left,right'; + + $div = $this->addBlankElement('div'); + $div->attr['align'] = $align; + + $dl = $this->addBlankElement('dl'); + $dl->attr['compact'] = 'Bool#compact'; + + for ($i = 1; $i <= 6; $i++) { + $h = $this->addBlankElement("h$i"); + $h->attr['align'] = $align; + } + + $hr = $this->addBlankElement('hr'); + $hr->attr['align'] = $align; + $hr->attr['noshade'] = 'Bool#noshade'; + $hr->attr['size'] = 'Pixels'; + $hr->attr['width'] = 'Length'; + + $img = $this->addBlankElement('img'); + $img->attr['align'] = 'IAlign'; + $img->attr['border'] = 'Pixels'; + $img->attr['hspace'] = 'Pixels'; + $img->attr['vspace'] = 'Pixels'; + + // figure out this integer business + + $li = $this->addBlankElement('li'); + $li->attr['value'] = new HTMLPurifier_AttrDef_Integer(); + $li->attr['type'] = 'Enum#s:1,i,I,a,A,disc,square,circle'; + + $ol = $this->addBlankElement('ol'); + $ol->attr['compact'] = 'Bool#compact'; + $ol->attr['start'] = new HTMLPurifier_AttrDef_Integer(); + $ol->attr['type'] = 'Enum#s:1,i,I,a,A'; + + $p = $this->addBlankElement('p'); + $p->attr['align'] = $align; + + $pre = $this->addBlankElement('pre'); + $pre->attr['width'] = 'Number'; + + // script omitted + + $table = $this->addBlankElement('table'); + $table->attr['align'] = 'Enum#left,center,right'; + $table->attr['bgcolor'] = 'Color'; + + $tr = $this->addBlankElement('tr'); + $tr->attr['bgcolor'] = 'Color'; + + $th = $this->addBlankElement('th'); + $th->attr['bgcolor'] = 'Color'; + $th->attr['height'] = 'Length'; + $th->attr['nowrap'] = 'Bool#nowrap'; + $th->attr['width'] = 'Length'; + + $td = $this->addBlankElement('td'); + $td->attr['bgcolor'] = 'Color'; + $td->attr['height'] = 'Length'; + $td->attr['nowrap'] = 'Bool#nowrap'; + $td->attr['width'] = 'Length'; + + $ul = $this->addBlankElement('ul'); + $ul->attr['compact'] = 'Bool#compact'; + $ul->attr['type'] = 'Enum#square,disc,circle'; + + // "safe" modifications to "unsafe" elements + // WARNING: If you want to add support for an unsafe, legacy + // attribute, make a new TrustedLegacy module with the trusted + // bit set appropriately + + $form = $this->addBlankElement('form'); + $form->content_model = 'Flow | #PCDATA'; + $form->content_model_type = 'optional'; + $form->attr['target'] = 'FrameTarget'; + + $input = $this->addBlankElement('input'); + $input->attr['align'] = 'IAlign'; + + $legend = $this->addBlankElement('legend'); + $legend->attr['align'] = 'LAlign'; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php new file mode 100644 index 00000000..7a20ff70 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php @@ -0,0 +1,51 @@ + 'List'); + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + $ol = $this->addElement('ol', 'List', new HTMLPurifier_ChildDef_List(), 'Common'); + $ul = $this->addElement('ul', 'List', new HTMLPurifier_ChildDef_List(), 'Common'); + // XXX The wrap attribute is handled by MakeWellFormed. This is all + // quite unsatisfactory, because we generated this + // *specifically* for lists, and now a big chunk of the handling + // is done properly by the List ChildDef. So actually, we just + // want enough information to make autoclosing work properly, + // and then hand off the tricky stuff to the ChildDef. + $ol->wrap = 'li'; + $ul->wrap = 'li'; + $this->addElement('dl', 'List', 'Required: dt | dd', 'Common'); + + $this->addElement('li', false, 'Flow', 'Common'); + + $this->addElement('dd', false, 'Flow', 'Common'); + $this->addElement('dt', false, 'Inline', 'Common'); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php new file mode 100644 index 00000000..60c05451 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php @@ -0,0 +1,26 @@ +addBlankElement($name); + $element->attr['name'] = 'CDATA'; + if (!$config->get('HTML.Attr.Name.UseCDATA')) { + $element->attr_transform_post[] = new HTMLPurifier_AttrTransform_NameSync(); + } + } + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php new file mode 100644 index 00000000..dc9410a8 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php @@ -0,0 +1,25 @@ +addBlankElement('a'); + $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_Nofollow(); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php new file mode 100644 index 00000000..da722253 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php @@ -0,0 +1,20 @@ + array( + 'lang' => 'LanguageCode', + ) + ); +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php new file mode 100644 index 00000000..2f9efc5c --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php @@ -0,0 +1,62 @@ + to cater to legacy browsers: this + * module does not allow this sort of behavior + */ +class HTMLPurifier_HTMLModule_Object extends HTMLPurifier_HTMLModule +{ + /** + * @type string + */ + public $name = 'Object'; + + /** + * @type bool + */ + public $safe = false; + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + $this->addElement( + 'object', + 'Inline', + 'Optional: #PCDATA | Flow | param', + 'Common', + array( + 'archive' => 'URI', + 'classid' => 'URI', + 'codebase' => 'URI', + 'codetype' => 'Text', + 'data' => 'URI', + 'declare' => 'Bool#declare', + 'height' => 'Length', + 'name' => 'CDATA', + 'standby' => 'Text', + 'tabindex' => 'Number', + 'type' => 'ContentType', + 'width' => 'Length' + ) + ); + + $this->addElement( + 'param', + false, + 'Empty', + null, + array( + 'id' => 'ID', + 'name*' => 'Text', + 'type' => 'Text', + 'value' => 'Text', + 'valuetype' => 'Enum#data,ref,object' + ) + ); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php new file mode 100644 index 00000000..6458ce9d --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php @@ -0,0 +1,42 @@ +addElement('hr', 'Block', 'Empty', 'Common'); + $this->addElement('sub', 'Inline', 'Inline', 'Common'); + $this->addElement('sup', 'Inline', 'Inline', 'Common'); + $b = $this->addElement('b', 'Inline', 'Inline', 'Common'); + $b->formatting = true; + $big = $this->addElement('big', 'Inline', 'Inline', 'Common'); + $big->formatting = true; + $i = $this->addElement('i', 'Inline', 'Inline', 'Common'); + $i->formatting = true; + $small = $this->addElement('small', 'Inline', 'Inline', 'Common'); + $small->formatting = true; + $tt = $this->addElement('tt', 'Inline', 'Inline', 'Common'); + $tt->formatting = true; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php new file mode 100644 index 00000000..5ee3c8e6 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php @@ -0,0 +1,40 @@ +addElement( + 'marquee', + 'Inline', + 'Flow', + 'Common', + array( + 'direction' => 'Enum#left,right,up,down', + 'behavior' => 'Enum#alternate', + 'width' => 'Length', + 'height' => 'Length', + 'scrolldelay' => 'Number', + 'scrollamount' => 'Number', + 'loop' => 'Number', + 'bgcolor' => 'Color', + 'hspace' => 'Pixels', + 'vspace' => 'Pixels', + ) + ); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php new file mode 100644 index 00000000..a0d48924 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php @@ -0,0 +1,36 @@ +addElement( + 'ruby', + 'Inline', + 'Custom: ((rb, (rt | (rp, rt, rp))) | (rbc, rtc, rtc?))', + 'Common' + ); + $this->addElement('rbc', false, 'Required: rb', 'Common'); + $this->addElement('rtc', false, 'Required: rt', 'Common'); + $rb = $this->addElement('rb', false, 'Inline', 'Common'); + $rb->excludes = array('ruby' => true); + $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number')); + $rt->excludes = array('ruby' => true); + $this->addElement('rp', false, 'Optional: #PCDATA', 'Common'); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php new file mode 100644 index 00000000..04e6689e --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php @@ -0,0 +1,40 @@ +get('HTML.MaxImgLength'); + $embed = $this->addElement( + 'embed', + 'Inline', + 'Empty', + 'Common', + array( + 'src*' => 'URI#embedded', + 'type' => 'Enum#application/x-shockwave-flash', + 'width' => 'Pixels#' . $max, + 'height' => 'Pixels#' . $max, + 'allowscriptaccess' => 'Enum#never', + 'allownetworking' => 'Enum#internal', + 'flashvars' => 'Text', + 'wmode' => 'Enum#window,transparent,opaque', + 'name' => 'ID', + ) + ); + $embed->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeEmbed(); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php new file mode 100644 index 00000000..1297f80a --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php @@ -0,0 +1,62 @@ +get('HTML.MaxImgLength'); + $object = $this->addElement( + 'object', + 'Inline', + 'Optional: param | Flow | #PCDATA', + 'Common', + array( + // While technically not required by the spec, we're forcing + // it to this value. + 'type' => 'Enum#application/x-shockwave-flash', + 'width' => 'Pixels#' . $max, + 'height' => 'Pixels#' . $max, + 'data' => 'URI#embedded', + 'codebase' => new HTMLPurifier_AttrDef_Enum( + array( + 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0' + ) + ), + ) + ); + $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject(); + + $param = $this->addElement( + 'param', + false, + 'Empty', + false, + array( + 'id' => 'ID', + 'name*' => 'Text', + 'value' => 'Text' + ) + ); + $param->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeParam(); + $this->info_injector[] = 'SafeObject'; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php new file mode 100644 index 00000000..0330cd97 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php @@ -0,0 +1,40 @@ +get('HTML.SafeScripting'); + $script = $this->addElement( + 'script', + 'Inline', + 'Empty', + null, + array( + // While technically not required by the spec, we're forcing + // it to this value. + 'type' => 'Enum#text/javascript', + 'src*' => new HTMLPurifier_AttrDef_Enum(array_keys($allowed)) + ) + ); + $script->attr_transform_pre[] = + $script->attr_transform_post[] = new HTMLPurifier_AttrTransform_ScriptRequired(); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php new file mode 100644 index 00000000..8b28a7b7 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php @@ -0,0 +1,73 @@ + 'script | noscript', 'Inline' => 'script | noscript'); + + /** + * @type bool + */ + public $safe = false; + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + // TODO: create custom child-definition for noscript that + // auto-wraps stray #PCDATA in a similar manner to + // blockquote's custom definition (we would use it but + // blockquote's contents are optional while noscript's contents + // are required) + + // TODO: convert this to new syntax, main problem is getting + // both content sets working + + // In theory, this could be safe, but I don't see any reason to + // allow it. + $this->info['noscript'] = new HTMLPurifier_ElementDef(); + $this->info['noscript']->attr = array(0 => array('Common')); + $this->info['noscript']->content_model = 'Heading | List | Block'; + $this->info['noscript']->content_model_type = 'required'; + + $this->info['script'] = new HTMLPurifier_ElementDef(); + $this->info['script']->attr = array( + 'defer' => new HTMLPurifier_AttrDef_Enum(array('defer')), + 'src' => new HTMLPurifier_AttrDef_URI(true), + 'type' => new HTMLPurifier_AttrDef_Enum(array('text/javascript')) + ); + $this->info['script']->content_model = '#PCDATA'; + $this->info['script']->content_model_type = 'optional'; + $this->info['script']->attr_transform_pre[] = + $this->info['script']->attr_transform_post[] = + new HTMLPurifier_AttrTransform_ScriptRequired(); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php new file mode 100644 index 00000000..497b832a --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php @@ -0,0 +1,33 @@ + array('style' => false), // see constructor + 'Core' => array(0 => array('Style')) + ); + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + $this->attr_collections['Style']['style'] = new HTMLPurifier_AttrDef_CSS(); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php new file mode 100644 index 00000000..8a0b3b46 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php @@ -0,0 +1,75 @@ +addElement('caption', false, 'Inline', 'Common'); + + $this->addElement( + 'table', + 'Block', + new HTMLPurifier_ChildDef_Table(), + 'Common', + array( + 'border' => 'Pixels', + 'cellpadding' => 'Length', + 'cellspacing' => 'Length', + 'frame' => 'Enum#void,above,below,hsides,lhs,rhs,vsides,box,border', + 'rules' => 'Enum#none,groups,rows,cols,all', + 'summary' => 'Text', + 'width' => 'Length' + ) + ); + + // common attributes + $cell_align = array( + 'align' => 'Enum#left,center,right,justify,char', + 'charoff' => 'Length', + 'valign' => 'Enum#top,middle,bottom,baseline', + ); + + $cell_t = array_merge( + array( + 'abbr' => 'Text', + 'colspan' => 'Number', + 'rowspan' => 'Number', + // Apparently, as of HTML5 this attribute only applies + // to 'th' elements. + 'scope' => 'Enum#row,col,rowgroup,colgroup', + ), + $cell_align + ); + $this->addElement('td', false, 'Flow', 'Common', $cell_t); + $this->addElement('th', false, 'Flow', 'Common', $cell_t); + + $this->addElement('tr', false, 'Required: td | th', 'Common', $cell_align); + + $cell_col = array_merge( + array( + 'span' => 'Number', + 'width' => 'MultiLength', + ), + $cell_align + ); + $this->addElement('col', false, 'Empty', 'Common', $cell_col); + $this->addElement('colgroup', false, 'Optional: col', 'Common', $cell_col); + + $this->addElement('tbody', false, 'Required: tr', 'Common', $cell_align); + $this->addElement('thead', false, 'Required: tr', 'Common', $cell_align); + $this->addElement('tfoot', false, 'Required: tr', 'Common', $cell_align); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php new file mode 100644 index 00000000..b188ac93 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php @@ -0,0 +1,28 @@ +addBlankElement($name); + $e->attr = array( + 'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget() + ); + } + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php new file mode 100644 index 00000000..58ccc689 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php @@ -0,0 +1,24 @@ +addBlankElement('a'); + $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetBlank(); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoopener.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoopener.php new file mode 100644 index 00000000..b967ff56 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoopener.php @@ -0,0 +1,21 @@ +addBlankElement('a'); + $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoopener(); + } +} diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoreferrer.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoreferrer.php new file mode 100644 index 00000000..32484d60 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoreferrer.php @@ -0,0 +1,21 @@ +addBlankElement('a'); + $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoreferrer(); + } +} diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php new file mode 100644 index 00000000..7a65e004 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php @@ -0,0 +1,87 @@ + 'Heading | Block | Inline' + ); + + /** + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + // Inline Phrasal ------------------------------------------------- + $this->addElement('abbr', 'Inline', 'Inline', 'Common'); + $this->addElement('acronym', 'Inline', 'Inline', 'Common'); + $this->addElement('cite', 'Inline', 'Inline', 'Common'); + $this->addElement('dfn', 'Inline', 'Inline', 'Common'); + $this->addElement('kbd', 'Inline', 'Inline', 'Common'); + $this->addElement('q', 'Inline', 'Inline', 'Common', array('cite' => 'URI')); + $this->addElement('samp', 'Inline', 'Inline', 'Common'); + $this->addElement('var', 'Inline', 'Inline', 'Common'); + + $em = $this->addElement('em', 'Inline', 'Inline', 'Common'); + $em->formatting = true; + + $strong = $this->addElement('strong', 'Inline', 'Inline', 'Common'); + $strong->formatting = true; + + $code = $this->addElement('code', 'Inline', 'Inline', 'Common'); + $code->formatting = true; + + // Inline Structural ---------------------------------------------- + $this->addElement('span', 'Inline', 'Inline', 'Common'); + $this->addElement('br', 'Inline', 'Empty', 'Core'); + + // Block Phrasal -------------------------------------------------- + $this->addElement('address', 'Block', 'Inline', 'Common'); + $this->addElement('blockquote', 'Block', 'Optional: Heading | Block | List', 'Common', array('cite' => 'URI')); + $pre = $this->addElement('pre', 'Block', 'Inline', 'Common'); + $pre->excludes = $this->makeLookup( + 'img', + 'big', + 'small', + 'object', + 'applet', + 'font', + 'basefont' + ); + $this->addElement('h1', 'Heading', 'Inline', 'Common'); + $this->addElement('h2', 'Heading', 'Inline', 'Common'); + $this->addElement('h3', 'Heading', 'Inline', 'Common'); + $this->addElement('h4', 'Heading', 'Inline', 'Common'); + $this->addElement('h5', 'Heading', 'Inline', 'Common'); + $this->addElement('h6', 'Heading', 'Inline', 'Common'); + + // Block Structural ----------------------------------------------- + $p = $this->addElement('p', 'Block', 'Inline', 'Common'); + $p->autoclose = array_flip( + array("address", "blockquote", "center", "dir", "div", "dl", "fieldset", "ol", "p", "ul") + ); + + $this->addElement('div', 'Block', 'Flow', 'Common'); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php new file mode 100644 index 00000000..08aa2324 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php @@ -0,0 +1,230 @@ + 'none', 'light', 'medium', 'heavy'); + + /** + * Default level to place all fixes in. + * Disabled by default. + * @type string + */ + public $defaultLevel = null; + + /** + * Lists of fixes used by getFixesForLevel(). + * Format is: + * HTMLModule_Tidy->fixesForLevel[$level] = array('fix-1', 'fix-2'); + * @type array + */ + public $fixesForLevel = array( + 'light' => array(), + 'medium' => array(), + 'heavy' => array() + ); + + /** + * Lazy load constructs the module by determining the necessary + * fixes to create and then delegating to the populate() function. + * @param HTMLPurifier_Config $config + * @todo Wildcard matching and error reporting when an added or + * subtracted fix has no effect. + */ + public function setup($config) + { + // create fixes, initialize fixesForLevel + $fixes = $this->makeFixes(); + $this->makeFixesForLevel($fixes); + + // figure out which fixes to use + $level = $config->get('HTML.TidyLevel'); + $fixes_lookup = $this->getFixesForLevel($level); + + // get custom fix declarations: these need namespace processing + $add_fixes = $config->get('HTML.TidyAdd'); + $remove_fixes = $config->get('HTML.TidyRemove'); + + foreach ($fixes as $name => $fix) { + // needs to be refactored a little to implement globbing + if (isset($remove_fixes[$name]) || + (!isset($add_fixes[$name]) && !isset($fixes_lookup[$name]))) { + unset($fixes[$name]); + } + } + + // populate this module with necessary fixes + $this->populate($fixes); + } + + /** + * Retrieves all fixes per a level, returning fixes for that specific + * level as well as all levels below it. + * @param string $level level identifier, see $levels for valid values + * @return array Lookup up table of fixes + */ + public function getFixesForLevel($level) + { + if ($level == $this->levels[0]) { + return array(); + } + $activated_levels = array(); + for ($i = 1, $c = count($this->levels); $i < $c; $i++) { + $activated_levels[] = $this->levels[$i]; + if ($this->levels[$i] == $level) { + break; + } + } + if ($i == $c) { + trigger_error( + 'Tidy level ' . htmlspecialchars($level) . ' not recognized', + E_USER_WARNING + ); + return array(); + } + $ret = array(); + foreach ($activated_levels as $level) { + foreach ($this->fixesForLevel[$level] as $fix) { + $ret[$fix] = true; + } + } + return $ret; + } + + /** + * Dynamically populates the $fixesForLevel member variable using + * the fixes array. It may be custom overloaded, used in conjunction + * with $defaultLevel, or not used at all. + * @param array $fixes + */ + public function makeFixesForLevel($fixes) + { + if (!isset($this->defaultLevel)) { + return; + } + if (!isset($this->fixesForLevel[$this->defaultLevel])) { + trigger_error( + 'Default level ' . $this->defaultLevel . ' does not exist', + E_USER_ERROR + ); + return; + } + $this->fixesForLevel[$this->defaultLevel] = array_keys($fixes); + } + + /** + * Populates the module with transforms and other special-case code + * based on a list of fixes passed to it + * @param array $fixes Lookup table of fixes to activate + */ + public function populate($fixes) + { + foreach ($fixes as $name => $fix) { + // determine what the fix is for + list($type, $params) = $this->getFixType($name); + switch ($type) { + case 'attr_transform_pre': + case 'attr_transform_post': + $attr = $params['attr']; + if (isset($params['element'])) { + $element = $params['element']; + if (empty($this->info[$element])) { + $e = $this->addBlankElement($element); + } else { + $e = $this->info[$element]; + } + } else { + $type = "info_$type"; + $e = $this; + } + // PHP does some weird parsing when I do + // $e->$type[$attr], so I have to assign a ref. + $f =& $e->$type; + $f[$attr] = $fix; + break; + case 'tag_transform': + $this->info_tag_transform[$params['element']] = $fix; + break; + case 'child': + case 'content_model_type': + $element = $params['element']; + if (empty($this->info[$element])) { + $e = $this->addBlankElement($element); + } else { + $e = $this->info[$element]; + } + $e->$type = $fix; + break; + default: + trigger_error("Fix type $type not supported", E_USER_ERROR); + break; + } + } + } + + /** + * Parses a fix name and determines what kind of fix it is, as well + * as other information defined by the fix + * @param $name String name of fix + * @return array(string $fix_type, array $fix_parameters) + * @note $fix_parameters is type dependant, see populate() for usage + * of these parameters + */ + public function getFixType($name) + { + // parse it + $property = $attr = null; + if (strpos($name, '#') !== false) { + list($name, $property) = explode('#', $name); + } + if (strpos($name, '@') !== false) { + list($name, $attr) = explode('@', $name); + } + + // figure out the parameters + $params = array(); + if ($name !== '') { + $params['element'] = $name; + } + if (!is_null($attr)) { + $params['attr'] = $attr; + } + + // special case: attribute transform + if (!is_null($attr)) { + if (is_null($property)) { + $property = 'pre'; + } + $type = 'attr_transform_' . $property; + return array($type, $params); + } + + // special case: tag transform + if (is_null($property)) { + return array('tag_transform', $params); + } + + return array($property, $params); + + } + + /** + * Defines all fixes the module will perform in a compact + * associative array of fix name to fix implementation. + * @return array + */ + public function makeFixes() + { + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php new file mode 100644 index 00000000..a995161b --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php @@ -0,0 +1,33 @@ +content_model_type != 'strictblockquote') { + return parent::getChildDef($def); + } + return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model); + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php new file mode 100644 index 00000000..c095ad97 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php @@ -0,0 +1,16 @@ + 'text-align:left;', + 'right' => 'text-align:right;', + 'top' => 'caption-side:top;', + 'bottom' => 'caption-side:bottom;' // not supported by IE + ) + ); + + // @align for img ------------------------------------------------- + $r['img@align'] = + new HTMLPurifier_AttrTransform_EnumToCSS( + 'align', + array( + 'left' => 'float:left;', + 'right' => 'float:right;', + 'top' => 'vertical-align:top;', + 'middle' => 'vertical-align:middle;', + 'bottom' => 'vertical-align:baseline;', + ) + ); + + // @align for table ----------------------------------------------- + $r['table@align'] = + new HTMLPurifier_AttrTransform_EnumToCSS( + 'align', + array( + 'left' => 'float:left;', + 'center' => 'margin-left:auto;margin-right:auto;', + 'right' => 'float:right;' + ) + ); + + // @align for hr ----------------------------------------------- + $r['hr@align'] = + new HTMLPurifier_AttrTransform_EnumToCSS( + 'align', + array( + // we use both text-align and margin because these work + // for different browsers (IE and Firefox, respectively) + // and the melange makes for a pretty cross-compatible + // solution + 'left' => 'margin-left:0;margin-right:auto;text-align:left;', + 'center' => 'margin-left:auto;margin-right:auto;text-align:center;', + 'right' => 'margin-left:auto;margin-right:0;text-align:right;' + ) + ); + + // @align for h1, h2, h3, h4, h5, h6, p, div ---------------------- + // {{{ + $align_lookup = array(); + $align_values = array('left', 'right', 'center', 'justify'); + foreach ($align_values as $v) { + $align_lookup[$v] = "text-align:$v;"; + } + // }}} + $r['h1@align'] = + $r['h2@align'] = + $r['h3@align'] = + $r['h4@align'] = + $r['h5@align'] = + $r['h6@align'] = + $r['p@align'] = + $r['div@align'] = + new HTMLPurifier_AttrTransform_EnumToCSS('align', $align_lookup); + + // @bgcolor for table, tr, td, th --------------------------------- + $r['table@bgcolor'] = + $r['td@bgcolor'] = + $r['th@bgcolor'] = + new HTMLPurifier_AttrTransform_BgColor(); + + // @border for img ------------------------------------------------ + $r['img@border'] = new HTMLPurifier_AttrTransform_Border(); + + // @clear for br -------------------------------------------------- + $r['br@clear'] = + new HTMLPurifier_AttrTransform_EnumToCSS( + 'clear', + array( + 'left' => 'clear:left;', + 'right' => 'clear:right;', + 'all' => 'clear:both;', + 'none' => 'clear:none;', + ) + ); + + // @height for td, th --------------------------------------------- + $r['td@height'] = + $r['th@height'] = + new HTMLPurifier_AttrTransform_Length('height'); + + // @hspace for img ------------------------------------------------ + $r['img@hspace'] = new HTMLPurifier_AttrTransform_ImgSpace('hspace'); + + // @noshade for hr ------------------------------------------------ + // this transformation is not precise but often good enough. + // different browsers use different styles to designate noshade + $r['hr@noshade'] = + new HTMLPurifier_AttrTransform_BoolToCSS( + 'noshade', + 'color:#808080;background-color:#808080;border:0;' + ); + + // @nowrap for td, th --------------------------------------------- + $r['td@nowrap'] = + $r['th@nowrap'] = + new HTMLPurifier_AttrTransform_BoolToCSS( + 'nowrap', + 'white-space:nowrap;' + ); + + // @size for hr -------------------------------------------------- + $r['hr@size'] = new HTMLPurifier_AttrTransform_Length('size', 'height'); + + // @type for li, ol, ul ------------------------------------------- + // {{{ + $ul_types = array( + 'disc' => 'list-style-type:disc;', + 'square' => 'list-style-type:square;', + 'circle' => 'list-style-type:circle;' + ); + $ol_types = array( + '1' => 'list-style-type:decimal;', + 'i' => 'list-style-type:lower-roman;', + 'I' => 'list-style-type:upper-roman;', + 'a' => 'list-style-type:lower-alpha;', + 'A' => 'list-style-type:upper-alpha;' + ); + $li_types = $ul_types + $ol_types; + // }}} + + $r['ul@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ul_types); + $r['ol@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ol_types, true); + $r['li@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $li_types, true); + + // @vspace for img ------------------------------------------------ + $r['img@vspace'] = new HTMLPurifier_AttrTransform_ImgSpace('vspace'); + + // @width for hr, td, th ------------------------------------------ + $r['td@width'] = + $r['th@width'] = + $r['hr@width'] = new HTMLPurifier_AttrTransform_Length('width'); + + return $r; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php new file mode 100644 index 00000000..01dbe9de --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php @@ -0,0 +1,20 @@ + array( + 'xml:lang' => 'LanguageCode', + ) + ); +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php new file mode 100644 index 00000000..38c058fe --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php @@ -0,0 +1,467 @@ +attrTypes = new HTMLPurifier_AttrTypes(); + $this->doctypes = new HTMLPurifier_DoctypeRegistry(); + + // setup basic modules + $common = array( + 'CommonAttributes', 'Text', 'Hypertext', 'List', + 'Presentation', 'Edit', 'Bdo', 'Tables', 'Image', + 'StyleAttribute', + // Unsafe: + 'Scripting', 'Object', 'Forms', + // Sorta legacy, but present in strict: + 'Name', + ); + $transitional = array('Legacy', 'Target', 'Iframe'); + $xml = array('XMLCommonAttributes'); + $non_xml = array('NonXMLCommonAttributes'); + + // setup basic doctypes + $this->doctypes->register( + 'HTML 4.01 Transitional', + false, + array_merge($common, $transitional, $non_xml), + array('Tidy_Transitional', 'Tidy_Proprietary'), + array(), + '-//W3C//DTD HTML 4.01 Transitional//EN', + 'http://www.w3.org/TR/html4/loose.dtd' + ); + + $this->doctypes->register( + 'HTML 4.01 Strict', + false, + array_merge($common, $non_xml), + array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'), + array(), + '-//W3C//DTD HTML 4.01//EN', + 'http://www.w3.org/TR/html4/strict.dtd' + ); + + $this->doctypes->register( + 'XHTML 1.0 Transitional', + true, + array_merge($common, $transitional, $xml, $non_xml), + array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'), + array(), + '-//W3C//DTD XHTML 1.0 Transitional//EN', + 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd' + ); + + $this->doctypes->register( + 'XHTML 1.0 Strict', + true, + array_merge($common, $xml, $non_xml), + array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'), + array(), + '-//W3C//DTD XHTML 1.0 Strict//EN', + 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd' + ); + + $this->doctypes->register( + 'XHTML 1.1', + true, + // Iframe is a real XHTML 1.1 module, despite being + // "transitional"! + array_merge($common, $xml, array('Ruby', 'Iframe')), + array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'), // Tidy_XHTML1_1 + array(), + '-//W3C//DTD XHTML 1.1//EN', + 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd' + ); + + } + + /** + * Registers a module to the recognized module list, useful for + * overloading pre-existing modules. + * @param $module Mixed: string module name, with or without + * HTMLPurifier_HTMLModule prefix, or instance of + * subclass of HTMLPurifier_HTMLModule. + * @param $overload Boolean whether or not to overload previous modules. + * If this is not set, and you do overload a module, + * HTML Purifier will complain with a warning. + * @note This function will not call autoload, you must instantiate + * (and thus invoke) autoload outside the method. + * @note If a string is passed as a module name, different variants + * will be tested in this order: + * - Check for HTMLPurifier_HTMLModule_$name + * - Check all prefixes with $name in order they were added + * - Check for literal object name + * - Throw fatal error + * If your object name collides with an internal class, specify + * your module manually. All modules must have been included + * externally: registerModule will not perform inclusions for you! + */ + public function registerModule($module, $overload = false) + { + if (is_string($module)) { + // attempt to load the module + $original_module = $module; + $ok = false; + foreach ($this->prefixes as $prefix) { + $module = $prefix . $original_module; + if (class_exists($module)) { + $ok = true; + break; + } + } + if (!$ok) { + $module = $original_module; + if (!class_exists($module)) { + trigger_error( + $original_module . ' module does not exist', + E_USER_ERROR + ); + return; + } + } + $module = new $module(); + } + if (empty($module->name)) { + trigger_error('Module instance of ' . get_class($module) . ' must have name'); + return; + } + if (!$overload && isset($this->registeredModules[$module->name])) { + trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING); + } + $this->registeredModules[$module->name] = $module; + } + + /** + * Adds a module to the current doctype by first registering it, + * and then tacking it on to the active doctype + */ + public function addModule($module) + { + $this->registerModule($module); + if (is_object($module)) { + $module = $module->name; + } + $this->userModules[] = $module; + } + + /** + * Adds a class prefix that registerModule() will use to resolve a + * string name to a concrete class + */ + public function addPrefix($prefix) + { + $this->prefixes[] = $prefix; + } + + /** + * Performs processing on modules, after being called you may + * use getElement() and getElements() + * @param HTMLPurifier_Config $config + */ + public function setup($config) + { + $this->trusted = $config->get('HTML.Trusted'); + + // generate + $this->doctype = $this->doctypes->make($config); + $modules = $this->doctype->modules; + + // take out the default modules that aren't allowed + $lookup = $config->get('HTML.AllowedModules'); + $special_cases = $config->get('HTML.CoreModules'); + + if (is_array($lookup)) { + foreach ($modules as $k => $m) { + if (isset($special_cases[$m])) { + continue; + } + if (!isset($lookup[$m])) { + unset($modules[$k]); + } + } + } + + // custom modules + if ($config->get('HTML.Proprietary')) { + $modules[] = 'Proprietary'; + } + if ($config->get('HTML.SafeObject')) { + $modules[] = 'SafeObject'; + } + if ($config->get('HTML.SafeEmbed')) { + $modules[] = 'SafeEmbed'; + } + if ($config->get('HTML.SafeScripting') !== array()) { + $modules[] = 'SafeScripting'; + } + if ($config->get('HTML.Nofollow')) { + $modules[] = 'Nofollow'; + } + if ($config->get('HTML.TargetBlank')) { + $modules[] = 'TargetBlank'; + } + // NB: HTML.TargetNoreferrer and HTML.TargetNoopener must be AFTER HTML.TargetBlank + // so that its post-attr-transform gets run afterwards. + if ($config->get('HTML.TargetNoreferrer')) { + $modules[] = 'TargetNoreferrer'; + } + if ($config->get('HTML.TargetNoopener')) { + $modules[] = 'TargetNoopener'; + } + + // merge in custom modules + $modules = array_merge($modules, $this->userModules); + + foreach ($modules as $module) { + $this->processModule($module); + $this->modules[$module]->setup($config); + } + + foreach ($this->doctype->tidyModules as $module) { + $this->processModule($module); + $this->modules[$module]->setup($config); + } + + // prepare any injectors + foreach ($this->modules as $module) { + $n = array(); + foreach ($module->info_injector as $injector) { + if (!is_object($injector)) { + $class = "HTMLPurifier_Injector_$injector"; + $injector = new $class; + } + $n[$injector->name] = $injector; + } + $module->info_injector = $n; + } + + // setup lookup table based on all valid modules + foreach ($this->modules as $module) { + foreach ($module->info as $name => $def) { + if (!isset($this->elementLookup[$name])) { + $this->elementLookup[$name] = array(); + } + $this->elementLookup[$name][] = $module->name; + } + } + + // note the different choice + $this->contentSets = new HTMLPurifier_ContentSets( + // content set assembly deals with all possible modules, + // not just ones deemed to be "safe" + $this->modules + ); + $this->attrCollections = new HTMLPurifier_AttrCollections( + $this->attrTypes, + // there is no way to directly disable a global attribute, + // but using AllowedAttributes or simply not including + // the module in your custom doctype should be sufficient + $this->modules + ); + } + + /** + * Takes a module and adds it to the active module collection, + * registering it if necessary. + */ + public function processModule($module) + { + if (!isset($this->registeredModules[$module]) || is_object($module)) { + $this->registerModule($module); + } + $this->modules[$module] = $this->registeredModules[$module]; + } + + /** + * Retrieves merged element definitions. + * @return Array of HTMLPurifier_ElementDef + */ + public function getElements() + { + $elements = array(); + foreach ($this->modules as $module) { + if (!$this->trusted && !$module->safe) { + continue; + } + foreach ($module->info as $name => $v) { + if (isset($elements[$name])) { + continue; + } + $elements[$name] = $this->getElement($name); + } + } + + // remove dud elements, this happens when an element that + // appeared to be safe actually wasn't + foreach ($elements as $n => $v) { + if ($v === false) { + unset($elements[$n]); + } + } + + return $elements; + + } + + /** + * Retrieves a single merged element definition + * @param string $name Name of element + * @param bool $trusted Boolean trusted overriding parameter: set to true + * if you want the full version of an element + * @return HTMLPurifier_ElementDef Merged HTMLPurifier_ElementDef + * @note You may notice that modules are getting iterated over twice (once + * in getElements() and once here). This + * is because + */ + public function getElement($name, $trusted = null) + { + if (!isset($this->elementLookup[$name])) { + return false; + } + + // setup global state variables + $def = false; + if ($trusted === null) { + $trusted = $this->trusted; + } + + // iterate through each module that has registered itself to this + // element + foreach ($this->elementLookup[$name] as $module_name) { + $module = $this->modules[$module_name]; + + // refuse to create/merge from a module that is deemed unsafe-- + // pretend the module doesn't exist--when trusted mode is not on. + if (!$trusted && !$module->safe) { + continue; + } + + // clone is used because, ideally speaking, the original + // definition should not be modified. Usually, this will + // make no difference, but for consistency's sake + $new_def = clone $module->info[$name]; + + if (!$def && $new_def->standalone) { + $def = $new_def; + } elseif ($def) { + // This will occur even if $new_def is standalone. In practice, + // this will usually result in a full replacement. + $def->mergeIn($new_def); + } else { + // :TODO: + // non-standalone definitions that don't have a standalone + // to merge into could be deferred to the end + // HOWEVER, it is perfectly valid for a non-standalone + // definition to lack a standalone definition, even + // after all processing: this allows us to safely + // specify extra attributes for elements that may not be + // enabled all in one place. In particular, this might + // be the case for trusted elements. WARNING: care must + // be taken that the /extra/ definitions are all safe. + continue; + } + + // attribute value expansions + $this->attrCollections->performInclusions($def->attr); + $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes); + + // descendants_are_inline, for ChildDef_Chameleon + if (is_string($def->content_model) && + strpos($def->content_model, 'Inline') !== false) { + if ($name != 'del' && $name != 'ins') { + // this is for you, ins/del + $def->descendants_are_inline = true; + } + } + + $this->contentSets->generateChildDef($def, $module); + } + + // This can occur if there is a blank definition, but no base to + // mix it in with + if (!$def) { + return false; + } + + // add information on required attributes + foreach ($def->attr as $attr_name => $attr_def) { + if ($attr_def->required) { + $def->required_attr[] = $attr_name; + } + } + return $def; + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/IDAccumulator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/IDAccumulator.php new file mode 100644 index 00000000..65c902c0 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/IDAccumulator.php @@ -0,0 +1,57 @@ +load($config->get('Attr.IDBlacklist')); + return $id_accumulator; + } + + /** + * Add an ID to the lookup table. + * @param string $id ID to be added. + * @return bool status, true if success, false if there's a dupe + */ + public function add($id) + { + if (isset($this->ids[$id])) { + return false; + } + return $this->ids[$id] = true; + } + + /** + * Load a list of IDs into the lookup table + * @param $array_of_ids Array of IDs to load + * @note This function doesn't care about duplicates + */ + public function load($array_of_ids) + { + foreach ($array_of_ids as $id) { + $this->ids[$id] = true; + } + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector.php new file mode 100644 index 00000000..116b470c --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector.php @@ -0,0 +1,283 @@ +processToken() + * documentation. + * + * @todo Allow injectors to request a re-run on their output. This + * would help if an operation is recursive. + */ +abstract class HTMLPurifier_Injector +{ + + /** + * Advisory name of injector, this is for friendly error messages. + * @type string + */ + public $name; + + /** + * @type HTMLPurifier_HTMLDefinition + */ + protected $htmlDefinition; + + /** + * Reference to CurrentNesting variable in Context. This is an array + * list of tokens that we are currently "inside" + * @type array + */ + protected $currentNesting; + + /** + * Reference to current token. + * @type HTMLPurifier_Token + */ + protected $currentToken; + + /** + * Reference to InputZipper variable in Context. + * @type HTMLPurifier_Zipper + */ + protected $inputZipper; + + /** + * Array of elements and attributes this injector creates and therefore + * need to be allowed by the definition. Takes form of + * array('element' => array('attr', 'attr2'), 'element2') + * @type array + */ + public $needed = array(); + + /** + * Number of elements to rewind backwards (relative). + * @type bool|int + */ + protected $rewindOffset = false; + + /** + * Rewind to a spot to re-perform processing. This is useful if you + * deleted a node, and now need to see if this change affected any + * earlier nodes. Rewinding does not affect other injectors, and can + * result in infinite loops if not used carefully. + * @param bool|int $offset + * @warning HTML Purifier will prevent you from fast-forwarding with this + * function. + */ + public function rewindOffset($offset) + { + $this->rewindOffset = $offset; + } + + /** + * Retrieves rewind offset, and then unsets it. + * @return bool|int + */ + public function getRewindOffset() + { + $r = $this->rewindOffset; + $this->rewindOffset = false; + return $r; + } + + /** + * Prepares the injector by giving it the config and context objects: + * this allows references to important variables to be made within + * the injector. This function also checks if the HTML environment + * will work with the Injector (see checkNeeded()). + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string Boolean false if success, string of missing needed element/attribute if failure + */ + public function prepare($config, $context) + { + $this->htmlDefinition = $config->getHTMLDefinition(); + // Even though this might fail, some unit tests ignore this and + // still test checkNeeded, so be careful. Maybe get rid of that + // dependency. + $result = $this->checkNeeded($config); + if ($result !== false) { + return $result; + } + $this->currentNesting =& $context->get('CurrentNesting'); + $this->currentToken =& $context->get('CurrentToken'); + $this->inputZipper =& $context->get('InputZipper'); + return false; + } + + /** + * This function checks if the HTML environment + * will work with the Injector: if p tags are not allowed, the + * Auto-Paragraphing injector should not be enabled. + * @param HTMLPurifier_Config $config + * @return bool|string Boolean false if success, string of missing needed element/attribute if failure + */ + public function checkNeeded($config) + { + $def = $config->getHTMLDefinition(); + foreach ($this->needed as $element => $attributes) { + if (is_int($element)) { + $element = $attributes; + } + if (!isset($def->info[$element])) { + return $element; + } + if (!is_array($attributes)) { + continue; + } + foreach ($attributes as $name) { + if (!isset($def->info[$element]->attr[$name])) { + return "$element.$name"; + } + } + } + return false; + } + + /** + * Tests if the context node allows a certain element + * @param string $name Name of element to test for + * @return bool True if element is allowed, false if it is not + */ + public function allowsElement($name) + { + if (!empty($this->currentNesting)) { + $parent_token = array_pop($this->currentNesting); + $this->currentNesting[] = $parent_token; + $parent = $this->htmlDefinition->info[$parent_token->name]; + } else { + $parent = $this->htmlDefinition->info_parent_def; + } + if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) { + return false; + } + // check for exclusion + if (!empty($this->currentNesting)) { + for ($i = count($this->currentNesting) - 2; $i >= 0; $i--) { + $node = $this->currentNesting[$i]; + $def = $this->htmlDefinition->info[$node->name]; + if (isset($def->excludes[$name])) { + return false; + } + } + } + return true; + } + + /** + * Iterator function, which starts with the next token and continues until + * you reach the end of the input tokens. + * @warning Please prevent previous references from interfering with this + * functions by setting $i = null beforehand! + * @param int $i Current integer index variable for inputTokens + * @param HTMLPurifier_Token $current Current token variable. + * Do NOT use $token, as that variable is also a reference + * @return bool + */ + protected function forward(&$i, &$current) + { + if ($i === null) { + $i = count($this->inputZipper->back) - 1; + } else { + $i--; + } + if ($i < 0) { + return false; + } + $current = $this->inputZipper->back[$i]; + return true; + } + + /** + * Similar to _forward, but accepts a third parameter $nesting (which + * should be initialized at 0) and stops when we hit the end tag + * for the node $this->inputIndex starts in. + * @param int $i Current integer index variable for inputTokens + * @param HTMLPurifier_Token $current Current token variable. + * Do NOT use $token, as that variable is also a reference + * @param int $nesting + * @return bool + */ + protected function forwardUntilEndToken(&$i, &$current, &$nesting) + { + $result = $this->forward($i, $current); + if (!$result) { + return false; + } + if ($nesting === null) { + $nesting = 0; + } + if ($current instanceof HTMLPurifier_Token_Start) { + $nesting++; + } elseif ($current instanceof HTMLPurifier_Token_End) { + if ($nesting <= 0) { + return false; + } + $nesting--; + } + return true; + } + + /** + * Iterator function, starts with the previous token and continues until + * you reach the beginning of input tokens. + * @warning Please prevent previous references from interfering with this + * functions by setting $i = null beforehand! + * @param int $i Current integer index variable for inputTokens + * @param HTMLPurifier_Token $current Current token variable. + * Do NOT use $token, as that variable is also a reference + * @return bool + */ + protected function backward(&$i, &$current) + { + if ($i === null) { + $i = count($this->inputZipper->front) - 1; + } else { + $i--; + } + if ($i < 0) { + return false; + } + $current = $this->inputZipper->front[$i]; + return true; + } + + /** + * Handler that is called when a text token is processed + */ + public function handleText(&$token) + { + } + + /** + * Handler that is called when a start or empty token is processed + */ + public function handleElement(&$token) + { + } + + /** + * Handler that is called when an end token is processed + */ + public function handleEnd(&$token) + { + $this->notifyEnd($token); + } + + /** + * Notifier that is called when an end token is processed + * @param HTMLPurifier_Token $token Current token variable. + * @note This differs from handlers in that the token is read-only + * @deprecated + */ + public function notifyEnd($token) + { + } +} + +// vim: et sw=4 sts=4 diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php new file mode 100644 index 00000000..4afdd128 --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php @@ -0,0 +1,356 @@ +armor['MakeWellFormed_TagClosedError'] = true; + return $par; + } + + /** + * @param HTMLPurifier_Token_Text $token + */ + public function handleText(&$token) + { + $text = $token->data; + // Does the current parent allow

tags? + if ($this->allowsElement('p')) { + if (empty($this->currentNesting) || strpos($text, "\n\n") !== false) { + // Note that we have differing behavior when dealing with text + // in the anonymous root node, or a node inside the document. + // If the text as a double-newline, the treatment is the same; + // if it doesn't, see the next if-block if you're in the document. + + $i = $nesting = null; + if (!$this->forwardUntilEndToken($i, $current, $nesting) && $token->is_whitespace) { + // State 1.1: ... ^ (whitespace, then document end) + // ---- + // This is a degenerate case + } else { + if (!$token->is_whitespace || $this->_isInline($current)) { + // State 1.2: PAR1 + // ---- + + // State 1.3: PAR1\n\nPAR2 + // ------------ + + // State 1.4:

PAR1\n\nPAR2 (see State 2) + // ------------ + $token = array($this->_pStart()); + $this->_splitText($text, $token); + } else { + // State 1.5: \n
+ // -- + } + } + } else { + // State 2:
PAR1... (similar to 1.4) + // ---- + + // We're in an element that allows paragraph tags, but we're not + // sure if we're going to need them. + if ($this->_pLookAhead()) { + // State 2.1:
PAR1PAR1\n\nPAR2 + // ---- + // Note: This will always be the first child, since any + // previous inline element would have triggered this very + // same routine, and found the double newline. One possible + // exception would be a comment. + $token = array($this->_pStart(), $token); + } else { + // State 2.2.1:
PAR1
+ // ---- + + // State 2.2.2:
PAR1PAR1
+ // ---- + } + } + // Is the current parent a

tag? + } elseif (!empty($this->currentNesting) && + $this->currentNesting[count($this->currentNesting) - 1]->name == 'p') { + // State 3.1: ...

PAR1 + // ---- + + // State 3.2: ...

PAR1\n\nPAR2 + // ------------ + $token = array(); + $this->_splitText($text, $token); + // Abort! + } else { + // State 4.1: ...PAR1 + // ---- + + // State 4.2: ...PAR1\n\nPAR2 + // ------------ + } + } + + /** + * @param HTMLPurifier_Token $token + */ + public function handleElement(&$token) + { + // We don't have to check if we're already in a

tag for block + // tokens, because the tag would have been autoclosed by MakeWellFormed. + if ($this->allowsElement('p')) { + if (!empty($this->currentNesting)) { + if ($this->_isInline($token)) { + // State 1:

... + // --- + // Check if this token is adjacent to the parent token + // (seek backwards until token isn't whitespace) + $i = null; + $this->backward($i, $prev); + + if (!$prev instanceof HTMLPurifier_Token_Start) { + // Token wasn't adjacent + if ($prev instanceof HTMLPurifier_Token_Text && + substr($prev->data, -2) === "\n\n" + ) { + // State 1.1.4:

PAR1

\n\n + // --- + // Quite frankly, this should be handled by splitText + $token = array($this->_pStart(), $token); + } else { + // State 1.1.1:

PAR1

+ // --- + // State 1.1.2:

+ // --- + // State 1.1.3:
PAR + // --- + } + } else { + // State 1.2.1:
+ // --- + // Lookahead to see if

is needed. + if ($this->_pLookAhead()) { + // State 1.3.1:

PAR1\n\nPAR2 + // --- + $token = array($this->_pStart(), $token); + } else { + // State 1.3.2:
PAR1
+ // --- + + // State 1.3.3:
PAR1
\n\n
+ // --- + } + } + } else { + // State 2.3: ...
+ // ----- + } + } else { + if ($this->_isInline($token)) { + // State 3.1: + // --- + // This is where the {p} tag is inserted, not reflected in + // inputTokens yet, however. + $token = array($this->_pStart(), $token); + } else { + // State 3.2:
+ // ----- + } + + $i = null; + if ($this->backward($i, $prev)) { + if (!$prev instanceof HTMLPurifier_Token_Text) { + // State 3.1.1: ...

{p} + // --- + // State 3.2.1: ...

+ // ----- + if (!is_array($token)) { + $token = array($token); + } + array_unshift($token, new HTMLPurifier_Token_Text("\n\n")); + } else { + // State 3.1.2: ...

\n\n{p} + // --- + // State 3.2.2: ...

\n\n
+ // ----- + // Note: PAR cannot occur because PAR would have been + // wrapped in

tags. + } + } + } + } else { + // State 2.2:

- 모든 능력치는 ( 10 <= 능력치 <= ) 사이로 잡으셔야 합니다.
+ 모든 능력치는 ( <= 능력치 <= ) 사이로 잡으셔야 합니다.
그 외의 능력치는 가입되지 않습니다.
국 가 방 침 & 임관 권유 메세지
국가 방침 name=btn value=국가방침>
임관 권유 maxlength=500 style=color:white;background-color:black;width:830px; name=scoutmsg value=''> name=btn value=임관권유>
국가 방침 name=btn value=국가방침>
임관 권유 maxlength=1000 style=color:white;background-color:black;width:830px; name=scoutmsg value=''> name=btn value=임관권유>
870 x 200px 넘는 크기를 점유할 시 통보없이 제한될 수 있습니다.
예 산 & 정 책
-
{$nation['name']}".$nation['scoutmsg']."
" + .$nation['name'] + ."" + .($nation['scoutmsg']?:'-') + .'
- + diff --git a/hwe/func_template.php b/hwe/func_template.php index b0cbd495..a5e9af0f 100644 --- a/hwe/func_template.php +++ b/hwe/func_template.php @@ -172,4 +172,15 @@ function getMapHtml(){ $templates = new \League\Plates\Engine(__dir__.'/templates'); return $templates->render('map'); +} + +function getInvitationList(array $nationList){ + $templates = new \League\Plates\Engine(__dir__.'/templates'); + + foreach($nationList as &$nation){ + $nation['textColor'] = newColor($nation['color']); + } + return $templates->render('invitationList', [ + 'nationList'=>$nationList + ]); } \ No newline at end of file diff --git a/hwe/join.php b/hwe/join.php index a7dc6690..680c4e64 100644 --- a/hwe/join.php +++ b/hwe/join.php @@ -63,27 +63,11 @@ if ($gencount >= $admin['maxgeneral']) { echo ""; exit(); } -?> -
국 가 방 침 & 임관 권유 메세지
국 가 방 침 & 임관 권유 메시지
국가 방침 name=btn value=국가방침>
임관 권유 maxlength=1000 style=color:white;background-color:black;width:830px; name=scoutmsg value=''> name=btn value=임관권유>
870 x 200px 넘는 크기를 점유할 시 통보없이 제한될 수 있습니다.
- -query('SELECT nation,`name`,color,scout,scoutmsg FROM nation ORDER BY rand()'); -for ($i=0; $i < $nationcount; $i++) { - $nation = MYDB_fetch_array($nationresult); - if ($nation['scoutmsg'] == "") { - echo " - "; - } else { - echo " - "; - } -} +echo getInvitationList($nationList); ?> -
임관 권유 메세지
{$nation['name']}-
{$nation['name']}{$nation['scoutmsg']}
diff --git a/hwe/processing.php b/hwe/processing.php index 06b26ed6..ac8e6450 100644 --- a/hwe/processing.php +++ b/hwe/processing.php @@ -972,6 +972,8 @@ function command_25($turn, $command) { $query = "select nation,name,color,scout,scoutmsg,sabotagelimit,gennum from nation order by gennum"; $result = MYDB_query($query, $connect) or Error("aaa_processing.php ".MYDB_error($connect),""); $count = MYDB_num_rows($result); + + $nationList = $db->query('SELECT nation,`name`,color,scout,scoutmsg FROM nation ORDER BY rand()'); echo " 국가에 임관합니다.
@@ -988,18 +990,7 @@ function command_25($turn, $command) { "; $scoutStr = ""; - for($i=1; $i <= $count; $i++) { - $nation = MYDB_fetch_array($result); - - $scoutStr .= - "" - ."" - ."'; - + foreach($nationList as $nation){ if($gameStor->year < $gameStor->startyear+3 && $nation['gennum'] >= GameConst::$initialNationGenLimit) { echo " "; @@ -1026,11 +1017,8 @@ function command_25($turn, $command) { } echo " - -
" - .$nation['name'] - ."" - .($nation['scoutmsg']?:'-') - .'
- -{$scoutStr} -
임관 권유 메세지
"; +"; + echo getInvitationList($nationList); ender(); } diff --git a/hwe/select_npc.php b/hwe/select_npc.php index 95486f97..6c29380a 100644 --- a/hwe/select_npc.php +++ b/hwe/select_npc.php @@ -18,7 +18,7 @@ if(!$npcmode) { $gencount = $db->queryFirstField('SELECT count(`no`) FROM general WHERE npc<2'); -$nations = $db->queryAllLists('SELECT `name`, scoutmsg, color FROM nation'); +$nationList = $db->query('SELECT nation,`name`,color,scout,scoutmsg FROM nation ORDER BY rand()'); ?> @@ -61,21 +61,7 @@ history.go(-1);
- - - - - - - - - - - - - - -
임관 권유 메세지
+
장수 빙의
diff --git a/hwe/templates/invitationList.php b/hwe/templates/invitationList.php new file mode 100644 index 00000000..2cddf86a --- /dev/null +++ b/hwe/templates/invitationList.php @@ -0,0 +1,18 @@ + + + + + + + + + + + + +
임관 권유 메시지
\ No newline at end of file -- 2.54.0 From c4f5962c7e3aaa5db0671ac77c8e5fec9adc598e Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 24 Jun 2018 04:18:54 +0900 Subject: [PATCH 200/304] =?UTF-8?q?=EA=B5=AD=EB=B0=A9=20=ED=91=9C=EA=B8=B0?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func.php | 13 +++++-------- hwe/index.php | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/hwe/func.php b/hwe/func.php index d06cd876..53bda63e 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -1116,15 +1116,12 @@ function nationMsg() { $connect=$db->get(); $userID = Session::getUserID(); - $query = "select no,nation from general where owner='{$userID}'"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $me = MYDB_fetch_array($result); + $msg = $db->queryFirstField( + 'SELECT msg FROM nation WHERE nation = (SELECT nation FROM general WHERE `owner` = %i)', + $userID + ); - $query = "select msg from nation where nation='{$me['nation']}'"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $nation = MYDB_fetch_array($result); - - echo "".$nation['msg'].""; + return $msg?:''; } function msgprint($msg, $name, $picture, $imgsvr, $when, $num, $type) { diff --git a/hwe/index.php b/hwe/index.php index f681b515..12d15123 100644 --- a/hwe/index.php +++ b/hwe/index.php @@ -195,7 +195,7 @@ echo " ?>
접속중인 국가:
【 국가방침 】
【 국가방침 】
【 접속자 】
+
명 장 일 람
- +
@@ -51,294 +41,156 @@ if ($con >= 2) {
- +
=2"; -} else { - $sel = "npc<2"; -} -$nationName = ['재야']; -$nationColor = ['#000000']; + +$nationName = [0=>'재야']; +$nationColor = [0=>'#000000']; foreach (getAllNationStaticInfo() as $nation) { $nationName[$nation['nation']] = $nation['name']; $nationColor[$nation['nation']] = $nation['color']; } -$type = array( - "명 성", - "계 급", - "계 략 성 공", - "전 투 횟 수", - "승 리", - "승 률", - "사 살", - "살 상 률", - "보 병 숙 련 도", - "궁 병 숙 련 도", - "기 병 숙 련 도", - "귀 병 숙 련 도", - "차 병 숙 련 도", - "전 력 전 승 률", - "통 솔 전 승 률", - "일 기 토 승 률", - "설 전 승 률", - "베 팅 투 자 액", - "베 팅 당 첨", - "베 팅 수 익 금", - "베 팅 수 익 률" -); +$types = [ + ["명 성", "int", function($v){$v['value'] = $v['experience']; return $v; }], + ["계 급", "int", function($v){$v['value'] = $v['dedication']; return $v; }], + ["계 략 성 공", "int", function($v){ + $v['value'] = $v['firenum']; + $v['nationName'] = '???'; + $v['pictureFullPath'] = GetImageURL(0)."/default.jpg"; + $v['name'] = '???'; + $v['owner_name'] = null; + $v['bgColor'] = GameConst::$basecolor4; + $v['fgColor'] = newColor($v['bgColor']); + return $v; + }], + ["전 투 횟 수", "int", function($v){$v['value'] = $v['warnum']; return $v; }], + ["승 리", "int", function($v){$v['value'] = $v['killnum']; return $v; }], + ["승 률", "percent", function($v){$v['value'] = $v['killnum'] / max(1, $v['warnum']); return $v; }], + ["사 살", "int", function($v){$v['value'] = $v['killcrew']; return $v; }], + ["살 상 률", "percent", function($v){$v['value'] = $v['killcrew'] / max(1, $v['deathcrew']); return $v; }], + ["보 병 숙 련 도", "int", function($v){$v['value'] = $v['dex0']; return $v; }], + ["궁 병 숙 련 도", "int", function($v){$v['value'] = $v['dex10']; return $v; }], + ["기 병 숙 련 도", "int", function($v){$v['value'] = $v['dex20']; return $v; }], + ["귀 병 숙 련 도", "int", function($v){$v['value'] = $v['dex30']; return $v; }], + ["차 병 숙 련 도", "int", function($v){$v['value'] = $v['dex40']; return $v; }], + ["전 력 전 승 률", "percent", function($v){$v['value'] = $v['ttw']/max(1, $v['ttw']+$v['ttd']+$v['ttl']); return $v; }], + ["통 솔 전 승 률", "percent", function($v){$v['value'] = $v['tlw']/max(1, $v['tlw']+$v['tld']+$v['tll']); return $v; }], + ["일 기 토 승 률", "percent", function($v){$v['value'] = $v['tpw']/max(1, $v['tpw']+$v['tpd']+$v['tpl']); return $v; }], + ["설 전 승 률", "percent", function($v){$v['value'] = $v['ttw']/max(1, $v['tiw']+$v['tid']+$v['til']); return $v; }], + ["베 팅 투 자 액", "int", function($v){$v['value'] = $v['betgold']; return $v; }], + ["베 팅 당 첨", "int", function($v){$v['value'] = $v['betwin']; return $v; }], + ["베 팅 수 익 금", "int", function($v){$v['value'] = $v['betwingold']; return $v; }], + ["베 팅 수 익 률", "percent", function($v){$v['value'] = $v['betwingold']/max(1, $v['betgold']); return $v; }], +]; - -for ($i=0; $i < 21; $i++) { - $name = []; - $data = []; - $color = []; - $pic = []; - - //FIXME: 쿼리에 index를 사용할 수 없는 녀석들이 있다. 그냥 모두 받아서 일괄 처리하는게 더 나을 수도 있음. - switch ($i) { - case 0: $query = "select nation,no,name,picture,imgsvr,experience as data from general where $sel order by data desc limit 0,10"; break; - case 1: $query = "select nation,no,name,picture,imgsvr,dedication as data from general where $sel order by data desc limit 0,10"; break; - case 2: $query = "select nation,no,name,picture,imgsvr,firenum as data from general where $sel order by data desc limit 0,10"; break; - case 3: $query = "select nation,no,name,picture,imgsvr,warnum as data from general where $sel order by data desc limit 0,10"; break; - case 4: $query = "select nation,no,name,picture,imgsvr,killnum as data from general where $sel order by data desc limit 0,10"; break; - case 5: $query = "select nation,no,name,picture,imgsvr,killnum/warnum*10000 as data from general where warnum>=10 and $sel order by data desc limit 0,10"; break; - case 6: $query = "select nation,no,name,picture,imgsvr,killcrew as data from general where $sel order by data desc limit 0,10"; break; - case 7: $query = "select nation,no,name,picture,imgsvr,killcrew/deathcrew*10000 as data from general where warnum>=10 and $sel order by data desc limit 0,10"; break; - case 8: $query = "select nation,no,name,picture,imgsvr,dex0 as data from general where $sel order by data desc limit 0,10"; break; - case 9: $query = "select nation,no,name,picture,imgsvr,dex10 as data from general where $sel order by data desc limit 0,10"; break; - case 10: $query = "select nation,no,name,picture,imgsvr,dex20 as data from general where $sel order by data desc limit 0,10"; break; - case 11: $query = "select nation,no,name,picture,imgsvr,dex30 as data from general where $sel order by data desc limit 0,10"; break; - case 12: $query = "select nation,no,name,picture,imgsvr,dex40 as data from general where $sel order by data desc limit 0,10"; break; - case 13: $query = "select nation,no,name,picture,imgsvr,ttw/(ttw+ttd+ttl)*10000 as data from general where $sel and (ttw+ttd+ttl)>=50 order by data desc limit 0,10"; break; - case 14: $query = "select nation,no,name,picture,imgsvr,tlw/(tlw+tld+tll)*10000 as data from general where $sel and (tlw+tld+tll)>=50 order by data desc limit 0,10"; break; - case 15: $query = "select nation,no,name,picture,imgsvr,tpw/(tpw+tpd+tpl)*10000 as data from general where $sel and (tpw+tpd+tpl)>=50 order by data desc limit 0,10"; break; - case 16: $query = "select nation,no,name,picture,imgsvr,tiw/(tiw+tid+til)*10000 as data from general where $sel and (tiw+tid+til)>=50 order by data desc limit 0,10"; break; - case 17: $query = "select nation,no,name,picture,imgsvr,betgold as data from general where $sel order by data desc limit 0,10"; break; - case 18: $query = "select nation,no,name,picture,imgsvr,betwin as data from general where $sel order by data desc limit 0,10"; break; - case 19: $query = "select nation,no,name,picture,imgsvr,betwingold as data from general where $sel order by data desc limit 0,10"; break; - case 20: $query = "select nation,no,name,picture,imgsvr,betwingold/betgold*10000 as data from general where $sel and betgold >= 1000 order by data desc limit 0,10"; break; +$generals = array_map(function($general) use($nationColor, $nationName) { + $general['bgColor'] = $nationColor[$general['nation']]??GameConst::$basecolor4; + $general['fgColor'] = newColor($general['bgColor']); + $general['nationName'] = $nationName[$general['nation']]; + + if(key_exists('picture', $general)){ + $imageTemp = GetImageURL($general['imgsvr']); + $general['pictureFullPath'] = "$imageTemp/{$general['picture']}"; } - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect), ""); - - echo " -
- - "; - - for ($k=0; $k < 10; $k++) { - $gen = MYDB_fetch_array($result); - - if ($i != 2) { - if (isset($gen)) { - $name[$k] = $gen['name']; - $nation[$k] = $nationName[$gen['nation']]; - $data[$k] = $gen['data']; - $color[$k] = $nationColor[$gen['nation']]; - $pic[$k] = $gen['picture']; - } else { - $name[$k] = "-"; - $nation[$k] = "-"; - $data[$k] = "-"; - $color[$k] = GameConst::$basecolor4; - $pic[$k] = ""; - } - } else { - $name[$k] = "???"; - $nation[$k] = "???"; - $data[$k] = $gen['data']; - $color[$k] = GameConst::$basecolor4; - $gen['imgsvr'] = 0; - $pic[$k] = "9999.jpg"; - } - if ($color[$k] == "") { - $color[$k] = GameConst::$basecolor4; - } - if ($nation[$k] == "") { - $nation[$k] = " "; - } - if ($pic[$k] == "") { - echo ""; - } else { - $imageTemp = GetImageURL($gen['imgsvr']); - echo ""; - } + else{ + $general['pictureFullPath'] = GetImageURL(0)."/default.jpg"; } - echo ""; + return $general; +}, $db->query( + "SELECT nation,no,name,name2 as owner_name, picture, imgsvr, + experience, dedication, firenum, warnum, killnum, killcrew, deathcrew, + dex0, dex10, dex20, dex30, dex40, + ttw, ttd, ttl, tlw, tld, tll, tpw, tpd, tpl, tiw, tid, til, + betgold, betwin, betwingold, + horse, weap, book, item + FROM general WHERE %l", $btn == "NPC 보기"?"npc>=2":"npc<2")); - for ($k=0; $k < 10; $k++) { - echo ""; - } - echo ""; +$templates = new \League\Plates\Engine('templates'); - for ($k=0; $k < 10; $k++) { - echo ""; - } +foreach($types as $idx=>[$typeName, $typeValue, $typeFunc]){ + $typeGenerals = array_map(function($general) use($typeValue, $typeFunc){ + $general = ($typeFunc)($general); + $value = $general['value']; - echo ""; - - for ($k=0; $k < 10; $k++) { - if($data[$k] === '-'){ - //do Nothing + if($typeValue == 'percent'){ + $general['printValue'] = number_format($value*100, 2).'%'; } - else if ($i == 5 || $i == 7 || $i == 20) { - $data[$k] = intdiv($data[$k], 100).".".($data[$k]%100)." %"; + else { + $general['printValue'] = number_format($value); } - else if ($i >= 13 && $i <= 16) { - $data[$k] = intdiv($data[$k], 100).".".($data[$k]%100)." %"; - } - echo ""; - } - echo ""; + return $general; + }, $generals); + + usort($typeGenerals, function($lhs, $rhs){ + //내림차순 + return -($lhs['value'] - $rhs['value']); + }); + + echo $templates->render('hallOfFrame', [ + 'typeName'=>$typeName, + 'generals'=>array_slice($typeGenerals, 0, 10, true) + ]); } - -echo " -
$type[$i]
1위2위3위4위5위6위7위8위9위10위
 
{$nation[$k]}
{$name[$k]}
{$data[$k]}
-
- -"; - -$type = array( - "명 마", - "명 검", - "명 서", - "도 구" -); - -$call = array( - "horse", - "weap", - "book", - "item" -); - -$func = array( - "\\sammo\\getHorseName", - "\\sammo\\getWeapName", - "\\sammo\\getBookName", - "\\sammo\\getItemName" -); - -for ($i=0; $i < 4; $i++) { - $name = []; - $data = []; - $color = []; - $pic = []; - - echo " - - "; - for ($k=26; $k > 16; $k--) { - $str = $func[$i]($k); - echo ""; - } - - echo ""; - - for ($k=26; $k > 16; $k--) { - $query = "select nation,no,name,picture,imgsvr from general where {$call[$i]}={$k}"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect), ""); - $gen = MYDB_fetch_array($result); - if (isset($gen)) { - $name[$k] = $gen['name']; - $nation[$k] = $nationName[$gen['nation']]; - $color[$k] = $nationColor[$gen['nation']]; - $pic[$k] = $gen['picture']; - } else { - $name[$k] = "미발견"; - $nation[$k] = "-"; - $color[$k] = ""; - $pic[$k] = ""; - } - if ($color[$k] == "") { - $color[$k] = GameConst::$basecolor4; - } - if ($nation[$k] == "") { - $nation[$k] = " "; - } - if ($pic[$k] == "") { - echo ""; - } else { - $imageTemp = GetImageURL($gen['imgsvr']??0); - echo ""; - } - } - - echo ""; - - for ($k=26; $k > 16; $k--) { - echo ""; - } - - echo ""; - - for ($k=26; $k > 16; $k--) { - echo ""; - } - - echo ""; - - echo " - "; - for ($k=16; $k > 6; $k--) { - $str = $func[$i]($k); - echo ""; - } - - echo ""; - - for ($k=16; $k > 6; $k--) { - $query = "select nation,no,name,picture,imgsvr from general where {$call[$i]}={$k}"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect), ""); - $gen = MYDB_fetch_array($result); - if (isset($gen)) { - $name[$k] = $gen['name']; - $nation[$k] = $nationName[$gen['nation']]; - $color[$k] = $nationColor[$gen['nation']]; - $pic[$k] = $gen['picture']; - } else { - $name[$k] = "미발견"; - $nation[$k] = "-"; - $color[$k] = ""; - $pic[$k] = ""; - } - if ($color[$k] == "") { - $color[$k] = GameConst::$basecolor4; - } - if ($nation[$k] == "") { - $nation[$k] = " "; - } - if ($pic[$k] == "") { - echo ""; - } else { - $imageTemp = GetImageURL($gen['imgsvr']??0); - echo ""; - } - } - - echo ""; - - for ($k=16; $k > 6; $k--) { - echo ""; - } - - echo ""; - - for ($k=16; $k > 6; $k--) { - echo ""; - } - - echo ""; -} - ?> -
$type[$i]
".$str."
 
{$nation[$k]}
{$name[$k]}
".$str."
 
{$nation[$k]}
{$name[$k]}
- + +
+[,$itemType, $itemFunc,,,]){ + $itemCode = $general[$itemType]; + if($itemCode <= 6){ + continue; + } + + $itemName = ($itemFunc)($itemCode); + $general['rankName'] = $itemName; + $general['value'] = $itemCode; + $itemTypes[$itemIdx][5][$itemCode] = $general; + } +} + +foreach($itemTypes as [$itemNameType, $itemType, $itemFunc, $itemMinCode, $itemMaxCode, $itemOwners]){ + $itemRanker = []; + for($itemCode = $itemMaxCode; $itemCode >= $itemMinCode; $itemCode--){ + if(!key_exists($itemCode, $itemOwners)){ + $emptyCard = [ + 'rankName' => ($itemFunc)($itemCode), + 'pictureFullPath' => GetImageURL(0)."/default.jpg", + 'value'=>$itemCode, + 'name'=>'미발견', + 'bgColor'=>GameConst::$basecolor4, + 'fgColor'=>newColor(GameConst::$basecolor4), + ]; + $itemRanker[$itemCode] = $emptyCard; + continue; + } + + $general = $itemOwners[$itemCode]; + $itemRanker[$itemCode] = $general; + } + + echo $templates->render('hallOfFrame', [ + 'typeName'=>$itemNameType, + 'generals'=>$itemRanker + ]); +} +?> +
+
diff --git a/hwe/a_hallOfFame.php b/hwe/a_hallOfFame.php index 3e46207e..1ef2d2c2 100644 --- a/hwe/a_hallOfFame.php +++ b/hwe/a_hallOfFame.php @@ -4,10 +4,12 @@ namespace sammo; include "lib.php"; include "func.php"; +$session = Session::getInstance()->setReadOnly(); + $db = DB::db(); $connect=$db->get(); -increaseRefresh("명예의전당", 2); +increaseRefresh("명예의전당", 1); ?> @@ -17,16 +19,16 @@ increaseRefresh("명예의전당", 2); <?=UniqueConst::$serverName?>: 명예의 전당 - + - +
명 예 의 전 당
- +
- - "; +$templates = new \League\Plates\Engine('templates'); - for ($k=0; $k < 10; $k++) { - $gen = MYDB_fetch_array($result); - $name[$k] = $gen['name']; - $nation[$k] = $gen['nation']; - $data[$k] = $gen['data']; - $color[$k] = $gen['color']; - $pic[$k] = $gen['picture']; - if ($color[$k] == "") { - $color[$k] = GameConst::$basecolor4; +foreach($types as $idx=>$typeName) { + $hallResult = $db->query('SELECT * FROM ng_hall WHERE server_id = %s AND `type`=%i ORDER BY `value` DESC LIMIT 10', UniqueConst::$serverID, $idx); + + $hallResult = array_map(function($general){ + $aux = Json::decode($general['aux']); + $general += $aux; + if(!key_exists('color', $general)){ + $general['color'] = GameConst::$basecolor4; + $general['fgColor'] = newColor($general['color']); } - if ($nation[$k] == "") { - $nation[$k] = " "; + if(key_exists('picture', $general)){ + $imageTemp = GetImageURL($general['imgsvr']); + $general['pictureFullPath'] = "$imageTemp/{$general['picture']}"; } - /* - if($pic[$k] == "") { - echo ""; - } else { - $imageTemp = GetImageURL($gen['imgsvr']); - echo ""; - } - */ - } - -// echo ""; - - for ($k=0; $k < 10; $k++) { - echo ""; - } - - echo ""; - - for ($k=0; $k < 10; $k++) { - echo ""; - } - - echo ""; - - for ($k=0; $k < 10; $k++) { - if ($i == 5 || $i == 7 || $i == 20) { - $data[$k] = intdiv($data[$k], 100).".".($data[$k]%100)." %"; + else{ + $general['pictureFullPath'] = GetImageURL(0)."/default.jpg"; } - if ($i >= 13 && $i <= 16) { - $data[$k] = intdiv($data[$k], 100).".".($data[$k]%100)." %"; - } - echo ""; - } - echo ""; + return $general; + }, $hallResult); + + echo $templates->render('hallOfFrame', [ + 'typeName'=>$typeName, + 'generals'=>$hallResult + ]); } ?> -
$type[$i]
1위2위3위4위5위6위7위8위9위10위
 
{$nation[$k]}
{$name[$k]}
{$data[$k]}
- + +
diff --git a/hwe/a_history.php b/hwe/a_history.php index 739658c3..72e127ba 100644 --- a/hwe/a_history.php +++ b/hwe/a_history.php @@ -30,15 +30,11 @@ if ($con >= 2) { exit(); } -$query = "select year,month from history order by no limit 1"; -$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect), ""); -$history = MYDB_fetch_array($result); -$s = ($history['year']*12) + $history['month']; +[$s_year, $s_month] = $db->queryFirstList('SELECT year, month FROM history WHERE server_id = %s ORDER BY year ASC, month ASC LIMIT 1', UniqueConst::$serverID); +$s = $s_year * 12 + $s_month; -$query = "select year,month from history order by no desc limit 1"; -$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect), ""); -$history = MYDB_fetch_array($result); -$e = ($history['year']*12) + $history['month']; +[$e_year, $e_month] = $db->queryFirstList('SELECT year, month FROM history WHERE server_id = %s ORDER BY year DESC, month DESC LIMIT 1', UniqueConst::$serverID); +$e = $e_year * 12 + $e_month; //FIXME: $yearmonth가 올바르지 않을 경우에 처리가 필요. if (!$yearmonth) { @@ -98,24 +94,19 @@ if ($month <= 0) { diff --git a/hwe/css/hallOfFrame.css b/hwe/css/hallOfFrame.css new file mode 100644 index 00000000..3df9e35c --- /dev/null +++ b/hwe/css/hallOfFrame.css @@ -0,0 +1,76 @@ + +.rankView { + width:1100px; + margin:auto; + position:relative; + font-size:13px; +} + +.rankView h3{ + text-align:center; + margin:0; + padding:2px; + border-bottom:1px solid gray; +} + +.rankView ul{ + list-style:none; + margin:0; + padding:0; + overflow:hidden; + box-sizing: border-box; + margin-top:-1px; + margin-bottom:-1px; +} + +.rankView li{ + display:inline-block; + vertical-align: top; + width:110px; + height:149px; + text-align:center; + border-right:1px solid gray; + border-top:1px solid gray; + margin:0; + box-sizing: border-box; +} + +.rankView li.no_value{ + height:128px; +} + +.rankView .no_value .hall_value{ + display:none; +} + +.rankView .hall_nation{ + font-size:11px; + border-top:1px solid gray; + border-bottom:1px solid gray; +} + +.rankView .hall_name{ + display: flex; + justify-content: center; + flex-direction: column; + resize: vertical; + font-size:11px; + height:28px; +} + +.rankView .hall_name p{ + padding:0; + margin:0; +} + +.rankView .hall_owner{ + font-size:95%; +} + +.rankView .hall_value{ + line-height:13px; + box-sizing: border-box; + border-top:1px solid gray; + padding-top:3px; + padding-bottom:3px; +} \ No newline at end of file diff --git a/hwe/func.php b/hwe/func.php index 53e44080..54539d0b 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -1916,10 +1916,6 @@ function updateTurntime($no) { $josaYi = JosaUtil::pick($general['name2'], '이'); $alllog[0] = "●{$admin['month']}월:{$general['name2']}{$josaYi} {$general['name']}의 육체에서 유체이탈합니다!"; pushGeneralPublicRecord($alllog, $admin['year'], $admin['month']); - - if($admin['isunited'] == 0) { - CheckHall($no); - } } else { // 군주였으면 유지 이음 if($general['level'] == 12) { @@ -2060,9 +2056,10 @@ function updateTurntime($no) { function CheckHall($no) { $db = DB::db(); - $connect=$db->get(); - $type = array( + + + $types = array( "experience", "dedication", "firenum", @@ -2086,25 +2083,27 @@ function CheckHall($no) { "betrate" ); - $query = "select name,nation,picture, - experience,dedication,warnum,firenum,killnum, - killnum/warnum*10000 as winrate,killcrew,killcrew/deathcrew*10000 as killrate, - dex0,dex10,dex20,dex30,dex40, - ttw/(ttw+ttd+ttl)*10000 as ttrate, ttw+ttd+ttl as tt, - tlw/(tlw+tld+tll)*10000 as tlrate, tlw+tld+tll as tl, - tpw/(tpw+tpd+tpl)*10000 as tprate, tpw+tpd+tpl as tp, - tiw/(tiw+tid+til)*10000 as tirate, tiw+tid+til as ti, - betgold, betwin, betwingold, betwingold/betgold*10000 as betrate - from general where no='$no'"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $general = MYDB_fetch_array($result); + $general = $db->queryFirstRow('SELECT name,name2,owner,nation,picture,imgsvr, + experience,dedication,warnum,firenum,killnum, + killnum/warnum*10000 as winrate,killcrew,killcrew/deathcrew*10000 as killrate, + dex0,dex10,dex20,dex30,dex40, + ttw/(ttw+ttd+ttl)*10000 as ttrate, ttw+ttd+ttl as tt, + tlw/(tlw+tld+tll)*10000 as tlrate, tlw+tld+tll as tl, + tpw/(tpw+tpd+tpl)*10000 as tprate, tpw+tpd+tpl as tp, + tiw/(tiw+tid+til)*10000 as tirate, tiw+tid+til as ti, + betgold, betwin, betwingold, betwingold/betgold*10000 as betrate + from general where no=%i', $no); + + if(!$general){ + return; + } $nation = getNationStaticInfo($general['nation']); - for($k=0; $k < 21; $k++) { - $query = "select * from hall where type='$k' order by data desc"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $count = MYDB_num_rows($result); + $serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games'); + + foreach($types as $idx=>$typeName) { + //승률,살상률인데 10회 미만 전투시 스킵 if(($k == 5 || $k == 7) && $general['warnum']<10) { continue; } @@ -2119,26 +2118,32 @@ function CheckHall($no) { //수익률인데 1000미만시 스킵 if($k == 20 && $general['betgold'] < 1000) { continue; } - $rank = 10; - for($i=0; $i < $count; $i++) { - $ranker = MYDB_fetch_array($result); + $aux = [ + 'nationName'=>$nation['name'], + 'bgColor'=>$nation['color'], + 'fgColor'=>newColor($nation['color']), + 'picture'=>$general['picture'], + 'imgsvr'=>$general['imgsvr'], + 'date'=>date('Y-m-d H:i:s'), + 'owner_name'=>$general['name2'], + 'server_id'=>UniqueConst::$serverID, + 'printValue'=>$general['value'] + ]; + $jsonAux = Json::encode($aux); - if($general[$type[$k]] >= $ranker['data']) { - $rank = $i; - break; - } - } - for($i=8; $i >= $rank; $i--) { - $j = $i + 1; - $query = "select * from hall where type='$k' and rank='$i'"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $ranker = MYDB_fetch_array($result); - - $query = "update hall set name='{$ranker['name']}', nation='{$ranker['nation']}', data='{$ranker['data']}', color='{$ranker['color']}', picture='{$ranker['picture']}' where type='$k' and rank='$j'"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - } - $query = "update hall set name='{$general['name']}', nation='{$nation['name']}', data='{$general[$type[$k]]}', color='{$nation['color']}', picture='{$general['picture']}' where type='$k' and rank='$rank'"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + $db->insertUpdate('ng_hall', [ + 'server_id'=>UniqueConst::$serverID, + 'type'=>$idx, + 'general_no'=>$no, + 'value'=>$general[$typeName], + 'owner'=>$general['owner']??null, + 'serverIdx'=>$serverCnt, + 'serverName'=>UniqueConst::$serverName, + 'aux'=>$jsonAux + ],[ + 'value'=>$general[$typeName], + 'aux'=>$jsonAux + ]); } } diff --git a/hwe/func_history.php b/hwe/func_history.php index 813916ec..0f6952bf 100644 --- a/hwe/func_history.php +++ b/hwe/func_history.php @@ -298,6 +298,7 @@ function LogHistory($isFirst=0) { if(STEP_LOG) pushStepLog(date('Y-m-d H:i:s').', contents collected'); $db->insert('history', [ + 'server_id' => UniqueConst::$serverID, 'year' => $year, 'month' => $month, 'map' => $map_json, diff --git a/hwe/func_map.php b/hwe/func_map.php index 74593e51..6fbdc56a 100644 --- a/hwe/func_map.php +++ b/hwe/func_map.php @@ -1,12 +1,14 @@ serverID = Util::array_get($obj['serverID'], null); $this->year = Util::array_get($obj['year']); $this->month = Util::array_get($obj['month']); $this->aux = Util::array_get($obj['aux'],[]); @@ -19,14 +21,20 @@ class MapRequest{ /** * @param int $year * @param int $month + * @param string|null $serverID * @return mixed */ -function getHistoryMap($year, $month){ +function getHistoryMap($year, $month, ?string $serverID=null){ if(!$year || !$month){ return ['result'=>false, 'reason'=>'연 월이 지정되지 않음']; } - $map = DB::db()->queryFirstField('select map from history where year=%i and month=%i', + if($serverID === null){ + $serverID = UniqueConst::$serverID; + } + + $map = DB::db()->queryFirstField('SELECT map FROM history WHERE server_id = %s AND year=%i and month=%i', + $serverID, $year, $month); @@ -47,7 +55,7 @@ function getWorldMap($req){ } if($req->year && $req->month){ - return getHistoryMap($req->year, $req->month); + return getHistoryMap($req->year, $req->month, $req->serverID??null); } $session = Session::getInstance(); diff --git a/hwe/func_message.php b/hwe/func_message.php index 3f512278..f00def43 100644 --- a/hwe/func_message.php +++ b/hwe/func_message.php @@ -44,15 +44,3 @@ function getMailboxList(){ return $result; } - - -//Legacy - -function moveMsg($table, $msgtype, $msgnum, $msg, $type, $who, $when, $column, $value) { - $db = DB::db(); - $connect=$db->get(); - - //TODO: moveMsg 쓰는 곳 모두 고쳐!!! - $query = "update {$table} set {$msgtype}{$msgnum}='$msg',{$msgtype}{$msgnum}_type='$type',{$msgtype}{$msgnum}_who='$who',{$msgtype}{$msgnum}_when='$when' where {$column}='$value'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); -} diff --git a/hwe/j_map_history.php b/hwe/j_map_history.php index 688cec7c..a9664449 100644 --- a/hwe/j_map_history.php +++ b/hwe/j_map_history.php @@ -6,6 +6,7 @@ include "func.php"; $year = Util::getReq('year', 'int'); $month = Util::getReq('month', 'int'); +$serverID = Util::getReq('server_id', 'string', null); extractMissingPostToGlobals(); @@ -26,13 +27,17 @@ if(!$year || !$month) { } +if(!$serverID){ + $serverID = UniqueConst::$serverID; +} + //로그인 검사 $session = Session::requireGameLogin([])->setReadOnly(); $db = DB::db(); $connect=$db->get(); -$map = $db->queryFirstField('SELECT map FROM history WHERE year=%i AND month=%i', $year, $month); +$map = $db->queryFirstField('SELECT map FROM history WHERE server_id=%s AND year=%i AND month=%i', $serverID, $year, $month); if(!$map){ Json::die([ diff --git a/hwe/sammo/ResetHelper.php b/hwe/sammo/ResetHelper.php index 90a8f2d8..3c546b44 100644 --- a/hwe/sammo/ResetHelper.php +++ b/hwe/sammo/ResetHelper.php @@ -94,7 +94,8 @@ class ResetHelper{ (KVStorage::getStorage($db, 'game_env'))->resetValues(); return [ - 'result'=>true + 'result'=>true, + 'serverID'=>$serverID ]; } @@ -128,6 +129,8 @@ class ResetHelper{ return $clearResult; } + $serverID = $clearResult['serverID']; + $scenarioObj = new Scenario($scenario, false); $scenarioObj->buildConf(); @@ -205,6 +208,14 @@ class ResetHelper{ $gameStor->$key = $value; } + $db->insert('ng_games', [ + 'server_id'=>$serverID, + 'date'=>$turntime, + 'winner_nation'=>null, + 'scenario'=>$scenario, + 'env'=>Json::encode($env) + ]); + $scenarioObj->build($env); $db->update('plock', [ diff --git a/hwe/sql/reset.sql b/hwe/sql/reset.sql index 83bc8cef..3b3174f0 100644 --- a/hwe/sql/reset.sql +++ b/hwe/sql/reset.sql @@ -31,9 +31,6 @@ DROP TABLE IF EXISTS auction; # 통계 테이블 삭제 DROP TABLE IF EXISTS statistic; -# 연감 테이블 삭제 -DROP TABLE IF EXISTS history; - # 이벤트 테이블 삭제 DROP TABLE IF EXISTS event; diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 1bf3bffb..c7739da7 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -362,19 +362,6 @@ ENGINE=InnoDB; ########################################################################### ## 명전 테이블 ########################################################################### -create table if not exists hall ( - no int(6) not null auto_increment, - type int(2) default 0, - rank int(2) default 0, - name char(64) default '', - nation char(12) default '', - data int(5) default 0, - color char(12) default '', - picture varchar(40) default '', - - PRIMARY KEY (no), - UNIQUE INDEX `type` (`type`, `rank`) - ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4; CREATE TABLE if not exists `ng_hall` ( `id` INT(11) NOT NULL AUTO_INCREMENT, @@ -382,12 +369,12 @@ CREATE TABLE if not exists `ng_hall` ( `scenario` INT(11) NOT NULL, `general_no` INT(11) NOT NULL, `type` INT(11) NOT NULL, - `value` INT(11) NOT NULL, + `value` DOUBLE NOT NULL, `owner` INT(11) NULL DEFAULT NULL, `aux` TEXT NOT NULL DEFAULT '{}', PRIMARY KEY (`id`), UNIQUE INDEX `server_general` (`server_id`, `type`, `general_no`), - UNIQUE INDEX `owner` (`owner`, `server_id`), + UNIQUE INDEX `owner` (`owner`, `server_id`, `type`), INDEX `server_show` (`server_id`, `type`, `value`), INDEX `scenario` (`scenario`, `type`, `value`) ) @@ -398,19 +385,20 @@ ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ## 왕조 테이블 ########################################################################### -CREATE TABLE `ng_games` ( +CREATE TABLE if not exists `ng_games` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `server_id` CHAR(20) NOT NULL, `date` DATETIME NOT NULL, `winner_nation` INT(11) NULL DEFAULT NULL, - `aux` TEXT NOT NULL COMMENT 'json', + `scenario` INT(11) NOT NULL, + `env` TEXT NOT NULL COMMENT 'json', PRIMARY KEY (`id`), UNIQUE INDEX `server_id` (`server_id`), INDEX `date` (`date`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; -CREATE TABLE `ng_old_nations` ( +CREATE TABLE if not exists `ng_old_nations` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `server_id` CHAR(20) NOT NULL DEFAULT '0', `nation` INT(11) NOT NULL DEFAULT '0', @@ -457,29 +445,6 @@ create table if not exists emperior ( PRIMARY KEY (no) ) ENGINE=INNODB ROW_FORMAT=COMPRESSED DEFAULT CHARSET=utf8mb4; -INSERT IGNORE INTO `hall` (`type`, `rank`) VALUES - ( 0, 0), ( 0, 1), ( 0, 2), ( 0, 3), ( 0, 4), ( 0, 5), ( 0, 6), ( 0, 7), ( 0, 8), ( 0, 9), - ( 1, 0), ( 1, 1), ( 1, 2), ( 1, 3), ( 1, 4), ( 1, 5), ( 1, 6), ( 1, 7), ( 1, 8), ( 1, 9), - ( 2, 0), ( 2, 1), ( 2, 2), ( 2, 3), ( 2, 4), ( 2, 5), ( 2, 6), ( 2, 7), ( 2, 8), ( 2, 9), - ( 3, 0), ( 3, 1), ( 3, 2), ( 3, 3), ( 3, 4), ( 3, 5), ( 3, 6), ( 3, 7), ( 3, 8), ( 3, 9), - ( 4, 0), ( 4, 1), ( 4, 2), ( 4, 3), ( 4, 4), ( 4, 5), ( 4, 6), ( 4, 7), ( 4, 8), ( 4, 9), - ( 5, 0), ( 5, 1), ( 5, 2), ( 5, 3), ( 5, 4), ( 5, 5), ( 5, 6), ( 5, 7), ( 5, 8), ( 5, 9), - ( 6, 0), ( 6, 1), ( 6, 2), ( 6, 3), ( 6, 4), ( 6, 5), ( 6, 6), ( 6, 7), ( 6, 8), ( 6, 9), - ( 7, 0), ( 7, 1), ( 7, 2), ( 7, 3), ( 7, 4), ( 7, 5), ( 7, 6), ( 7, 7), ( 7, 8), ( 7, 9), - ( 8, 0), ( 8, 1), ( 8, 2), ( 8, 3), ( 8, 4), ( 8, 5), ( 8, 6), ( 8, 7), ( 8, 8), ( 8, 9), - ( 9, 0), ( 9, 1), ( 9, 2), ( 9, 3), ( 9, 4), ( 9, 5), ( 9, 6), ( 9, 7), ( 9, 8), ( 9, 9), - (10, 0), (10, 1), (10, 2), (10, 3), (10, 4), (10, 5), (10, 6), (10, 7), (10, 8), (10, 9), - (11, 0), (11, 1), (11, 2), (11, 3), (11, 4), (11, 5), (11, 6), (11, 7), (11, 8), (11, 9), - (12, 0), (12, 1), (12, 2), (12, 3), (12, 4), (12, 5), (12, 6), (12, 7), (12, 8), (12, 9), - (13, 0), (13, 1), (13, 2), (13, 3), (13, 4), (13, 5), (13, 6), (13, 7), (13, 8), (13, 9), - (14, 0), (14, 1), (14, 2), (14, 3), (14, 4), (14, 5), (14, 6), (14, 7), (14, 8), (14, 9), - (15, 0), (15, 1), (15, 2), (15, 3), (15, 4), (15, 5), (15, 6), (15, 7), (15, 8), (15, 9), - (16, 0), (16, 1), (16, 2), (16, 3), (16, 4), (16, 5), (16, 6), (16, 7), (16, 8), (16, 9), - (17, 0), (17, 1), (17, 2), (17, 3), (17, 4), (17, 5), (17, 6), (17, 7), (17, 8), (17, 9), - (18, 0), (18, 1), (18, 2), (18, 3), (18, 4), (18, 5), (18, 6), (18, 7), (18, 8), (18, 9), - (19, 0), (19, 1), (19, 2), (19, 3), (19, 4), (19, 5), (19, 6), (19, 7), (19, 8), (19, 9), - (20, 0), (20, 1), (20, 2), (20, 3), (20, 4), (20, 5), (20, 6), (20, 7), (20, 8), (20, 9); - ########################################################################### ## 외교 테이블 ########################################################################### @@ -566,20 +531,22 @@ create table statistic ( ########################################################################### ## 연감 테이블 ########################################################################### -create table history ( - no int(6) not null auto_increment, - year int(4) default 0, - month int(2) default 0, - map mediumtext default '', - log text default '', - genlog mediumtext default '', - nation text default '', - power text default '', - gen text default '', - city text default '', - - PRIMARY KEY (no) - ) ENGINE=INNODB ROW_FORMAT=COMPRESSED DEFAULT CHARSET=utf8mb4; +CREATE TABLE if not exists `history` ( + `no` INT(6) NOT NULL AUTO_INCREMENT, + `server_id` CHAR(20) NOT NULL DEFAULT '', + `year` INT(4) NULL DEFAULT '0', + `month` INT(2) NULL DEFAULT '0', + `map` MEDIUMTEXT NULL DEFAULT '', + `log` TEXT NULL DEFAULT '', + `genlog` MEDIUMTEXT NULL DEFAULT '', + `nation` TEXT NULL DEFAULT '', + `power` TEXT NULL DEFAULT '', + `gen` TEXT NULL DEFAULT '', + `city` TEXT NULL DEFAULT '', + PRIMARY KEY (`no`), + INDEX `server_id` (`server_id`, `year`, `month`) +) +ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ########################################################################### ## 이벤트 핸들러 테이블 diff --git a/hwe/templates/hallOfFrame.php b/hwe/templates/hallOfFrame.php new file mode 100644 index 00000000..bde96ea1 --- /dev/null +++ b/hwe/templates/hallOfFrame.php @@ -0,0 +1,21 @@ +
+

+
    +$general): ?>
  • > +
    + +
    +
    + +
    + +
    +

    + +

    ()
    + +

    +
    +
  • +
+
\ No newline at end of file diff --git a/i_entrance/entrance.php b/i_entrance/entrance.php index 59b972fc..182c8500 100644 --- a/i_entrance/entrance.php +++ b/i_entrance/entrance.php @@ -92,12 +92,12 @@ if ($userGrade >= 5) { 계정은 한번 등록으로 계속 사용합니다. 각 서버 리셋시 캐릭터만 새로 생성하면 됩니다.

체섭 : 메인서버입니다. 천하통일에 도전하여 왕조일람과 명예의전당에 올라봅시다! (주로 1턴=60분)
-퀘섭 : 실제 장수가 되어 가상의 역사를 만들어 봅시다!
-풰섭 : 실제 장수들과 어울려 사실적 역사를 체험해 봅시다!
-퉤섭 : 주로 패치사항 미리보기 테스트 서버입니다.
-냐섭 : 신 서버.
-퍄섭 : 신 서버2.
-훼섭 : 1일천하 서버. 또는 운영자 테스트용 서버입니다.
+퀘섭 : 마이너 서버 그룹1. 비교적 느린 시간으로 운영됩니다.
+풰섭 : 마이너 서버 그룹1. 비교적 느린 시간으로 운영됩니다.
+퉤섭 : 마이너 서버 그룹2. 비교적 빠른 시간으로 운영됩니다.
+냐섭 : 마이너 서버 그룹3. 독특한 컨셉 위주로 운영됩니다.
+퍄섭 : 마이너 서버 그룹3. 독특한 컨셉 위주로 운영됩니다.
+훼섭 : 운영자 테스트 서버입니다. 기습적으로 열리고, 닫힐 수 있습니다.
+
명 예 의 전 당
+시나리오 검색 : +
$typeName) { - $hallResult = $db->query('SELECT * FROM ng_hall WHERE server_id = %s AND `type`=%i ORDER BY `value` DESC LIMIT 10', UniqueConst::$serverID, $idx); +foreach($types as $idx=>[$typeName, $typeValue]) { + $hallResult = $db->query('SELECT * FROM ng_hall WHERE `type`=%i AND %? ORDER BY `value` DESC LIMIT 10', $idx, $searchFilter); - $hallResult = array_map(function($general){ + $hallResult = array_map(function($general)use($typeValue){ $aux = Json::decode($general['aux']); $general += $aux; if(!key_exists('color', $general)){ - $general['color'] = GameConst::$basecolor4; - $general['fgColor'] = newColor($general['color']); + $general['bgColor'] = GameConst::$basecolor4; + } + if(!key_exists('fgColor', $general)){ + $general['fgColor'] = newColor($general['bgColor']); + } + if(key_exists('picture', $general)){ $imageTemp = GetImageURL($general['imgsvr']); $general['pictureFullPath'] = "$imageTemp/{$general['picture']}"; @@ -71,6 +107,17 @@ foreach($types as $idx=>$typeName) { else{ $general['pictureFullPath'] = GetImageURL(0)."/default.jpg"; } + + if(!key_exists('printValue', $general)){ + $value = $general['value']; + if($typeValue == 'percent'){ + $general['printValue'] = number_format($value*100, 2).'%'; + } + else { + $general['printValue'] = number_format($value); + } + } + return $general; }, $hallResult); diff --git a/hwe/css/common.css b/hwe/css/common.css index 81edf37e..f49e8ec8 100644 --- a/hwe/css/common.css +++ b/hwe/css/common.css @@ -181,3 +181,39 @@ div.bar_out div.bar_in{ float:left; } +.tooltip{ + position: relative; +} + +.tooltip .tooltiptext { + visibility: hidden; + width: 120px; + background-color: #555; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px 0; + position: absolute; + z-index: 1; + bottom: 125%; + left: 50%; + margin-left: -60px; + opacity: 0; + transition: opacity 0.3s; +} + +.tooltip .tooltiptext::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: #555 transparent transparent transparent; +} + +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} \ No newline at end of file diff --git a/hwe/css/hallOfFrame.css b/hwe/css/hallOfFame.css similarity index 83% rename from hwe/css/hallOfFrame.css rename to hwe/css/hallOfFame.css index 3df9e35c..dc3caffa 100644 --- a/hwe/css/hallOfFrame.css +++ b/hwe/css/hallOfFame.css @@ -17,7 +17,6 @@ list-style:none; margin:0; padding:0; - overflow:hidden; box-sizing: border-box; margin-top:-1px; margin-bottom:-1px; @@ -39,6 +38,25 @@ height:128px; } +.rankView li.has_server { + height:168px; +} + +.rankView li.no_value.has_server { + height:146px; +} + +.rankView .hall_server{ + border-top:1px solid gray; +} + +.rankView .tooltip .tooltiptext{ + width:220px; + margin-left: -110px; +} + + + .rankView .no_value .hall_value{ display:none; } diff --git a/hwe/func.php b/hwe/func.php index 54539d0b..42d4738a 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -2056,8 +2056,7 @@ function updateTurntime($no) { function CheckHall($no) { $db = DB::db(); - - + $gameStor = KVStorage::getStorage($db, 'game_env'); $types = array( "experience", @@ -2085,63 +2084,69 @@ function CheckHall($no) { $general = $db->queryFirstRow('SELECT name,name2,owner,nation,picture,imgsvr, experience,dedication,warnum,firenum,killnum, - killnum/warnum*10000 as winrate,killcrew,killcrew/deathcrew*10000 as killrate, + killnum/warnum as winrate,killcrew,killcrew/deathcrew as killrate, dex0,dex10,dex20,dex30,dex40, - ttw/(ttw+ttd+ttl)*10000 as ttrate, ttw+ttd+ttl as tt, - tlw/(tlw+tld+tll)*10000 as tlrate, tlw+tld+tll as tl, - tpw/(tpw+tpd+tpl)*10000 as tprate, tpw+tpd+tpl as tp, - tiw/(tiw+tid+til)*10000 as tirate, tiw+tid+til as ti, - betgold, betwin, betwingold, betwingold/betgold*10000 as betrate + ttw/(ttw+ttd+ttl) as ttrate, ttw+ttd+ttl as tt, + tlw/(tlw+tld+tll) as tlrate, tlw+tld+tll as tl, + tpw/(tpw+tpd+tpl) as tprate, tpw+tpd+tpl as tp, + tiw/(tiw+tid+til) as tirate, tiw+tid+til as ti, + betgold, betwin, betwingold, betwingold/betgold as betrate from general where no=%i', $no); if(!$general){ return; } + $unitedDate = date('Y-m-d H:i:s'); $nation = getNationStaticInfo($general['nation']); $serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games'); + [$scenarioIdx, $scenarioName, $startTime] = $gameStor->getValuesAsArray(['scenario', 'scenario_text', 'starttime']); + foreach($types as $idx=>$typeName) { //승률,살상률인데 10회 미만 전투시 스킵 - if(($k == 5 || $k == 7) && $general['warnum']<10) { continue; } + if(($typeName === 'winrate' || $typeName === 'killrate') && $general['warnum']<10) { continue; } //토너승률인데 50회 미만시 스킵 - if($k == 13 && $general['tt'] < 50) { continue; } + if($typeName === 'ttrate' && $general['tt'] < 50) { continue; } //토너승률인데 50회 미만시 스킵 - if($k == 14 && $general['tl'] < 50) { continue; } + if($typeName === 'tlrate' && $general['tl'] < 50) { continue; } //토너승률인데 50회 미만시 스킵 - if($k == 15 && $general['tp'] < 50) { continue; } + if($typeName === 'tprate' && $general['tp'] < 50) { continue; } //토너승률인데 50회 미만시 스킵 - if($k == 16 && $general['ti'] < 50) { continue; } + if($typeName === 'tirate' && $general['ti'] < 50) { continue; } //수익률인데 1000미만시 스킵 - if($k == 20 && $general['betgold'] < 1000) { continue; } + if($typeName === 'betrate' && $general['betgold'] < 1000) { continue; } $aux = [ + 'name'=>$general['name'], 'nationName'=>$nation['name'], 'bgColor'=>$nation['color'], 'fgColor'=>newColor($nation['color']), 'picture'=>$general['picture'], 'imgsvr'=>$general['imgsvr'], - 'date'=>date('Y-m-d H:i:s'), + 'startTime'=>$startTime, + 'unitedTime'=>$unitedDate, 'owner_name'=>$general['name2'], - 'server_id'=>UniqueConst::$serverID, - 'printValue'=>$general['value'] + 'serverID'=>UniqueConst::$serverID, + 'serverIdx'=>$serverCnt, + 'serverName'=>UniqueConst::$serverName, + 'scenarioName'=>$scenarioName, ]; $jsonAux = Json::encode($aux); $db->insertUpdate('ng_hall', [ 'server_id'=>UniqueConst::$serverID, - 'type'=>$idx, + 'scenario'=>$scenarioIdx, 'general_no'=>$no, - 'value'=>$general[$typeName], + 'type'=>$idx, + 'value'=>$general[$typeName]??0, 'owner'=>$general['owner']??null, - 'serverIdx'=>$serverCnt, - 'serverName'=>UniqueConst::$serverName, 'aux'=>$jsonAux ],[ - 'value'=>$general[$typeName], + 'value'=>$general[$typeName]??0, 'aux'=>$jsonAux ]); } diff --git a/hwe/func_gamerule.php b/hwe/func_gamerule.php index 39fad85f..0023c8e7 100644 --- a/hwe/func_gamerule.php +++ b/hwe/func_gamerule.php @@ -1126,7 +1126,7 @@ function checkEmperior() { $gameStor->isunited = 2; $gameStor->conlimit = $gameStor->conlimit*100; - $query = "select no from general where npc<2 and age>=45"; + $query = "select no from general where npc<2 and age>=40"; $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $count = MYDB_num_rows($result); diff --git a/hwe/func_history.php b/hwe/func_history.php index 0f6952bf..9be0b23e 100644 --- a/hwe/func_history.php +++ b/hwe/func_history.php @@ -127,6 +127,34 @@ function getBatLogRecent(int $no, int $count) { return join('
', array_reverse(getFormattedFileLogRecent(__dir__."/logs/".UniqueConst::$serverID."/batlog{$no}.txt", $count, 300))); } +function pushOldNationStop(int $no, int $nationNo){ + $logPrefixList = ['batres', 'gen', 'batlog']; + + $result = []; + foreach($logPrefixList as $logPrefix){ + $path = __dir__."/logs/".UniqueConst::$serverID."/{$logPrefix}{$no}.txt"; + if(file_exists($path)){ + $fileLen = @filesize($path); + } + else{ + $fileLen = 0; + } + $result[$logPrefix] = $fileLen; + } + + $baseStopPath = __dir__."/logs/".UniqueConst::$serverID."/stop{$no}.txt"; + if(file_exists($baseStopPath)){ + $oldValues = Json::decode(file_get_contents($baseStopPath)); + } + else{ + $oldValues = []; + } + + $oldValues[$nationNo] = $result; + + file_put_contents($baseStopPath, Json::encode($oldValues, Json::PRETTY)); +} + //DB-based function pushNationHistory($nation, $history) { if(!$nation || !$nation['nation']){ diff --git a/hwe/js/hallOfFame.js b/hwe/js/hallOfFame.js new file mode 100644 index 00000000..ad475cfd --- /dev/null +++ b/hwe/js/hallOfFame.js @@ -0,0 +1,7 @@ +jQuery(function($){ + $('#by_scenario').change(function(){ + var scenarioIdx = $(this).val(); + $.redirect('a_hallOfFame.php', {scenarioIdx:scenarioIdx}, 'get'); + }) + //$.redirect("processing.php",{ commandtype: commandtype, turn: turn}, 'post'); +}); \ No newline at end of file diff --git a/hwe/process_war.php b/hwe/process_war.php index 01bb6b1e..798c6198 100644 --- a/hwe/process_war.php +++ b/hwe/process_war.php @@ -1805,13 +1805,16 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) { $loseGeneralGold = 0; $loseGeneralRice = 0; //멸망국 장수들 역사 기록 및 로그 전달 - $query = "select no,name,nation,npc,gold,rice from general where nation='{$city['nation']}'"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $gencount = MYDB_num_rows($result); $josaYi = JosaUtil::pick($losenation['name'], '이'); $genlog = ["{$losenation['name']}{$josaYi} 멸망했습니다."]; - for($i=0; $i < $gencount; $i++) { - $gen = MYDB_fetch_array($result); + + + // 국가 백업 + $oldNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation=%i', $city['nation']); + $oldNationGenerals = $db->query('SELECT * FROM general WHERE nation=%i', $city['nation']); + $oldNation['generals'] = $oldNationGenerals; + + foreach($oldNationGenerals as $gen){ $loseGold = intdiv($gen['gold'] * (rand()%30+20), 100); $loseRice = intdiv($gen['rice'] * (rand()%30+20), 100); @@ -1823,6 +1826,7 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) { pushGenLog($gen, $genlog); pushGeneralHistory($gen, "●{$year}년 {$month}월:{$losenation['name']}{$josaYi} 멸망"); + pushOldNationStop($gen['no'], $city['nation']); $loseGeneralGold += $loseGold; $loseGeneralRice += $loseRice; @@ -1833,7 +1837,7 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) { || $nation['name'] == "왜족") { //등용장 미발부 } elseif(Util::randBool(0.5)) { - $msg = ScoutMessage::buildScoutMessage($general['no'], $gen['no'], $reason); + $msg = ScoutMessage::buildScoutMessage($general['no'], $gen['no']); if($msg){ $msg->send(true); } @@ -1874,6 +1878,7 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) { pushGenLog($gen, $genlog); } + //분쟁기록 모두 지움 DeleteConflict($city['nation']); // 전 장수 공헌 명성치 깎음 @@ -1892,8 +1897,6 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) { $query = "delete from diplomacy where me='{$city['nation']}' or you='{$city['nation']}'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - // 국가 백업 - $oldNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation=%i', $city['nation']); $db->insert('ng_old_nations', [ 'server_id'=>UniqueConst::$serverID, 'nation'=>$city['nation'], diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index c7739da7..05be10c4 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -363,7 +363,7 @@ ENGINE=InnoDB; ## 명전 테이블 ########################################################################### -CREATE TABLE if not exists `ng_hall` ( +CREATE TABLE `ng_hall` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `server_id` CHAR(20) NOT NULL, `scenario` INT(11) NOT NULL, @@ -378,7 +378,7 @@ CREATE TABLE if not exists `ng_hall` ( INDEX `server_show` (`server_id`, `type`, `value`), INDEX `scenario` (`scenario`, `type`, `value`) ) -ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPRESSED; ########################################################################### @@ -398,15 +398,16 @@ CREATE TABLE if not exists `ng_games` ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; -CREATE TABLE if not exists `ng_old_nations` ( +CREATE TABLE `ng_old_nations` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `server_id` CHAR(20) NOT NULL DEFAULT '0', `nation` INT(11) NOT NULL DEFAULT '0', `data` LONGTEXT NOT NULL DEFAULT '0' COMMENT 'json', + `date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `server_id` (`server_id`, `nation`) ) -ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPRESSED; create table if not exists emperior ( no int(6) not null auto_increment, @@ -546,7 +547,7 @@ CREATE TABLE if not exists `history` ( PRIMARY KEY (`no`), INDEX `server_id` (`server_id`, `year`, `month`) ) -ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPRESSED; ########################################################################### ## 이벤트 핸들러 테이블 diff --git a/hwe/templates/hallOfFrame.php b/hwe/templates/hallOfFrame.php index bde96ea1..c41182d4 100644 --- a/hwe/templates/hallOfFrame.php +++ b/hwe/templates/hallOfFrame.php @@ -1,13 +1,18 @@

    -$general): ?>
  • > +$general): ?>
  • '>
    - -
    + +
    기 + +
    + ~ +
    +

    -- 2.54.0 From 4c5ed4ce865e5e1b11f19ae10efbcbefa3b006a3 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 03:00:02 +0900 Subject: [PATCH 210/304] =?UTF-8?q?=EC=B2=9C=ED=86=B5=EC=8B=9C=EC=97=90?= =?UTF-8?q?=EB=8F=84=20ng=5Fold=5Fnations=EC=97=90=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EA=B0=80=20=EC=A0=80=EC=9E=A5=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_gamerule.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hwe/func_gamerule.php b/hwe/func_gamerule.php index 0023c8e7..17947042 100644 --- a/hwe/func_gamerule.php +++ b/hwe/func_gamerule.php @@ -1178,6 +1178,10 @@ function checkEmperior() { $genresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $level5 = MYDB_fetch_array($genresult); + $oldNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation=%i', $nation['nation']); + $oldNationGenerals = $db->query('SELECT * FROM general WHERE nation=%i', $nation['nation']); + $oldNation['generals'] = $oldNationGenerals; + $query = "select name,picture,killnum from general where nation='{$nation['nation']}' order by killnum desc limit 5"; // 오호장군 $tigerresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $tigernum = MYDB_num_rows($tigerresult); @@ -1234,6 +1238,16 @@ function checkEmperior() { $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $statGeneral = MYDB_fetch_array($result); + $oldNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation=%i', $nation['nation']); + $oldNation['generals'] = $db->query('SELECT * FROM general WHERE nation=%i', $nation['nation']); + + $db->insert('ng_old_nations', [ + 'server_id'=>UniqueConst::$serverID, + 'nation'=>$city['nation'], + 'data'=>Json::encode($oldNation) + ]); + + $nationHistory = DB::db()->queryFirstField('SELECT `history` FROM `nation` WHERE `nation` = %i', $nation['nation']); $db->insert('emperior', [ -- 2.54.0 From 93c8ef025936a9f2d88dac67355796213a760123 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 03:06:34 +0900 Subject: [PATCH 211/304] =?UTF-8?q?schema=20=EA=B0=92=20=EA=BC=AC=EC=9D=B4?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sql/schema.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 05be10c4..a0f1daac 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -363,7 +363,7 @@ ENGINE=InnoDB; ## 명전 테이블 ########################################################################### -CREATE TABLE `ng_hall` ( +CREATE TABLE if not exists `ng_hall` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `server_id` CHAR(20) NOT NULL, `scenario` INT(11) NOT NULL, @@ -398,7 +398,7 @@ CREATE TABLE if not exists `ng_games` ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; -CREATE TABLE `ng_old_nations` ( +CREATE TABLE if not exists `ng_old_nations` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `server_id` CHAR(20) NOT NULL DEFAULT '0', `nation` INT(11) NOT NULL DEFAULT '0', -- 2.54.0 From bf1575e92b661d620977f1f7842797ffa78b3f94 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 03:10:50 +0900 Subject: [PATCH 212/304] =?UTF-8?q?ng=5Fgames=20=EB=88=84=EB=9D=BD?= =?UTF-8?q?=EB=B6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sql/schema.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index a0f1daac..eafa5246 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -391,6 +391,7 @@ CREATE TABLE if not exists `ng_games` ( `date` DATETIME NOT NULL, `winner_nation` INT(11) NULL DEFAULT NULL, `scenario` INT(11) NOT NULL, + `scenario_name` TEXT NOT NULL, `env` TEXT NOT NULL COMMENT 'json', PRIMARY KEY (`id`), UNIQUE INDEX `server_id` (`server_id`), -- 2.54.0 From 1c69656d2cfd1081f61a09d29c2be0e9dda97698 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 03:15:38 +0900 Subject: [PATCH 213/304] =?UTF-8?q?=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/ResetHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/sammo/ResetHelper.php b/hwe/sammo/ResetHelper.php index 3c546b44..97e8f6c1 100644 --- a/hwe/sammo/ResetHelper.php +++ b/hwe/sammo/ResetHelper.php @@ -57,8 +57,8 @@ class ResetHelper{ $serverID = DB::prefix().'_'.date("ymd").'_'.Util::randomStr(4); - mkdir($servRoot.'/logs/'.$serverID, 0644); - mkdir($servRoot.'/data/'.$serverID, 0644); + mkdir($servRoot.'/logs/'.$serverID, 0744); + mkdir($servRoot.'/data/'.$serverID, 0744); $result = Util::generateFileUsingSimpleTemplate( $servRoot.'/d_setting/UniqueConst.orig.php', -- 2.54.0 From 974edf710cc31bac60f596c5091d4c99143db5f3 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 03:19:47 +0900 Subject: [PATCH 214/304] =?UTF-8?q?chmod=20=EC=9E=AC=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/ResetHelper.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hwe/sammo/ResetHelper.php b/hwe/sammo/ResetHelper.php index 97e8f6c1..d29505b0 100644 --- a/hwe/sammo/ResetHelper.php +++ b/hwe/sammo/ResetHelper.php @@ -16,8 +16,8 @@ class ResetHelper{ 'reason'=>'logs, data 디렉토리를 생성할 권한이 없습니다.' ]; } - mkdir($servRoot.'/logs', 0644); - mkdir($servRoot.'/data', 0644); + mkdir($servRoot.'/logs', 0755); + mkdir($servRoot.'/data', 0755); } if(!is_writable($servRoot.'/logs')){ @@ -57,8 +57,8 @@ class ResetHelper{ $serverID = DB::prefix().'_'.date("ymd").'_'.Util::randomStr(4); - mkdir($servRoot.'/logs/'.$serverID, 0744); - mkdir($servRoot.'/data/'.$serverID, 0744); + mkdir($servRoot.'/logs/'.$serverID, 0755); + mkdir($servRoot.'/data/'.$serverID, 0755); $result = Util::generateFileUsingSimpleTemplate( $servRoot.'/d_setting/UniqueConst.orig.php', -- 2.54.0 From 0762ca8e4f9b6bb9516774ba7c11f07f78a70503 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 03:20:57 +0900 Subject: [PATCH 215/304] =?UTF-8?q?ng=5Fgames=EC=97=90=20=EC=8B=9C?= =?UTF-8?q?=EB=82=98=EB=A6=AC=EC=98=A4=EB=AA=85=20=EB=82=A8=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sammo/ResetHelper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/hwe/sammo/ResetHelper.php b/hwe/sammo/ResetHelper.php index d29505b0..2447a7fb 100644 --- a/hwe/sammo/ResetHelper.php +++ b/hwe/sammo/ResetHelper.php @@ -213,6 +213,7 @@ class ResetHelper{ 'date'=>$turntime, 'winner_nation'=>null, 'scenario'=>$scenario, + 'scenario_name'=>$scenarioObj->getTitle(), 'env'=>Json::encode($env) ]); -- 2.54.0 From 86e29c457940c153907111584eee46548b19d809 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 03:33:56 +0900 Subject: [PATCH 216/304] =?UTF-8?q?=EC=B2=9C=ED=86=B5=EC=8B=9C=20=EC=9E=AC?= =?UTF-8?q?=EC=95=BC=20=EC=9E=A5=EC=88=98=EB=8F=84=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_gamerule.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hwe/func_gamerule.php b/hwe/func_gamerule.php index 17947042..00d8e168 100644 --- a/hwe/func_gamerule.php +++ b/hwe/func_gamerule.php @@ -1247,6 +1247,12 @@ function checkEmperior() { 'data'=>Json::encode($oldNation) ]); + $noNationGeneral = $db->query('SELECT * FROM general WHERE nation=0'); + $db->insert('ng_old_nations', [ + 'server_id'=>UniqueConst::$serverID, + 'nation'=>0, + 'data'=>Json::encode($noNationGeneral) + ]); $nationHistory = DB::db()->queryFirstField('SELECT `history` FROM `nation` WHERE `nation` = %i', $nation['nation']); -- 2.54.0 From 3b7912e2d5d3ef3dbc4c48d282d07c24ea3338b7 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 03:52:17 +0900 Subject: [PATCH 217/304] =?UTF-8?q?=EC=B4=88=EB=B0=98=203=EB=85=84=20?= =?UTF-8?q?=EC=88=98=EB=87=8C=20=EC=9E=84=EB=AA=85=20=EB=B0=A9=EC=8B=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_npc.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hwe/func_npc.php b/hwe/func_npc.php index 2e6c169d..1db6816e 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -1305,8 +1305,11 @@ function Promotion($nation, $level) { $query = "update general set level=1 where level<11 and level>4 and nation='$nation'"; MYDB_query($query, $connect) or Error("Promotion_02 ".MYDB_error($connect),""); + $maxBelong = $db->queryFirstField('SELECT max(belong) FROM `general` WHERE nation=%i', $nation); + $maxBelong = min($maxBelong - 1, 3); + //유저 후보 선택 - $query = "select no from general where nation='$nation' and npc<2 and level=1 and belong>=3 and killturn>='{$admin['killturn']}' order by rand() limit 0,1"; + $query = "select no from general where nation='$nation' and npc<2 and level=1 and belong>=$maxBelong and killturn>='{$admin['killturn']}' order by rand() limit 0,1"; $result = MYDB_query($query, $connect) or Error("Promotion_00 ".MYDB_error($connect),""); $userCandidate = MYDB_fetch_array($result); // 유저수뇌 안함 -- 2.54.0 From b1fe39ccb0416f7380e954eb083449a1d43778cf Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 26 Jun 2018 04:13:41 +0900 Subject: [PATCH 218/304] =?UTF-8?q?=EC=9E=84=EA=B4=80=20=EA=B6=8C=EC=9C=A0?= =?UTF-8?q?=EB=AC=B8=20=EA=B8=B8=EC=9D=B4=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/templates/invitationList.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/templates/invitationList.php b/hwe/templates/invitationList.php index 2cddf86a..097a09f8 100644 --- a/hwe/templates/invitationList.php +++ b/hwe/templates/invitationList.php @@ -10,8 +10,8 @@ >

- + diff --git a/hwe/processing.php b/hwe/processing.php index 1675a09f..5f0ba4d0 100644 --- a/hwe/processing.php +++ b/hwe/processing.php @@ -123,7 +123,7 @@ function starter($name, $type=0) { <?=$name?> - + diff --git a/hwe/select_npc.php b/hwe/select_npc.php index 6c29380a..12f919c9 100644 --- a/hwe/select_npc.php +++ b/hwe/select_npc.php @@ -32,7 +32,7 @@ $nationList = $db->query('SELECT nation,`name`,color,scout,scoutmsg FROM nation - + diff --git a/i_entrance/admin_userlist.php b/i_entrance/admin_userlist.php index e6aebe29..a2436b06 100644 --- a/i_entrance/admin_userlist.php +++ b/i_entrance/admin_userlist.php @@ -14,7 +14,7 @@ require(__dir__.'/../vendor/autoload.php'); - + diff --git a/i_entrance/entrance.php b/i_entrance/entrance.php index 182c8500..5e30b26f 100644 --- a/i_entrance/entrance.php +++ b/i_entrance/entrance.php @@ -28,7 +28,7 @@ $acl = $session->acl; - + diff --git a/i_entrance/user_info.php b/i_entrance/user_info.php index 53905a3b..b37a1a75 100644 --- a/i_entrance/user_info.php +++ b/i_entrance/user_info.php @@ -18,7 +18,7 @@ require(__dir__.'/../vendor/autoload.php'); - + diff --git a/index.php b/index.php index 6cba1f2a..40b1fa06 100644 --- a/index.php +++ b/index.php @@ -25,7 +25,7 @@ if ($session->isLoggedIn()) { 삼국지 모의전투 HiDCHe - + diff --git a/oauth_kakao/index.php b/oauth_kakao/index.php index 9b55fe26..587f9851 100644 --- a/oauth_kakao/index.php +++ b/oauth_kakao/index.php @@ -12,7 +12,7 @@ use \kakao\KakaoKey as KakaoKey; 카카오 로그인하기 - + diff --git a/hwe/func_legacy.php b/hwe/func_legacy.php index 81f8c272..914c877a 100644 --- a/hwe/func_legacy.php +++ b/hwe/func_legacy.php @@ -8,7 +8,9 @@ function printLimitMsg($turntime) { 접속제한 - + + + diff --git a/hwe/index.php b/hwe/index.php index 26059e8b..e3184f42 100644 --- a/hwe/index.php +++ b/hwe/index.php @@ -83,7 +83,8 @@ $color = "cyan"; <?=UniqueConst::$serverName?>: 메인 - + + diff --git a/hwe/install.php b/hwe/install.php index fa588c35..3f2cfef4 100644 --- a/hwe/install.php +++ b/hwe/install.php @@ -26,7 +26,8 @@ if($session->userGrade < 5 && !$allowReset){ 설치 - + + diff --git a/hwe/install_db.php b/hwe/install_db.php index aa01f23b..19766683 100644 --- a/hwe/install_db.php +++ b/hwe/install_db.php @@ -19,7 +19,8 @@ if($session->userGrade == 5){ 설치 - + + diff --git a/hwe/join.php b/hwe/join.php index 517d1588..fdac845d 100644 --- a/hwe/join.php +++ b/hwe/join.php @@ -31,7 +31,9 @@ $connect=$db->get(); <?=UniqueConst::$serverName?>: 장수생성 - + + + diff --git a/hwe/func_legacy.php b/hwe/func_legacy.php index 914c877a..3a831e0b 100644 --- a/hwe/func_legacy.php +++ b/hwe/func_legacy.php @@ -10,7 +10,7 @@ function printLimitMsg($turntime) { 접속제한 - + diff --git a/hwe/index.php b/hwe/index.php index e3184f42..791eecd2 100644 --- a/hwe/index.php +++ b/hwe/index.php @@ -84,7 +84,7 @@ $color = "cyan"; <?=UniqueConst::$serverName?>: 메인 - + diff --git a/hwe/join.php b/hwe/join.php index fdac845d..ada54e0c 100644 --- a/hwe/join.php +++ b/hwe/join.php @@ -33,7 +33,7 @@ $connect=$db->get(); <?=UniqueConst::$serverName?>: 장수생성 - + diff --git a/hwe/func_legacy.php b/hwe/func_legacy.php index 3a831e0b..9479cd82 100644 --- a/hwe/func_legacy.php +++ b/hwe/func_legacy.php @@ -10,7 +10,7 @@ function printLimitMsg($turntime) { 접속제한 - + diff --git a/hwe/index.php b/hwe/index.php index 791eecd2..dd0a7102 100644 --- a/hwe/index.php +++ b/hwe/index.php @@ -84,7 +84,7 @@ $color = "cyan"; <?=UniqueConst::$serverName?>: 메인 - + diff --git a/hwe/join.php b/hwe/join.php index ada54e0c..ba7a7ffc 100644 --- a/hwe/join.php +++ b/hwe/join.php @@ -33,7 +33,7 @@ $connect=$db->get(); <?=UniqueConst::$serverName?>: 장수생성 - + -- 2.54.0 From 4d0808efead30befb314225afae023cb91e43518 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 1 Jul 2018 15:27:41 +0900 Subject: [PATCH 266/304] =?UTF-8?q?=EB=AA=85=EC=9E=A5=EC=9D=BC=EB=9E=8C?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=A0=9C=EC=95=BD=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_bestGeneral.php | 85 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/hwe/a_bestGeneral.php b/hwe/a_bestGeneral.php index 5070e37f..95782bf9 100644 --- a/hwe/a_bestGeneral.php +++ b/hwe/a_bestGeneral.php @@ -69,22 +69,83 @@ $types = [ }], ["전 투 횟 수", "int", function($v){$v['value'] = $v['warnum']; return $v; }], ["승 리", "int", function($v){$v['value'] = $v['killnum']; return $v; }], - ["승 률", "percent", function($v){$v['value'] = $v['killnum'] / max(1, $v['warnum']); return $v; }], + ["승 률", "percent", function($v){ + if($v['warnum'] < 10){ + $v['value'] = 0; + } + else{ + $v['value'] = $v['killnum'] / max(1, $v['warnum']); + } + return $v; + }], ["사 살", "int", function($v){$v['value'] = $v['killcrew']; return $v; }], - ["살 상 률", "percent", function($v){$v['value'] = $v['killcrew'] / max(1, $v['deathcrew']); return $v; }], + ["살 상 률", "percent", function($v){ + if($v['warnum'] < 10){ + $v['value'] = 0; + } + else{ + $v['value'] = $v['killcrew'] / max(1, $v['deathcrew']); + } + return $v; + }], ["보 병 숙 련 도", "int", function($v){$v['value'] = $v['dex0']; return $v; }], ["궁 병 숙 련 도", "int", function($v){$v['value'] = $v['dex10']; return $v; }], ["기 병 숙 련 도", "int", function($v){$v['value'] = $v['dex20']; return $v; }], ["귀 병 숙 련 도", "int", function($v){$v['value'] = $v['dex30']; return $v; }], ["차 병 숙 련 도", "int", function($v){$v['value'] = $v['dex40']; return $v; }], - ["전 력 전 승 률", "percent", function($v){$v['value'] = $v['ttw']/max(1, $v['ttw']+$v['ttd']+$v['ttl']); return $v; }], - ["통 솔 전 승 률", "percent", function($v){$v['value'] = $v['tlw']/max(1, $v['tlw']+$v['tld']+$v['tll']); return $v; }], - ["일 기 토 승 률", "percent", function($v){$v['value'] = $v['tpw']/max(1, $v['tpw']+$v['tpd']+$v['tpl']); return $v; }], - ["설 전 승 률", "percent", function($v){$v['value'] = $v['tiw']/max(1, $v['tiw']+$v['tid']+$v['til']); return $v; }], + ["전 력 전 승 률", "percent", function($v){ + $totalCnt = $v['ttw']+$v['ttd']+$v['ttl']; + if($totalCnt < 50){ + $v['value'] = 0; + } + else{ + $v['value'] = $v['ttw']/max(1, $totalCnt); + } + return $v; + }], + ["통 솔 전 승 률", "percent", function($v){ + $totalCnt = $v['tlw']+$v['tld']+$v['tll']; + if($totalCnt < 50){ + $v['value'] = 0; + } + else{ + $v['value'] = $v['tlw']/max(1, $totalCnt); + } + return $v; + }], + ["일 기 토 승 률", "percent", function($v){ + $totalCnt = $v['tpw']+$v['tpd']+$v['tpl']; + if($totalCnt < 50){ + $v['value'] = 0; + } + else{ + $v['value'] = $v['tpw']/max(1, $totalCnt); + } + return $v; + }], + ["설 전 승 률", "percent", function($v){ + $totalCnt = $v['tiw']+$v['tid']+$v['til']; + if($totalCnt < 50){ + $v['value'] = 0; + } + else{ + $v['value'] = $v['tiw']/max(1, $totalCnt); + } + + return $v; + }], ["베 팅 투 자 액", "int", function($v){$v['value'] = $v['betgold']; return $v; }], ["베 팅 당 첨", "int", function($v){$v['value'] = $v['betwin']; return $v; }], ["베 팅 수 익 금", "int", function($v){$v['value'] = $v['betwingold']; return $v; }], - ["베 팅 수 익 률", "percent", function($v){$v['value'] = $v['betwingold']/max(1, $v['betgold']); return $v; }], + ["베 팅 수 익 률", "percent", function($v){ + if($v['getgold'] < 1000){ + $v['value'] = 0; + } + else{ + $v['value'] = $v['betwingold']/max(1, $v['betgold']); + } + return $v; + }], ]; $generals = array_map(function($general) use($nationColor, $nationName) { @@ -114,10 +175,15 @@ $generals = array_map(function($general) use($nationColor, $nationName) { $templates = new \League\Plates\Engine('templates'); foreach($types as $idx=>[$typeName, $typeValue, $typeFunc]){ - $typeGenerals = array_map(function($general) use($typeValue, $typeFunc){ + $validCnt = 0; + $typeGenerals = array_map(function($general) use($typeValue, $typeFunc, $validCnt){ $general = ($typeFunc)($general); $value = $general['value']; + if($value > 0){ + $validCnt+=1; + } + if($typeValue == 'percent'){ $general['printValue'] = number_format($value*100, 2).'%'; } @@ -132,9 +198,10 @@ foreach($types as $idx=>[$typeName, $typeValue, $typeFunc]){ return -($lhs['value'] <=> $rhs['value']); }); + echo $templates->render('hallOfFrame', [ 'typeName'=>$typeName, - 'generals'=>array_slice($typeGenerals, 0, 10) + 'generals'=>array_slice($typeGenerals, 0, min(10, $validCnt)) ]); } ?> -- 2.54.0 From 97ceaf221c12b71770264ad915c4c5c909c57606 Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 1 Jul 2018 15:30:15 +0900 Subject: [PATCH 267/304] =?UTF-8?q?=EB=AA=85=EC=9E=A5=20=EC=9D=BC=EB=9E=8C?= =?UTF-8?q?=20=EB=A0=88=ED=8D=BC=EB=9F=B0=EC=8A=A4=20=20=EA=B0=92=20?= =?UTF-8?q?=EB=AF=B8=ED=91=9C=EA=B8=B0=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_bestGeneral.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/a_bestGeneral.php b/hwe/a_bestGeneral.php index 95782bf9..4bc2c8ca 100644 --- a/hwe/a_bestGeneral.php +++ b/hwe/a_bestGeneral.php @@ -176,7 +176,7 @@ $templates = new \League\Plates\Engine('templates'); foreach($types as $idx=>[$typeName, $typeValue, $typeFunc]){ $validCnt = 0; - $typeGenerals = array_map(function($general) use($typeValue, $typeFunc, $validCnt){ + $typeGenerals = array_map(function($general) use($typeValue, $typeFunc, &$validCnt){ $general = ($typeFunc)($general); $value = $general['value']; -- 2.54.0 From 0f0bea61d009fd67325a7d4873249c5f28b0efca Mon Sep 17 00:00:00 2001 From: hide_d Date: Sun, 1 Jul 2018 15:34:20 +0900 Subject: [PATCH 268/304] =?UTF-8?q?=EB=B2=A0=ED=8C=85=EC=88=98=EC=9D=B5?= =?UTF-8?q?=EB=A5=A0=20=EC=98=A4=ED=83=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_bestGeneral.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/a_bestGeneral.php b/hwe/a_bestGeneral.php index 4bc2c8ca..9ed3378e 100644 --- a/hwe/a_bestGeneral.php +++ b/hwe/a_bestGeneral.php @@ -138,7 +138,7 @@ $types = [ ["베 팅 당 첨", "int", function($v){$v['value'] = $v['betwin']; return $v; }], ["베 팅 수 익 금", "int", function($v){$v['value'] = $v['betwingold']; return $v; }], ["베 팅 수 익 률", "percent", function($v){ - if($v['getgold'] < 1000){ + if($v['betgold'] < 1000){ $v['value'] = 0; } else{ -- 2.54.0 From 19e979769d84adf0004cc41f46e44a3ab2da8eab Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 2 Jul 2018 03:23:20 +0900 Subject: [PATCH 269/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=20=ED=95=B4=EC=A0=9C?= =?UTF-8?q?=EC=8B=9C=20=EB=8B=89=EB=84=A4=EC=9E=84=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hwe/func.php b/hwe/func.php index f030bb09..98635b2c 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -1910,8 +1910,14 @@ function updateTurntime($no) { if($general['npc'] == 1 && $general['deadyear'] > $admin['year']) { $general['killturn'] = ($general['deadyear'] - $admin['year']) * 12; $general['npc'] = $general['npc_org']; - $query = "update general set owner=-1,npc='{$general['npc']}',killturn='{$general['killturn']}',mode=2 where no='$no'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + + $db->update('general', [ + 'owner'=>-1, + 'npc'=>$general['npc_org'], + 'killturn'=>$general['killturn'], + 'mode'=>2, + 'name2'=>null + ], 'no=%i',$no); $josaYi = JosaUtil::pick($general['name2'], '이'); $alllog[0] = "●{$admin['month']}월:{$general['name2']}{$josaYi} {$general['name']}의 육체에서 유체이탈합니다!"; -- 2.54.0 From 21740b76e60fb4216a4a7ac647bba217cf33268b Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 2 Jul 2018 19:15:43 +0900 Subject: [PATCH 270/304] =?UTF-8?q?=EB=AA=85=EC=98=88=EC=9D=98=20=EC=A0=84?= =?UTF-8?q?=EB=8B=B9=20=EB=B0=B0=EA=B2=BD=EC=83=89=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_hallOfFame.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/hwe/a_hallOfFame.php b/hwe/a_hallOfFame.php index e27f929a..465fd537 100644 --- a/hwe/a_hallOfFame.php +++ b/hwe/a_hallOfFame.php @@ -94,10 +94,15 @@ foreach($types as $idx=>[$typeName, $typeValue]) { $hallResult = array_map(function($general)use($typeValue){ $aux = Json::decode($general['aux']); $general += $aux; - if(!key_exists('color', $general)){ - $general['bgColor'] = GameConst::$basecolor4; - + if(!key_exists('bgColor', $general)){ + if(!key_exists('color', $general)){ + $general['bgColor'] = GameConst::$basecolor4; + } + else{ + $general['bgColor'] = $general['color']; + } } + if(!key_exists('fgColor', $general)){ $general['fgColor'] = newColor($general['bgColor']); } -- 2.54.0 From 1b5d39afcf97c4841d07df801d2990d445c562f5 Mon Sep 17 00:00:00 2001 From: hide_d Date: Mon, 2 Jul 2018 19:45:41 +0900 Subject: [PATCH 271/304] =?UTF-8?q?=EB=AA=85=EC=98=88=EC=9D=98=20=EC=A0=84?= =?UTF-8?q?=EB=8B=B9=20=EC=8B=9C=EB=82=98=EB=A6=AC=EC=98=A4=EB=AA=85=20?= =?UTF-8?q?=EC=A0=9C=EB=8C=80=EB=A1=9C=20=ED=91=9C=EA=B8=B0=20=EC=95=88?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_hallOfFame.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/a_hallOfFame.php b/hwe/a_hallOfFame.php index 465fd537..310a8318 100644 --- a/hwe/a_hallOfFame.php +++ b/hwe/a_hallOfFame.php @@ -12,7 +12,7 @@ $db = DB::db(); increaseRefresh("명예의전당", 1); $scenarioList= []; -foreach($db->query('SELECT distinct(scenario_name) as name, count(scenario) as cnt, scenario from ng_games order by scenario asc') as $scenarioInfo){ +foreach($db->query('SELECT scenario_name as name, count(scenario) as cnt, scenario from ng_games group by scenario order by scenario asc') as $scenarioInfo){ $scenarioList[$scenarioInfo['scenario']] = $scenarioInfo; } -- 2.54.0 From 6324563acd4308d5d8b60ead8b65b1ad8530ceb9 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 3 Jul 2018 02:55:45 +0900 Subject: [PATCH 272/304] =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=8C=80=EC=B2=B4?= =?UTF-8?q?=20css=20=EC=B6=94=EA=B0=80.=20form=20=EC=9D=BC=EB=B6=80=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/config.css | 74 ++++++++++++++++++++++++++++++++++++++++++++++ hwe/a_emperior.php | 13 +++----- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/css/config.css b/css/config.css index 53b22574..d539781c 100644 --- a/css/config.css +++ b/css/config.css @@ -89,4 +89,78 @@ input.with_skin:disabled { caption.section_title { caption-side:top; +} + + +.samButton{ + float:left; + display:block; + padding:0; + margin:0; + text-decoration: none; + position:relative; +} + +a.samButton.disabled { + pointer-events: none; + cursor: default; +} + +.samButtonOuter{ + display: table; + border: outset 2px white; + box-sizing: border-box; + font-weight:bold; + padding: 1px 6px; + width:125px; + height:30px; + background-color:#000000; + color:#ffffff; +} + +.samButtonOuter:active{ + border: inset 2px white; +} + + +.samButtonInner{ + display: table-cell; + vertical-align: middle; + width: 100%; + margin: 0 auto; + text-align: center; + user-select: none; +} + +.samToolbarButton > .samButtonOuter{ + width:125px; + height:30px; + background-color:#225500; + color:white; +} + +.samToolbarButton2 > .samButtonOuter{ + width:125px; + height:30px; + background-color:#225500; + color:magenta; +} + +.samCommandButton > .samButtonOuter{ + width:111px; + height:30px; +} + +.samCommandButton.disabled > .samButtonOuter{ + background-color:transparent !important; + color:gray !important; + border-color:transparent !important; +} + +.samCommandButton.disabled > .samButtonOuter > .samButtonInner::before{ + content:'【' +} + +.samCommandButton.disabled > .samButtonOuter > .samButtonInner::after{ + content:'】' } \ No newline at end of file diff --git a/hwe/a_emperior.php b/hwe/a_emperior.php index 9b5edccb..81e0e60f 100644 --- a/hwe/a_emperior.php +++ b/hwe/a_emperior.php @@ -29,7 +29,7 @@ increaseRefresh("왕조일람", 1);
역 대 왕 조
-
+
@@ -48,16 +48,13 @@ if ($select == 0) { @@ -108,10 +105,8 @@ $emperior = $db->queryFirstRow('SELECT * FROM emperior WHERE `no`=%i',$select);
-
- - '> + - + -
-- 2.54.0 From 49606adcf99ec4937ed12451061593fb0c83a963 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 3 Jul 2018 06:59:27 +0900 Subject: [PATCH 273/304] =?UTF-8?q?=EC=99=95=EC=A1=B0=EC=9D=BC=EB=9E=8C=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=20=EA=B0=B1=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_emperior.php | 39 +++++++++++++++++++------ hwe/templates/oldNation.php | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 hwe/templates/oldNation.php diff --git a/hwe/a_emperior.php b/hwe/a_emperior.php index 81e0e60f..75feafeb 100644 --- a/hwe/a_emperior.php +++ b/hwe/a_emperior.php @@ -6,11 +6,11 @@ include "func.php"; $select = Util::getReq('select', 'int', 0); -extractMissingPostToGlobals(); - $db = DB::db(); -$connect=$db->get(); +$templates = new \League\Plates\Engine('templates'); +$templates->registerFunction('ConvertLog', '\sammo\ConvertLog'); +$templates->registerFunction('newColor', '\sammo\newColor'); increaseRefresh("왕조일람", 1); ?> @@ -36,12 +36,7 @@ increaseRefresh("왕조일람", 1); query('SELECT * FROM emperior ORDER BY `no` DESC') as $emperior){ ?> @@ -100,6 +95,7 @@ if ($select == 0) { } $emperior = $db->queryFirstRow('SELECT * FROM emperior WHERE `no`=%i',$select); +$serverID = $emperior['server_id']??($emperior['serverID']??null); ?>
-
- -
+
@@ -210,6 +206,31 @@ $emperior = $db->queryFirstRow('SELECT * FROM emperior WHERE `no`=%i',$select);
+query('SELECT * FROM ng_old_nations WHERE server_id=%s ORDER BY date DESC', $serverID); + foreach($nations as $nation){ + if(!$nation['nation']??null){ + continue; + } + $nation += Json::decode($nation['data']); + + $nation['typeName'] = getNationType($nation['type']); + $nation['levelName'] = getNationLevel($nation['level']); + if($nation['generals']){ + $generals = $db->query('SELECT `general_no`, `name` FROM ng_old_generals WHERE server_id=%s AND general_no IN %li', $serverID, $nation['generals']); + $nation['generalsFull'] = $generals; + } + else{ + $nation['generalsFull'] = []; + } + + + echo $templates->render('oldNation', $nation); + } +} +?> + diff --git a/hwe/templates/oldNation.php b/hwe/templates/oldNation.php new file mode 100644 index 00000000..c2de9f36 --- /dev/null +++ b/hwe/templates/oldNation.php @@ -0,0 +1,58 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
성향멸망 시기일자
최종 작위최종 장수 수기술력
최대 영토 수최대 병력 수최대 국력
최대영토
장수명단 + + , + +
국가열전ConvertLog($history)?>
-- 2.54.0 From 39c774625de61631484ad6ca0f66510e01ab7847 Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 3 Jul 2018 20:09:36 +0900 Subject: [PATCH 274/304] =?UTF-8?q?nation=20=ED=85=8C=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=EC=97=90=20aux=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/sql/schema.sql | 377 ++++++++++++++++++++++++++++++++------------- 1 file changed, 269 insertions(+), 108 deletions(-) diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index f9c8576c..1e27f523 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -165,114 +165,275 @@ ENGINE=MyISAM; ## 국가 테이블 ########################################################################### -create table nation ( - nation int(6) not null auto_increment, - `name` CHAR(64) NOT NULL COLLATE 'utf8mb4_bin', - color char(10) not null, can_change_flag int(1) default 1, - onlinegen varchar(1024) default '', - msg text default '', - capital int(1) default 0, capset int(1) default 0, - gennum int(3) default 1, - gennum2 int(3) default 1, - chemi int(3) default 0, - gold int(8) default 0, - rice int(8) default 0, - bill int(3) default 0, - rate int(3) default 0, - rate_tmp int(3) default 0, - secretlimit int(2) default 3, - l12set int(1) default 0, - l11set int(1) default 0, - l10set int(1) default 0, - l9set int(1) default 0, - l8set int(1) default 0, - l7set int(1) default 0, - l6set int(1) default 0, - l5set int(1) default 0, - scout int(1) default 0, - war int(1) default 0, - sabotagelimit int(4) default 36, - surlimit int(4) default 72, - scoutmsg text default '', - tech int(8) default 0, totaltech int(8) default 0, - power int(8) default 0, - spy char(255) default '', - level int(1) default 0, - type int(2) default 0, - rule text default '', - history mediumtext default '', - board0 text default '', board0_who int(6) default 0, board0_when datetime, - board1 text default '', board1_who int(6) default 0, board1_when datetime, - board2 text default '', board2_who int(6) default 0, board2_when datetime, - board3 text default '', board3_who int(6) default 0, board3_when datetime, - board4 text default '', board4_who int(6) default 0, board4_when datetime, - board5 text default '', board5_who int(6) default 0, board5_when datetime, - board6 text default '', board6_who int(6) default 0, board6_when datetime, - board7 text default '', board7_who int(6) default 0, board7_when datetime, - board8 text default '', board8_who int(6) default 0, board8_when datetime, - board9 text default '', board9_who int(6) default 0, board9_when datetime, - board10 text default '', board10_who int(6) default 0, board10_when datetime, - board11 text default '', board11_who int(6) default 0, board11_when datetime, - board12 text default '', board12_who int(6) default 0, board12_when datetime, - board13 text default '', board13_who int(6) default 0, board13_when datetime, - board14 text default '', board14_who int(6) default 0, board14_when datetime, - board15 text default '', board15_who int(6) default 0, board15_when datetime, - board16 text default '', board16_who int(6) default 0, board16_when datetime, - board17 text default '', board17_who int(6) default 0, board17_when datetime, - board18 text default '', board18_who int(6) default 0, board18_when datetime, - board19 text default '', board19_who int(6) default 0, board19_when datetime, - coreboard0 text default '', coreboard0_who int(6) default 0, coreboard0_when datetime, - coreboard1 text default '', coreboard1_who int(6) default 0, coreboard1_when datetime, - coreboard2 text default '', coreboard2_who int(6) default 0, coreboard2_when datetime, - coreboard3 text default '', coreboard3_who int(6) default 0, coreboard3_when datetime, - coreboard4 text default '', coreboard4_who int(6) default 0, coreboard4_when datetime, - coreboard5 text default '', coreboard5_who int(6) default 0, coreboard5_when datetime, - coreboard6 text default '', coreboard6_who int(6) default 0, coreboard6_when datetime, - coreboard7 text default '', coreboard7_who int(6) default 0, coreboard7_when datetime, - coreboard8 text default '', coreboard8_who int(6) default 0, coreboard8_when datetime, - coreboard9 text default '', coreboard9_who int(6) default 0, coreboard9_when datetime, - coreboard10 text default '', coreboard10_who int(6) default 0, coreboard10_when datetime, - coreboard11 text default '', coreboard11_who int(6) default 0, coreboard11_when datetime, - coreboard12 text default '', coreboard12_who int(6) default 0, coreboard12_when datetime, - coreboard13 text default '', coreboard13_who int(6) default 0, coreboard13_when datetime, - coreboard14 text default '', coreboard14_who int(6) default 0, coreboard14_when datetime, - coreboard15 text default '', coreboard15_who int(6) default 0, coreboard15_when datetime, - coreboard16 text default '', coreboard16_who int(6) default 0, coreboard16_when datetime, - coreboard17 text default '', coreboard17_who int(6) default 0, coreboard17_when datetime, - coreboard18 text default '', coreboard18_who int(6) default 0, coreboard18_when datetime, - coreboard19 text default '', coreboard19_who int(6) default 0, coreboard19_when datetime, - boardindex int(2) default 19, - coreindex int(2) default 19, - l12term int(4) default 0, l11term int(4) default 0, l10term int(4) default 0, l9term int(4) default 0, - l12turn0 char(14) default '00000000000099', l11turn0 char(14) default '00000000000099', l10turn0 char(14) default '00000000000099', l9turn0 char(14) default '00000000000099', - l12turn1 char(14) default '00000000000099', l11turn1 char(14) default '00000000000099', l10turn1 char(14) default '00000000000099', l9turn1 char(14) default '00000000000099', - l12turn2 char(14) default '00000000000099', l11turn2 char(14) default '00000000000099', l10turn2 char(14) default '00000000000099', l9turn2 char(14) default '00000000000099', - l12turn3 char(14) default '00000000000099', l11turn3 char(14) default '00000000000099', l10turn3 char(14) default '00000000000099', l9turn3 char(14) default '00000000000099', - l12turn4 char(14) default '00000000000099', l11turn4 char(14) default '00000000000099', l10turn4 char(14) default '00000000000099', l9turn4 char(14) default '00000000000099', - l12turn5 char(14) default '00000000000099', l11turn5 char(14) default '00000000000099', l10turn5 char(14) default '00000000000099', l9turn5 char(14) default '00000000000099', - l12turn6 char(14) default '00000000000099', l11turn6 char(14) default '00000000000099', l10turn6 char(14) default '00000000000099', l9turn6 char(14) default '00000000000099', - l12turn7 char(14) default '00000000000099', l11turn7 char(14) default '00000000000099', l10turn7 char(14) default '00000000000099', l9turn7 char(14) default '00000000000099', - l12turn8 char(14) default '00000000000099', l11turn8 char(14) default '00000000000099', l10turn8 char(14) default '00000000000099', l9turn8 char(14) default '00000000000099', - l12turn9 char(14) default '00000000000099', l11turn9 char(14) default '00000000000099', l10turn9 char(14) default '00000000000099', l9turn9 char(14) default '00000000000099', - l12turn10 char(14) default '00000000000099', l11turn10 char(14) default '00000000000099', l10turn10 char(14) default '00000000000099', l9turn10 char(14) default '00000000000099', - l12turn11 char(14) default '00000000000099', l11turn11 char(14) default '00000000000099', l10turn11 char(14) default '00000000000099', l9turn11 char(14) default '00000000000099', - - l8term int(4) default 0, l7term int(4) default 0, l6term int(4) default 0, l5term int(4) default 0, - l8turn0 char(14) default '00000000000099', l7turn0 char(14) default '00000000000099', l6turn0 char(14) default '00000000000099', l5turn0 char(14) default '00000000000099', - l8turn1 char(14) default '00000000000099', l7turn1 char(14) default '00000000000099', l6turn1 char(14) default '00000000000099', l5turn1 char(14) default '00000000000099', - l8turn2 char(14) default '00000000000099', l7turn2 char(14) default '00000000000099', l6turn2 char(14) default '00000000000099', l5turn2 char(14) default '00000000000099', - l8turn3 char(14) default '00000000000099', l7turn3 char(14) default '00000000000099', l6turn3 char(14) default '00000000000099', l5turn3 char(14) default '00000000000099', - l8turn4 char(14) default '00000000000099', l7turn4 char(14) default '00000000000099', l6turn4 char(14) default '00000000000099', l5turn4 char(14) default '00000000000099', - l8turn5 char(14) default '00000000000099', l7turn5 char(14) default '00000000000099', l6turn5 char(14) default '00000000000099', l5turn5 char(14) default '00000000000099', - l8turn6 char(14) default '00000000000099', l7turn6 char(14) default '00000000000099', l6turn6 char(14) default '00000000000099', l5turn6 char(14) default '00000000000099', - l8turn7 char(14) default '00000000000099', l7turn7 char(14) default '00000000000099', l6turn7 char(14) default '00000000000099', l5turn7 char(14) default '00000000000099', - l8turn8 char(14) default '00000000000099', l7turn8 char(14) default '00000000000099', l6turn8 char(14) default '00000000000099', l5turn8 char(14) default '00000000000099', - l8turn9 char(14) default '00000000000099', l7turn9 char(14) default '00000000000099', l6turn9 char(14) default '00000000000099', l5turn9 char(14) default '00000000000099', - l8turn10 char(14) default '00000000000099', l7turn10 char(14) default '00000000000099', l6turn10 char(14) default '00000000000099', l5turn10 char(14) default '00000000000099', - l8turn11 char(14) default '00000000000099', l7turn11 char(14) default '00000000000099', l6turn11 char(14) default '00000000000099', l5turn11 char(14) default '00000000000099', - - PRIMARY KEY (nation) -) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; +CREATE TABLE `nation` ( + `nation` INT(6) NOT NULL AUTO_INCREMENT, + `name` CHAR(64) NOT NULL COLLATE 'utf8mb4_bin', + `color` CHAR(10) NOT NULL, + `can_change_flag` INT(1) NULL DEFAULT '1', + `onlinegen` VARCHAR(1024) NULL DEFAULT '', + `msg` TEXT NULL DEFAULT '', + `capital` INT(1) NULL DEFAULT '0', + `capset` INT(1) NULL DEFAULT '0', + `gennum` INT(3) NULL DEFAULT '1', + `gennum2` INT(3) NULL DEFAULT '1', + `chemi` INT(3) NULL DEFAULT '0', + `gold` INT(8) NULL DEFAULT '0', + `rice` INT(8) NULL DEFAULT '0', + `bill` INT(3) NULL DEFAULT '0', + `rate` INT(3) NULL DEFAULT '0', + `rate_tmp` INT(3) NULL DEFAULT '0', + `secretlimit` INT(2) NULL DEFAULT '3', + `l12set` INT(1) NULL DEFAULT '0', + `l11set` INT(1) NULL DEFAULT '0', + `l10set` INT(1) NULL DEFAULT '0', + `l9set` INT(1) NULL DEFAULT '0', + `l8set` INT(1) NULL DEFAULT '0', + `l7set` INT(1) NULL DEFAULT '0', + `l6set` INT(1) NULL DEFAULT '0', + `l5set` INT(1) NULL DEFAULT '0', + `scout` INT(1) NULL DEFAULT '0', + `war` INT(1) NULL DEFAULT '0', + `sabotagelimit` INT(4) NULL DEFAULT '36', + `surlimit` INT(4) NULL DEFAULT '72', + `scoutmsg` TEXT NULL DEFAULT '', + `tech` INT(8) NULL DEFAULT '0', + `totaltech` INT(8) NULL DEFAULT '0', + `power` INT(8) NULL DEFAULT '0', + `spy` CHAR(255) NULL DEFAULT '', + `level` INT(1) NULL DEFAULT '0', + `type` INT(2) NULL DEFAULT '0', + `rule` TEXT NULL DEFAULT '', + `history` MEDIUMTEXT NULL DEFAULT '', + `board0` TEXT NULL DEFAULT '', + `board0_who` INT(6) NULL DEFAULT '0', + `board0_when` DATETIME NULL DEFAULT NULL, + `board1` TEXT NULL DEFAULT '', + `board1_who` INT(6) NULL DEFAULT '0', + `board1_when` DATETIME NULL DEFAULT NULL, + `board2` TEXT NULL DEFAULT '', + `board2_who` INT(6) NULL DEFAULT '0', + `board2_when` DATETIME NULL DEFAULT NULL, + `board3` TEXT NULL DEFAULT '', + `board3_who` INT(6) NULL DEFAULT '0', + `board3_when` DATETIME NULL DEFAULT NULL, + `board4` TEXT NULL DEFAULT '', + `board4_who` INT(6) NULL DEFAULT '0', + `board4_when` DATETIME NULL DEFAULT NULL, + `board5` TEXT NULL DEFAULT '', + `board5_who` INT(6) NULL DEFAULT '0', + `board5_when` DATETIME NULL DEFAULT NULL, + `board6` TEXT NULL DEFAULT '', + `board6_who` INT(6) NULL DEFAULT '0', + `board6_when` DATETIME NULL DEFAULT NULL, + `board7` TEXT NULL DEFAULT '', + `board7_who` INT(6) NULL DEFAULT '0', + `board7_when` DATETIME NULL DEFAULT NULL, + `board8` TEXT NULL DEFAULT '', + `board8_who` INT(6) NULL DEFAULT '0', + `board8_when` DATETIME NULL DEFAULT NULL, + `board9` TEXT NULL DEFAULT '', + `board9_who` INT(6) NULL DEFAULT '0', + `board9_when` DATETIME NULL DEFAULT NULL, + `board10` TEXT NULL DEFAULT '', + `board10_who` INT(6) NULL DEFAULT '0', + `board10_when` DATETIME NULL DEFAULT NULL, + `board11` TEXT NULL DEFAULT '', + `board11_who` INT(6) NULL DEFAULT '0', + `board11_when` DATETIME NULL DEFAULT NULL, + `board12` TEXT NULL DEFAULT '', + `board12_who` INT(6) NULL DEFAULT '0', + `board12_when` DATETIME NULL DEFAULT NULL, + `board13` TEXT NULL DEFAULT '', + `board13_who` INT(6) NULL DEFAULT '0', + `board13_when` DATETIME NULL DEFAULT NULL, + `board14` TEXT NULL DEFAULT '', + `board14_who` INT(6) NULL DEFAULT '0', + `board14_when` DATETIME NULL DEFAULT NULL, + `board15` TEXT NULL DEFAULT '', + `board15_who` INT(6) NULL DEFAULT '0', + `board15_when` DATETIME NULL DEFAULT NULL, + `board16` TEXT NULL DEFAULT '', + `board16_who` INT(6) NULL DEFAULT '0', + `board16_when` DATETIME NULL DEFAULT NULL, + `board17` TEXT NULL DEFAULT '', + `board17_who` INT(6) NULL DEFAULT '0', + `board17_when` DATETIME NULL DEFAULT NULL, + `board18` TEXT NULL DEFAULT '', + `board18_who` INT(6) NULL DEFAULT '0', + `board18_when` DATETIME NULL DEFAULT NULL, + `board19` TEXT NULL DEFAULT '', + `board19_who` INT(6) NULL DEFAULT '0', + `board19_when` DATETIME NULL DEFAULT NULL, + `coreboard0` TEXT NULL DEFAULT '', + `coreboard0_who` INT(6) NULL DEFAULT '0', + `coreboard0_when` DATETIME NULL DEFAULT NULL, + `coreboard1` TEXT NULL DEFAULT '', + `coreboard1_who` INT(6) NULL DEFAULT '0', + `coreboard1_when` DATETIME NULL DEFAULT NULL, + `coreboard2` TEXT NULL DEFAULT '', + `coreboard2_who` INT(6) NULL DEFAULT '0', + `coreboard2_when` DATETIME NULL DEFAULT NULL, + `coreboard3` TEXT NULL DEFAULT '', + `coreboard3_who` INT(6) NULL DEFAULT '0', + `coreboard3_when` DATETIME NULL DEFAULT NULL, + `coreboard4` TEXT NULL DEFAULT '', + `coreboard4_who` INT(6) NULL DEFAULT '0', + `coreboard4_when` DATETIME NULL DEFAULT NULL, + `coreboard5` TEXT NULL DEFAULT '', + `coreboard5_who` INT(6) NULL DEFAULT '0', + `coreboard5_when` DATETIME NULL DEFAULT NULL, + `coreboard6` TEXT NULL DEFAULT '', + `coreboard6_who` INT(6) NULL DEFAULT '0', + `coreboard6_when` DATETIME NULL DEFAULT NULL, + `coreboard7` TEXT NULL DEFAULT '', + `coreboard7_who` INT(6) NULL DEFAULT '0', + `coreboard7_when` DATETIME NULL DEFAULT NULL, + `coreboard8` TEXT NULL DEFAULT '', + `coreboard8_who` INT(6) NULL DEFAULT '0', + `coreboard8_when` DATETIME NULL DEFAULT NULL, + `coreboard9` TEXT NULL DEFAULT '', + `coreboard9_who` INT(6) NULL DEFAULT '0', + `coreboard9_when` DATETIME NULL DEFAULT NULL, + `coreboard10` TEXT NULL DEFAULT '', + `coreboard10_who` INT(6) NULL DEFAULT '0', + `coreboard10_when` DATETIME NULL DEFAULT NULL, + `coreboard11` TEXT NULL DEFAULT '', + `coreboard11_who` INT(6) NULL DEFAULT '0', + `coreboard11_when` DATETIME NULL DEFAULT NULL, + `coreboard12` TEXT NULL DEFAULT '', + `coreboard12_who` INT(6) NULL DEFAULT '0', + `coreboard12_when` DATETIME NULL DEFAULT NULL, + `coreboard13` TEXT NULL DEFAULT '', + `coreboard13_who` INT(6) NULL DEFAULT '0', + `coreboard13_when` DATETIME NULL DEFAULT NULL, + `coreboard14` TEXT NULL DEFAULT '', + `coreboard14_who` INT(6) NULL DEFAULT '0', + `coreboard14_when` DATETIME NULL DEFAULT NULL, + `coreboard15` TEXT NULL DEFAULT '', + `coreboard15_who` INT(6) NULL DEFAULT '0', + `coreboard15_when` DATETIME NULL DEFAULT NULL, + `coreboard16` TEXT NULL DEFAULT '', + `coreboard16_who` INT(6) NULL DEFAULT '0', + `coreboard16_when` DATETIME NULL DEFAULT NULL, + `coreboard17` TEXT NULL DEFAULT '', + `coreboard17_who` INT(6) NULL DEFAULT '0', + `coreboard17_when` DATETIME NULL DEFAULT NULL, + `coreboard18` TEXT NULL DEFAULT '', + `coreboard18_who` INT(6) NULL DEFAULT '0', + `coreboard18_when` DATETIME NULL DEFAULT NULL, + `coreboard19` TEXT NULL DEFAULT '', + `coreboard19_who` INT(6) NULL DEFAULT '0', + `coreboard19_when` DATETIME NULL DEFAULT NULL, + `boardindex` INT(2) NULL DEFAULT '19', + `coreindex` INT(2) NULL DEFAULT '19', + `l12term` INT(4) NULL DEFAULT '0', + `l11term` INT(4) NULL DEFAULT '0', + `l10term` INT(4) NULL DEFAULT '0', + `l9term` INT(4) NULL DEFAULT '0', + `l12turn0` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn0` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn0` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn0` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn1` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn1` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn1` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn1` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn2` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn2` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn2` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn2` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn3` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn3` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn3` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn3` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn4` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn4` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn4` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn4` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn5` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn5` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn5` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn5` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn6` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn6` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn6` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn6` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn7` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn7` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn7` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn7` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn8` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn8` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn8` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn8` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn9` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn9` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn9` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn9` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn10` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn10` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn10` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn10` CHAR(14) NULL DEFAULT '00000000000099', + `l12turn11` CHAR(14) NULL DEFAULT '00000000000099', + `l11turn11` CHAR(14) NULL DEFAULT '00000000000099', + `l10turn11` CHAR(14) NULL DEFAULT '00000000000099', + `l9turn11` CHAR(14) NULL DEFAULT '00000000000099', + `l8term` INT(4) NULL DEFAULT '0', + `l7term` INT(4) NULL DEFAULT '0', + `l6term` INT(4) NULL DEFAULT '0', + `l5term` INT(4) NULL DEFAULT '0', + `l8turn0` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn0` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn0` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn0` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn1` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn1` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn1` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn1` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn2` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn2` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn2` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn2` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn3` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn3` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn3` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn3` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn4` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn4` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn4` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn4` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn5` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn5` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn5` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn5` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn6` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn6` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn6` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn6` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn7` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn7` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn7` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn7` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn8` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn8` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn8` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn8` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn9` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn9` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn9` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn9` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn10` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn10` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn10` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn10` CHAR(14) NULL DEFAULT '00000000000099', + `l8turn11` CHAR(14) NULL DEFAULT '00000000000099', + `l7turn11` CHAR(14) NULL DEFAULT '00000000000099', + `l6turn11` CHAR(14) NULL DEFAULT '00000000000099', + `l5turn11` CHAR(14) NULL DEFAULT '00000000000099', + `aux` TEXT NOT NULL DEFAULT '{}', + PRIMARY KEY (`nation`) +) +ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; ########################################################################### ## 도시 테이블 -- 2.54.0 From 100bb0073928a2b10a4d3b1e219972d2037a7e4c Mon Sep 17 00:00:00 2001 From: hide_d Date: Tue, 3 Jul 2018 20:43:14 +0900 Subject: [PATCH 275/304] =?UTF-8?q?=EC=9B=94=EB=A7=88=EB=8B=A4=20=EC=B5=9C?= =?UTF-8?q?=EB=8C=80=20=EA=B5=AD=EB=A0=A5,=20=EC=B5=9C=EB=8C=80=20?= =?UTF-8?q?=EB=B3=91=EB=A0=A5,=20=EC=B5=9C=EB=8C=80=20=EB=8F=84=EC=8B=9C?= =?UTF-8?q?=20=EC=88=98=EB=A5=BC=20=EA=B3=84=EC=82=B0=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80.=20ng=5Fold=5Fnations?= =?UTF-8?q?=EC=97=90=EB=8F=84=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_emperior.php | 5 ++- hwe/func.php | 1 + hwe/func_gamerule.php | 79 ++++++++++++++++++++++++------------- hwe/process_war.php | 1 + hwe/templates/oldNation.php | 6 +-- 5 files changed, 60 insertions(+), 32 deletions(-) diff --git a/hwe/a_emperior.php b/hwe/a_emperior.php index 75feafeb..7f4b5c3a 100644 --- a/hwe/a_emperior.php +++ b/hwe/a_emperior.php @@ -224,7 +224,10 @@ if($serverID){ else{ $nation['generalsFull'] = []; } - + + if(key_exists('aux', $nation)){ + $nation += $nation['aux']; + } echo $templates->render('oldNation', $nation); } diff --git a/hwe/func.php b/hwe/func.php index 98635b2c..a5d0afb9 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -2455,6 +2455,7 @@ function deleteNation($general) { $oldNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation=%i', $general['nation']); $oldNationGenerals = $db->queryFirstColumn('SELECT `no` FROM general WHERE nation=%i', $general['nation']); $oldNation['generals'] = $oldNationGenerals; + $oldNation['aux'] = Json::decode($oldNation['aux']); // 전 장수 재야로 // 전 장수 소속 무소속으로 $query = "update general set belong=0,troop=0,level=0,nation=0,makelimit=12 where nation='{$general['nation']}'"; diff --git a/hwe/func_gamerule.php b/hwe/func_gamerule.php index 6c93881e..bb9db7ac 100644 --- a/hwe/func_gamerule.php +++ b/hwe/func_gamerule.php @@ -427,6 +427,15 @@ function postUpdateMonthly() { $history = []; + //도시 수 측정 + $cityNations = []; + foreach($db->queryAllLists('SELECT city, name, nation FROM city') as [$cityID, $cityName, $cityNation]){ + if(!key_exists($cityNation, $cityNations)){ + $cityNations[$cityNation] = []; + } + $cityNations[$cityNation][] = $cityName; + } + //각 국가 전월 장수수 대비 당월 장수수로 단합도 산정 //각 국가 장수수를 구하고 국력 산정 // $query = "select nation,gennum from nation where level>0"; @@ -438,34 +447,33 @@ function postUpdateMonthly() { // 접속률 // 숙련도 // 명성,공헌 - $query = " -select -A.nation, -A.gennum, A.gennum2, A.chemi, -round(( - round(((A.gold+A.rice)+(select sum(gold+rice) from general where nation=A.nation))/100) - +A.tech - +if(A.level=0,0,( - select round( - sum(pop)*sum(pop+agri+comm+secu+wall+def)/sum(pop2+agri2+comm2+secu2+wall2+def2)/100 - ) from city where nation=A.nation and supply=1 - )) - +(select sum(leader+power+intel) from general where nation=A.nation) - +(select round(sum(dex0+dex10+dex20+dex30+dex40)/1000) from general where nation=A.nation) - +(select round(sum(experience+dedication)/100) from general where nation=A.nation) - +(select round(avg(connect)) from general where nation=A.nation) -)/10) -as power -from nation A -group by A.nation -"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $nationCount = MYDB_num_rows($result); - $genNum = []; - for($i=0; $i < $nationCount; $i++) { - $nation = MYDB_fetch_array($result); + $nations = $db->query('SELECT + A.nation, + A.gennum, A.gennum2, A.chemi, A.aux, + round(( + round(((A.gold+A.rice)+(select sum(gold+rice) from general where nation=A.nation))/100) + +A.tech + +if(A.level=0,0,( + select round( + sum(pop)*sum(pop+agri+comm+secu+wall+def)/sum(pop2+agri2+comm2+secu2+wall2+def2)/100 + ) from city where nation=A.nation and supply=1 + )) + +(select sum(leader+power+intel) from general where nation=A.nation) + +(select round(sum(dex0+dex10+dex20+dex30+dex40)/1000) from general where nation=A.nation) + +(select round(sum(experience+dedication)/100) from general where nation=A.nation) + +(select round(avg(connect)) from general where nation=A.nation) + )/10) + as power, + (select sum(crew) from general where nation=A.nation) as totalCrew + from nation A + group by A.nation'); + foreach($nations as $nation) { $genNum[$nation['nation']] = $nation['gennum']; + $aux = Json::decode($nation['aux']); + + + if($nation['gennum'] > $nation['gennum2']) { // 장수가 증가했을때 $nation['chemi'] -= ceil(($nation['gennum'] - $nation['gennum2']) / $nation['gennum'] * 100); @@ -479,9 +487,21 @@ group by A.nation if($nation['chemi'] > 100) { $nation['chemi'] = 100; } //약간의 랜덤치 부여 (95% ~ 105%) + $nation['power'] = Util::round($nation['power'] * (rand()%101 + 950) / 1000); - $query = "update nation set power='{$nation['power']}',gennum2='{$nation['gennum']}',chemi='{$nation['chemi']}' where nation='{$nation['nation']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + $aux['maxPower'] = max($aux['maxPower']??0, $nation['power']); + $aux['maxCrew'] = max($aux['maxCrew']??0, Util::toInt($nation['totalCrew'])); + + if(count($cityNations[$nation['nation']]??[]) > count($aux['maxCities']??[])){ + $aux['maxCities'] = $cityNations[$nation['nation']]; + } + + $db->update('nation', [ + 'power'=>$nation['power'], + 'gennum2'=>$nation['gennum'], + 'chemi'=>$nation['chemi'], + 'aux'=>Json::encode($aux) + ], 'nation=%i', $nation['nation']); } // 전쟁기한 세팅 @@ -690,6 +710,7 @@ function checkMerge() { $oldNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation=%i', $me['nation']); $oldNationGenerals = $db->queryFirstColumn('SELECT `no` FROM general WHERE nation=%i', $me['nation']); $oldNation['generals'] = $oldNationGenerals; + $oldNation['aux'] = Json::decode($oldNation['aux']); // 자금 통합, 외교제한 5년, 기술유지 $query = "update nation set name='{$you['makenation']}',gold=gold+'{$mynation['gold']}',rice=rice+'{$mynation['rice']}',surlimit='24',totaltech='$newTotalTech',tech='$newTech',gennum='{$newGenCount}' where nation='{$younation['nation']}'"; @@ -820,6 +841,7 @@ function checkSurrender() { $oldNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation=%i', $me['nation']); $oldNationGenerals = $db->queryFirstColumn('SELECT `no` FROM general WHERE nation=%i', $me['nation']); $oldNation['generals'] = $oldNationGenerals; + $oldNation['aux'] = Json::decode($oldNation['aux']); $newGenCount = $gencount + $gencount2; if($newGenCount < GameConst::$initialNationGenLimit) { $newGenCount = GameConst::$initialNationGenLimit; } @@ -1332,6 +1354,7 @@ function checkEmperior() { $oldNation = $db->queryFirstRow('SELECT * FROM nation WHERE nation=%i', $nation['nation']); $oldNation['generals'] = $db->queryFirstColumn('SELECT `no` FROM general WHERE nation=%i', $nation['nation']); + $oldNation['aux'] = Json::decode($oldNation['aux']); storeOldGenerals(0, $admin['year'], $admin['month']); storeOldGenerals($nation['nation'], $admin['year'], $admin['month']); diff --git a/hwe/process_war.php b/hwe/process_war.php index b8f31378..711813c9 100644 --- a/hwe/process_war.php +++ b/hwe/process_war.php @@ -1816,6 +1816,7 @@ function ConquerCity($admin, $general, $city, $nation, $destnation) { //다른 코드와는 다르게 공용으로 쓰므로 남겨둠 return $gen['no']; }, $oldNationGenerals); + $oldNation['aux'] = Json::decode($oldNation['aux']); foreach($oldNationGenerals as $gen){ diff --git a/hwe/templates/oldNation.php b/hwe/templates/oldNation.php index c2de9f36..46f445e3 100644 --- a/hwe/templates/oldNation.php +++ b/hwe/templates/oldNation.php @@ -32,15 +32,15 @@
최대 영토 수 최대 병력 수 최대 국력
최대영토
장수명단
성향 멸망 시기-- 일자
+
+ + + +
+ 현재 (月) + + + + +
+ + + +
- + @@ -84,157 +104,8 @@ if ($select == 0) { - - - -
- - -queryFirstRow('SELECT * FROM emperior WHERE `no`=%i',$select); -$serverID = $emperior['server_id']??($emperior['serverID']??null); - -?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
국 가 수
(최종 / 최대)
장 수 수
(최종 / 최대)
등 장 국 가
국가별 성향
장 수 성 격
장 수 특 기
; bgcolor= colspan=6> - (月) -
국 력성 향
장 수속 령
총 인 구인 구 율
국 고병 량
황 제 승 상 
위 장 군 사 공 
표 기 장 군 태 위 
거 기 장 군 사 도 
오 호 장 군
건 안 칠 자
장 수 들
(공헌도 순서)
역 사 기 록
- -query('SELECT * FROM ng_old_nations WHERE server_id=%s ORDER BY date DESC', $serverID); - foreach($nations as $nation){ - if(!$nation['nation']??null){ - continue; - } - $nation += Json::decode($nation['data']); - - $nation['typeName'] = getNationType($nation['type']); - $nation['levelName'] = getNationLevel($nation['level']); - if($nation['generals']){ - $generals = $db->query('SELECT `general_no`, `name` FROM ng_old_generals WHERE server_id=%s AND general_no IN %li', $serverID, $nation['generals']); - $nation['generalsFull'] = $generals; - } - else{ - $nation['generalsFull'] = []; - } - - if(key_exists('aux', $nation)){ - $nation += $nation['aux']; - } - - echo $templates->render('oldNation', $nation); - } -} -?> - - +
diff --git a/hwe/a_emperior_detail.php b/hwe/a_emperior_detail.php new file mode 100644 index 00000000..df9d6bc4 --- /dev/null +++ b/hwe/a_emperior_detail.php @@ -0,0 +1,197 @@ +registerFunction('ConvertLog', '\sammo\ConvertLog'); +$templates->registerFunction('newColor', '\sammo\newColor'); +increaseRefresh("왕조일람", 1); + +$emperior = $db->queryFirstRow('SELECT * FROM emperior WHERE `no`=%i',$select); +$serverID = $emperior['server_id']??($emperior['serverID']??null); + +?> + + + + + + + + +<?=UniqueConst::$serverName?>: 왕조일람 + + + + + + + + + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
국 가 수
(최종 / 최대)
장 수 수
(최종 / 최대)
등 장 국 가
국가별 성향
장 수 성 격
장 수 특 기
; bgcolor= colspan=6> + (月) +
국 력성 향
장 수속 령
총 인 구인 구 율
국 고병 량
황 제 승 상 
위 장 군 사 공 
표 기 장 군 태 위 
거 기 장 군 사 도 
오 호 장 군
건 안 칠 자
장 수 들
(공헌도 순서)
역 사 기 록
+ +query('SELECT * FROM ng_old_nations WHERE server_id=%s ORDER BY date DESC', $serverID); + foreach($nations as $nation){ + if(!$nation['nation']??null){ + continue; + } + $nation += Json::decode($nation['data']); + + $nation['typeName'] = getNationType($nation['type']); + $nation['levelName'] = getNationLevel($nation['level']); + if($nation['generals']){ + $generals = $db->query('SELECT `general_no`, `name` FROM ng_old_generals WHERE server_id=%s AND general_no IN %li', $serverID, $nation['generals']); + $nation['generalsFull'] = $generals; + } + else{ + $nation['generalsFull'] = []; + } + + if(key_exists('aux', $nation)){ + $nation += $nation['aux']; + } + + echo $templates->render('oldNation', $nation); + } +} +?> + + + + +
+ + \ No newline at end of file -- 2.54.0 From aad7ddbc0448202134990707e0e2023f267cdc1b Mon Sep 17 00:00:00 2001 From: hide_d Date: Thu, 5 Jul 2018 00:10:33 +0900 Subject: [PATCH 280/304] =?UTF-8?q?=EC=99=95=EC=A1=B0=20=EC=9D=BC=EB=9E=8C?= =?UTF-8?q?=EC=97=90=20=EC=95=84=EB=AC=B4=EA=B2=83=EB=8F=84=20=EC=97=86?= =?UTF-8?q?=EC=9D=84=20=EB=95=8C=20'=ED=98=84=EC=9E=AC'=EA=B0=80=20?= =?UTF-8?q?=EC=95=88=EB=B3=B4=EC=9D=B4=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_emperior.php | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/hwe/a_emperior.php b/hwe/a_emperior.php index 9a71e9ed..c4d26172 100644 --- a/hwe/a_emperior.php +++ b/hwe/a_emperior.php @@ -32,14 +32,18 @@ increaseRefresh("왕조일람", 1); $showCurrentNation = true; -foreach($db->query('SELECT * FROM emperior ORDER BY `no` DESC') as $emperior){ - $serverID = $emperior['server_id']??($emperior['serverID']??null); - if($showCurrentNation && $serverID == UniqueConst::$serverID){ +$emperiors = $db->query('SELECT * FROM emperior ORDER BY `no` DESC'); + +if($emperiors){ + $serverID = $emperior[0]['server_id']??($emperior[0]['serverID']??null); + if($serverID == UniqueConst::$serverID){ $showCurrentNation = false; } - if ($showCurrentNation) { - $gameStor = KVStorage::getStorage($db, 'game_env'); - [$year, $month] = $gameStor->getValuesAsArray(['year', 'month']); +} + +if ($showCurrentNation) { + $gameStor = KVStorage::getStorage($db, 'game_env'); + [$year, $month] = $gameStor->getValuesAsArray(['year', 'month']); ?> @@ -55,9 +59,10 @@ foreach($db->query('SELECT * FROM emperior ORDER BY `no` DESC') as $emperior){
@@ -102,7 +107,7 @@ foreach($db->query('SELECT * FROM emperior ORDER BY `no` DESC') as $emperior){
-- 2.54.0 From 51b376add22a9ae552c1ecbd33b361980df9c7d0 Mon Sep 17 00:00:00 2001 From: hide_d Date: Thu, 5 Jul 2018 00:12:34 +0900 Subject: [PATCH 281/304] =?UTF-8?q?=EC=99=95=EC=A1=B0=EC=9D=BC=EB=9E=8C=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_emperior_detail.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/a_emperior_detail.php b/hwe/a_emperior_detail.php index df9d6bc4..e290ab11 100644 --- a/hwe/a_emperior_detail.php +++ b/hwe/a_emperior_detail.php @@ -181,7 +181,7 @@ if($showServers){ } if(key_exists('aux', $nation)){ - $nation += $nation['aux']; + $nation += Json::decode($nation['aux']); } echo $templates->render('oldNation', $nation); -- 2.54.0 From 14d9b0dd31a76a33873323289164b2bc15d4f146 Mon Sep 17 00:00:00 2001 From: hide_d Date: Thu, 5 Jul 2018 00:24:03 +0900 Subject: [PATCH 282/304] =?UTF-8?q?=ED=98=84=EC=9E=AC=20=EC=A7=84=ED=96=89?= =?UTF-8?q?=EC=A4=91=EC=9D=B8=20=EA=B9=83=EC=88=98=EC=9D=B8=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EC=9E=A5=EC=88=98=EA=B0=80=20=EC=95=88=20=EC=A3=BD?= =?UTF-8?q?=EC=97=88=EC=9C=BC=EB=A9=B4=20=EC=95=88=EB=82=98=ED=83=80?= =?UTF-8?q?=EB=82=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_emperior_detail.php | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/hwe/a_emperior_detail.php b/hwe/a_emperior_detail.php index e290ab11..6636ef31 100644 --- a/hwe/a_emperior_detail.php +++ b/hwe/a_emperior_detail.php @@ -173,14 +173,27 @@ if($showServers){ $nation['typeName'] = getNationType($nation['type']); $nation['levelName'] = getNationLevel($nation['level']); if($nation['generals']){ - $generals = $db->query('SELECT `general_no`, `name` FROM ng_old_generals WHERE server_id=%s AND general_no IN %li', $serverID, $nation['generals']); - $nation['generalsFull'] = $generals; + $generals = $db->query('SELECT `general_no`, `name`, `last_yearmonth` FROM ng_old_generals WHERE server_id=%s AND general_no IN %li', $serverID, $nation['generals']); + + if(count($generals) != $nation['generals'] && $serverID == UniqueConst::$serverID){ + $liveGenerals = $nation['generals']; + foreach($generals as $general){ + if(in_array($general['general_no'], $nation['generals'])){ + unset($nation['generals'][$general['general_no']]); + } + } + $liveGenerals = $db->query('SELECT `no`as`general_no`, `name` FROM general WHERE no IN %li', $liveGenerals); + $nation['generalsFull'] = array_merge($liveGenerals, $generals); + } + else{ + $nation['generalsFull'] = $generals; + } } else{ $nation['generalsFull'] = []; } - if(key_exists('aux', $nation)){ + if(key_exists('aux', $nation) && !is_array($nation['aux'])){ $nation += Json::decode($nation['aux']); } -- 2.54.0 From 9b4b835c08e9ab00388e15bc4cc3a456d35c449f Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 7 Jul 2018 04:39:44 +0900 Subject: [PATCH 283/304] =?UTF-8?q?=EC=B2=A9=EB=B3=B4=EB=A5=BC=20join=20'|?= =?UTF-8?q?'=20=EC=97=90=EC=84=9C=20json=20dict=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/b_currentCity.php | 47 ++++++++++++++++++++++++------------------- hwe/func.php | 2 +- hwe/func_gamerule.php | 42 +++++++++++++++++++++++--------------- hwe/func_map.php | 28 +++++++++++++++++--------- hwe/func_npc.php | 8 ++++---- hwe/func_process.php | 44 ++++++++++++++++++++-------------------- hwe/j_map.php | 1 + hwe/js/map.js | 14 +------------ hwe/sql/schema.sql | 2 +- src/sammo/Json.php | 19 +++++++++++++---- src/sammo/Util.php | 3 +++ 11 files changed, 119 insertions(+), 91 deletions(-) diff --git a/hwe/b_currentCity.php b/hwe/b_currentCity.php index 9ca0426b..64079166 100644 --- a/hwe/b_currentCity.php +++ b/hwe/b_currentCity.php @@ -21,9 +21,7 @@ $query = "select no,nation,level,city from general where owner='{$userID}'"; $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); $me = MYDB_fetch_array($result); -$query = "select nation,level,spy from nation where nation='{$me['nation']}'"; -$result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); -$myNation = MYDB_fetch_array($result); +$myNation = $db->queryFirstRow('SELECT nation,level,spy FROM nation WHERE nation=%i', $me['nation']); $templates = new \League\Plates\Engine('templates'); @@ -121,27 +119,34 @@ if($me['level'] == 0) { if($myNation['level'] > 0) { // 첩보도시도 목록에 추가 - $where = 'city=0'; - $cities = array_map('intval',explode("|", $myNation['spy'])); - foreach($cities as $citySpy) { - $city = intdiv($citySpy, 10); - $where .= " or city='{$city}'"; + + $rawSpy = $myNation['spy']; + + if($rawSpy == ''){ + $spyCities = []; + } + else if(strpos($rawSpy, '|') !== false || is_integer($rawSpy)){ + //TODO: 0.8 버전 이후에는 삭제할 것. 이후 버전은 json으로 변경됨. + $spyCities = array_map(function($val){ + $val = intval($val); + return intdiv($val, 10); + }, 'intval',explode('|', $myNation['spy'])); + } + else{ + $spyCities = array_keys(Json::decode($rawSpy)); } - $query = "select city,name,nation from city where {$where}"; - $cityresult = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $citycount = MYDB_num_rows($cityresult); - for($i=0; $i < $citycount; $i++) { - $city = MYDB_fetch_array($cityresult); - echo " - "; + if($spyCities){ + foreach ($db->query('SELECT city,name,nation FROM city WHERE city in %li', $spyCities) as $city) { + echo ""; + } } } diff --git a/hwe/func.php b/hwe/func.php index a5d0afb9..6ff68682 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -1128,7 +1128,7 @@ function msgprint($msg, $name, $picture, $imgsvr, $when, $num, $type) { $db = DB::db(); $connect=$db->get(); - $message = explode("|", $msg); + $message = explode('|', $msg); $count = (count($message) - 2)/2; $message[0] = Tag2Code($message[0]); $message[1] = Tag2Code($message[1]); diff --git a/hwe/func_gamerule.php b/hwe/func_gamerule.php index bb9db7ac..4dd8cc77 100644 --- a/hwe/func_gamerule.php +++ b/hwe/func_gamerule.php @@ -395,23 +395,33 @@ function preUpdateMonthly() { } //첩보-1 - $query = "select nation,spy from nation where spy!=''"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $nationCount = MYDB_num_rows($result); - for($i=0; $i < $nationCount; $i++) { - $nation = MYDB_fetch_array($result); - $spy = ""; $k = 0; - $cities = []; - if($nation['spy'] != "") { $cities = explode("|", (string)$nation['spy']); } - while(count($cities)) { - $cities[$k]--; - if($cities[$k]%10 != 0) { $spy .= (string)$cities[$k]; } - $k++; - if($k >= count($cities)) { break; } - if($cities[$k-1]%10 != 0) { $spy .= "|"; } + foreach($db->queryAllLists("SELECT nation, spy FROM nation WHERE spy!='' AND spy!='{}'") as [$nationNo, $rawSpy]){ + if (strpos($rawSpy, '|') !== false || is_integer($rawSpy)) { + //TODO: 0.8 버전 이후에는 삭제할 것. 이후 버전은 json으로 변경됨. + $spyInfo = []; + foreach(explode('|', $rawSpy) as $value){ + $value = intval($value); + $cityNo = intdiv($value, 10); + $remainMonth = $value % 10; + $spyInfo[$cityNo] = $remainMonth; + } } - $query = "update nation set spy='$spy' where nation='{$nation['nation']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + else{ + $spyInfo = Json::decode($rawSpy); + } + + foreach($spyInfo as $cityNo => $remainMonth){ + if($remainMonth <= 1){ + unset($spyInfo[$cityNo]); + } + else{ + $spyInfo[$cityNo] -= 1; + } + } + + $db->update('nation', [ + 'spy'=>Json::encode($spyInfo, Json::EMPTY_ARRAY_IS_DICT) + ], 'nation=%i', $nationNo); } return true; diff --git a/hwe/func_map.php b/hwe/func_map.php index 6fbdc56a..3dd2b6fc 100644 --- a/hwe/func_map.php +++ b/hwe/func_map.php @@ -94,19 +94,29 @@ function getWorldMap($req){ $myNation = null; } + $spyInfo = (object)null; + if($myNation){ - $spyList = $db->queryFirstField('select `spy` from `nation` where `nation`=%i', + $rawSpy = $db->queryFirstField('select `spy` from `nation` where `nation`=%i', $myNation); - if($spyList){ - $spyList = array_map('\sammo\Util::toInt', explode("|", $spyList)); + + if(strpos($rawSpy, '|') !== false || is_integer($rawSpy)){ + //NOTE: 0.8 이전 데이터가 남아있으므로, 0.8버전으로 마이그레이션 이후에도 이곳은 삭제하면 안됨 + $spyInfo = []; + foreach(explode('|', $rawSpy) as $value){ + $value = intval($value); + $cityNo = intdiv($value, 10); + $remainMonth = $value % 10; + $spyInfo[$cityNo] = $remainMonth; + } } - else{ - $spyList = []; + else if($rawSpy != ''){ + $spyInfo = Json::decode($rawSpy); } - } - else{ - $spyList = []; + + if(!$spyInfo){ + $spyInfo = (object)null; } $nationList = []; @@ -142,7 +152,7 @@ function getWorldMap($req){ 'month' => $month, 'cityList' => $cityList, 'nationList' => $nationList, - 'spyList' => $spyList, + 'spyList' => $spyInfo, 'shownByGeneralList' => $shownByGeneralList, 'myCity' => $myCity, 'myNation' => $myNation, diff --git a/hwe/func_npc.php b/hwe/func_npc.php index c117f548..0a52d7c9 100644 --- a/hwe/func_npc.php +++ b/hwe/func_npc.php @@ -361,7 +361,7 @@ function processAI($no) { } break; case 2: //이동 20% - $paths = explode("|", $city['path']); + $paths = explode('|', $city['path']); $command = EncodeCommand(0, 0, $paths[rand()%count($paths)], 21); break; default: @@ -429,7 +429,7 @@ function processAI($no) { return; } elseif(rand()%4 > 0) { //이동 - $paths = explode("|", $city['path']); + $paths = explode('|', $city['path']); $command = EncodeCommand(0, 0, $paths[rand()%count($paths)], 21); $query = "update general set turn0='$command' where no='{$general['no']}'"; MYDB_query($query, $connect) or Error("processAI09 ".MYDB_error($connect),""); @@ -600,7 +600,7 @@ function processAI($no) { //현도시가 전방이면 공격 가능성 체크 if($city['front'] > 0) { //주변도시 체크 - $paths = explode("|", $city['path']); + $paths = explode('|', $city['path']); for($i=0; $i < count($paths); $i++) { $query = "select city,nation from city where city='$paths[$i]'"; $result = MYDB_query($query, $connect) or Error("processAI20 ".MYDB_error($connect),""); @@ -766,7 +766,7 @@ function processAI($no) { $command = EncodeCommand(0, 0, 0, (rand()%2)*8 + 1); // 준비는 됐으나 아직 선포중이면 내정, 조달 } else { //공격 & 내정 - $paths = explode("|", $city['path']); + $paths = explode('|', $city['path']); for($i=0; $i < count($paths); $i++) { $query = "select city,nation from city where city='$paths[$i]'"; $result = MYDB_query($query, $connect) or Error("processAI21 ".MYDB_error($connect),""); diff --git a/hwe/func_process.php b/hwe/func_process.php index 09301876..98a2425a 100644 --- a/hwe/func_process.php +++ b/hwe/func_process.php @@ -1743,32 +1743,32 @@ function process_31(&$general) { $query = "update general set resturn='SUCCESS',gold='{$general['gold']}',rice='{$general['rice']}',leader2='{$general['leader2']}',dedication=dedication+'$ded',experience=experience+'$exp' where no='{$general['no']}'"; MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $query = "select spy from nation where nation='{$general['nation']}'"; - $result = MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); - $nation = MYDB_fetch_array($result); - if($nation['spy'] != "") - { - $cities = array_map('intval', explode("|", $nation['spy'])); + + + $rawSpy = $db->queryFirstField('SELECT spy FROM nation WHERE nation = %i', $general['nation']); + + if($rawSpy == ''){ + $spyInfo = []; } - else{ - $cities = []; - } - $exist = 0; - for($i=0; $i < count($cities); $i++) { - if(intdiv($cities[$i], 10) == $destination) { - $exist = 1; - break; + else if(strpos($rawSpy, '|') !== false || is_integer($rawSpy)){ + //TODO: 0.8 버전 이후에는 삭제할 것. 이후 버전은 json으로 변경됨. + $spyInfo = []; + foreach(explode('|', $rawSpy) as $value){ + $value = intval($value); + $cityNo = intdiv($value, 10); + $remainMonth = $value % 10; + $spyInfo[$cityNo] = $remainMonth; } } - // 기존 첩보 목록에 없으면 새로 등록, 있으면 갱신 - if($exist == 0) { - $cities[] = $destination * 10 + 3; - } else { - $cities[$i] = $destination * 10 + 3; + else{ + $spyInfo = Json::decode($rawSpy); } - $spy = implode("|", $cities); - $query = "update nation set spy='$spy' where nation='{$general['nation']}'"; - MYDB_query($query, $connect) or Error(__LINE__.MYDB_error($connect),""); + + $spyInfo[$destination] = 3; + + $db->update('nation', [ + 'spy'=>Json::encode($spyInfo, Json::EMPTY_ARRAY_IS_DICT) + ], 'nation=%i', $general['nation']); $log = checkAbility($general, $log); } diff --git a/hwe/j_map.php b/hwe/j_map.php index f68a380c..576e78d5 100644 --- a/hwe/j_map.php +++ b/hwe/j_map.php @@ -14,6 +14,7 @@ $defaultPost = [ 'showMe' => true ]; $post = Json::decode(Util::getReq('data', 'string', '{}')); +$post += $defaultPost; if(!$session->isGameLoggedIn()){ $post['neutralView'] = true; diff --git a/hwe/js/map.js b/hwe/js/map.js index ec8d4af8..59219ec2 100644 --- a/hwe/js/map.js +++ b/hwe/js/map.js @@ -119,23 +119,11 @@ function reloadWorldMap(option){ }; } - function convertSpyList(arr){ - var result = []; - arr.forEach(function(v){ - var cityId = (v / 10) | 0; - var spy = v % 10; - result[cityId] = spy; - }); - return result; - } - - - var cityList = obj.cityList.map(toCityObj); var nationList = obj.nationList.map(toNationObj); nationList = convertDictById(nationList); //array of object -> dict - var spyList = convertSpyList(obj.spyList);//Array -> Dict + var spyList = obj.spyList; var shownByGeneralList = convertSet(obj.shownByGeneralList);//Array -> Set var myCity = obj.myCity; diff --git a/hwe/sql/schema.sql b/hwe/sql/schema.sql index 1e27f523..1f6b22fd 100644 --- a/hwe/sql/schema.sql +++ b/hwe/sql/schema.sql @@ -199,7 +199,7 @@ CREATE TABLE `nation` ( `tech` INT(8) NULL DEFAULT '0', `totaltech` INT(8) NULL DEFAULT '0', `power` INT(8) NULL DEFAULT '0', - `spy` CHAR(255) NULL DEFAULT '', + `spy` CHAR(255) NOT NULL DEFAULT '{}', `level` INT(1) NULL DEFAULT '0', `type` INT(2) NULL DEFAULT '0', `rule` TEXT NULL DEFAULT '', diff --git a/src/sammo/Json.php b/src/sammo/Json.php index 10fa3fc6..6db51d6f 100644 --- a/src/sammo/Json.php +++ b/src/sammo/Json.php @@ -3,10 +3,11 @@ namespace sammo; class Json { - const PRETTY = 1; - const DELETE_NULL = 2; - const NO_CACHE = 4; - const PASS_THROUGH = 8; + const PRETTY = 1 << 0; + const DELETE_NULL = 1 << 1; + const NO_CACHE = 1 << 2; + const PASS_THROUGH = 1 << 3; + const EMPTY_ARRAY_IS_DICT = 1 << 4; public static function encode($value, $flag = 0) { @@ -17,6 +18,10 @@ class Json if ($flag & static::DELETE_NULL) { $value = Util::eraseNullValue($value); } + + if(($flag & static::EMPTY_ARRAY_IS_DICT) && $value === []){ + $value = (object)null; + } return json_encode($value, $rawFlag); } @@ -25,6 +30,12 @@ class Json return json_decode($value, true); } + public static function decodeObj($value){ + //NOTE: 구 코드가 모두 '배열'을 가정하기 때문에 decode는 연관배열로 반환하였으나, + //호환을 위해서는object로 반환하는 것이 더 나을것 + return json_decode($value); + } + public static function die($value, $flag = self::NO_CACHE) { //NOTE: REST 형식에 맞게, ok(), fail()로 쪼개는게 낫지 않을까 생각해봄. diff --git a/src/sammo/Util.php b/src/sammo/Util.php index fdce6f0a..804106c0 100644 --- a/src/sammo/Util.php +++ b/src/sammo/Util.php @@ -160,6 +160,9 @@ class Util extends \utilphp\util if ($val == null) { return null; } + if ($val == ''){ + return null; + } return intval($val); } throw new \InvalidArgumentException('올바르지 않은 타입형 :'.$val); -- 2.54.0 From 5364e2008c6dab464d7b2a0db0b6a67e52ca6c7f Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 7 Jul 2018 04:58:19 +0900 Subject: [PATCH 284/304] =?UTF-8?q?=EA=B5=AC=EB=B2=84=EC=A0=84=20=EC=B2=A9?= =?UTF-8?q?=EB=B3=B4=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98=EC=8B=9C=20=EC=A7=80=EB=8F=84=20=ED=91=9C=EA=B8=B0=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/b_currentCity.php | 2 +- hwe/func_gamerule.php | 2 +- hwe/func_map.php | 2 +- hwe/func_process.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hwe/b_currentCity.php b/hwe/b_currentCity.php index 64079166..37f2dd6f 100644 --- a/hwe/b_currentCity.php +++ b/hwe/b_currentCity.php @@ -125,7 +125,7 @@ if($myNation['level'] > 0) { if($rawSpy == ''){ $spyCities = []; } - else if(strpos($rawSpy, '|') !== false || is_integer($rawSpy)){ + else if(strpos($rawSpy, '|') !== false || is_numeric($rawSpy)){ //TODO: 0.8 버전 이후에는 삭제할 것. 이후 버전은 json으로 변경됨. $spyCities = array_map(function($val){ $val = intval($val); diff --git a/hwe/func_gamerule.php b/hwe/func_gamerule.php index 4dd8cc77..00d92925 100644 --- a/hwe/func_gamerule.php +++ b/hwe/func_gamerule.php @@ -396,7 +396,7 @@ function preUpdateMonthly() { //첩보-1 foreach($db->queryAllLists("SELECT nation, spy FROM nation WHERE spy!='' AND spy!='{}'") as [$nationNo, $rawSpy]){ - if (strpos($rawSpy, '|') !== false || is_integer($rawSpy)) { + if (strpos($rawSpy, '|') !== false || is_numeric($rawSpy)) { //TODO: 0.8 버전 이후에는 삭제할 것. 이후 버전은 json으로 변경됨. $spyInfo = []; foreach(explode('|', $rawSpy) as $value){ diff --git a/hwe/func_map.php b/hwe/func_map.php index 3dd2b6fc..69ad6030 100644 --- a/hwe/func_map.php +++ b/hwe/func_map.php @@ -100,7 +100,7 @@ function getWorldMap($req){ $rawSpy = $db->queryFirstField('select `spy` from `nation` where `nation`=%i', $myNation); - if(strpos($rawSpy, '|') !== false || is_integer($rawSpy)){ + if(strpos($rawSpy, '|') !== false || is_numeric($rawSpy)){ //NOTE: 0.8 이전 데이터가 남아있으므로, 0.8버전으로 마이그레이션 이후에도 이곳은 삭제하면 안됨 $spyInfo = []; foreach(explode('|', $rawSpy) as $value){ diff --git a/hwe/func_process.php b/hwe/func_process.php index 98a2425a..32433844 100644 --- a/hwe/func_process.php +++ b/hwe/func_process.php @@ -1750,7 +1750,7 @@ function process_31(&$general) { if($rawSpy == ''){ $spyInfo = []; } - else if(strpos($rawSpy, '|') !== false || is_integer($rawSpy)){ + else if(strpos($rawSpy, '|') !== false || is_numeric($rawSpy)){ //TODO: 0.8 버전 이후에는 삭제할 것. 이후 버전은 json으로 변경됨. $spyInfo = []; foreach(explode('|', $rawSpy) as $value){ -- 2.54.0 From f2486e74706e482334af894cef6083684f5e3b0f Mon Sep 17 00:00:00 2001 From: hide_d Date: Sat, 7 Jul 2018 05:13:22 +0900 Subject: [PATCH 285/304] =?UTF-8?q?=EC=84=B8=EB=A0=A5=EB=8F=84=EC=8B=9C?= =?UTF-8?q?=EC=97=90=EC=84=9C=20n=EC=9E=A5,=20m=EC=9E=A5=20=EC=9E=84?= =?UTF-8?q?=EB=AA=85=EC=9D=B4=20=EB=B6=88=EA=B0=80=EB=8A=A5=ED=95=9C=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/js/ext.expand_city.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/js/ext.expand_city.js b/hwe/js/ext.expand_city.js index f37303c7..08729f71 100644 --- a/hwe/js/ext.expand_city.js +++ b/hwe/js/ext.expand_city.js @@ -564,7 +564,7 @@ $(function() { var $name = $this.children('.i_name'); $name.addClass('nameplate'); - var name = $name.find('.t_name').html(); + var name = $name.find('.t_name').text(); var $work = $this.children('.i_action'); -- 2.54.0 From dc9c586dc82c0b7257c861bf45a35a6e665b18e7 Mon Sep 17 00:00:00 2001 From: hide_d Date: Wed, 11 Jul 2018 00:53:10 +0900 Subject: [PATCH 286/304] =?UTF-8?q?=EA=B3=B5=EC=84=B1=20=EC=A0=84=ED=8A=B9?= =?UTF-8?q?=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95.=20=EC=A0=84?= =?UTF-8?q?=ED=8A=B9=20=EC=84=A4=EB=AA=85=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/func_converter.php | 49 ++++++++++++++++++++++++++++++++++++++++++ hwe/func_process.php | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/hwe/func_converter.php b/hwe/func_converter.php index a8fa7934..3886a0c6 100644 --- a/hwe/func_converter.php +++ b/hwe/func_converter.php @@ -179,6 +179,55 @@ function getGenSpecial($type) { return $call; } +function getSpecialInfo(?int $type):?string{ + if($type === null){ + return null; + } + + //앞칸은 '설명을 위해' '그냥' 적어둠 + $infoText = [ + 0 => ['-', null], + 1 => ['경작', '[내정] 농지 개간 : 기본 보정 +10%, 성공률 +10%p, 비용 -20%'], + 2 => ['상재', '[내정] 상업 투자 : 기본 보정 +10%, 성공률 +10%p, 비용 -20%'], + 3 => ['발명', '[내정] 기술 연구 : 기본 보정 +10%, 성공률 +10%p, 비용 -20%'], + + 10 => ['축성', '[내정] 성벽 보수 : 기본 보정 +10%, 성공률 +10%p, 비용 -20%'], + 11 => ['수비', '[내정] 수비 강화 : 기본 보정 +10%, 성공률 +10%p, 비용 -20%'], + 12 => ['통찰', '[내정] 치안 강화 : 기본 보정 +10%, 성공률 +10%p, 비용 -20%'], + + 20 => ['인덕', '[내정] 주민 선정·정착 장려 : 기본 보정 +10%, 성공률 +10%p, 비용 -20%'], + + 30 => ['거상', '이것 저것'], + 31 => ['귀모', '[내정] 화계·탈취·파괴·선동 : 성공률 +20%p'], + + 40 => ['귀병', '[군사] 귀병 계통 징·모병비 -10%
[전투] 계략 시도 확률 +20%p'], + 41 => ['신산', '[내정] 화계·탈취·파괴·선동 : 성공률 +10%p
[전투] 계략 시도 확률 +20%p, 계략 성공 확률 +20%p '], + 42 => ['환술', '[전투] 계략 시도 확률 +10%p, 계략 성공 시 대미지 +30%'], + 43 => ['집중', '[전투] 계략 성공 시 대미지 +50%'], + 44 => ['신중', '[전투] 계략 성공 확률 100%'], + 45 => ['반계', '[전투] 상대의 계략을 30% 확률로 되돌림, 반목 성공시 대미지 추가(+60% → +100%)'], + + 50 => ['보병', '[군사] 보병 계통 징·모병비 -10%
[전투] 공격 시 아군 피해 -10%, 수비 시 아군 피해 -20%'], + 51 => ['궁병', '[군사] 궁병 계통 징·모병비 -10%
[전투] 회피 확률 +20%p'], + 52 => ['기병', '[군사] 기병 계통 징·모병비 -10%
[전투] 수비 시 대미지 +10%, 공격 시 대미지 +20%'], + 53 => ['공성', '[군사] 차병 계통 징·모병비 -10%
[전투] 성벽 공격 시 대미지 +100%'], + + 60 => ['돌격', '[전투] 상대 회피 불가, 공격 시 전투 페이즈 +1, 공격 시 대미지 +10%'], + 61 => ['무쌍', '[전투] 대미지 +10%, 공격 시 필살 확률 +10%p'], + 62 => ['견고', '[전투] 수비 시 아군 피해 -10%, 공격 시 대미지 +10%'], + 63 => ['위압', '[전투] 훈련/사기≥90, 병력≥1,000 일 때 첫 페이즈 위압 발동(적 공격 불가)'], + + 70 => ['저격', '[전투] 전투 개시 시 1/3 확률로 저격 발동'], + 71 => ['필살', '[전투] 필살 확률 +20%p'], + 72 => ['징병', '[군사] 징·모병비 -50%'], + 73 => ['의술', '[군사] 매 턴마다 자신(100%)과 소속 도시 장수(적 포함 50%) 부상 회복
[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)'], + 74 => ['격노', '[전투] 상대방 필살 및 회피 시도시 일정 확률로 격노(필살) 발동, 공격 시 일정 확률로 진노(1페이즈 추가)'], + 75 => ['척사', '[전투] 지역·도시 병종 상대로 대미지 +10%, 아군 피해 -10%'] + ]; + + return $infoText[$type][1]??null; +} + function getNationType($type) { switch($type) { case 13: $call = '명 가'; break; diff --git a/hwe/func_process.php b/hwe/func_process.php index 32433844..0d06177c 100644 --- a/hwe/func_process.php +++ b/hwe/func_process.php @@ -898,7 +898,7 @@ function process_11(&$general, $type) { if(intdiv($armtype, 10) == 1 && $general['special2'] == 51) { $cost *= 0.9; } if(intdiv($armtype, 10) == 2 && $general['special2'] == 52) { $cost *= 0.9; } if(intdiv($armtype, 10) == 3 && $general['special2'] == 40) { $cost *= 0.9; } - if(intdiv($armtype, 10) == 4 && $general['special2'] == 43) { $cost *= 0.9; } + if(intdiv($armtype, 10) == 4 && $general['special2'] == 53) { $cost *= 0.9; } if($general['special2'] == 72) { $cost *= 0.5; } if($type == 1) { $dtype = "징병"; } -- 2.54.0 From d1af971d9a4773a2f8378140980f43390ef6e505 Mon Sep 17 00:00:00 2001 From: hide_d Date: Wed, 11 Jul 2018 01:15:02 +0900 Subject: [PATCH 287/304] =?UTF-8?q?bootstrap=20=EC=B5=9C=EC=8B=A0=20?= =?UTF-8?q?=EB=B2=84=EC=A0=84=EC=9C=BC=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- e_lib/bootstrap.bundle.min.js | 4 ++-- e_lib/bootstrap.bundle.min.js.map | 2 +- e_lib/bootstrap.min.css | 11 +++-------- e_lib/bootstrap.min.css.map | 1 + 4 files changed, 7 insertions(+), 11 deletions(-) create mode 100644 e_lib/bootstrap.min.css.map diff --git a/e_lib/bootstrap.bundle.min.js b/e_lib/bootstrap.bundle.min.js index 7d50e873..e0608e8b 100644 --- a/e_lib/bootstrap.bundle.min.js +++ b/e_lib/bootstrap.bundle.min.js @@ -1,7 +1,7 @@ /*! - * Bootstrap v4.0.0 (https://getbootstrap.com) + * Bootstrap v4.1.0 (https://getbootstrap.com/) * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],e):e(t.bootstrap={},t.jQuery)}(this,function(t,e){"use strict";function n(t,e){for(var n=0;n0?i:null}catch(t){return null}},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(n){t(n).trigger(e.end)},supportsTransitionEnd:function(){return Boolean(e)},isElement:function(t){return(t[0]||t).nodeType},typeCheckConfig:function(t,e,n){for(var r in n)if(Object.prototype.hasOwnProperty.call(n,r)){var o=n[r],s=e[r],a=s&&i.isElement(s)?"element":(l=s,{}.toString.call(l).match(/\s([a-zA-Z]+)/)[1].toLowerCase());if(!new RegExp(o).test(a))throw new Error(t.toUpperCase()+': Option "'+r+'" provided type "'+a+'" but expected type "'+o+'".')}var l}};return e=("undefined"==typeof window||!window.QUnit)&&{end:"transitionend"},t.fn.emulateTransitionEnd=n,i.supportsTransitionEnd()&&(t.event.special[i.TRANSITION_END]={bindType:e.end,delegateType:e.end,handle:function(e){if(t(e.target).is(this))return e.handleObj.handler.apply(this,arguments)}}),i}(e=e&&e.hasOwnProperty("default")?e.default:e),L=(s="alert",l="."+(a="bs.alert"),c=(o=e).fn[s],h={CLOSE:"close"+l,CLOSED:"closed"+l,CLICK_DATA_API:"click"+l+".data-api"},f="alert",u="fade",d="show",p=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){t=t||this._element;var e=this._getRootElement(t);this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){o.removeData(this._element,a),this._element=null},e._getRootElement=function(t){var e=k.getSelectorFromElement(t),n=!1;return e&&(n=o(e)[0]),n||(n=o(t).closest("."+f)[0]),n},e._triggerCloseEvent=function(t){var e=o.Event(h.CLOSE);return o(t).trigger(e),e},e._removeElement=function(t){var e=this;o(t).removeClass(d),k.supportsTransitionEnd()&&o(t).hasClass(u)?o(t).one(k.TRANSITION_END,function(n){return e._destroyElement(t,n)}).emulateTransitionEnd(150):this._destroyElement(t)},e._destroyElement=function(t){o(t).detach().trigger(h.CLOSED).remove()},t._jQueryInterface=function(e){return this.each(function(){var n=o(this),i=n.data(a);i||(i=new t(this),n.data(a,i)),"close"===e&&i[e](this)})},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},i(t,null,[{key:"VERSION",get:function(){return"4.0.0"}}]),t}(),o(document).on(h.CLICK_DATA_API,'[data-dismiss="alert"]',p._handleDismiss(new p)),o.fn[s]=p._jQueryInterface,o.fn[s].Constructor=p,o.fn[s].noConflict=function(){return o.fn[s]=c,p._jQueryInterface},p),P=(m="button",v="."+(_="bs.button"),E=".data-api",y=(g=e).fn[m],b="active",T="btn",C="focus",w='[data-toggle^="button"]',I='[data-toggle="buttons"]',A="input",D=".active",S=".btn",O={CLICK_DATA_API:"click"+v+E,FOCUS_BLUR_DATA_API:"focus"+v+E+" blur"+v+E},N=function(){function t(t){this._element=t}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=g(this._element).closest(I)[0];if(n){var i=g(this._element).find(A)[0];if(i){if("radio"===i.type)if(i.checked&&g(this._element).hasClass(b))t=!1;else{var r=g(n).find(D)[0];r&&g(r).removeClass(b)}if(t){if(i.hasAttribute("disabled")||n.hasAttribute("disabled")||i.classList.contains("disabled")||n.classList.contains("disabled"))return;i.checked=!g(this._element).hasClass(b),g(i).trigger("change")}i.focus(),e=!1}}e&&this._element.setAttribute("aria-pressed",!g(this._element).hasClass(b)),t&&g(this._element).toggleClass(b)},e.dispose=function(){g.removeData(this._element,_),this._element=null},t._jQueryInterface=function(e){return this.each(function(){var n=g(this).data(_);n||(n=new t(this),g(this).data(_,n)),"toggle"===e&&n[e]()})},i(t,null,[{key:"VERSION",get:function(){return"4.0.0"}}]),t}(),g(document).on(O.CLICK_DATA_API,w,function(t){t.preventDefault();var e=t.target;g(e).hasClass(T)||(e=g(e).closest(S)),N._jQueryInterface.call(g(e),"toggle")}).on(O.FOCUS_BLUR_DATA_API,w,function(t){var e=g(t.target).closest(S)[0];g(e).toggleClass(C,/^focus(in)?$/.test(t.type))}),g.fn[m]=N._jQueryInterface,g.fn[m].Constructor=N,g.fn[m].noConflict=function(){return g.fn[m]=y,N._jQueryInterface},N),x=function(t){var e="carousel",n="bs.carousel",o="."+n,s=t.fn[e],a={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0},l={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean"},c="next",h="prev",f="left",u="right",d={SLIDE:"slide"+o,SLID:"slid"+o,KEYDOWN:"keydown"+o,MOUSEENTER:"mouseenter"+o,MOUSELEAVE:"mouseleave"+o,TOUCHEND:"touchend"+o,LOAD_DATA_API:"load"+o+".data-api",CLICK_DATA_API:"click"+o+".data-api"},p="carousel",g="active",m="slide",_="carousel-item-right",v="carousel-item-left",E="carousel-item-next",y="carousel-item-prev",b={ACTIVE:".active",ACTIVE_ITEM:".active.carousel-item",ITEM:".carousel-item",NEXT_PREV:".carousel-item-next, .carousel-item-prev",INDICATORS:".carousel-indicators",DATA_SLIDE:"[data-slide], [data-slide-to]",DATA_RIDE:'[data-ride="carousel"]'},T=function(){function s(e,n){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this._config=this._getConfig(n),this._element=t(e)[0],this._indicatorsElement=t(this._element).find(b.INDICATORS)[0],this._addEventListeners()}var T=s.prototype;return T.next=function(){this._isSliding||this._slide(c)},T.nextWhenVisible=function(){!document.hidden&&t(this._element).is(":visible")&&"hidden"!==t(this._element).css("visibility")&&this.next()},T.prev=function(){this._isSliding||this._slide(h)},T.pause=function(e){e||(this._isPaused=!0),t(this._element).find(b.NEXT_PREV)[0]&&k.supportsTransitionEnd()&&(k.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},T.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},T.to=function(e){var n=this;this._activeElement=t(this._element).find(b.ACTIVE_ITEM)[0];var i=this._getItemIndex(this._activeElement);if(!(e>this._items.length-1||e<0))if(this._isSliding)t(this._element).one(d.SLID,function(){return n.to(e)});else{if(i===e)return this.pause(),void this.cycle();var r=e>i?c:h;this._slide(r,this._items[e])}},T.dispose=function(){t(this._element).off(o),t.removeData(this._element,n),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},T._getConfig=function(t){return t=r({},a,t),k.typeCheckConfig(e,t,l),t},T._addEventListeners=function(){var e=this;this._config.keyboard&&t(this._element).on(d.KEYDOWN,function(t){return e._keydown(t)}),"hover"===this._config.pause&&(t(this._element).on(d.MOUSEENTER,function(t){return e.pause(t)}).on(d.MOUSELEAVE,function(t){return e.cycle(t)}),"ontouchstart"in document.documentElement&&t(this._element).on(d.TOUCHEND,function(){e.pause(),e.touchTimeout&&clearTimeout(e.touchTimeout),e.touchTimeout=setTimeout(function(t){return e.cycle(t)},500+e._config.interval)}))},T._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},T._getItemIndex=function(e){return this._items=t.makeArray(t(e).parent().find(b.ITEM)),this._items.indexOf(e)},T._getItemByDirection=function(t,e){var n=t===c,i=t===h,r=this._getItemIndex(e),o=this._items.length-1;if((i&&0===r||n&&r===o)&&!this._config.wrap)return e;var s=(r+(t===h?-1:1))%this._items.length;return-1===s?this._items[this._items.length-1]:this._items[s]},T._triggerSlideEvent=function(e,n){var i=this._getItemIndex(e),r=this._getItemIndex(t(this._element).find(b.ACTIVE_ITEM)[0]),o=t.Event(d.SLIDE,{relatedTarget:e,direction:n,from:r,to:i});return t(this._element).trigger(o),o},T._setActiveIndicatorElement=function(e){if(this._indicatorsElement){t(this._indicatorsElement).find(b.ACTIVE).removeClass(g);var n=this._indicatorsElement.children[this._getItemIndex(e)];n&&t(n).addClass(g)}},T._slide=function(e,n){var i,r,o,s=this,a=t(this._element).find(b.ACTIVE_ITEM)[0],l=this._getItemIndex(a),h=n||a&&this._getItemByDirection(e,a),p=this._getItemIndex(h),T=Boolean(this._interval);if(e===c?(i=v,r=E,o=f):(i=_,r=y,o=u),h&&t(h).hasClass(g))this._isSliding=!1;else if(!this._triggerSlideEvent(h,o).isDefaultPrevented()&&a&&h){this._isSliding=!0,T&&this.pause(),this._setActiveIndicatorElement(h);var C=t.Event(d.SLID,{relatedTarget:h,direction:o,from:l,to:p});k.supportsTransitionEnd()&&t(this._element).hasClass(m)?(t(h).addClass(r),k.reflow(h),t(a).addClass(i),t(h).addClass(i),t(a).one(k.TRANSITION_END,function(){t(h).removeClass(i+" "+r).addClass(g),t(a).removeClass(g+" "+r+" "+i),s._isSliding=!1,setTimeout(function(){return t(s._element).trigger(C)},0)}).emulateTransitionEnd(600)):(t(a).removeClass(g),t(h).addClass(g),this._isSliding=!1,t(this._element).trigger(C)),T&&this.cycle()}},s._jQueryInterface=function(e){return this.each(function(){var i=t(this).data(n),o=r({},a,t(this).data());"object"==typeof e&&(o=r({},o,e));var l="string"==typeof e?e:o.slide;if(i||(i=new s(this,o),t(this).data(n,i)),"number"==typeof e)i.to(e);else if("string"==typeof l){if("undefined"==typeof i[l])throw new TypeError('No method named "'+l+'"');i[l]()}else o.interval&&(i.pause(),i.cycle())})},s._dataApiClickHandler=function(e){var i=k.getSelectorFromElement(this);if(i){var o=t(i)[0];if(o&&t(o).hasClass(p)){var a=r({},t(o).data(),t(this).data()),l=this.getAttribute("data-slide-to");l&&(a.interval=!1),s._jQueryInterface.call(t(o),a),l&&t(o).data(n).to(l),e.preventDefault()}}},i(s,null,[{key:"VERSION",get:function(){return"4.0.0"}},{key:"Default",get:function(){return a}}]),s}();return t(document).on(d.CLICK_DATA_API,b.DATA_SLIDE,T._dataApiClickHandler),t(window).on(d.LOAD_DATA_API,function(){t(b.DATA_RIDE).each(function(){var e=t(this);T._jQueryInterface.call(e,e.data())})}),t.fn[e]=T._jQueryInterface,t.fn[e].Constructor=T,t.fn[e].noConflict=function(){return t.fn[e]=s,T._jQueryInterface},T}(e),R=function(t){var e="collapse",n="bs.collapse",o="."+n,s=t.fn[e],a={toggle:!0,parent:""},l={toggle:"boolean",parent:"(string|element)"},c={SHOW:"show"+o,SHOWN:"shown"+o,HIDE:"hide"+o,HIDDEN:"hidden"+o,CLICK_DATA_API:"click"+o+".data-api"},h="show",f="collapse",u="collapsing",d="collapsed",p="width",g="height",m={ACTIVES:".show, .collapsing",DATA_TOGGLE:'[data-toggle="collapse"]'},_=function(){function o(e,n){this._isTransitioning=!1,this._element=e,this._config=this._getConfig(n),this._triggerArray=t.makeArray(t('[data-toggle="collapse"][href="#'+e.id+'"],[data-toggle="collapse"][data-target="#'+e.id+'"]'));for(var i=t(m.DATA_TOGGLE),r=0;r0&&(this._selector=s,this._triggerArray.push(o))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var s=o.prototype;return s.toggle=function(){t(this._element).hasClass(h)?this.hide():this.show()},s.show=function(){var e,i,r=this;if(!this._isTransitioning&&!t(this._element).hasClass(h)&&(this._parent&&0===(e=t.makeArray(t(this._parent).find(m.ACTIVES).filter('[data-parent="'+this._config.parent+'"]'))).length&&(e=null),!(e&&(i=t(e).not(this._selector).data(n))&&i._isTransitioning))){var s=t.Event(c.SHOW);if(t(this._element).trigger(s),!s.isDefaultPrevented()){e&&(o._jQueryInterface.call(t(e).not(this._selector),"hide"),i||t(e).data(n,null));var a=this._getDimension();t(this._element).removeClass(f).addClass(u),this._element.style[a]=0,this._triggerArray.length>0&&t(this._triggerArray).removeClass(d).attr("aria-expanded",!0),this.setTransitioning(!0);var l=function(){t(r._element).removeClass(u).addClass(f).addClass(h),r._element.style[a]="",r.setTransitioning(!1),t(r._element).trigger(c.SHOWN)};if(k.supportsTransitionEnd()){var p="scroll"+(a[0].toUpperCase()+a.slice(1));t(this._element).one(k.TRANSITION_END,l).emulateTransitionEnd(600),this._element.style[a]=this._element[p]+"px"}else l()}}},s.hide=function(){var e=this;if(!this._isTransitioning&&t(this._element).hasClass(h)){var n=t.Event(c.HIDE);if(t(this._element).trigger(n),!n.isDefaultPrevented()){var i=this._getDimension();if(this._element.style[i]=this._element.getBoundingClientRect()[i]+"px",k.reflow(this._element),t(this._element).addClass(u).removeClass(f).removeClass(h),this._triggerArray.length>0)for(var r=0;r0&&t(n).toggleClass(d,!i).attr("aria-expanded",i)}},o._getTargetFromElement=function(e){var n=k.getSelectorFromElement(e);return n?t(n)[0]:null},o._jQueryInterface=function(e){return this.each(function(){var i=t(this),s=i.data(n),l=r({},a,i.data(),"object"==typeof e&&e);if(!s&&l.toggle&&/show|hide/.test(e)&&(l.toggle=!1),s||(s=new o(this,l),i.data(n,s)),"string"==typeof e){if("undefined"==typeof s[e])throw new TypeError('No method named "'+e+'"');s[e]()}})},i(o,null,[{key:"VERSION",get:function(){return"4.0.0"}},{key:"Default",get:function(){return a}}]),o}();return t(document).on(c.CLICK_DATA_API,m.DATA_TOGGLE,function(e){"A"===e.currentTarget.tagName&&e.preventDefault();var i=t(this),r=k.getSelectorFromElement(this);t(r).each(function(){var e=t(this),r=e.data(n)?"toggle":i.data();_._jQueryInterface.call(e,r)})}),t.fn[e]=_._jQueryInterface,t.fn[e].Constructor=_,t.fn[e].noConflict=function(){return t.fn[e]=s,_._jQueryInterface},_}(e),j="undefined"!=typeof window&&"undefined"!=typeof document,H=["Edge","Trident","Firefox"],M=0,W=0;W=0){M=1;break}var U=j&&window.Promise?function(t){var e=!1;return function(){e||(e=!0,window.Promise.resolve().then(function(){e=!1,t()}))}}:function(t){var e=!1;return function(){e||(e=!0,setTimeout(function(){e=!1,t()},M))}};function B(t){return t&&"[object Function]"==={}.toString.call(t)}function F(t,e){if(1!==t.nodeType)return[];var n=getComputedStyle(t,null);return e?n[e]:n}function K(t){return"HTML"===t.nodeName?t:t.parentNode||t.host}function V(t){if(!t)return document.body;switch(t.nodeName){case"HTML":case"BODY":return t.ownerDocument.body;case"#document":return t.body}var e=F(t),n=e.overflow,i=e.overflowX,r=e.overflowY;return/(auto|scroll)/.test(n+r+i)?t:V(K(t))}function Q(t){var e=t&&t.offsetParent,n=e&&e.nodeName;return n&&"BODY"!==n&&"HTML"!==n?-1!==["TD","TABLE"].indexOf(e.nodeName)&&"static"===F(e,"position")?Q(e):e:t?t.ownerDocument.documentElement:document.documentElement}function Y(t){return null!==t.parentNode?Y(t.parentNode):t}function G(t,e){if(!(t&&t.nodeType&&e&&e.nodeType))return document.documentElement;var n=t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING,i=n?t:e,r=n?e:t,o=document.createRange();o.setStart(i,0),o.setEnd(r,0);var s,a,l=o.commonAncestorContainer;if(t!==l&&e!==l||i.contains(r))return"BODY"===(a=(s=l).nodeName)||"HTML"!==a&&Q(s.firstElementChild)!==s?Q(l):l;var c=Y(t);return c.host?G(c.host,e):G(t,Y(e).host)}function q(t){var e="top"===(arguments.length>1&&void 0!==arguments[1]?arguments[1]:"top")?"scrollTop":"scrollLeft",n=t.nodeName;if("BODY"===n||"HTML"===n){var i=t.ownerDocument.documentElement;return(t.ownerDocument.scrollingElement||i)[e]}return t[e]}function z(t,e){var n="x"===e?"Left":"Top",i="Left"===n?"Right":"Bottom";return parseFloat(t["border"+n+"Width"],10)+parseFloat(t["border"+i+"Width"],10)}var X=void 0,Z=function(){return void 0===X&&(X=-1!==navigator.appVersion.indexOf("MSIE 10")),X};function J(t,e,n,i){return Math.max(e["offset"+t],e["scroll"+t],n["client"+t],n["offset"+t],n["scroll"+t],Z()?n["offset"+t]+i["margin"+("Height"===t?"Top":"Left")]+i["margin"+("Height"===t?"Bottom":"Right")]:0)}function $(){var t=document.body,e=document.documentElement,n=Z()&&getComputedStyle(e);return{height:J("Height",t,e,n),width:J("Width",t,e,n)}}var tt=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},et=function(){function t(t,e){for(var n=0;n2&&void 0!==arguments[2]&&arguments[2],i=q(e,"top"),r=q(e,"left"),o=n?-1:1;return t.top+=i*o,t.bottom+=i*o,t.left+=r*o,t.right+=r*o,t}(h,e)),h}function at(t,e,n,i){var r,o,s,a,l,c,h,f={top:0,left:0},u=G(t,e);if("viewport"===i)o=(r=u).ownerDocument.documentElement,s=st(r,o),a=Math.max(o.clientWidth,window.innerWidth||0),l=Math.max(o.clientHeight,window.innerHeight||0),c=q(o),h=q(o,"left"),f=rt({top:c-s.top+s.marginTop,left:h-s.left+s.marginLeft,width:a,height:l});else{var d=void 0;"scrollParent"===i?"BODY"===(d=V(K(e))).nodeName&&(d=t.ownerDocument.documentElement):d="window"===i?t.ownerDocument.documentElement:i;var p=st(d,u);if("HTML"!==d.nodeName||function t(e){var n=e.nodeName;return"BODY"!==n&&"HTML"!==n&&("fixed"===F(e,"position")||t(K(e)))}(u))f=p;else{var g=$(),m=g.height,_=g.width;f.top+=p.top-p.marginTop,f.bottom=m+p.top,f.left+=p.left-p.marginLeft,f.right=_+p.left}}return f.left+=n,f.top+=n,f.right-=n,f.bottom-=n,f}function lt(t,e,n,i,r){var o=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;if(-1===t.indexOf("auto"))return t;var s=at(n,i,o,r),a={top:{width:s.width,height:e.top-s.top},right:{width:s.right-e.right,height:s.height},bottom:{width:s.width,height:s.bottom-e.bottom},left:{width:e.left-s.left,height:s.height}},l=Object.keys(a).map(function(t){return it({key:t},a[t],{area:(e=a[t],e.width*e.height)});var e}).sort(function(t,e){return e.area-t.area}),c=l.filter(function(t){var e=t.width,i=t.height;return e>=n.clientWidth&&i>=n.clientHeight}),h=c.length>0?c[0].key:l[0].key,f=t.split("-")[1];return h+(f?"-"+f:"")}function ct(t,e,n){return st(n,G(e,n))}function ht(t){var e=getComputedStyle(t),n=parseFloat(e.marginTop)+parseFloat(e.marginBottom),i=parseFloat(e.marginLeft)+parseFloat(e.marginRight);return{width:t.offsetWidth+i,height:t.offsetHeight+n}}function ft(t){var e={left:"right",right:"left",bottom:"top",top:"bottom"};return t.replace(/left|right|bottom|top/g,function(t){return e[t]})}function ut(t,e,n){n=n.split("-")[0];var i=ht(t),r={width:i.width,height:i.height},o=-1!==["right","left"].indexOf(n),s=o?"top":"left",a=o?"left":"top",l=o?"height":"width",c=o?"width":"height";return r[s]=e[s]+e[l]/2-i[l]/2,r[a]=n===a?e[a]-i[c]:e[ft(a)],r}function dt(t,e){return Array.prototype.find?t.find(e):t.filter(e)[0]}function pt(t,e,n){return(void 0===n?t:t.slice(0,function(t,e,n){if(Array.prototype.findIndex)return t.findIndex(function(t){return t[e]===n});var i=dt(t,function(t){return t[e]===n});return t.indexOf(i)}(t,"name",n))).forEach(function(t){t.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var n=t.function||t.fn;t.enabled&&B(n)&&(e.offsets.popper=rt(e.offsets.popper),e.offsets.reference=rt(e.offsets.reference),e=n(e,t))}),e}function gt(t,e){return t.some(function(t){var n=t.name;return t.enabled&&n===e})}function mt(t){for(var e=[!1,"ms","Webkit","Moz","O"],n=t.charAt(0).toUpperCase()+t.slice(1),i=0;i1&&void 0!==arguments[1]&&arguments[1],n=wt.indexOf(t),i=wt.slice(n+1).concat(wt.slice(0,n));return e?i.reverse():i}var At={FLIP:"flip",CLOCKWISE:"clockwise",COUNTERCLOCKWISE:"counterclockwise"};function Dt(t,e,n,i){var r=[0,0],o=-1!==["right","left"].indexOf(i),s=t.split(/(\+|\-)/).map(function(t){return t.trim()}),a=s.indexOf(dt(s,function(t){return-1!==t.search(/,|\s/)}));s[a]&&-1===s[a].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var l=/\s*,\s*|\s+/,c=-1!==a?[s.slice(0,a).concat([s[a].split(l)[0]]),[s[a].split(l)[1]].concat(s.slice(a+1))]:[s];return(c=c.map(function(t,i){var r=(1===i?!o:o)?"height":"width",s=!1;return t.reduce(function(t,e){return""===t[t.length-1]&&-1!==["+","-"].indexOf(e)?(t[t.length-1]=e,s=!0,t):s?(t[t.length-1]+=e,s=!1,t):t.concat(e)},[]).map(function(t){return function(t,e,n,i){var r=t.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),o=+r[1],s=r[2];if(!o)return t;if(0===s.indexOf("%")){var a=void 0;switch(s){case"%p":a=n;break;case"%":case"%r":default:a=i}return rt(a)[e]/100*o}if("vh"===s||"vw"===s)return("vh"===s?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*o;return o}(t,r,e,n)})})).forEach(function(t,e){t.forEach(function(n,i){yt(n)&&(r[e]+=n*("-"===t[i-1]?-1:1))})}),r}var St={placement:"bottom",eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(t){var e=t.placement,n=e.split("-")[0],i=e.split("-")[1];if(i){var r=t.offsets,o=r.reference,s=r.popper,a=-1!==["bottom","top"].indexOf(n),l=a?"left":"top",c=a?"width":"height",h={start:nt({},l,o[l]),end:nt({},l,o[l]+o[c]-s[c])};t.offsets.popper=it({},s,h[i])}return t}},offset:{order:200,enabled:!0,fn:function(t,e){var n=e.offset,i=t.placement,r=t.offsets,o=r.popper,s=r.reference,a=i.split("-")[0],l=void 0;return l=yt(+n)?[+n,0]:Dt(n,o,s,a),"left"===a?(o.top+=l[0],o.left-=l[1]):"right"===a?(o.top+=l[0],o.left+=l[1]):"top"===a?(o.left+=l[0],o.top-=l[1]):"bottom"===a&&(o.left+=l[0],o.top+=l[1]),t.popper=o,t},offset:0},preventOverflow:{order:300,enabled:!0,fn:function(t,e){var n=e.boundariesElement||Q(t.instance.popper);t.instance.reference===n&&(n=Q(n));var i=at(t.instance.popper,t.instance.reference,e.padding,n);e.boundaries=i;var r=e.priority,o=t.offsets.popper,s={primary:function(t){var n=o[t];return o[t]i[t]&&!e.escapeWithReference&&(r=Math.min(o[n],i[t]-("right"===t?o.width:o.height))),nt({},n,r)}};return r.forEach(function(t){var e=-1!==["left","top"].indexOf(t)?"primary":"secondary";o=it({},o,s[e](t))}),t.offsets.popper=o,t},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(t){var e=t.offsets,n=e.popper,i=e.reference,r=t.placement.split("-")[0],o=Math.floor,s=-1!==["top","bottom"].indexOf(r),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]o(i[a])&&(t.offsets.popper[l]=o(i[a])),t}},arrow:{order:500,enabled:!0,fn:function(t,e){var n;if(!Tt(t.instance.modifiers,"arrow","keepTogether"))return t;var i=e.element;if("string"==typeof i){if(!(i=t.instance.popper.querySelector(i)))return t}else if(!t.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),t;var r=t.placement.split("-")[0],o=t.offsets,s=o.popper,a=o.reference,l=-1!==["left","right"].indexOf(r),c=l?"height":"width",h=l?"Top":"Left",f=h.toLowerCase(),u=l?"left":"top",d=l?"bottom":"right",p=ht(i)[c];a[d]-ps[d]&&(t.offsets.popper[f]+=a[f]+p-s[d]),t.offsets.popper=rt(t.offsets.popper);var g=a[f]+a[c]/2-p/2,m=F(t.instance.popper),_=parseFloat(m["margin"+h],10),v=parseFloat(m["border"+h+"Width"],10),E=g-t.offsets.popper[f]-_-v;return E=Math.max(Math.min(s[c]-p,E),0),t.arrowElement=i,t.offsets.arrow=(nt(n={},f,Math.round(E)),nt(n,u,""),n),t},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(t,e){if(gt(t.instance.modifiers,"inner"))return t;if(t.flipped&&t.placement===t.originalPlacement)return t;var n=at(t.instance.popper,t.instance.reference,e.padding,e.boundariesElement),i=t.placement.split("-")[0],r=ft(i),o=t.placement.split("-")[1]||"",s=[];switch(e.behavior){case At.FLIP:s=[i,r];break;case At.CLOCKWISE:s=It(i);break;case At.COUNTERCLOCKWISE:s=It(i,!0);break;default:s=e.behavior}return s.forEach(function(a,l){if(i!==a||s.length===l+1)return t;i=t.placement.split("-")[0],r=ft(i);var c,h=t.offsets.popper,f=t.offsets.reference,u=Math.floor,d="left"===i&&u(h.right)>u(f.left)||"right"===i&&u(h.left)u(f.top)||"bottom"===i&&u(h.top)u(n.right),m=u(h.top)u(n.bottom),v="left"===i&&p||"right"===i&&g||"top"===i&&m||"bottom"===i&&_,E=-1!==["top","bottom"].indexOf(i),y=!!e.flipVariations&&(E&&"start"===o&&p||E&&"end"===o&&g||!E&&"start"===o&&m||!E&&"end"===o&&_);(d||v||y)&&(t.flipped=!0,(d||v)&&(i=s[l+1]),y&&(o="end"===(c=o)?"start":"start"===c?"end":c),t.placement=i+(o?"-"+o:""),t.offsets.popper=it({},t.offsets.popper,ut(t.instance.popper,t.offsets.reference,t.placement)),t=pt(t.instance.modifiers,t,"flip"))}),t},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(t){var e=t.placement,n=e.split("-")[0],i=t.offsets,r=i.popper,o=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return r[s?"left":"top"]=o[n]-(a?r[s?"width":"height"]:0),t.placement=ft(e),t.offsets.popper=rt(r),t}},hide:{order:800,enabled:!0,fn:function(t){if(!Tt(t.instance.modifiers,"hide","preventOverflow"))return t;var e=t.offsets.reference,n=dt(t.instance.modifiers,function(t){return"preventOverflow"===t.name}).boundaries;if(e.bottomn.right||e.top>n.bottom||e.right2&&void 0!==arguments[2]?arguments[2]:{};tt(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=U(this.update.bind(this)),this.options=it({},t.Defaults,r),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=e&&e.jquery?e[0]:e,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(it({},t.Defaults.modifiers,r.modifiers)).forEach(function(e){i.options.modifiers[e]=it({},t.Defaults.modifiers[e]||{},r.modifiers?r.modifiers[e]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(t){return it({name:t},i.options.modifiers[t])}).sort(function(t,e){return t.order-e.order}),this.modifiers.forEach(function(t){t.enabled&&B(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)}),this.update();var o=this.options.eventsEnabled;o&&this.enableEventListeners(),this.state.eventsEnabled=o}return et(t,[{key:"update",value:function(){return function(){if(!this.state.isDestroyed){var t={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};t.offsets.reference=ct(this.state,this.popper,this.reference),t.placement=lt(this.options.placement,t.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),t.originalPlacement=t.placement,t.offsets.popper=ut(this.popper,t.offsets.reference,t.placement),t.offsets.popper.position="absolute",t=pt(this.modifiers,t),this.state.isCreated?this.options.onUpdate(t):(this.state.isCreated=!0,this.options.onCreate(t))}}.call(this)}},{key:"destroy",value:function(){return function(){return this.state.isDestroyed=!0,gt(this.modifiers,"applyStyle")&&(this.popper.removeAttribute("x-placement"),this.popper.style.left="",this.popper.style.position="",this.popper.style.top="",this.popper.style[mt("transform")]=""),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}.call(this)}},{key:"enableEventListeners",value:function(){return function(){this.state.eventsEnabled||(this.state=vt(this.reference,this.options,this.state,this.scheduleUpdate))}.call(this)}},{key:"disableEventListeners",value:function(){return Et.call(this)}}]),t}();Ot.Utils=("undefined"!=typeof window?window:global).PopperUtils,Ot.placements=Ct,Ot.Defaults=St;var Nt=function(t){var e="dropdown",n="bs.dropdown",o="."+n,s=t.fn[e],a=new RegExp("38|40|27"),l={HIDE:"hide"+o,HIDDEN:"hidden"+o,SHOW:"show"+o,SHOWN:"shown"+o,CLICK:"click"+o,CLICK_DATA_API:"click"+o+".data-api",KEYDOWN_DATA_API:"keydown"+o+".data-api",KEYUP_DATA_API:"keyup"+o+".data-api"},c="disabled",h="show",f="dropup",u="dropright",d="dropleft",p="dropdown-menu-right",g="dropdown-menu-left",m="position-static",_='[data-toggle="dropdown"]',v=".dropdown form",E=".dropdown-menu",y=".navbar-nav",b=".dropdown-menu .dropdown-item:not(.disabled)",T="top-start",C="top-end",w="bottom-start",I="bottom-end",A="right-start",D="left-start",S={offset:0,flip:!0,boundary:"scrollParent"},O={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)"},N=function(){function s(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var v=s.prototype;return v.toggle=function(){if(!this._element.disabled&&!t(this._element).hasClass(c)){var e=s._getParentFromElement(this._element),n=t(this._menu).hasClass(h);if(s._clearMenus(),!n){var i={relatedTarget:this._element},r=t.Event(l.SHOW,i);if(t(e).trigger(r),!r.isDefaultPrevented()){if(!this._inNavbar){if("undefined"==typeof Ot)throw new TypeError("Bootstrap dropdown require Popper.js (https://popper.js.org)");var o=this._element;t(e).hasClass(f)&&(t(this._menu).hasClass(g)||t(this._menu).hasClass(p))&&(o=e),"scrollParent"!==this._config.boundary&&t(e).addClass(m),this._popper=new Ot(o,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===t(e).closest(y).length&&t("body").children().on("mouseover",null,t.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),t(this._menu).toggleClass(h),t(e).toggleClass(h).trigger(t.Event(l.SHOWN,i))}}}},v.dispose=function(){t.removeData(this._element,n),t(this._element).off(o),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},v.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},v._addEventListeners=function(){var e=this;t(this._element).on(l.CLICK,function(t){t.preventDefault(),t.stopPropagation(),e.toggle()})},v._getConfig=function(n){return n=r({},this.constructor.Default,t(this._element).data(),n),k.typeCheckConfig(e,n,this.constructor.DefaultType),n},v._getMenuElement=function(){if(!this._menu){var e=s._getParentFromElement(this._element);this._menu=t(e).find(E)[0]}return this._menu},v._getPlacement=function(){var e=t(this._element).parent(),n=w;return e.hasClass(f)?(n=T,t(this._menu).hasClass(p)&&(n=C)):e.hasClass(u)?n=A:e.hasClass(d)?n=D:t(this._menu).hasClass(p)&&(n=I),n},v._detectNavbar=function(){return t(this._element).closest(".navbar").length>0},v._getPopperConfig=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=r({},e.offsets,t._config.offset(e.offsets)||{}),e}:e.offset=this._config.offset,{placement:this._getPlacement(),modifiers:{offset:e,flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}}},s._jQueryInterface=function(e){return this.each(function(){var i=t(this).data(n);if(i||(i=new s(this,"object"==typeof e?e:null),t(this).data(n,i)),"string"==typeof e){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}})},s._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var i=t.makeArray(t(_)),r=0;r0&&o--,40===e.which&&odocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},g._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},g._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},f="show",u="out",d={HIDE:"hide"+o,HIDDEN:"hidden"+o,SHOW:"show"+o,SHOWN:"shown"+o,INSERTED:"inserted"+o,CLICK:"click"+o,FOCUSIN:"focusin"+o,FOCUSOUT:"focusout"+o,MOUSEENTER:"mouseenter"+o,MOUSELEAVE:"mouseleave"+o},p="fade",g="show",m=".tooltip-inner",_=".arrow",v="hover",E="focus",y="click",b="manual",T=function(){function s(t,e){if("undefined"==typeof Ot)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var T=s.prototype;return T.enable=function(){this._isEnabled=!0},T.disable=function(){this._isEnabled=!1},T.toggleEnabled=function(){this._isEnabled=!this._isEnabled},T.toggle=function(e){if(this._isEnabled)if(e){var n=this.constructor.DATA_KEY,i=t(e.currentTarget).data(n);i||(i=new this.constructor(e.currentTarget,this._getDelegateConfig()),t(e.currentTarget).data(n,i)),i._activeTrigger.click=!i._activeTrigger.click,i._isWithActiveTrigger()?i._enter(null,i):i._leave(null,i)}else{if(t(this.getTipElement()).hasClass(g))return void this._leave(null,this);this._enter(null,this)}},T.dispose=function(){clearTimeout(this._timeout),t.removeData(this.element,this.constructor.DATA_KEY),t(this.element).off(this.constructor.EVENT_KEY),t(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&t(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,null!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},T.show=function(){var e=this;if("none"===t(this.element).css("display"))throw new Error("Please use show on visible elements");var n=t.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){t(this.element).trigger(n);var i=t.contains(this.element.ownerDocument.documentElement,this.element);if(n.isDefaultPrevented()||!i)return;var r=this.getTipElement(),o=k.getUID(this.constructor.NAME);r.setAttribute("id",o),this.element.setAttribute("aria-describedby",o),this.setContent(),this.config.animation&&t(r).addClass(p);var a="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,l=this._getAttachment(a);this.addAttachmentClass(l);var c=!1===this.config.container?document.body:t(this.config.container);t(r).data(this.constructor.DATA_KEY,this),t.contains(this.element.ownerDocument.documentElement,this.tip)||t(r).appendTo(c),t(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new Ot(this.element,r,{placement:l,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:_},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){e._handlePopperPlacementChange(t)}}),t(r).addClass(g),"ontouchstart"in document.documentElement&&t("body").children().on("mouseover",null,t.noop);var h=function(){e.config.animation&&e._fixTransition();var n=e._hoverState;e._hoverState=null,t(e.element).trigger(e.constructor.Event.SHOWN),n===u&&e._leave(null,e)};k.supportsTransitionEnd()&&t(this.tip).hasClass(p)?t(this.tip).one(k.TRANSITION_END,h).emulateTransitionEnd(s._TRANSITION_DURATION):h()}},T.hide=function(e){var n=this,i=this.getTipElement(),r=t.Event(this.constructor.Event.HIDE),o=function(){n._hoverState!==f&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),t(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),e&&e()};t(this.element).trigger(r),r.isDefaultPrevented()||(t(i).removeClass(g),"ontouchstart"in document.documentElement&&t("body").children().off("mouseover",null,t.noop),this._activeTrigger[y]=!1,this._activeTrigger[E]=!1,this._activeTrigger[v]=!1,k.supportsTransitionEnd()&&t(this.tip).hasClass(p)?t(i).one(k.TRANSITION_END,o).emulateTransitionEnd(150):o(),this._hoverState="")},T.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},T.isWithContent=function(){return Boolean(this.getTitle())},T.addAttachmentClass=function(e){t(this.getTipElement()).addClass("bs-tooltip-"+e)},T.getTipElement=function(){return this.tip=this.tip||t(this.config.template)[0],this.tip},T.setContent=function(){var e=t(this.getTipElement());this.setElementContent(e.find(m),this.getTitle()),e.removeClass(p+" "+g)},T.setElementContent=function(e,n){var i=this.config.html;"object"==typeof n&&(n.nodeType||n.jquery)?i?t(n).parent().is(e)||e.empty().append(n):e.text(t(n).text()):e[i?"html":"text"](n)},T.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},T._getAttachment=function(t){return c[t.toUpperCase()]},T._setListeners=function(){var e=this;this.config.trigger.split(" ").forEach(function(n){if("click"===n)t(e.element).on(e.constructor.Event.CLICK,e.config.selector,function(t){return e.toggle(t)});else if(n!==b){var i=n===v?e.constructor.Event.MOUSEENTER:e.constructor.Event.FOCUSIN,r=n===v?e.constructor.Event.MOUSELEAVE:e.constructor.Event.FOCUSOUT;t(e.element).on(i,e.config.selector,function(t){return e._enter(t)}).on(r,e.config.selector,function(t){return e._leave(t)})}t(e.element).closest(".modal").on("hide.bs.modal",function(){return e.hide()})}),this.config.selector?this.config=r({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},T._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},T._enter=function(e,n){var i=this.constructor.DATA_KEY;(n=n||t(e.currentTarget).data(i))||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),t(e.currentTarget).data(i,n)),e&&(n._activeTrigger["focusin"===e.type?E:v]=!0),t(n.getTipElement()).hasClass(g)||n._hoverState===f?n._hoverState=f:(clearTimeout(n._timeout),n._hoverState=f,n.config.delay&&n.config.delay.show?n._timeout=setTimeout(function(){n._hoverState===f&&n.show()},n.config.delay.show):n.show())},T._leave=function(e,n){var i=this.constructor.DATA_KEY;(n=n||t(e.currentTarget).data(i))||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),t(e.currentTarget).data(i,n)),e&&(n._activeTrigger["focusout"===e.type?E:v]=!1),n._isWithActiveTrigger()||(clearTimeout(n._timeout),n._hoverState=u,n.config.delay&&n.config.delay.hide?n._timeout=setTimeout(function(){n._hoverState===u&&n.hide()},n.config.delay.hide):n.hide())},T._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},T._getConfig=function(n){return"number"==typeof(n=r({},this.constructor.Default,t(this.element).data(),n)).delay&&(n.delay={show:n.delay,hide:n.delay}),"number"==typeof n.title&&(n.title=n.title.toString()),"number"==typeof n.content&&(n.content=n.content.toString()),k.typeCheckConfig(e,n,this.constructor.DefaultType),n},T._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},T._cleanTipClass=function(){var e=t(this.getTipElement()),n=e.attr("class").match(a);null!==n&&n.length>0&&e.removeClass(n.join(""))},T._handlePopperPlacementChange=function(t){this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},T._fixTransition=function(){var e=this.getTipElement(),n=this.config.animation;null===e.getAttribute("x-placement")&&(t(e).removeClass(p),this.config.animation=!1,this.hide(),this.show(),this.config.animation=n)},s._jQueryInterface=function(e){return this.each(function(){var i=t(this).data(n),r="object"==typeof e&&e;if((i||!/dispose|hide/.test(e))&&(i||(i=new s(this,r),t(this).data(n,i)),"string"==typeof e)){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}})},i(s,null,[{key:"VERSION",get:function(){return"4.0.0"}},{key:"Default",get:function(){return h}},{key:"NAME",get:function(){return e}},{key:"DATA_KEY",get:function(){return n}},{key:"Event",get:function(){return d}},{key:"EVENT_KEY",get:function(){return o}},{key:"DefaultType",get:function(){return l}}]),s}();return t.fn[e]=T._jQueryInterface,t.fn[e].Constructor=T,t.fn[e].noConflict=function(){return t.fn[e]=s,T._jQueryInterface},T}(e),Pt=function(t){var e="popover",n="bs.popover",o="."+n,s=t.fn[e],a=new RegExp("(^|\\s)bs-popover\\S+","g"),l=r({},Lt.Default,{placement:"right",trigger:"click",content:"",template:''}),c=r({},Lt.DefaultType,{content:"(string|element|function)"}),h="fade",f="show",u=".popover-header",d=".popover-body",p={HIDE:"hide"+o,HIDDEN:"hidden"+o,SHOW:"show"+o,SHOWN:"shown"+o,INSERTED:"inserted"+o,CLICK:"click"+o,FOCUSIN:"focusin"+o,FOCUSOUT:"focusout"+o,MOUSEENTER:"mouseenter"+o,MOUSELEAVE:"mouseleave"+o},g=function(r){var s,g;function m(){return r.apply(this,arguments)||this}g=r,(s=m).prototype=Object.create(g.prototype),s.prototype.constructor=s,s.__proto__=g;var _=m.prototype;return _.isWithContent=function(){return this.getTitle()||this._getContent()},_.addAttachmentClass=function(e){t(this.getTipElement()).addClass("bs-popover-"+e)},_.getTipElement=function(){return this.tip=this.tip||t(this.config.template)[0],this.tip},_.setContent=function(){var e=t(this.getTipElement());this.setElementContent(e.find(u),this.getTitle());var n=this._getContent();"function"==typeof n&&(n=n.call(this.element)),this.setElementContent(e.find(d),n),e.removeClass(h+" "+f)},_._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},_._cleanTipClass=function(){var e=t(this.getTipElement()),n=e.attr("class").match(a);null!==n&&n.length>0&&e.removeClass(n.join(""))},m._jQueryInterface=function(e){return this.each(function(){var i=t(this).data(n),r="object"==typeof e?e:null;if((i||!/destroy|hide/.test(e))&&(i||(i=new m(this,r),t(this).data(n,i)),"string"==typeof e)){if("undefined"==typeof i[e])throw new TypeError('No method named "'+e+'"');i[e]()}})},i(m,null,[{key:"VERSION",get:function(){return"4.0.0"}},{key:"Default",get:function(){return l}},{key:"NAME",get:function(){return e}},{key:"DATA_KEY",get:function(){return n}},{key:"Event",get:function(){return p}},{key:"EVENT_KEY",get:function(){return o}},{key:"DefaultType",get:function(){return c}}]),m}(Lt);return t.fn[e]=g._jQueryInterface,t.fn[e].Constructor=g,t.fn[e].noConflict=function(){return t.fn[e]=s,g._jQueryInterface},g}(e),xt=function(t){var e="scrollspy",n="bs.scrollspy",o="."+n,s=t.fn[e],a={offset:10,method:"auto",target:""},l={offset:"number",method:"string",target:"(string|element)"},c={ACTIVATE:"activate"+o,SCROLL:"scroll"+o,LOAD_DATA_API:"load"+o+".data-api"},h="dropdown-item",f="active",u={DATA_SPY:'[data-spy="scroll"]',ACTIVE:".active",NAV_LIST_GROUP:".nav, .list-group",NAV_LINKS:".nav-link",NAV_ITEMS:".nav-item",LIST_ITEMS:".list-group-item",DROPDOWN:".dropdown",DROPDOWN_ITEMS:".dropdown-item",DROPDOWN_TOGGLE:".dropdown-toggle"},d="offset",p="position",g=function(){function s(e,n){var i=this;this._element=e,this._scrollElement="BODY"===e.tagName?window:e,this._config=this._getConfig(n),this._selector=this._config.target+" "+u.NAV_LINKS+","+this._config.target+" "+u.LIST_ITEMS+","+this._config.target+" "+u.DROPDOWN_ITEMS,this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,t(this._scrollElement).on(c.SCROLL,function(t){return i._process(t)}),this.refresh(),this._process()}var g=s.prototype;return g.refresh=function(){var e=this,n=this._scrollElement===this._scrollElement.window?d:p,i="auto"===this._config.method?n:this._config.method,r=i===p?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),t.makeArray(t(this._selector)).map(function(e){var n,o=k.getSelectorFromElement(e);if(o&&(n=t(o)[0]),n){var s=n.getBoundingClientRect();if(s.width||s.height)return[t(n)[i]().top+r,o]}return null}).filter(function(t){return t}).sort(function(t,e){return t[0]-e[0]}).forEach(function(t){e._offsets.push(t[0]),e._targets.push(t[1])})},g.dispose=function(){t.removeData(this._element,n),t(this._scrollElement).off(o),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},g._getConfig=function(n){if("string"!=typeof(n=r({},a,n)).target){var i=t(n.target).attr("id");i||(i=k.getUID(e),t(n.target).attr("id",i)),n.target="#"+i}return k.typeCheckConfig(e,n,l),n},g._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},g._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},g._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},g._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var r=this._offsets.length;r--;){this._activeTarget!==this._targets[r]&&t>=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||t=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}(e),t.Util=k,t.Alert=L,t.Button=P,t.Carousel=x,t.Collapse=R,t.Dropdown=Nt,t.Modal=kt,t.Popover=Pt,t.Scrollspy=xt,t.Tab=Rt,t.Tooltip=Lt,Object.defineProperty(t,"__esModule",{value:!0})}); +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],e):e(t.bootstrap={},t.jQuery)}(this,function(t,e){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)k(this._element).one(K.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=n=i.clientWidth&&n>=i.clientHeight}),f=0r[t]&&!i.escapeWithReference&&(n=Math.min(o[e],r[t]-("right"===t?o.width:o.height))),Ft({},e,n)}};return n.forEach(function(t){var e=-1!==["left","top"].indexOf(t)?"primary":"secondary";o=Ut({},o,s[e](t))}),t.offsets.popper=o,t},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(t){var e=t.offsets,n=e.popper,i=e.reference,r=t.placement.split("-")[0],o=Math.floor,s=-1!==["top","bottom"].indexOf(r),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]o(i[a])&&(t.offsets.popper[l]=o(i[a])),t}},arrow:{order:500,enabled:!0,fn:function(t,e){var n;if(!ae(t.instance.modifiers,"arrow","keepTogether"))return t;var i=e.element;if("string"==typeof i){if(!(i=t.instance.popper.querySelector(i)))return t}else if(!t.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),t;var r=t.placement.split("-")[0],o=t.offsets,s=o.popper,a=o.reference,l=-1!==["left","right"].indexOf(r),c=l?"height":"width",f=l?"Top":"Left",h=f.toLowerCase(),u=l?"left":"top",d=l?"bottom":"right",p=zt(i)[c];a[d]-ps[d]&&(t.offsets.popper[h]+=a[h]+p-s[d]),t.offsets.popper=Bt(t.offsets.popper);var g=a[h]+a[c]/2-p/2,m=Dt(t.instance.popper),_=parseFloat(m["margin"+f],10),v=parseFloat(m["border"+f+"Width"],10),E=g-t.offsets.popper[h]-_-v;return E=Math.max(Math.min(s[c]-p,E),0),t.arrowElement=i,t.offsets.arrow=(Ft(n={},h,Math.round(E)),Ft(n,u,""),n),t},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(p,g){if(te(p.instance.modifiers,"inner"))return p;if(p.flipped&&p.placement===p.originalPlacement)return p;var m=Yt(p.instance.popper,p.instance.reference,g.padding,g.boundariesElement,p.positionFixed),_=p.placement.split("-")[0],v=Xt(_),E=p.placement.split("-")[1]||"",y=[];switch(g.behavior){case he.FLIP:y=[_,v];break;case he.CLOCKWISE:y=fe(_);break;case he.COUNTERCLOCKWISE:y=fe(_,!0);break;default:y=g.behavior}return y.forEach(function(t,e){if(_!==t||y.length===e+1)return p;_=p.placement.split("-")[0],v=Xt(_);var n,i=p.offsets.popper,r=p.offsets.reference,o=Math.floor,s="left"===_&&o(i.right)>o(r.left)||"right"===_&&o(i.left)o(r.top)||"bottom"===_&&o(i.top)o(m.right),c=o(i.top)o(m.bottom),h="left"===_&&a||"right"===_&&l||"top"===_&&c||"bottom"===_&&f,u=-1!==["top","bottom"].indexOf(_),d=!!g.flipVariations&&(u&&"start"===E&&a||u&&"end"===E&&l||!u&&"start"===E&&c||!u&&"end"===E&&f);(s||h||d)&&(p.flipped=!0,(s||h)&&(_=y[e+1]),d&&(E="end"===(n=E)?"start":"start"===n?"end":n),p.placement=_+(E?"-"+E:""),p.offsets.popper=Ut({},p.offsets.popper,Jt(p.instance.popper,p.offsets.reference,p.placement)),p=$t(p.instance.modifiers,p,"flip"))}),p},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(t){var e=t.placement,n=e.split("-")[0],i=t.offsets,r=i.popper,o=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return r[s?"left":"top"]=o[n]-(a?r[s?"width":"height"]:0),t.placement=Xt(e),t.offsets.popper=Bt(r),t}},hide:{order:800,enabled:!0,fn:function(t){if(!ae(t.instance.modifiers,"hide","preventOverflow"))return t;var e=t.offsets.reference,n=Zt(t.instance.modifiers,function(t){return"preventOverflow"===t.name}).boundaries;if(e.bottomn.right||e.top>n.bottom||e.rightdocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!(pn={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(dn={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},_n="out",vn={HIDE:"hide"+cn,HIDDEN:"hidden"+cn,SHOW:(mn="show")+cn,SHOWN:"shown"+cn,INSERTED:"inserted"+cn,CLICK:"click"+cn,FOCUSIN:"focusin"+cn,FOCUSOUT:"focusout"+cn,MOUSEENTER:"mouseenter"+cn,MOUSELEAVE:"mouseleave"+cn},En="fade",yn="show",bn=".tooltip-inner",Tn=".arrow",Cn="hover",wn="focus",In="click",Dn="manual",An=function(){function i(t,e){if("undefined"==typeof pe)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=sn(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),sn(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(sn(this.getTipElement()).hasClass(yn))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),sn.removeData(this.element,this.constructor.DATA_KEY),sn(this.element).off(this.constructor.EVENT_KEY),sn(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&sn(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===sn(this.element).css("display"))throw new Error("Please use show on visible elements");var t=sn.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){sn(this.element).trigger(t);var n=sn.contains(this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=gt.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&sn(i).addClass(En);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:sn(this.config.container);sn(i).data(this.constructor.DATA_KEY,this),sn.contains(this.element.ownerDocument.documentElement,this.tip)||sn(i).appendTo(a),sn(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new pe(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:Tn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){e._handlePopperPlacementChange(t)}}),sn(i).addClass(yn),"ontouchstart"in document.documentElement&&sn(document.body).children().on("mouseover",null,sn.noop);var l=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,sn(e.element).trigger(e.constructor.Event.SHOWN),t===_n&&e._leave(null,e)};if(sn(this.tip).hasClass(En)){var c=gt.getTransitionDurationFromElement(this.tip);sn(this.tip).one(gt.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=sn.Event(this.constructor.Event.HIDE),r=function(){e._hoverState!==mn&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),sn(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(sn(this.element).trigger(i),!i.isDefaultPrevented()){if(sn(n).removeClass(yn),"ontouchstart"in document.documentElement&&sn(document.body).children().off("mouseover",null,sn.noop),this._activeTrigger[In]=!1,this._activeTrigger[wn]=!1,this._activeTrigger[Cn]=!1,sn(this.tip).hasClass(En)){var o=gt.getTransitionDurationFromElement(n);sn(n).one(gt.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){sn(this.getTipElement()).addClass(hn+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||sn(this.config.template)[0],this.tip},t.setContent=function(){var t=sn(this.getTipElement());this.setElementContent(t.find(bn),this.getTitle()),t.removeClass(En+" "+yn)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?sn(e).parent().is(t)||t.empty().append(e):t.text(sn(e).text()):t[n?"html":"text"](e)},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getAttachment=function(t){return pn[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)sn(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==Dn){var e=t===Cn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Cn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;sn(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}sn(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=c({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||sn(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),sn(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?wn:Cn]=!0),sn(e.getTipElement()).hasClass(yn)||e._hoverState===mn?e._hoverState=mn:(clearTimeout(e._timeout),e._hoverState=mn,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===mn&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||sn(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),sn(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?wn:Cn]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=_n,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===_n&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=c({},this.constructor.Default,sn(this.element).data(),t)).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),gt.typeCheckConfig(an,t,this.constructor.DefaultType),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=sn(this.getTipElement()),e=t.attr("class").match(un);null!==e&&0

'}),Rn=c({},Ci.DefaultType,{content:"(string|element|function)"}),Mn="fade",Wn=".popover-header",Fn=".popover-body",Un={HIDE:"hide"+kn,HIDDEN:"hidden"+kn,SHOW:(Hn="show")+kn,SHOWN:"shown"+kn,INSERTED:"inserted"+kn,CLICK:"click"+kn,FOCUSIN:"focusin"+kn,FOCUSOUT:"focusout"+kn,MOUSEENTER:"mouseenter"+kn,MOUSELEAVE:"mouseleave"+kn},Bn=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){Sn(this.getTipElement()).addClass(Pn+"-"+t)},r.getTipElement=function(){return this.tip=this.tip||Sn(this.config.template)[0],this.tip},r.setContent=function(){var t=Sn(this.getTipElement());this.setElementContent(t.find(Wn),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(Fn),e),t.removeClass(Mn+" "+Hn)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=Sn(this.getTipElement()),e=t.attr("class").match(xn);null!==e&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||t= 0) {\n timeoutDuration = 1;\n break;\n }\n}\n\nfunction microtaskDebounce(fn) {\n var called = false;\n return function () {\n if (called) {\n return;\n }\n called = true;\n window.Promise.resolve().then(function () {\n called = false;\n fn();\n });\n };\n}\n\nfunction taskDebounce(fn) {\n var scheduled = false;\n return function () {\n if (!scheduled) {\n scheduled = true;\n setTimeout(function () {\n scheduled = false;\n fn();\n }, timeoutDuration);\n }\n };\n}\n\nvar supportsMicroTasks = isBrowser && window.Promise;\n\n/**\n* Create a debounced version of a method, that's asynchronously deferred\n* but called in the minimum time possible.\n*\n* @method\n* @memberof Popper.Utils\n* @argument {Function} fn\n* @returns {Function}\n*/\nvar debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce;\n\n/**\n * Check if the given variable is a function\n * @method\n * @memberof Popper.Utils\n * @argument {Any} functionToCheck - variable to check\n * @returns {Boolean} answer to: is a function?\n */\nfunction isFunction(functionToCheck) {\n var getType = {};\n return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';\n}\n\n/**\n * Get CSS computed property of the given element\n * @method\n * @memberof Popper.Utils\n * @argument {Eement} element\n * @argument {String} property\n */\nfunction getStyleComputedProperty(element, property) {\n if (element.nodeType !== 1) {\n return [];\n }\n // NOTE: 1 DOM access here\n var css = getComputedStyle(element, null);\n return property ? css[property] : css;\n}\n\n/**\n * Returns the parentNode or the host of the element\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @returns {Element} parent\n */\nfunction getParentNode(element) {\n if (element.nodeName === 'HTML') {\n return element;\n }\n return element.parentNode || element.host;\n}\n\n/**\n * Returns the scrolling parent of the given element\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @returns {Element} scroll parent\n */\nfunction getScrollParent(element) {\n // Return body, `getScroll` will take care to get the correct `scrollTop` from it\n if (!element) {\n return document.body;\n }\n\n switch (element.nodeName) {\n case 'HTML':\n case 'BODY':\n return element.ownerDocument.body;\n case '#document':\n return element.body;\n }\n\n // Firefox want us to check `-x` and `-y` variations as well\n\n var _getStyleComputedProp = getStyleComputedProperty(element),\n overflow = _getStyleComputedProp.overflow,\n overflowX = _getStyleComputedProp.overflowX,\n overflowY = _getStyleComputedProp.overflowY;\n\n if (/(auto|scroll)/.test(overflow + overflowY + overflowX)) {\n return element;\n }\n\n return getScrollParent(getParentNode(element));\n}\n\n/**\n * Returns the offset parent of the given element\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @returns {Element} offset parent\n */\nfunction getOffsetParent(element) {\n // NOTE: 1 DOM access here\n var offsetParent = element && element.offsetParent;\n var nodeName = offsetParent && offsetParent.nodeName;\n\n if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') {\n if (element) {\n return element.ownerDocument.documentElement;\n }\n\n return document.documentElement;\n }\n\n // .offsetParent will return the closest TD or TABLE in case\n // no offsetParent is present, I hate this job...\n if (['TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') {\n return getOffsetParent(offsetParent);\n }\n\n return offsetParent;\n}\n\nfunction isOffsetContainer(element) {\n var nodeName = element.nodeName;\n\n if (nodeName === 'BODY') {\n return false;\n }\n return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element;\n}\n\n/**\n * Finds the root node (document, shadowDOM root) of the given element\n * @method\n * @memberof Popper.Utils\n * @argument {Element} node\n * @returns {Element} root node\n */\nfunction getRoot(node) {\n if (node.parentNode !== null) {\n return getRoot(node.parentNode);\n }\n\n return node;\n}\n\n/**\n * Finds the offset parent common to the two provided nodes\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element1\n * @argument {Element} element2\n * @returns {Element} common offset parent\n */\nfunction findCommonOffsetParent(element1, element2) {\n // This check is needed to avoid errors in case one of the elements isn't defined for any reason\n if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) {\n return document.documentElement;\n }\n\n // Here we make sure to give as \"start\" the element that comes first in the DOM\n var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING;\n var start = order ? element1 : element2;\n var end = order ? element2 : element1;\n\n // Get common ancestor container\n var range = document.createRange();\n range.setStart(start, 0);\n range.setEnd(end, 0);\n var commonAncestorContainer = range.commonAncestorContainer;\n\n // Both nodes are inside #document\n\n if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) {\n if (isOffsetContainer(commonAncestorContainer)) {\n return commonAncestorContainer;\n }\n\n return getOffsetParent(commonAncestorContainer);\n }\n\n // one of the nodes is inside shadowDOM, find which one\n var element1root = getRoot(element1);\n if (element1root.host) {\n return findCommonOffsetParent(element1root.host, element2);\n } else {\n return findCommonOffsetParent(element1, getRoot(element2).host);\n }\n}\n\n/**\n * Gets the scroll value of the given element in the given side (top and left)\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @argument {String} side `top` or `left`\n * @returns {number} amount of scrolled pixels\n */\nfunction getScroll(element) {\n var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top';\n\n var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft';\n var nodeName = element.nodeName;\n\n if (nodeName === 'BODY' || nodeName === 'HTML') {\n var html = element.ownerDocument.documentElement;\n var scrollingElement = element.ownerDocument.scrollingElement || html;\n return scrollingElement[upperSide];\n }\n\n return element[upperSide];\n}\n\n/*\n * Sum or subtract the element scroll values (left and top) from a given rect object\n * @method\n * @memberof Popper.Utils\n * @param {Object} rect - Rect object you want to change\n * @param {HTMLElement} element - The element from the function reads the scroll values\n * @param {Boolean} subtract - set to true if you want to subtract the scroll values\n * @return {Object} rect - The modifier rect object\n */\nfunction includeScroll(rect, element) {\n var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n var scrollTop = getScroll(element, 'top');\n var scrollLeft = getScroll(element, 'left');\n var modifier = subtract ? -1 : 1;\n rect.top += scrollTop * modifier;\n rect.bottom += scrollTop * modifier;\n rect.left += scrollLeft * modifier;\n rect.right += scrollLeft * modifier;\n return rect;\n}\n\n/*\n * Helper to detect borders of a given element\n * @method\n * @memberof Popper.Utils\n * @param {CSSStyleDeclaration} styles\n * Result of `getStyleComputedProperty` on the given element\n * @param {String} axis - `x` or `y`\n * @return {number} borders - The borders size of the given axis\n */\n\nfunction getBordersSize(styles, axis) {\n var sideA = axis === 'x' ? 'Left' : 'Top';\n var sideB = sideA === 'Left' ? 'Right' : 'Bottom';\n\n return parseFloat(styles['border' + sideA + 'Width'], 10) + parseFloat(styles['border' + sideB + 'Width'], 10);\n}\n\n/**\n * Tells if you are running Internet Explorer 10\n * @method\n * @memberof Popper.Utils\n * @returns {Boolean} isIE10\n */\nvar isIE10 = undefined;\n\nvar isIE10$1 = function () {\n if (isIE10 === undefined) {\n isIE10 = navigator.appVersion.indexOf('MSIE 10') !== -1;\n }\n return isIE10;\n};\n\nfunction getSize(axis, body, html, computedStyle) {\n return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE10$1() ? html['offset' + axis] + computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')] + computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')] : 0);\n}\n\nfunction getWindowSizes() {\n var body = document.body;\n var html = document.documentElement;\n var computedStyle = isIE10$1() && getComputedStyle(html);\n\n return {\n height: getSize('Height', body, html, computedStyle),\n width: getSize('Width', body, html, computedStyle)\n };\n}\n\nvar classCallCheck = function (instance, Constructor) {\n if (!(instance instanceof Constructor)) {\n throw new TypeError(\"Cannot call a class as a function\");\n }\n};\n\nvar createClass = function () {\n function defineProperties(target, props) {\n for (var i = 0; i < props.length; i++) {\n var descriptor = props[i];\n descriptor.enumerable = descriptor.enumerable || false;\n descriptor.configurable = true;\n if (\"value\" in descriptor) descriptor.writable = true;\n Object.defineProperty(target, descriptor.key, descriptor);\n }\n }\n\n return function (Constructor, protoProps, staticProps) {\n if (protoProps) defineProperties(Constructor.prototype, protoProps);\n if (staticProps) defineProperties(Constructor, staticProps);\n return Constructor;\n };\n}();\n\n\n\n\n\nvar defineProperty = function (obj, key, value) {\n if (key in obj) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n\n return obj;\n};\n\nvar _extends = Object.assign || function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n\n return target;\n};\n\n/**\n * Given element offsets, generate an output similar to getBoundingClientRect\n * @method\n * @memberof Popper.Utils\n * @argument {Object} offsets\n * @returns {Object} ClientRect like output\n */\nfunction getClientRect(offsets) {\n return _extends({}, offsets, {\n right: offsets.left + offsets.width,\n bottom: offsets.top + offsets.height\n });\n}\n\n/**\n * Get bounding client rect of given element\n * @method\n * @memberof Popper.Utils\n * @param {HTMLElement} element\n * @return {Object} client rect\n */\nfunction getBoundingClientRect(element) {\n var rect = {};\n\n // IE10 10 FIX: Please, don't ask, the element isn't\n // considered in DOM in some circumstances...\n // This isn't reproducible in IE10 compatibility mode of IE11\n if (isIE10$1()) {\n try {\n rect = element.getBoundingClientRect();\n var scrollTop = getScroll(element, 'top');\n var scrollLeft = getScroll(element, 'left');\n rect.top += scrollTop;\n rect.left += scrollLeft;\n rect.bottom += scrollTop;\n rect.right += scrollLeft;\n } catch (err) {}\n } else {\n rect = element.getBoundingClientRect();\n }\n\n var result = {\n left: rect.left,\n top: rect.top,\n width: rect.right - rect.left,\n height: rect.bottom - rect.top\n };\n\n // subtract scrollbar size from sizes\n var sizes = element.nodeName === 'HTML' ? getWindowSizes() : {};\n var width = sizes.width || element.clientWidth || result.right - result.left;\n var height = sizes.height || element.clientHeight || result.bottom - result.top;\n\n var horizScrollbar = element.offsetWidth - width;\n var vertScrollbar = element.offsetHeight - height;\n\n // if an hypothetical scrollbar is detected, we must be sure it's not a `border`\n // we make this check conditional for performance reasons\n if (horizScrollbar || vertScrollbar) {\n var styles = getStyleComputedProperty(element);\n horizScrollbar -= getBordersSize(styles, 'x');\n vertScrollbar -= getBordersSize(styles, 'y');\n\n result.width -= horizScrollbar;\n result.height -= vertScrollbar;\n }\n\n return getClientRect(result);\n}\n\nfunction getOffsetRectRelativeToArbitraryNode(children, parent) {\n var isIE10 = isIE10$1();\n var isHTML = parent.nodeName === 'HTML';\n var childrenRect = getBoundingClientRect(children);\n var parentRect = getBoundingClientRect(parent);\n var scrollParent = getScrollParent(children);\n\n var styles = getStyleComputedProperty(parent);\n var borderTopWidth = parseFloat(styles.borderTopWidth, 10);\n var borderLeftWidth = parseFloat(styles.borderLeftWidth, 10);\n\n var offsets = getClientRect({\n top: childrenRect.top - parentRect.top - borderTopWidth,\n left: childrenRect.left - parentRect.left - borderLeftWidth,\n width: childrenRect.width,\n height: childrenRect.height\n });\n offsets.marginTop = 0;\n offsets.marginLeft = 0;\n\n // Subtract margins of documentElement in case it's being used as parent\n // we do this only on HTML because it's the only element that behaves\n // differently when margins are applied to it. The margins are included in\n // the box of the documentElement, in the other cases not.\n if (!isIE10 && isHTML) {\n var marginTop = parseFloat(styles.marginTop, 10);\n var marginLeft = parseFloat(styles.marginLeft, 10);\n\n offsets.top -= borderTopWidth - marginTop;\n offsets.bottom -= borderTopWidth - marginTop;\n offsets.left -= borderLeftWidth - marginLeft;\n offsets.right -= borderLeftWidth - marginLeft;\n\n // Attach marginTop and marginLeft because in some circumstances we may need them\n offsets.marginTop = marginTop;\n offsets.marginLeft = marginLeft;\n }\n\n if (isIE10 ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') {\n offsets = includeScroll(offsets, parent);\n }\n\n return offsets;\n}\n\nfunction getViewportOffsetRectRelativeToArtbitraryNode(element) {\n var html = element.ownerDocument.documentElement;\n var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html);\n var width = Math.max(html.clientWidth, window.innerWidth || 0);\n var height = Math.max(html.clientHeight, window.innerHeight || 0);\n\n var scrollTop = getScroll(html);\n var scrollLeft = getScroll(html, 'left');\n\n var offset = {\n top: scrollTop - relativeOffset.top + relativeOffset.marginTop,\n left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft,\n width: width,\n height: height\n };\n\n return getClientRect(offset);\n}\n\n/**\n * Check if the given element is fixed or is inside a fixed parent\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @argument {Element} customContainer\n * @returns {Boolean} answer to \"isFixed?\"\n */\nfunction isFixed(element) {\n var nodeName = element.nodeName;\n if (nodeName === 'BODY' || nodeName === 'HTML') {\n return false;\n }\n if (getStyleComputedProperty(element, 'position') === 'fixed') {\n return true;\n }\n return isFixed(getParentNode(element));\n}\n\n/**\n * Computed the boundaries limits and return them\n * @method\n * @memberof Popper.Utils\n * @param {HTMLElement} popper\n * @param {HTMLElement} reference\n * @param {number} padding\n * @param {HTMLElement} boundariesElement - Element used to define the boundaries\n * @returns {Object} Coordinates of the boundaries\n */\nfunction getBoundaries(popper, reference, padding, boundariesElement) {\n // NOTE: 1 DOM access here\n var boundaries = { top: 0, left: 0 };\n var offsetParent = findCommonOffsetParent(popper, reference);\n\n // Handle viewport case\n if (boundariesElement === 'viewport') {\n boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent);\n } else {\n // Handle other cases based on DOM element used as boundaries\n var boundariesNode = void 0;\n if (boundariesElement === 'scrollParent') {\n boundariesNode = getScrollParent(getParentNode(reference));\n if (boundariesNode.nodeName === 'BODY') {\n boundariesNode = popper.ownerDocument.documentElement;\n }\n } else if (boundariesElement === 'window') {\n boundariesNode = popper.ownerDocument.documentElement;\n } else {\n boundariesNode = boundariesElement;\n }\n\n var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent);\n\n // In case of HTML, we need a different computation\n if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) {\n var _getWindowSizes = getWindowSizes(),\n height = _getWindowSizes.height,\n width = _getWindowSizes.width;\n\n boundaries.top += offsets.top - offsets.marginTop;\n boundaries.bottom = height + offsets.top;\n boundaries.left += offsets.left - offsets.marginLeft;\n boundaries.right = width + offsets.left;\n } else {\n // for all the other DOM elements, this one is good\n boundaries = offsets;\n }\n }\n\n // Add paddings\n boundaries.left += padding;\n boundaries.top += padding;\n boundaries.right -= padding;\n boundaries.bottom -= padding;\n\n return boundaries;\n}\n\nfunction getArea(_ref) {\n var width = _ref.width,\n height = _ref.height;\n\n return width * height;\n}\n\n/**\n * Utility used to transform the `auto` placement to the placement with more\n * available space.\n * @method\n * @memberof Popper.Utils\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) {\n var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0;\n\n if (placement.indexOf('auto') === -1) {\n return placement;\n }\n\n var boundaries = getBoundaries(popper, reference, padding, boundariesElement);\n\n var rects = {\n top: {\n width: boundaries.width,\n height: refRect.top - boundaries.top\n },\n right: {\n width: boundaries.right - refRect.right,\n height: boundaries.height\n },\n bottom: {\n width: boundaries.width,\n height: boundaries.bottom - refRect.bottom\n },\n left: {\n width: refRect.left - boundaries.left,\n height: boundaries.height\n }\n };\n\n var sortedAreas = Object.keys(rects).map(function (key) {\n return _extends({\n key: key\n }, rects[key], {\n area: getArea(rects[key])\n });\n }).sort(function (a, b) {\n return b.area - a.area;\n });\n\n var filteredAreas = sortedAreas.filter(function (_ref2) {\n var width = _ref2.width,\n height = _ref2.height;\n return width >= popper.clientWidth && height >= popper.clientHeight;\n });\n\n var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key;\n\n var variation = placement.split('-')[1];\n\n return computedPlacement + (variation ? '-' + variation : '');\n}\n\n/**\n * Get offsets to the reference element\n * @method\n * @memberof Popper.Utils\n * @param {Object} state\n * @param {Element} popper - the popper element\n * @param {Element} reference - the reference element (the popper will be relative to this)\n * @returns {Object} An object containing the offsets which will be applied to the popper\n */\nfunction getReferenceOffsets(state, popper, reference) {\n var commonOffsetParent = findCommonOffsetParent(popper, reference);\n return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent);\n}\n\n/**\n * Get the outer sizes of the given element (offset size + margins)\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element\n * @returns {Object} object containing width and height properties\n */\nfunction getOuterSizes(element) {\n var styles = getComputedStyle(element);\n var x = parseFloat(styles.marginTop) + parseFloat(styles.marginBottom);\n var y = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight);\n var result = {\n width: element.offsetWidth + y,\n height: element.offsetHeight + x\n };\n return result;\n}\n\n/**\n * Get the opposite placement of the given one\n * @method\n * @memberof Popper.Utils\n * @argument {String} placement\n * @returns {String} flipped placement\n */\nfunction getOppositePlacement(placement) {\n var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' };\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}\n\n/**\n * Get offsets to the popper\n * @method\n * @memberof Popper.Utils\n * @param {Object} position - CSS position the Popper will get applied\n * @param {HTMLElement} popper - the popper element\n * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this)\n * @param {String} placement - one of the valid placement options\n * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper\n */\nfunction getPopperOffsets(popper, referenceOffsets, placement) {\n placement = placement.split('-')[0];\n\n // Get popper node sizes\n var popperRect = getOuterSizes(popper);\n\n // Add position, width and height to our offsets object\n var popperOffsets = {\n width: popperRect.width,\n height: popperRect.height\n };\n\n // depending by the popper placement we have to compute its offsets slightly differently\n var isHoriz = ['right', 'left'].indexOf(placement) !== -1;\n var mainSide = isHoriz ? 'top' : 'left';\n var secondarySide = isHoriz ? 'left' : 'top';\n var measurement = isHoriz ? 'height' : 'width';\n var secondaryMeasurement = !isHoriz ? 'height' : 'width';\n\n popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2;\n if (placement === secondarySide) {\n popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement];\n } else {\n popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)];\n }\n\n return popperOffsets;\n}\n\n/**\n * Mimics the `find` method of Array\n * @method\n * @memberof Popper.Utils\n * @argument {Array} arr\n * @argument prop\n * @argument value\n * @returns index or -1\n */\nfunction find(arr, check) {\n // use native find if supported\n if (Array.prototype.find) {\n return arr.find(check);\n }\n\n // use `filter` to obtain the same behavior of `find`\n return arr.filter(check)[0];\n}\n\n/**\n * Return the index of the matching object\n * @method\n * @memberof Popper.Utils\n * @argument {Array} arr\n * @argument prop\n * @argument value\n * @returns index or -1\n */\nfunction findIndex(arr, prop, value) {\n // use native findIndex if supported\n if (Array.prototype.findIndex) {\n return arr.findIndex(function (cur) {\n return cur[prop] === value;\n });\n }\n\n // use `find` + `indexOf` if `findIndex` isn't supported\n var match = find(arr, function (obj) {\n return obj[prop] === value;\n });\n return arr.indexOf(match);\n}\n\n/**\n * Loop trough the list of modifiers and run them in order,\n * each of them will then edit the data object.\n * @method\n * @memberof Popper.Utils\n * @param {dataObject} data\n * @param {Array} modifiers\n * @param {String} ends - Optional modifier name used as stopper\n * @returns {dataObject}\n */\nfunction runModifiers(modifiers, data, ends) {\n var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends));\n\n modifiersToRun.forEach(function (modifier) {\n if (modifier['function']) {\n // eslint-disable-line dot-notation\n console.warn('`modifier.function` is deprecated, use `modifier.fn`!');\n }\n var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation\n if (modifier.enabled && isFunction(fn)) {\n // Add properties to offsets to make them a complete clientRect object\n // we do this before each modifier to make sure the previous one doesn't\n // mess with these values\n data.offsets.popper = getClientRect(data.offsets.popper);\n data.offsets.reference = getClientRect(data.offsets.reference);\n\n data = fn(data, modifier);\n }\n });\n\n return data;\n}\n\n/**\n * Updates the position of the popper, computing the new offsets and applying\n * the new style.
\n * Prefer `scheduleUpdate` over `update` because of performance reasons.\n * @method\n * @memberof Popper\n */\nfunction update() {\n // if popper is destroyed, don't perform any further update\n if (this.state.isDestroyed) {\n return;\n }\n\n var data = {\n instance: this,\n styles: {},\n arrowStyles: {},\n attributes: {},\n flipped: false,\n offsets: {}\n };\n\n // compute reference element offsets\n data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference);\n\n // compute auto placement, store placement inside the data object,\n // modifiers will be able to edit `placement` if needed\n // and refer to originalPlacement to know the original value\n data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding);\n\n // store the computed placement inside `originalPlacement`\n data.originalPlacement = data.placement;\n\n // compute the popper offsets\n data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement);\n data.offsets.popper.position = 'absolute';\n\n // run the modifiers\n data = runModifiers(this.modifiers, data);\n\n // the first `update` will call `onCreate` callback\n // the other ones will call `onUpdate` callback\n if (!this.state.isCreated) {\n this.state.isCreated = true;\n this.options.onCreate(data);\n } else {\n this.options.onUpdate(data);\n }\n}\n\n/**\n * Helper used to know if the given modifier is enabled.\n * @method\n * @memberof Popper.Utils\n * @returns {Boolean}\n */\nfunction isModifierEnabled(modifiers, modifierName) {\n return modifiers.some(function (_ref) {\n var name = _ref.name,\n enabled = _ref.enabled;\n return enabled && name === modifierName;\n });\n}\n\n/**\n * Get the prefixed supported property name\n * @method\n * @memberof Popper.Utils\n * @argument {String} property (camelCase)\n * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix)\n */\nfunction getSupportedPropertyName(property) {\n var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O'];\n var upperProp = property.charAt(0).toUpperCase() + property.slice(1);\n\n for (var i = 0; i < prefixes.length - 1; i++) {\n var prefix = prefixes[i];\n var toCheck = prefix ? '' + prefix + upperProp : property;\n if (typeof document.body.style[toCheck] !== 'undefined') {\n return toCheck;\n }\n }\n return null;\n}\n\n/**\n * Destroy the popper\n * @method\n * @memberof Popper\n */\nfunction destroy() {\n this.state.isDestroyed = true;\n\n // touch DOM only if `applyStyle` modifier is enabled\n if (isModifierEnabled(this.modifiers, 'applyStyle')) {\n this.popper.removeAttribute('x-placement');\n this.popper.style.left = '';\n this.popper.style.position = '';\n this.popper.style.top = '';\n this.popper.style[getSupportedPropertyName('transform')] = '';\n }\n\n this.disableEventListeners();\n\n // remove the popper if user explicity asked for the deletion on destroy\n // do not use `remove` because IE11 doesn't support it\n if (this.options.removeOnDestroy) {\n this.popper.parentNode.removeChild(this.popper);\n }\n return this;\n}\n\n/**\n * Get the window associated with the element\n * @argument {Element} element\n * @returns {Window}\n */\nfunction getWindow(element) {\n var ownerDocument = element.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView : window;\n}\n\nfunction attachToScrollParents(scrollParent, event, callback, scrollParents) {\n var isBody = scrollParent.nodeName === 'BODY';\n var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent;\n target.addEventListener(event, callback, { passive: true });\n\n if (!isBody) {\n attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents);\n }\n scrollParents.push(target);\n}\n\n/**\n * Setup needed event listeners used to update the popper position\n * @method\n * @memberof Popper.Utils\n * @private\n */\nfunction setupEventListeners(reference, options, state, updateBound) {\n // Resize event listener on window\n state.updateBound = updateBound;\n getWindow(reference).addEventListener('resize', state.updateBound, { passive: true });\n\n // Scroll event listener on scroll parents\n var scrollElement = getScrollParent(reference);\n attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents);\n state.scrollElement = scrollElement;\n state.eventsEnabled = true;\n\n return state;\n}\n\n/**\n * It will add resize/scroll events and start recalculating\n * position of the popper element when they are triggered.\n * @method\n * @memberof Popper\n */\nfunction enableEventListeners() {\n if (!this.state.eventsEnabled) {\n this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate);\n }\n}\n\n/**\n * Remove event listeners used to update the popper position\n * @method\n * @memberof Popper.Utils\n * @private\n */\nfunction removeEventListeners(reference, state) {\n // Remove resize event listener on window\n getWindow(reference).removeEventListener('resize', state.updateBound);\n\n // Remove scroll event listener on scroll parents\n state.scrollParents.forEach(function (target) {\n target.removeEventListener('scroll', state.updateBound);\n });\n\n // Reset state\n state.updateBound = null;\n state.scrollParents = [];\n state.scrollElement = null;\n state.eventsEnabled = false;\n return state;\n}\n\n/**\n * It will remove resize/scroll events and won't recalculate popper position\n * when they are triggered. It also won't trigger onUpdate callback anymore,\n * unless you call `update` method manually.\n * @method\n * @memberof Popper\n */\nfunction disableEventListeners() {\n if (this.state.eventsEnabled) {\n cancelAnimationFrame(this.scheduleUpdate);\n this.state = removeEventListeners(this.reference, this.state);\n }\n}\n\n/**\n * Tells if a given input is a number\n * @method\n * @memberof Popper.Utils\n * @param {*} input to check\n * @return {Boolean}\n */\nfunction isNumeric(n) {\n return n !== '' && !isNaN(parseFloat(n)) && isFinite(n);\n}\n\n/**\n * Set the style to the given popper\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element - Element to apply the style to\n * @argument {Object} styles\n * Object with a list of properties and values which will be applied to the element\n */\nfunction setStyles(element, styles) {\n Object.keys(styles).forEach(function (prop) {\n var unit = '';\n // add unit if the value is numeric and is one of the following\n if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) {\n unit = 'px';\n }\n element.style[prop] = styles[prop] + unit;\n });\n}\n\n/**\n * Set the attributes to the given popper\n * @method\n * @memberof Popper.Utils\n * @argument {Element} element - Element to apply the attributes to\n * @argument {Object} styles\n * Object with a list of properties and values which will be applied to the element\n */\nfunction setAttributes(element, attributes) {\n Object.keys(attributes).forEach(function (prop) {\n var value = attributes[prop];\n if (value !== false) {\n element.setAttribute(prop, attributes[prop]);\n } else {\n element.removeAttribute(prop);\n }\n });\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Object} data.styles - List of style properties - values to apply to popper element\n * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The same data object\n */\nfunction applyStyle(data) {\n // any property present in `data.styles` will be applied to the popper,\n // in this way we can make the 3rd party modifiers add custom styles to it\n // Be aware, modifiers could override the properties defined in the previous\n // lines of this modifier!\n setStyles(data.instance.popper, data.styles);\n\n // any property present in `data.attributes` will be applied to the popper,\n // they will be set as HTML attributes of the element\n setAttributes(data.instance.popper, data.attributes);\n\n // if arrowElement is defined and arrowStyles has some properties\n if (data.arrowElement && Object.keys(data.arrowStyles).length) {\n setStyles(data.arrowElement, data.arrowStyles);\n }\n\n return data;\n}\n\n/**\n * Set the x-placement attribute before everything else because it could be used\n * to add margins to the popper margins needs to be calculated to get the\n * correct popper offsets.\n * @method\n * @memberof Popper.modifiers\n * @param {HTMLElement} reference - The reference element used to position the popper\n * @param {HTMLElement} popper - The HTML element used as popper.\n * @param {Object} options - Popper.js options\n */\nfunction applyStyleOnLoad(reference, popper, options, modifierOptions, state) {\n // compute reference element offsets\n var referenceOffsets = getReferenceOffsets(state, popper, reference);\n\n // compute auto placement, store placement inside the data object,\n // modifiers will be able to edit `placement` if needed\n // and refer to originalPlacement to know the original value\n var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding);\n\n popper.setAttribute('x-placement', placement);\n\n // Apply `position` to popper before anything else because\n // without the position applied we can't guarantee correct computations\n setStyles(popper, { position: 'absolute' });\n\n return options;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction computeStyle(data, options) {\n var x = options.x,\n y = options.y;\n var popper = data.offsets.popper;\n\n // Remove this legacy support in Popper.js v2\n\n var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) {\n return modifier.name === 'applyStyle';\n }).gpuAcceleration;\n if (legacyGpuAccelerationOption !== undefined) {\n console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');\n }\n var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration;\n\n var offsetParent = getOffsetParent(data.instance.popper);\n var offsetParentRect = getBoundingClientRect(offsetParent);\n\n // Styles\n var styles = {\n position: popper.position\n };\n\n // floor sides to avoid blurry text\n var offsets = {\n left: Math.floor(popper.left),\n top: Math.floor(popper.top),\n bottom: Math.floor(popper.bottom),\n right: Math.floor(popper.right)\n };\n\n var sideA = x === 'bottom' ? 'top' : 'bottom';\n var sideB = y === 'right' ? 'left' : 'right';\n\n // if gpuAcceleration is set to `true` and transform is supported,\n // we use `translate3d` to apply the position to the popper we\n // automatically use the supported prefixed version if needed\n var prefixedProperty = getSupportedPropertyName('transform');\n\n // now, let's make a step back and look at this code closely (wtf?)\n // If the content of the popper grows once it's been positioned, it\n // may happen that the popper gets misplaced because of the new content\n // overflowing its reference element\n // To avoid this problem, we provide two options (x and y), which allow\n // the consumer to define the offset origin.\n // If we position a popper on top of a reference element, we can set\n // `x` to `top` to make the popper grow towards its top instead of\n // its bottom.\n var left = void 0,\n top = void 0;\n if (sideA === 'bottom') {\n top = -offsetParentRect.height + offsets.bottom;\n } else {\n top = offsets.top;\n }\n if (sideB === 'right') {\n left = -offsetParentRect.width + offsets.right;\n } else {\n left = offsets.left;\n }\n if (gpuAcceleration && prefixedProperty) {\n styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)';\n styles[sideA] = 0;\n styles[sideB] = 0;\n styles.willChange = 'transform';\n } else {\n // othwerise, we use the standard `top`, `left`, `bottom` and `right` properties\n var invertTop = sideA === 'bottom' ? -1 : 1;\n var invertLeft = sideB === 'right' ? -1 : 1;\n styles[sideA] = top * invertTop;\n styles[sideB] = left * invertLeft;\n styles.willChange = sideA + ', ' + sideB;\n }\n\n // Attributes\n var attributes = {\n 'x-placement': data.placement\n };\n\n // Update `data` attributes, styles and arrowStyles\n data.attributes = _extends({}, attributes, data.attributes);\n data.styles = _extends({}, styles, data.styles);\n data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles);\n\n return data;\n}\n\n/**\n * Helper used to know if the given modifier depends from another one.
\n * It checks if the needed modifier is listed and enabled.\n * @method\n * @memberof Popper.Utils\n * @param {Array} modifiers - list of modifiers\n * @param {String} requestingName - name of requesting modifier\n * @param {String} requestedName - name of requested modifier\n * @returns {Boolean}\n */\nfunction isModifierRequired(modifiers, requestingName, requestedName) {\n var requesting = find(modifiers, function (_ref) {\n var name = _ref.name;\n return name === requestingName;\n });\n\n var isRequired = !!requesting && modifiers.some(function (modifier) {\n return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order;\n });\n\n if (!isRequired) {\n var _requesting = '`' + requestingName + '`';\n var requested = '`' + requestedName + '`';\n console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!');\n }\n return isRequired;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction arrow(data, options) {\n var _data$offsets$arrow;\n\n // arrow depends on keepTogether in order to work\n if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {\n return data;\n }\n\n var arrowElement = options.element;\n\n // if arrowElement is a string, suppose it's a CSS selector\n if (typeof arrowElement === 'string') {\n arrowElement = data.instance.popper.querySelector(arrowElement);\n\n // if arrowElement is not found, don't run the modifier\n if (!arrowElement) {\n return data;\n }\n } else {\n // if the arrowElement isn't a query selector we must check that the\n // provided DOM node is child of its popper node\n if (!data.instance.popper.contains(arrowElement)) {\n console.warn('WARNING: `arrow.element` must be child of its popper element!');\n return data;\n }\n }\n\n var placement = data.placement.split('-')[0];\n var _data$offsets = data.offsets,\n popper = _data$offsets.popper,\n reference = _data$offsets.reference;\n\n var isVertical = ['left', 'right'].indexOf(placement) !== -1;\n\n var len = isVertical ? 'height' : 'width';\n var sideCapitalized = isVertical ? 'Top' : 'Left';\n var side = sideCapitalized.toLowerCase();\n var altSide = isVertical ? 'left' : 'top';\n var opSide = isVertical ? 'bottom' : 'right';\n var arrowElementSize = getOuterSizes(arrowElement)[len];\n\n //\n // extends keepTogether behavior making sure the popper and its\n // reference have enough pixels in conjuction\n //\n\n // top/left side\n if (reference[opSide] - arrowElementSize < popper[side]) {\n data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize);\n }\n // bottom/right side\n if (reference[side] + arrowElementSize > popper[opSide]) {\n data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide];\n }\n data.offsets.popper = getClientRect(data.offsets.popper);\n\n // compute center of the popper\n var center = reference[side] + reference[len] / 2 - arrowElementSize / 2;\n\n // Compute the sideValue using the updated popper offsets\n // take popper margin in account because we don't have this info available\n var css = getStyleComputedProperty(data.instance.popper);\n var popperMarginSide = parseFloat(css['margin' + sideCapitalized], 10);\n var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width'], 10);\n var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide;\n\n // prevent arrowElement from being placed not contiguously to its popper\n sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0);\n\n data.arrowElement = arrowElement;\n data.offsets.arrow = (_data$offsets$arrow = {}, defineProperty(_data$offsets$arrow, side, Math.round(sideValue)), defineProperty(_data$offsets$arrow, altSide, ''), _data$offsets$arrow);\n\n return data;\n}\n\n/**\n * Get the opposite placement variation of the given one\n * @method\n * @memberof Popper.Utils\n * @argument {String} placement variation\n * @returns {String} flipped placement variation\n */\nfunction getOppositeVariation(variation) {\n if (variation === 'end') {\n return 'start';\n } else if (variation === 'start') {\n return 'end';\n }\n return variation;\n}\n\n/**\n * List of accepted placements to use as values of the `placement` option.
\n * Valid placements are:\n * - `auto`\n * - `top`\n * - `right`\n * - `bottom`\n * - `left`\n *\n * Each placement can have a variation from this list:\n * - `-start`\n * - `-end`\n *\n * Variations are interpreted easily if you think of them as the left to right\n * written languages. Horizontally (`top` and `bottom`), `start` is left and `end`\n * is right.
\n * Vertically (`left` and `right`), `start` is top and `end` is bottom.\n *\n * Some valid examples are:\n * - `top-end` (on top of reference, right aligned)\n * - `right-start` (on right of reference, top aligned)\n * - `bottom` (on bottom, centered)\n * - `auto-right` (on the side with more space available, alignment depends by placement)\n *\n * @static\n * @type {Array}\n * @enum {String}\n * @readonly\n * @method placements\n * @memberof Popper\n */\nvar placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start'];\n\n// Get rid of `auto` `auto-start` and `auto-end`\nvar validPlacements = placements.slice(3);\n\n/**\n * Given an initial placement, returns all the subsequent placements\n * clockwise (or counter-clockwise).\n *\n * @method\n * @memberof Popper.Utils\n * @argument {String} placement - A valid placement (it accepts variations)\n * @argument {Boolean} counter - Set to true to walk the placements counterclockwise\n * @returns {Array} placements including their variations\n */\nfunction clockwise(placement) {\n var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n var index = validPlacements.indexOf(placement);\n var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index));\n return counter ? arr.reverse() : arr;\n}\n\nvar BEHAVIORS = {\n FLIP: 'flip',\n CLOCKWISE: 'clockwise',\n COUNTERCLOCKWISE: 'counterclockwise'\n};\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction flip(data, options) {\n // if `inner` modifier is enabled, we can't use the `flip` modifier\n if (isModifierEnabled(data.instance.modifiers, 'inner')) {\n return data;\n }\n\n if (data.flipped && data.placement === data.originalPlacement) {\n // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides\n return data;\n }\n\n var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement);\n\n var placement = data.placement.split('-')[0];\n var placementOpposite = getOppositePlacement(placement);\n var variation = data.placement.split('-')[1] || '';\n\n var flipOrder = [];\n\n switch (options.behavior) {\n case BEHAVIORS.FLIP:\n flipOrder = [placement, placementOpposite];\n break;\n case BEHAVIORS.CLOCKWISE:\n flipOrder = clockwise(placement);\n break;\n case BEHAVIORS.COUNTERCLOCKWISE:\n flipOrder = clockwise(placement, true);\n break;\n default:\n flipOrder = options.behavior;\n }\n\n flipOrder.forEach(function (step, index) {\n if (placement !== step || flipOrder.length === index + 1) {\n return data;\n }\n\n placement = data.placement.split('-')[0];\n placementOpposite = getOppositePlacement(placement);\n\n var popperOffsets = data.offsets.popper;\n var refOffsets = data.offsets.reference;\n\n // using floor because the reference offsets may contain decimals we are not going to consider here\n var floor = Math.floor;\n var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom);\n\n var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left);\n var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right);\n var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top);\n var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom);\n\n var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom;\n\n // flip the variation if required\n var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;\n var flippedVariation = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom);\n\n if (overlapsRef || overflowsBoundaries || flippedVariation) {\n // this boolean to detect any flip loop\n data.flipped = true;\n\n if (overlapsRef || overflowsBoundaries) {\n placement = flipOrder[index + 1];\n }\n\n if (flippedVariation) {\n variation = getOppositeVariation(variation);\n }\n\n data.placement = placement + (variation ? '-' + variation : '');\n\n // this object contains `position`, we want to preserve it along with\n // any additional property we may add in the future\n data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement));\n\n data = runModifiers(data.instance.modifiers, data, 'flip');\n }\n });\n return data;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction keepTogether(data) {\n var _data$offsets = data.offsets,\n popper = _data$offsets.popper,\n reference = _data$offsets.reference;\n\n var placement = data.placement.split('-')[0];\n var floor = Math.floor;\n var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;\n var side = isVertical ? 'right' : 'bottom';\n var opSide = isVertical ? 'left' : 'top';\n var measurement = isVertical ? 'width' : 'height';\n\n if (popper[side] < floor(reference[opSide])) {\n data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement];\n }\n if (popper[opSide] > floor(reference[side])) {\n data.offsets.popper[opSide] = floor(reference[side]);\n }\n\n return data;\n}\n\n/**\n * Converts a string containing value + unit into a px value number\n * @function\n * @memberof {modifiers~offset}\n * @private\n * @argument {String} str - Value + unit string\n * @argument {String} measurement - `height` or `width`\n * @argument {Object} popperOffsets\n * @argument {Object} referenceOffsets\n * @returns {Number|String}\n * Value in pixels, or original string if no values were extracted\n */\nfunction toValue(str, measurement, popperOffsets, referenceOffsets) {\n // separate value from unit\n var split = str.match(/((?:\\-|\\+)?\\d*\\.?\\d*)(.*)/);\n var value = +split[1];\n var unit = split[2];\n\n // If it's not a number it's an operator, I guess\n if (!value) {\n return str;\n }\n\n if (unit.indexOf('%') === 0) {\n var element = void 0;\n switch (unit) {\n case '%p':\n element = popperOffsets;\n break;\n case '%':\n case '%r':\n default:\n element = referenceOffsets;\n }\n\n var rect = getClientRect(element);\n return rect[measurement] / 100 * value;\n } else if (unit === 'vh' || unit === 'vw') {\n // if is a vh or vw, we calculate the size based on the viewport\n var size = void 0;\n if (unit === 'vh') {\n size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);\n } else {\n size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);\n }\n return size / 100 * value;\n } else {\n // if is an explicit pixel unit, we get rid of the unit and keep the value\n // if is an implicit unit, it's px, and we return just the value\n return value;\n }\n}\n\n/**\n * Parse an `offset` string to extrapolate `x` and `y` numeric offsets.\n * @function\n * @memberof {modifiers~offset}\n * @private\n * @argument {String} offset\n * @argument {Object} popperOffsets\n * @argument {Object} referenceOffsets\n * @argument {String} basePlacement\n * @returns {Array} a two cells array with x and y offsets in numbers\n */\nfunction parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) {\n var offsets = [0, 0];\n\n // Use height if placement is left or right and index is 0 otherwise use width\n // in this way the first offset will use an axis and the second one\n // will use the other one\n var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1;\n\n // Split the offset string to obtain a list of values and operands\n // The regex addresses values with the plus or minus sign in front (+10, -20, etc)\n var fragments = offset.split(/(\\+|\\-)/).map(function (frag) {\n return frag.trim();\n });\n\n // Detect if the offset string contains a pair of values or a single one\n // they could be separated by comma or space\n var divider = fragments.indexOf(find(fragments, function (frag) {\n return frag.search(/,|\\s/) !== -1;\n }));\n\n if (fragments[divider] && fragments[divider].indexOf(',') === -1) {\n console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');\n }\n\n // If divider is found, we divide the list of values and operands to divide\n // them by ofset X and Y.\n var splitRegex = /\\s*,\\s*|\\s+/;\n var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments];\n\n // Convert the values with units to absolute pixels to allow our computations\n ops = ops.map(function (op, index) {\n // Most of the units rely on the orientation of the popper\n var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width';\n var mergeWithPrevious = false;\n return op\n // This aggregates any `+` or `-` sign that aren't considered operators\n // e.g.: 10 + +5 => [10, +, +5]\n .reduce(function (a, b) {\n if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) {\n a[a.length - 1] = b;\n mergeWithPrevious = true;\n return a;\n } else if (mergeWithPrevious) {\n a[a.length - 1] += b;\n mergeWithPrevious = false;\n return a;\n } else {\n return a.concat(b);\n }\n }, [])\n // Here we convert the string values into number values (in px)\n .map(function (str) {\n return toValue(str, measurement, popperOffsets, referenceOffsets);\n });\n });\n\n // Loop trough the offsets arrays and execute the operations\n ops.forEach(function (op, index) {\n op.forEach(function (frag, index2) {\n if (isNumeric(frag)) {\n offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1);\n }\n });\n });\n return offsets;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @argument {Number|String} options.offset=0\n * The offset value as described in the modifier description\n * @returns {Object} The data object, properly modified\n */\nfunction offset(data, _ref) {\n var offset = _ref.offset;\n var placement = data.placement,\n _data$offsets = data.offsets,\n popper = _data$offsets.popper,\n reference = _data$offsets.reference;\n\n var basePlacement = placement.split('-')[0];\n\n var offsets = void 0;\n if (isNumeric(+offset)) {\n offsets = [+offset, 0];\n } else {\n offsets = parseOffset(offset, popper, reference, basePlacement);\n }\n\n if (basePlacement === 'left') {\n popper.top += offsets[0];\n popper.left -= offsets[1];\n } else if (basePlacement === 'right') {\n popper.top += offsets[0];\n popper.left += offsets[1];\n } else if (basePlacement === 'top') {\n popper.left += offsets[0];\n popper.top -= offsets[1];\n } else if (basePlacement === 'bottom') {\n popper.left += offsets[0];\n popper.top += offsets[1];\n }\n\n data.popper = popper;\n return data;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction preventOverflow(data, options) {\n var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper);\n\n // If offsetParent is the reference element, we really want to\n // go one step up and use the next offsetParent as reference to\n // avoid to make this modifier completely useless and look like broken\n if (data.instance.reference === boundariesElement) {\n boundariesElement = getOffsetParent(boundariesElement);\n }\n\n var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement);\n options.boundaries = boundaries;\n\n var order = options.priority;\n var popper = data.offsets.popper;\n\n var check = {\n primary: function primary(placement) {\n var value = popper[placement];\n if (popper[placement] < boundaries[placement] && !options.escapeWithReference) {\n value = Math.max(popper[placement], boundaries[placement]);\n }\n return defineProperty({}, placement, value);\n },\n secondary: function secondary(placement) {\n var mainSide = placement === 'right' ? 'left' : 'top';\n var value = popper[mainSide];\n if (popper[placement] > boundaries[placement] && !options.escapeWithReference) {\n value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height));\n }\n return defineProperty({}, mainSide, value);\n }\n };\n\n order.forEach(function (placement) {\n var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary';\n popper = _extends({}, popper, check[side](placement));\n });\n\n data.offsets.popper = popper;\n\n return data;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction shift(data) {\n var placement = data.placement;\n var basePlacement = placement.split('-')[0];\n var shiftvariation = placement.split('-')[1];\n\n // if shift shiftvariation is specified, run the modifier\n if (shiftvariation) {\n var _data$offsets = data.offsets,\n reference = _data$offsets.reference,\n popper = _data$offsets.popper;\n\n var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1;\n var side = isVertical ? 'left' : 'top';\n var measurement = isVertical ? 'width' : 'height';\n\n var shiftOffsets = {\n start: defineProperty({}, side, reference[side]),\n end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement])\n };\n\n data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]);\n }\n\n return data;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by update method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction hide(data) {\n if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) {\n return data;\n }\n\n var refRect = data.offsets.reference;\n var bound = find(data.instance.modifiers, function (modifier) {\n return modifier.name === 'preventOverflow';\n }).boundaries;\n\n if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) {\n // Avoid unnecessary DOM access if visibility hasn't changed\n if (data.hide === true) {\n return data;\n }\n\n data.hide = true;\n data.attributes['x-out-of-boundaries'] = '';\n } else {\n // Avoid unnecessary DOM access if visibility hasn't changed\n if (data.hide === false) {\n return data;\n }\n\n data.hide = false;\n data.attributes['x-out-of-boundaries'] = false;\n }\n\n return data;\n}\n\n/**\n * @function\n * @memberof Modifiers\n * @argument {Object} data - The data object generated by `update` method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {Object} The data object, properly modified\n */\nfunction inner(data) {\n var placement = data.placement;\n var basePlacement = placement.split('-')[0];\n var _data$offsets = data.offsets,\n popper = _data$offsets.popper,\n reference = _data$offsets.reference;\n\n var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1;\n\n var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1;\n\n popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0);\n\n data.placement = getOppositePlacement(placement);\n data.offsets.popper = getClientRect(popper);\n\n return data;\n}\n\n/**\n * Modifier function, each modifier can have a function of this type assigned\n * to its `fn` property.
\n * These functions will be called on each update, this means that you must\n * make sure they are performant enough to avoid performance bottlenecks.\n *\n * @function ModifierFn\n * @argument {dataObject} data - The data object generated by `update` method\n * @argument {Object} options - Modifiers configuration and options\n * @returns {dataObject} The data object, properly modified\n */\n\n/**\n * Modifiers are plugins used to alter the behavior of your poppers.
\n * Popper.js uses a set of 9 modifiers to provide all the basic functionalities\n * needed by the library.\n *\n * Usually you don't want to override the `order`, `fn` and `onLoad` props.\n * All the other properties are configurations that could be tweaked.\n * @namespace modifiers\n */\nvar modifiers = {\n /**\n * Modifier used to shift the popper on the start or end of its reference\n * element.
\n * It will read the variation of the `placement` property.
\n * It can be one either `-end` or `-start`.\n * @memberof modifiers\n * @inner\n */\n shift: {\n /** @prop {number} order=100 - Index used to define the order of execution */\n order: 100,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: shift\n },\n\n /**\n * The `offset` modifier can shift your popper on both its axis.\n *\n * It accepts the following units:\n * - `px` or unitless, interpreted as pixels\n * - `%` or `%r`, percentage relative to the length of the reference element\n * - `%p`, percentage relative to the length of the popper element\n * - `vw`, CSS viewport width unit\n * - `vh`, CSS viewport height unit\n *\n * For length is intended the main axis relative to the placement of the popper.
\n * This means that if the placement is `top` or `bottom`, the length will be the\n * `width`. In case of `left` or `right`, it will be the height.\n *\n * You can provide a single value (as `Number` or `String`), or a pair of values\n * as `String` divided by a comma or one (or more) white spaces.
\n * The latter is a deprecated method because it leads to confusion and will be\n * removed in v2.
\n * Additionally, it accepts additions and subtractions between different units.\n * Note that multiplications and divisions aren't supported.\n *\n * Valid examples are:\n * ```\n * 10\n * '10%'\n * '10, 10'\n * '10%, 10'\n * '10 + 10%'\n * '10 - 5vh + 3%'\n * '-10px + 5vh, 5px - 6%'\n * ```\n * > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap\n * > with their reference element, unfortunately, you will have to disable the `flip` modifier.\n * > More on this [reading this issue](https://github.com/FezVrasta/popper.js/issues/373)\n *\n * @memberof modifiers\n * @inner\n */\n offset: {\n /** @prop {number} order=200 - Index used to define the order of execution */\n order: 200,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: offset,\n /** @prop {Number|String} offset=0\n * The offset value as described in the modifier description\n */\n offset: 0\n },\n\n /**\n * Modifier used to prevent the popper from being positioned outside the boundary.\n *\n * An scenario exists where the reference itself is not within the boundaries.
\n * We can say it has \"escaped the boundaries\" — or just \"escaped\".
\n * In this case we need to decide whether the popper should either:\n *\n * - detach from the reference and remain \"trapped\" in the boundaries, or\n * - if it should ignore the boundary and \"escape with its reference\"\n *\n * When `escapeWithReference` is set to`true` and reference is completely\n * outside its boundaries, the popper will overflow (or completely leave)\n * the boundaries in order to remain attached to the edge of the reference.\n *\n * @memberof modifiers\n * @inner\n */\n preventOverflow: {\n /** @prop {number} order=300 - Index used to define the order of execution */\n order: 300,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: preventOverflow,\n /**\n * @prop {Array} [priority=['left','right','top','bottom']]\n * Popper will try to prevent overflow following these priorities by default,\n * then, it could overflow on the left and on top of the `boundariesElement`\n */\n priority: ['left', 'right', 'top', 'bottom'],\n /**\n * @prop {number} padding=5\n * Amount of pixel used to define a minimum distance between the boundaries\n * and the popper this makes sure the popper has always a little padding\n * between the edges of its container\n */\n padding: 5,\n /**\n * @prop {String|HTMLElement} boundariesElement='scrollParent'\n * Boundaries used by the modifier, can be `scrollParent`, `window`,\n * `viewport` or any DOM element.\n */\n boundariesElement: 'scrollParent'\n },\n\n /**\n * Modifier used to make sure the reference and its popper stay near eachothers\n * without leaving any gap between the two. Expecially useful when the arrow is\n * enabled and you want to assure it to point to its reference element.\n * It cares only about the first axis, you can still have poppers with margin\n * between the popper and its reference element.\n * @memberof modifiers\n * @inner\n */\n keepTogether: {\n /** @prop {number} order=400 - Index used to define the order of execution */\n order: 400,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: keepTogether\n },\n\n /**\n * This modifier is used to move the `arrowElement` of the popper to make\n * sure it is positioned between the reference element and its popper element.\n * It will read the outer size of the `arrowElement` node to detect how many\n * pixels of conjuction are needed.\n *\n * It has no effect if no `arrowElement` is provided.\n * @memberof modifiers\n * @inner\n */\n arrow: {\n /** @prop {number} order=500 - Index used to define the order of execution */\n order: 500,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: arrow,\n /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */\n element: '[x-arrow]'\n },\n\n /**\n * Modifier used to flip the popper's placement when it starts to overlap its\n * reference element.\n *\n * Requires the `preventOverflow` modifier before it in order to work.\n *\n * **NOTE:** this modifier will interrupt the current update cycle and will\n * restart it if it detects the need to flip the placement.\n * @memberof modifiers\n * @inner\n */\n flip: {\n /** @prop {number} order=600 - Index used to define the order of execution */\n order: 600,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: flip,\n /**\n * @prop {String|Array} behavior='flip'\n * The behavior used to change the popper's placement. It can be one of\n * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid\n * placements (with optional variations).\n */\n behavior: 'flip',\n /**\n * @prop {number} padding=5\n * The popper will flip if it hits the edges of the `boundariesElement`\n */\n padding: 5,\n /**\n * @prop {String|HTMLElement} boundariesElement='viewport'\n * The element which will define the boundaries of the popper position,\n * the popper will never be placed outside of the defined boundaries\n * (except if keepTogether is enabled)\n */\n boundariesElement: 'viewport'\n },\n\n /**\n * Modifier used to make the popper flow toward the inner of the reference element.\n * By default, when this modifier is disabled, the popper will be placed outside\n * the reference element.\n * @memberof modifiers\n * @inner\n */\n inner: {\n /** @prop {number} order=700 - Index used to define the order of execution */\n order: 700,\n /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */\n enabled: false,\n /** @prop {ModifierFn} */\n fn: inner\n },\n\n /**\n * Modifier used to hide the popper when its reference element is outside of the\n * popper boundaries. It will set a `x-out-of-boundaries` attribute which can\n * be used to hide with a CSS selector the popper when its reference is\n * out of boundaries.\n *\n * Requires the `preventOverflow` modifier before it in order to work.\n * @memberof modifiers\n * @inner\n */\n hide: {\n /** @prop {number} order=800 - Index used to define the order of execution */\n order: 800,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: hide\n },\n\n /**\n * Computes the style that will be applied to the popper element to gets\n * properly positioned.\n *\n * Note that this modifier will not touch the DOM, it just prepares the styles\n * so that `applyStyle` modifier can apply it. This separation is useful\n * in case you need to replace `applyStyle` with a custom implementation.\n *\n * This modifier has `850` as `order` value to maintain backward compatibility\n * with previous versions of Popper.js. Expect the modifiers ordering method\n * to change in future major versions of the library.\n *\n * @memberof modifiers\n * @inner\n */\n computeStyle: {\n /** @prop {number} order=850 - Index used to define the order of execution */\n order: 850,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: computeStyle,\n /**\n * @prop {Boolean} gpuAcceleration=true\n * If true, it uses the CSS 3d transformation to position the popper.\n * Otherwise, it will use the `top` and `left` properties.\n */\n gpuAcceleration: true,\n /**\n * @prop {string} [x='bottom']\n * Where to anchor the X axis (`bottom` or `top`). AKA X offset origin.\n * Change this if your popper should grow in a direction different from `bottom`\n */\n x: 'bottom',\n /**\n * @prop {string} [x='left']\n * Where to anchor the Y axis (`left` or `right`). AKA Y offset origin.\n * Change this if your popper should grow in a direction different from `right`\n */\n y: 'right'\n },\n\n /**\n * Applies the computed styles to the popper element.\n *\n * All the DOM manipulations are limited to this modifier. This is useful in case\n * you want to integrate Popper.js inside a framework or view library and you\n * want to delegate all the DOM manipulations to it.\n *\n * Note that if you disable this modifier, you must make sure the popper element\n * has its position set to `absolute` before Popper.js can do its work!\n *\n * Just disable this modifier and define you own to achieve the desired effect.\n *\n * @memberof modifiers\n * @inner\n */\n applyStyle: {\n /** @prop {number} order=900 - Index used to define the order of execution */\n order: 900,\n /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */\n enabled: true,\n /** @prop {ModifierFn} */\n fn: applyStyle,\n /** @prop {Function} */\n onLoad: applyStyleOnLoad,\n /**\n * @deprecated since version 1.10.0, the property moved to `computeStyle` modifier\n * @prop {Boolean} gpuAcceleration=true\n * If true, it uses the CSS 3d transformation to position the popper.\n * Otherwise, it will use the `top` and `left` properties.\n */\n gpuAcceleration: undefined\n }\n};\n\n/**\n * The `dataObject` is an object containing all the informations used by Popper.js\n * this object get passed to modifiers and to the `onCreate` and `onUpdate` callbacks.\n * @name dataObject\n * @property {Object} data.instance The Popper.js instance\n * @property {String} data.placement Placement applied to popper\n * @property {String} data.originalPlacement Placement originally defined on init\n * @property {Boolean} data.flipped True if popper has been flipped by flip modifier\n * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper.\n * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier\n * @property {Object} data.styles Any CSS property defined here will be applied to the popper, it expects the JavaScript nomenclature (eg. `marginBottom`)\n * @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow, it expects the JavaScript nomenclature (eg. `marginBottom`)\n * @property {Object} data.boundaries Offsets of the popper boundaries\n * @property {Object} data.offsets The measurements of popper, reference and arrow elements.\n * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values\n * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values\n * @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0\n */\n\n/**\n * Default options provided to Popper.js constructor.
\n * These can be overriden using the `options` argument of Popper.js.
\n * To override an option, simply pass as 3rd argument an object with the same\n * structure of this object, example:\n * ```\n * new Popper(ref, pop, {\n * modifiers: {\n * preventOverflow: { enabled: false }\n * }\n * })\n * ```\n * @type {Object}\n * @static\n * @memberof Popper\n */\nvar Defaults = {\n /**\n * Popper's placement\n * @prop {Popper.placements} placement='bottom'\n */\n placement: 'bottom',\n\n /**\n * Whether events (resize, scroll) are initially enabled\n * @prop {Boolean} eventsEnabled=true\n */\n eventsEnabled: true,\n\n /**\n * Set to true if you want to automatically remove the popper when\n * you call the `destroy` method.\n * @prop {Boolean} removeOnDestroy=false\n */\n removeOnDestroy: false,\n\n /**\n * Callback called when the popper is created.
\n * By default, is set to no-op.
\n * Access Popper.js instance with `data.instance`.\n * @prop {onCreate}\n */\n onCreate: function onCreate() {},\n\n /**\n * Callback called when the popper is updated, this callback is not called\n * on the initialization/creation of the popper, but only on subsequent\n * updates.
\n * By default, is set to no-op.
\n * Access Popper.js instance with `data.instance`.\n * @prop {onUpdate}\n */\n onUpdate: function onUpdate() {},\n\n /**\n * List of modifiers used to modify the offsets before they are applied to the popper.\n * They provide most of the functionalities of Popper.js\n * @prop {modifiers}\n */\n modifiers: modifiers\n};\n\n/**\n * @callback onCreate\n * @param {dataObject} data\n */\n\n/**\n * @callback onUpdate\n * @param {dataObject} data\n */\n\n// Utils\n// Methods\nvar Popper = function () {\n /**\n * Create a new Popper.js instance\n * @class Popper\n * @param {HTMLElement|referenceObject} reference - The reference element used to position the popper\n * @param {HTMLElement} popper - The HTML element used as popper.\n * @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults)\n * @return {Object} instance - The generated Popper.js instance\n */\n function Popper(reference, popper) {\n var _this = this;\n\n var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n classCallCheck(this, Popper);\n\n this.scheduleUpdate = function () {\n return requestAnimationFrame(_this.update);\n };\n\n // make update() debounced, so that it only runs at most once-per-tick\n this.update = debounce(this.update.bind(this));\n\n // with {} we create a new object with the options inside it\n this.options = _extends({}, Popper.Defaults, options);\n\n // init state\n this.state = {\n isDestroyed: false,\n isCreated: false,\n scrollParents: []\n };\n\n // get reference and popper elements (allow jQuery wrappers)\n this.reference = reference && reference.jquery ? reference[0] : reference;\n this.popper = popper && popper.jquery ? popper[0] : popper;\n\n // Deep merge modifiers options\n this.options.modifiers = {};\n Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) {\n _this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {});\n });\n\n // Refactoring modifiers' list (Object => Array)\n this.modifiers = Object.keys(this.options.modifiers).map(function (name) {\n return _extends({\n name: name\n }, _this.options.modifiers[name]);\n })\n // sort the modifiers by order\n .sort(function (a, b) {\n return a.order - b.order;\n });\n\n // modifiers have the ability to execute arbitrary code when Popper.js get inited\n // such code is executed in the same order of its modifier\n // they could add new properties to their options configuration\n // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`!\n this.modifiers.forEach(function (modifierOptions) {\n if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) {\n modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state);\n }\n });\n\n // fire the first update to position the popper in the right place\n this.update();\n\n var eventsEnabled = this.options.eventsEnabled;\n if (eventsEnabled) {\n // setup event listeners, they will take care of update the position in specific situations\n this.enableEventListeners();\n }\n\n this.state.eventsEnabled = eventsEnabled;\n }\n\n // We can't use class properties because they don't get listed in the\n // class prototype and break stuff like Sinon stubs\n\n\n createClass(Popper, [{\n key: 'update',\n value: function update$$1() {\n return update.call(this);\n }\n }, {\n key: 'destroy',\n value: function destroy$$1() {\n return destroy.call(this);\n }\n }, {\n key: 'enableEventListeners',\n value: function enableEventListeners$$1() {\n return enableEventListeners.call(this);\n }\n }, {\n key: 'disableEventListeners',\n value: function disableEventListeners$$1() {\n return disableEventListeners.call(this);\n }\n\n /**\n * Schedule an update, it will run on the next UI update available\n * @method scheduleUpdate\n * @memberof Popper\n */\n\n\n /**\n * Collection of utilities useful when writing custom modifiers.\n * Starting from version 1.7, this method is available only if you\n * include `popper-utils.js` before `popper.js`.\n *\n * **DEPRECATION**: This way to access PopperUtils is deprecated\n * and will be removed in v2! Use the PopperUtils module directly instead.\n * Due to the high instability of the methods contained in Utils, we can't\n * guarantee them to follow semver. Use them at your own risk!\n * @static\n * @private\n * @type {Object}\n * @deprecated since version 1.8\n * @member Utils\n * @memberof Popper\n */\n\n }]);\n return Popper;\n}();\n\n/**\n * The `referenceObject` is an object that provides an interface compatible with Popper.js\n * and lets you use it as replacement of a real DOM node.
\n * You can use this method to position a popper relatively to a set of coordinates\n * in case you don't have a DOM node to use as reference.\n *\n * ```\n * new Popper(referenceObject, popperNode);\n * ```\n *\n * NB: This feature isn't supported in Internet Explorer 10\n * @name referenceObject\n * @property {Function} data.getBoundingClientRect\n * A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method.\n * @property {number} data.clientWidth\n * An ES6 getter that will return the width of the virtual reference element.\n * @property {number} data.clientHeight\n * An ES6 getter that will return the height of the virtual reference element.\n */\n\n\nPopper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils;\nPopper.placements = placements;\nPopper.Defaults = Defaults;\n\nexport default Popper;\n//# sourceMappingURL=popper.js.map\n","import $ from 'jquery'\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.0.0): util.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst Util = (($) => {\n /**\n * ------------------------------------------------------------------------\n * Private TransitionEnd Helpers\n * ------------------------------------------------------------------------\n */\n\n let transition = false\n\n const MAX_UID = 1000000\n\n // Shoutout AngusCroll (https://goo.gl/pxwQGp)\n function toType(obj) {\n return {}.toString.call(obj).match(/\\s([a-zA-Z]+)/)[1].toLowerCase()\n }\n\n function getSpecialTransitionEndEvent() {\n return {\n bindType: transition.end,\n delegateType: transition.end,\n handle(event) {\n if ($(event.target).is(this)) {\n return event.handleObj.handler.apply(this, arguments) // eslint-disable-line prefer-rest-params\n }\n return undefined // eslint-disable-line no-undefined\n }\n }\n }\n\n function transitionEndTest() {\n if (typeof window !== 'undefined' && window.QUnit) {\n return false\n }\n\n return {\n end: 'transitionend'\n }\n }\n\n function transitionEndEmulator(duration) {\n let called = false\n\n $(this).one(Util.TRANSITION_END, () => {\n called = true\n })\n\n setTimeout(() => {\n if (!called) {\n Util.triggerTransitionEnd(this)\n }\n }, duration)\n\n return this\n }\n\n function setTransitionEndSupport() {\n transition = transitionEndTest()\n\n $.fn.emulateTransitionEnd = transitionEndEmulator\n\n if (Util.supportsTransitionEnd()) {\n $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent()\n }\n }\n\n function escapeId(selector) {\n // We escape IDs in case of special selectors (selector = '#myId:something')\n // $.escapeSelector does not exist in jQuery < 3\n selector = typeof $.escapeSelector === 'function' ? $.escapeSelector(selector).substr(1)\n : selector.replace(/(:|\\.|\\[|\\]|,|=|@)/g, '\\\\$1')\n\n return selector\n }\n\n /**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\n const Util = {\n\n TRANSITION_END: 'bsTransitionEnd',\n\n getUID(prefix) {\n do {\n // eslint-disable-next-line no-bitwise\n prefix += ~~(Math.random() * MAX_UID) // \"~~\" acts like a faster Math.floor() here\n } while (document.getElementById(prefix))\n return prefix\n },\n\n getSelectorFromElement(element) {\n let selector = element.getAttribute('data-target')\n if (!selector || selector === '#') {\n selector = element.getAttribute('href') || ''\n }\n\n // If it's an ID\n if (selector.charAt(0) === '#') {\n selector = escapeId(selector)\n }\n\n try {\n const $selector = $(document).find(selector)\n return $selector.length > 0 ? selector : null\n } catch (err) {\n return null\n }\n },\n\n reflow(element) {\n return element.offsetHeight\n },\n\n triggerTransitionEnd(element) {\n $(element).trigger(transition.end)\n },\n\n supportsTransitionEnd() {\n return Boolean(transition)\n },\n\n isElement(obj) {\n return (obj[0] || obj).nodeType\n },\n\n typeCheckConfig(componentName, config, configTypes) {\n for (const property in configTypes) {\n if (Object.prototype.hasOwnProperty.call(configTypes, property)) {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && Util.isElement(value)\n ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new Error(\n `${componentName.toUpperCase()}: ` +\n `Option \"${property}\" provided type \"${valueType}\" ` +\n `but expected type \"${expectedTypes}\".`)\n }\n }\n }\n }\n }\n\n setTransitionEndSupport()\n\n return Util\n})($)\n\nexport default Util\n","import $ from 'jquery'\nimport Util from './util'\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.0.0): alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst Alert = (($) => {\n /**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\n const NAME = 'alert'\n const VERSION = '4.0.0'\n const DATA_KEY = 'bs.alert'\n const EVENT_KEY = `.${DATA_KEY}`\n const DATA_API_KEY = '.data-api'\n const JQUERY_NO_CONFLICT = $.fn[NAME]\n const TRANSITION_DURATION = 150\n\n const Selector = {\n DISMISS : '[data-dismiss=\"alert\"]'\n }\n\n const Event = {\n CLOSE : `close${EVENT_KEY}`,\n CLOSED : `closed${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n }\n\n const ClassName = {\n ALERT : 'alert',\n FADE : 'fade',\n SHOW : 'show'\n }\n\n /**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\n class Alert {\n constructor(element) {\n this._element = element\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n // Public\n\n close(element) {\n element = element || this._element\n\n const rootElement = this._getRootElement(element)\n const customEvent = this._triggerCloseEvent(rootElement)\n\n if (customEvent.isDefaultPrevented()) {\n return\n }\n\n this._removeElement(rootElement)\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n this._element = null\n }\n\n // Private\n\n _getRootElement(element) {\n const selector = Util.getSelectorFromElement(element)\n let parent = false\n\n if (selector) {\n parent = $(selector)[0]\n }\n\n if (!parent) {\n parent = $(element).closest(`.${ClassName.ALERT}`)[0]\n }\n\n return parent\n }\n\n _triggerCloseEvent(element) {\n const closeEvent = $.Event(Event.CLOSE)\n\n $(element).trigger(closeEvent)\n return closeEvent\n }\n\n _removeElement(element) {\n $(element).removeClass(ClassName.SHOW)\n\n if (!Util.supportsTransitionEnd() ||\n !$(element).hasClass(ClassName.FADE)) {\n this._destroyElement(element)\n return\n }\n\n $(element)\n .one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))\n .emulateTransitionEnd(TRANSITION_DURATION)\n }\n\n _destroyElement(element) {\n $(element)\n .detach()\n .trigger(Event.CLOSED)\n .remove()\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n const $element = $(this)\n let data = $element.data(DATA_KEY)\n\n if (!data) {\n data = new Alert(this)\n $element.data(DATA_KEY, data)\n }\n\n if (config === 'close') {\n data[config](this)\n }\n })\n }\n\n static _handleDismiss(alertInstance) {\n return function (event) {\n if (event) {\n event.preventDefault()\n }\n\n alertInstance.close(this)\n }\n }\n }\n\n /**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n $(document).on(\n Event.CLICK_DATA_API,\n Selector.DISMISS,\n Alert._handleDismiss(new Alert())\n )\n\n /**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n $.fn[NAME] = Alert._jQueryInterface\n $.fn[NAME].Constructor = Alert\n $.fn[NAME].noConflict = function () {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Alert._jQueryInterface\n }\n\n return Alert\n})($)\n\nexport default Alert\n","import $ from 'jquery'\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.0.0): button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst Button = (($) => {\n /**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\n const NAME = 'button'\n const VERSION = '4.0.0'\n const DATA_KEY = 'bs.button'\n const EVENT_KEY = `.${DATA_KEY}`\n const DATA_API_KEY = '.data-api'\n const JQUERY_NO_CONFLICT = $.fn[NAME]\n\n const ClassName = {\n ACTIVE : 'active',\n BUTTON : 'btn',\n FOCUS : 'focus'\n }\n\n const Selector = {\n DATA_TOGGLE_CARROT : '[data-toggle^=\"button\"]',\n DATA_TOGGLE : '[data-toggle=\"buttons\"]',\n INPUT : 'input',\n ACTIVE : '.active',\n BUTTON : '.btn'\n }\n\n const Event = {\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,\n FOCUS_BLUR_DATA_API : `focus${EVENT_KEY}${DATA_API_KEY} ` +\n `blur${EVENT_KEY}${DATA_API_KEY}`\n }\n\n /**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\n class Button {\n constructor(element) {\n this._element = element\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n // Public\n\n toggle() {\n let triggerChangeEvent = true\n let addAriaPressed = true\n const rootElement = $(this._element).closest(\n Selector.DATA_TOGGLE\n )[0]\n\n if (rootElement) {\n const input = $(this._element).find(Selector.INPUT)[0]\n\n if (input) {\n if (input.type === 'radio') {\n if (input.checked &&\n $(this._element).hasClass(ClassName.ACTIVE)) {\n triggerChangeEvent = false\n } else {\n const activeElement = $(rootElement).find(Selector.ACTIVE)[0]\n\n if (activeElement) {\n $(activeElement).removeClass(ClassName.ACTIVE)\n }\n }\n }\n\n if (triggerChangeEvent) {\n if (input.hasAttribute('disabled') ||\n rootElement.hasAttribute('disabled') ||\n input.classList.contains('disabled') ||\n rootElement.classList.contains('disabled')) {\n return\n }\n input.checked = !$(this._element).hasClass(ClassName.ACTIVE)\n $(input).trigger('change')\n }\n\n input.focus()\n addAriaPressed = false\n }\n }\n\n if (addAriaPressed) {\n this._element.setAttribute('aria-pressed',\n !$(this._element).hasClass(ClassName.ACTIVE))\n }\n\n if (triggerChangeEvent) {\n $(this._element).toggleClass(ClassName.ACTIVE)\n }\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n this._element = null\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n\n if (!data) {\n data = new Button(this)\n $(this).data(DATA_KEY, data)\n }\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n }\n\n /**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n $(document)\n .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {\n event.preventDefault()\n\n let button = event.target\n\n if (!$(button).hasClass(ClassName.BUTTON)) {\n button = $(button).closest(Selector.BUTTON)\n }\n\n Button._jQueryInterface.call($(button), 'toggle')\n })\n .on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => {\n const button = $(event.target).closest(Selector.BUTTON)[0]\n $(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type))\n })\n\n /**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n $.fn[NAME] = Button._jQueryInterface\n $.fn[NAME].Constructor = Button\n $.fn[NAME].noConflict = function () {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Button._jQueryInterface\n }\n\n return Button\n})($)\n\nexport default Button\n","import $ from 'jquery'\nimport Util from './util'\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.0.0): carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst Carousel = (($) => {\n /**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\n const NAME = 'carousel'\n const VERSION = '4.0.0'\n const DATA_KEY = 'bs.carousel'\n const EVENT_KEY = `.${DATA_KEY}`\n const DATA_API_KEY = '.data-api'\n const JQUERY_NO_CONFLICT = $.fn[NAME]\n const TRANSITION_DURATION = 600\n const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key\n const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key\n const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\n\n const Default = {\n interval : 5000,\n keyboard : true,\n slide : false,\n pause : 'hover',\n wrap : true\n }\n\n const DefaultType = {\n interval : '(number|boolean)',\n keyboard : 'boolean',\n slide : '(boolean|string)',\n pause : '(string|boolean)',\n wrap : 'boolean'\n }\n\n const Direction = {\n NEXT : 'next',\n PREV : 'prev',\n LEFT : 'left',\n RIGHT : 'right'\n }\n\n const Event = {\n SLIDE : `slide${EVENT_KEY}`,\n SLID : `slid${EVENT_KEY}`,\n KEYDOWN : `keydown${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`,\n TOUCHEND : `touchend${EVENT_KEY}`,\n LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n }\n\n const ClassName = {\n CAROUSEL : 'carousel',\n ACTIVE : 'active',\n SLIDE : 'slide',\n RIGHT : 'carousel-item-right',\n LEFT : 'carousel-item-left',\n NEXT : 'carousel-item-next',\n PREV : 'carousel-item-prev',\n ITEM : 'carousel-item'\n }\n\n const Selector = {\n ACTIVE : '.active',\n ACTIVE_ITEM : '.active.carousel-item',\n ITEM : '.carousel-item',\n NEXT_PREV : '.carousel-item-next, .carousel-item-prev',\n INDICATORS : '.carousel-indicators',\n DATA_SLIDE : '[data-slide], [data-slide-to]',\n DATA_RIDE : '[data-ride=\"carousel\"]'\n }\n\n /**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\n class Carousel {\n constructor(element, config) {\n this._items = null\n this._interval = null\n this._activeElement = null\n\n this._isPaused = false\n this._isSliding = false\n\n this.touchTimeout = null\n\n this._config = this._getConfig(config)\n this._element = $(element)[0]\n this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0]\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n next() {\n if (!this._isSliding) {\n this._slide(Direction.NEXT)\n }\n }\n\n nextWhenVisible() {\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden &&\n ($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {\n this.next()\n }\n }\n\n prev() {\n if (!this._isSliding) {\n this._slide(Direction.PREV)\n }\n }\n\n pause(event) {\n if (!event) {\n this._isPaused = true\n }\n\n if ($(this._element).find(Selector.NEXT_PREV)[0] &&\n Util.supportsTransitionEnd()) {\n Util.triggerTransitionEnd(this._element)\n this.cycle(true)\n }\n\n clearInterval(this._interval)\n this._interval = null\n }\n\n cycle(event) {\n if (!event) {\n this._isPaused = false\n }\n\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n\n if (this._config.interval && !this._isPaused) {\n this._interval = setInterval(\n (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),\n this._config.interval\n )\n }\n }\n\n to(index) {\n this._activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0]\n\n const activeIndex = this._getItemIndex(this._activeElement)\n\n if (index > this._items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n $(this._element).one(Event.SLID, () => this.to(index))\n return\n }\n\n if (activeIndex === index) {\n this.pause()\n this.cycle()\n return\n }\n\n const direction = index > activeIndex\n ? Direction.NEXT\n : Direction.PREV\n\n this._slide(direction, this._items[index])\n }\n\n dispose() {\n $(this._element).off(EVENT_KEY)\n $.removeData(this._element, DATA_KEY)\n\n this._items = null\n this._config = null\n this._element = null\n this._interval = null\n this._isPaused = null\n this._isSliding = null\n this._activeElement = null\n this._indicatorsElement = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n $(this._element)\n .on(Event.KEYDOWN, (event) => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n $(this._element)\n .on(Event.MOUSEENTER, (event) => this.pause(event))\n .on(Event.MOUSELEAVE, (event) => this.cycle(event))\n if ('ontouchstart' in document.documentElement) {\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n $(this._element).on(Event.TOUCHEND, () => {\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n })\n }\n }\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n switch (event.which) {\n case ARROW_LEFT_KEYCODE:\n event.preventDefault()\n this.prev()\n break\n case ARROW_RIGHT_KEYCODE:\n event.preventDefault()\n this.next()\n break\n default:\n }\n }\n\n _getItemIndex(element) {\n this._items = $.makeArray($(element).parent().find(Selector.ITEM))\n return this._items.indexOf(element)\n }\n\n _getItemByDirection(direction, activeElement) {\n const isNextDirection = direction === Direction.NEXT\n const isPrevDirection = direction === Direction.PREV\n const activeIndex = this._getItemIndex(activeElement)\n const lastItemIndex = this._items.length - 1\n const isGoingToWrap = isPrevDirection && activeIndex === 0 ||\n isNextDirection && activeIndex === lastItemIndex\n\n if (isGoingToWrap && !this._config.wrap) {\n return activeElement\n }\n\n const delta = direction === Direction.PREV ? -1 : 1\n const itemIndex = (activeIndex + delta) % this._items.length\n\n return itemIndex === -1\n ? this._items[this._items.length - 1] : this._items[itemIndex]\n }\n\n _triggerSlideEvent(relatedTarget, eventDirectionName) {\n const targetIndex = this._getItemIndex(relatedTarget)\n const fromIndex = this._getItemIndex($(this._element).find(Selector.ACTIVE_ITEM)[0])\n const slideEvent = $.Event(Event.SLIDE, {\n relatedTarget,\n direction: eventDirectionName,\n from: fromIndex,\n to: targetIndex\n })\n\n $(this._element).trigger(slideEvent)\n\n return slideEvent\n }\n\n _setActiveIndicatorElement(element) {\n if (this._indicatorsElement) {\n $(this._indicatorsElement)\n .find(Selector.ACTIVE)\n .removeClass(ClassName.ACTIVE)\n\n const nextIndicator = this._indicatorsElement.children[\n this._getItemIndex(element)\n ]\n\n if (nextIndicator) {\n $(nextIndicator).addClass(ClassName.ACTIVE)\n }\n }\n }\n\n _slide(direction, element) {\n const activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0]\n const activeElementIndex = this._getItemIndex(activeElement)\n const nextElement = element || activeElement &&\n this._getItemByDirection(direction, activeElement)\n const nextElementIndex = this._getItemIndex(nextElement)\n const isCycling = Boolean(this._interval)\n\n let directionalClassName\n let orderClassName\n let eventDirectionName\n\n if (direction === Direction.NEXT) {\n directionalClassName = ClassName.LEFT\n orderClassName = ClassName.NEXT\n eventDirectionName = Direction.LEFT\n } else {\n directionalClassName = ClassName.RIGHT\n orderClassName = ClassName.PREV\n eventDirectionName = Direction.RIGHT\n }\n\n if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {\n this._isSliding = false\n return\n }\n\n const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)\n if (slideEvent.isDefaultPrevented()) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n return\n }\n\n this._isSliding = true\n\n if (isCycling) {\n this.pause()\n }\n\n this._setActiveIndicatorElement(nextElement)\n\n const slidEvent = $.Event(Event.SLID, {\n relatedTarget: nextElement,\n direction: eventDirectionName,\n from: activeElementIndex,\n to: nextElementIndex\n })\n\n if (Util.supportsTransitionEnd() &&\n $(this._element).hasClass(ClassName.SLIDE)) {\n $(nextElement).addClass(orderClassName)\n\n Util.reflow(nextElement)\n\n $(activeElement).addClass(directionalClassName)\n $(nextElement).addClass(directionalClassName)\n\n $(activeElement)\n .one(Util.TRANSITION_END, () => {\n $(nextElement)\n .removeClass(`${directionalClassName} ${orderClassName}`)\n .addClass(ClassName.ACTIVE)\n\n $(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)\n\n this._isSliding = false\n\n setTimeout(() => $(this._element).trigger(slidEvent), 0)\n })\n .emulateTransitionEnd(TRANSITION_DURATION)\n } else {\n $(activeElement).removeClass(ClassName.ACTIVE)\n $(nextElement).addClass(ClassName.ACTIVE)\n\n this._isSliding = false\n $(this._element).trigger(slidEvent)\n }\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n let _config = {\n ...Default,\n ...$(this).data()\n }\n\n if (typeof config === 'object') {\n _config = {\n ..._config,\n ...config\n }\n }\n\n const action = typeof config === 'string' ? config : _config.slide\n\n if (!data) {\n data = new Carousel(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'number') {\n data.to(config)\n } else if (typeof action === 'string') {\n if (typeof data[action] === 'undefined') {\n throw new TypeError(`No method named \"${action}\"`)\n }\n data[action]()\n } else if (_config.interval) {\n data.pause()\n data.cycle()\n }\n })\n }\n\n static _dataApiClickHandler(event) {\n const selector = Util.getSelectorFromElement(this)\n\n if (!selector) {\n return\n }\n\n const target = $(selector)[0]\n\n if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {\n return\n }\n\n const config = {\n ...$(target).data(),\n ...$(this).data()\n }\n const slideIndex = this.getAttribute('data-slide-to')\n\n if (slideIndex) {\n config.interval = false\n }\n\n Carousel._jQueryInterface.call($(target), config)\n\n if (slideIndex) {\n $(target).data(DATA_KEY).to(slideIndex)\n }\n\n event.preventDefault()\n }\n }\n\n /**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n $(document)\n .on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)\n\n $(window).on(Event.LOAD_DATA_API, () => {\n $(Selector.DATA_RIDE).each(function () {\n const $carousel = $(this)\n Carousel._jQueryInterface.call($carousel, $carousel.data())\n })\n })\n\n /**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n $.fn[NAME] = Carousel._jQueryInterface\n $.fn[NAME].Constructor = Carousel\n $.fn[NAME].noConflict = function () {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Carousel._jQueryInterface\n }\n\n return Carousel\n})($)\n\nexport default Carousel\n","import $ from 'jquery'\nimport Util from './util'\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.0.0): collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst Collapse = (($) => {\n /**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\n const NAME = 'collapse'\n const VERSION = '4.0.0'\n const DATA_KEY = 'bs.collapse'\n const EVENT_KEY = `.${DATA_KEY}`\n const DATA_API_KEY = '.data-api'\n const JQUERY_NO_CONFLICT = $.fn[NAME]\n const TRANSITION_DURATION = 600\n\n const Default = {\n toggle : true,\n parent : ''\n }\n\n const DefaultType = {\n toggle : 'boolean',\n parent : '(string|element)'\n }\n\n const Event = {\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n }\n\n const ClassName = {\n SHOW : 'show',\n COLLAPSE : 'collapse',\n COLLAPSING : 'collapsing',\n COLLAPSED : 'collapsed'\n }\n\n const Dimension = {\n WIDTH : 'width',\n HEIGHT : 'height'\n }\n\n const Selector = {\n ACTIVES : '.show, .collapsing',\n DATA_TOGGLE : '[data-toggle=\"collapse\"]'\n }\n\n /**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\n class Collapse {\n constructor(element, config) {\n this._isTransitioning = false\n this._element = element\n this._config = this._getConfig(config)\n this._triggerArray = $.makeArray($(\n `[data-toggle=\"collapse\"][href=\"#${element.id}\"],` +\n `[data-toggle=\"collapse\"][data-target=\"#${element.id}\"]`\n ))\n const tabToggles = $(Selector.DATA_TOGGLE)\n for (let i = 0; i < tabToggles.length; i++) {\n const elem = tabToggles[i]\n const selector = Util.getSelectorFromElement(elem)\n if (selector !== null && $(selector).filter(element).length > 0) {\n this._selector = selector\n this._triggerArray.push(elem)\n }\n }\n\n this._parent = this._config.parent ? this._getParent() : null\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._element, this._triggerArray)\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle() {\n if ($(this._element).hasClass(ClassName.SHOW)) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning ||\n $(this._element).hasClass(ClassName.SHOW)) {\n return\n }\n\n let actives\n let activesData\n\n if (this._parent) {\n actives = $.makeArray(\n $(this._parent)\n .find(Selector.ACTIVES)\n .filter(`[data-parent=\"${this._config.parent}\"]`)\n )\n if (actives.length === 0) {\n actives = null\n }\n }\n\n if (actives) {\n activesData = $(actives).not(this._selector).data(DATA_KEY)\n if (activesData && activesData._isTransitioning) {\n return\n }\n }\n\n const startEvent = $.Event(Event.SHOW)\n $(this._element).trigger(startEvent)\n if (startEvent.isDefaultPrevented()) {\n return\n }\n\n if (actives) {\n Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide')\n if (!activesData) {\n $(actives).data(DATA_KEY, null)\n }\n }\n\n const dimension = this._getDimension()\n\n $(this._element)\n .removeClass(ClassName.COLLAPSE)\n .addClass(ClassName.COLLAPSING)\n\n this._element.style[dimension] = 0\n\n if (this._triggerArray.length > 0) {\n $(this._triggerArray)\n .removeClass(ClassName.COLLAPSED)\n .attr('aria-expanded', true)\n }\n\n this.setTransitioning(true)\n\n const complete = () => {\n $(this._element)\n .removeClass(ClassName.COLLAPSING)\n .addClass(ClassName.COLLAPSE)\n .addClass(ClassName.SHOW)\n\n this._element.style[dimension] = ''\n\n this.setTransitioning(false)\n\n $(this._element).trigger(Event.SHOWN)\n }\n\n if (!Util.supportsTransitionEnd()) {\n complete()\n return\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n\n $(this._element)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(TRANSITION_DURATION)\n\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning ||\n !$(this._element).hasClass(ClassName.SHOW)) {\n return\n }\n\n const startEvent = $.Event(Event.HIDE)\n $(this._element).trigger(startEvent)\n if (startEvent.isDefaultPrevented()) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n Util.reflow(this._element)\n\n $(this._element)\n .addClass(ClassName.COLLAPSING)\n .removeClass(ClassName.COLLAPSE)\n .removeClass(ClassName.SHOW)\n\n if (this._triggerArray.length > 0) {\n for (let i = 0; i < this._triggerArray.length; i++) {\n const trigger = this._triggerArray[i]\n const selector = Util.getSelectorFromElement(trigger)\n if (selector !== null) {\n const $elem = $(selector)\n if (!$elem.hasClass(ClassName.SHOW)) {\n $(trigger).addClass(ClassName.COLLAPSED)\n .attr('aria-expanded', false)\n }\n }\n }\n }\n\n this.setTransitioning(true)\n\n const complete = () => {\n this.setTransitioning(false)\n $(this._element)\n .removeClass(ClassName.COLLAPSING)\n .addClass(ClassName.COLLAPSE)\n .trigger(Event.HIDDEN)\n }\n\n this._element.style[dimension] = ''\n\n if (!Util.supportsTransitionEnd()) {\n complete()\n return\n }\n\n $(this._element)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(TRANSITION_DURATION)\n }\n\n setTransitioning(isTransitioning) {\n this._isTransitioning = isTransitioning\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n\n this._config = null\n this._parent = null\n this._element = null\n this._triggerArray = null\n this._isTransitioning = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n config.toggle = Boolean(config.toggle) // Coerce string values\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _getDimension() {\n const hasWidth = $(this._element).hasClass(Dimension.WIDTH)\n return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT\n }\n\n _getParent() {\n let parent = null\n if (Util.isElement(this._config.parent)) {\n parent = this._config.parent\n\n // It's a jQuery object\n if (typeof this._config.parent.jquery !== 'undefined') {\n parent = this._config.parent[0]\n }\n } else {\n parent = $(this._config.parent)[0]\n }\n\n const selector =\n `[data-toggle=\"collapse\"][data-parent=\"${this._config.parent}\"]`\n\n $(parent).find(selector).each((i, element) => {\n this._addAriaAndCollapsedClass(\n Collapse._getTargetFromElement(element),\n [element]\n )\n })\n\n return parent\n }\n\n _addAriaAndCollapsedClass(element, triggerArray) {\n if (element) {\n const isOpen = $(element).hasClass(ClassName.SHOW)\n\n if (triggerArray.length > 0) {\n $(triggerArray)\n .toggleClass(ClassName.COLLAPSED, !isOpen)\n .attr('aria-expanded', isOpen)\n }\n }\n }\n\n // Static\n\n static _getTargetFromElement(element) {\n const selector = Util.getSelectorFromElement(element)\n return selector ? $(selector)[0] : null\n }\n\n static _jQueryInterface(config) {\n return this.each(function () {\n const $this = $(this)\n let data = $this.data(DATA_KEY)\n const _config = {\n ...Default,\n ...$this.data(),\n ...typeof config === 'object' && config\n }\n\n if (!data && _config.toggle && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n if (!data) {\n data = new Collapse(this, _config)\n $this.data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n }\n\n /**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.currentTarget.tagName === 'A') {\n event.preventDefault()\n }\n\n const $trigger = $(this)\n const selector = Util.getSelectorFromElement(this)\n $(selector).each(function () {\n const $target = $(this)\n const data = $target.data(DATA_KEY)\n const config = data ? 'toggle' : $trigger.data()\n Collapse._jQueryInterface.call($target, config)\n })\n })\n\n /**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n $.fn[NAME] = Collapse._jQueryInterface\n $.fn[NAME].Constructor = Collapse\n $.fn[NAME].noConflict = function () {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Collapse._jQueryInterface\n }\n\n return Collapse\n})($)\n\nexport default Collapse\n","import $ from 'jquery'\nimport Popper from 'popper.js'\nimport Util from './util'\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.0.0): dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst Dropdown = (($) => {\n /**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\n const NAME = 'dropdown'\n const VERSION = '4.0.0'\n const DATA_KEY = 'bs.dropdown'\n const EVENT_KEY = `.${DATA_KEY}`\n const DATA_API_KEY = '.data-api'\n const JQUERY_NO_CONFLICT = $.fn[NAME]\n const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key\n const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key\n const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key\n const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key\n const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key\n const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)\n const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)\n\n const Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`,\n KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`,\n KEYUP_DATA_API : `keyup${EVENT_KEY}${DATA_API_KEY}`\n }\n\n const ClassName = {\n DISABLED : 'disabled',\n SHOW : 'show',\n DROPUP : 'dropup',\n DROPRIGHT : 'dropright',\n DROPLEFT : 'dropleft',\n MENURIGHT : 'dropdown-menu-right',\n MENULEFT : 'dropdown-menu-left',\n POSITION_STATIC : 'position-static'\n }\n\n const Selector = {\n DATA_TOGGLE : '[data-toggle=\"dropdown\"]',\n FORM_CHILD : '.dropdown form',\n MENU : '.dropdown-menu',\n NAVBAR_NAV : '.navbar-nav',\n VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled)'\n }\n\n const AttachmentMap = {\n TOP : 'top-start',\n TOPEND : 'top-end',\n BOTTOM : 'bottom-start',\n BOTTOMEND : 'bottom-end',\n RIGHT : 'right-start',\n RIGHTEND : 'right-end',\n LEFT : 'left-start',\n LEFTEND : 'left-end'\n }\n\n const Default = {\n offset : 0,\n flip : true,\n boundary : 'scrollParent'\n }\n\n const DefaultType = {\n offset : '(number|string|function)',\n flip : 'boolean',\n boundary : '(string|element)'\n }\n\n /**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\n class Dropdown {\n constructor(element, config) {\n this._element = element\n this._popper = null\n this._config = this._getConfig(config)\n this._menu = this._getMenuElement()\n this._inNavbar = this._detectNavbar()\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\n toggle() {\n if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) {\n return\n }\n\n const parent = Dropdown._getParentFromElement(this._element)\n const isActive = $(this._menu).hasClass(ClassName.SHOW)\n\n Dropdown._clearMenus()\n\n if (isActive) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n const showEvent = $.Event(Event.SHOW, relatedTarget)\n\n $(parent).trigger(showEvent)\n\n if (showEvent.isDefaultPrevented()) {\n return\n }\n\n // Disable totally Popper.js for Dropdown in Navbar\n if (!this._inNavbar) {\n /**\n * Check for Popper dependency\n * Popper - https://popper.js.org\n */\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap dropdown require Popper.js (https://popper.js.org)')\n }\n let element = this._element\n // For dropup with alignment we use the parent as popper container\n if ($(parent).hasClass(ClassName.DROPUP)) {\n if ($(this._menu).hasClass(ClassName.MENULEFT) || $(this._menu).hasClass(ClassName.MENURIGHT)) {\n element = parent\n }\n }\n // If boundary is not `scrollParent`, then set position to `static`\n // to allow the menu to \"escape\" the scroll parent's boundaries\n // https://github.com/twbs/bootstrap/issues/24251\n if (this._config.boundary !== 'scrollParent') {\n $(parent).addClass(ClassName.POSITION_STATIC)\n }\n this._popper = new Popper(element, this._menu, this._getPopperConfig())\n }\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement &&\n $(parent).closest(Selector.NAVBAR_NAV).length === 0) {\n $('body').children().on('mouseover', null, $.noop)\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n $(this._menu).toggleClass(ClassName.SHOW)\n $(parent)\n .toggleClass(ClassName.SHOW)\n .trigger($.Event(Event.SHOWN, relatedTarget))\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n $(this._element).off(EVENT_KEY)\n this._element = null\n this._menu = null\n if (this._popper !== null) {\n this._popper.destroy()\n this._popper = null\n }\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper !== null) {\n this._popper.scheduleUpdate()\n }\n }\n\n // Private\n\n _addEventListeners() {\n $(this._element).on(Event.CLICK, (event) => {\n event.preventDefault()\n event.stopPropagation()\n this.toggle()\n })\n }\n\n _getConfig(config) {\n config = {\n ...this.constructor.Default,\n ...$(this._element).data(),\n ...config\n }\n\n Util.typeCheckConfig(\n NAME,\n config,\n this.constructor.DefaultType\n )\n\n return config\n }\n\n _getMenuElement() {\n if (!this._menu) {\n const parent = Dropdown._getParentFromElement(this._element)\n this._menu = $(parent).find(Selector.MENU)[0]\n }\n return this._menu\n }\n\n _getPlacement() {\n const $parentDropdown = $(this._element).parent()\n let placement = AttachmentMap.BOTTOM\n\n // Handle dropup\n if ($parentDropdown.hasClass(ClassName.DROPUP)) {\n placement = AttachmentMap.TOP\n if ($(this._menu).hasClass(ClassName.MENURIGHT)) {\n placement = AttachmentMap.TOPEND\n }\n } else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) {\n placement = AttachmentMap.RIGHT\n } else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) {\n placement = AttachmentMap.LEFT\n } else if ($(this._menu).hasClass(ClassName.MENURIGHT)) {\n placement = AttachmentMap.BOTTOMEND\n }\n return placement\n }\n\n _detectNavbar() {\n return $(this._element).closest('.navbar').length > 0\n }\n\n _getPopperConfig() {\n const offsetConf = {}\n if (typeof this._config.offset === 'function') {\n offsetConf.fn = (data) => {\n data.offsets = {\n ...data.offsets,\n ...this._config.offset(data.offsets) || {}\n }\n return data\n }\n } else {\n offsetConf.offset = this._config.offset\n }\n const popperConfig = {\n placement: this._getPlacement(),\n modifiers: {\n offset: offsetConf,\n flip: {\n enabled: this._config.flip\n },\n preventOverflow: {\n boundariesElement: this._config.boundary\n }\n }\n }\n\n return popperConfig\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' ? config : null\n\n if (!data) {\n data = new Dropdown(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n\n static _clearMenus(event) {\n if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||\n event.type === 'keyup' && event.which !== TAB_KEYCODE)) {\n return\n }\n\n const toggles = $.makeArray($(Selector.DATA_TOGGLE))\n for (let i = 0; i < toggles.length; i++) {\n const parent = Dropdown._getParentFromElement(toggles[i])\n const context = $(toggles[i]).data(DATA_KEY)\n const relatedTarget = {\n relatedTarget: toggles[i]\n }\n\n if (!context) {\n continue\n }\n\n const dropdownMenu = context._menu\n if (!$(parent).hasClass(ClassName.SHOW)) {\n continue\n }\n\n if (event && (event.type === 'click' &&\n /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&\n $.contains(parent, event.target)) {\n continue\n }\n\n const hideEvent = $.Event(Event.HIDE, relatedTarget)\n $(parent).trigger(hideEvent)\n if (hideEvent.isDefaultPrevented()) {\n continue\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n $('body').children().off('mouseover', null, $.noop)\n }\n\n toggles[i].setAttribute('aria-expanded', 'false')\n\n $(dropdownMenu).removeClass(ClassName.SHOW)\n $(parent)\n .removeClass(ClassName.SHOW)\n .trigger($.Event(Event.HIDDEN, relatedTarget))\n }\n }\n\n static _getParentFromElement(element) {\n let parent\n const selector = Util.getSelectorFromElement(element)\n\n if (selector) {\n parent = $(selector)[0]\n }\n\n return parent || element.parentNode\n }\n\n // eslint-disable-next-line complexity\n static _dataApiKeydownHandler(event) {\n // If not input/textarea:\n // - And not a key in REGEXP_KEYDOWN => not a dropdown command\n // If input/textarea:\n // - If space key => not a dropdown command\n // - If key is other than escape\n // - If key is not up or down => not a dropdown command\n // - If trigger inside the menu => not a dropdown command\n if (/input|textarea/i.test(event.target.tagName)\n ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&\n (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||\n $(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {\n return\n }\n\n event.preventDefault()\n event.stopPropagation()\n\n if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {\n return\n }\n\n const parent = Dropdown._getParentFromElement(this)\n const isActive = $(parent).hasClass(ClassName.SHOW)\n\n if (!isActive && (event.which !== ESCAPE_KEYCODE || event.which !== SPACE_KEYCODE) ||\n isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {\n if (event.which === ESCAPE_KEYCODE) {\n const toggle = $(parent).find(Selector.DATA_TOGGLE)[0]\n $(toggle).trigger('focus')\n }\n\n $(this).trigger('click')\n return\n }\n\n const items = $(parent).find(Selector.VISIBLE_ITEMS).get()\n\n if (items.length === 0) {\n return\n }\n\n let index = items.indexOf(event.target)\n\n if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up\n index--\n }\n\n if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down\n index++\n }\n\n if (index < 0) {\n index = 0\n }\n\n items[index].focus()\n }\n }\n\n /**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n $(document)\n .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler)\n .on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler)\n .on(`${Event.CLICK_DATA_API} ${Event.KEYUP_DATA_API}`, Dropdown._clearMenus)\n .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n event.preventDefault()\n event.stopPropagation()\n Dropdown._jQueryInterface.call($(this), 'toggle')\n })\n .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {\n e.stopPropagation()\n })\n\n /**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n $.fn[NAME] = Dropdown._jQueryInterface\n $.fn[NAME].Constructor = Dropdown\n $.fn[NAME].noConflict = function () {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Dropdown._jQueryInterface\n }\n\n return Dropdown\n})($, Popper)\n\nexport default Dropdown\n","import $ from 'jquery'\nimport Util from './util'\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.0.0): modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst Modal = (($) => {\n /**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\n const NAME = 'modal'\n const VERSION = '4.0.0'\n const DATA_KEY = 'bs.modal'\n const EVENT_KEY = `.${DATA_KEY}`\n const DATA_API_KEY = '.data-api'\n const JQUERY_NO_CONFLICT = $.fn[NAME]\n const TRANSITION_DURATION = 300\n const BACKDROP_TRANSITION_DURATION = 150\n const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key\n\n const Default = {\n backdrop : true,\n keyboard : true,\n focus : true,\n show : true\n }\n\n const DefaultType = {\n backdrop : '(boolean|string)',\n keyboard : 'boolean',\n focus : 'boolean',\n show : 'boolean'\n }\n\n const Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n RESIZE : `resize${EVENT_KEY}`,\n CLICK_DISMISS : `click.dismiss${EVENT_KEY}`,\n KEYDOWN_DISMISS : `keydown.dismiss${EVENT_KEY}`,\n MOUSEUP_DISMISS : `mouseup.dismiss${EVENT_KEY}`,\n MOUSEDOWN_DISMISS : `mousedown.dismiss${EVENT_KEY}`,\n CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`\n }\n\n const ClassName = {\n SCROLLBAR_MEASURER : 'modal-scrollbar-measure',\n BACKDROP : 'modal-backdrop',\n OPEN : 'modal-open',\n FADE : 'fade',\n SHOW : 'show'\n }\n\n const Selector = {\n DIALOG : '.modal-dialog',\n DATA_TOGGLE : '[data-toggle=\"modal\"]',\n DATA_DISMISS : '[data-dismiss=\"modal\"]',\n FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',\n STICKY_CONTENT : '.sticky-top',\n NAVBAR_TOGGLER : '.navbar-toggler'\n }\n\n /**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\n class Modal {\n constructor(element, config) {\n this._config = this._getConfig(config)\n this._element = element\n this._dialog = $(element).find(Selector.DIALOG)[0]\n this._backdrop = null\n this._isShown = false\n this._isBodyOverflowing = false\n this._ignoreBackdropClick = false\n this._originalBodyPadding = 0\n this._scrollbarWidth = 0\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isTransitioning || this._isShown) {\n return\n }\n\n if (Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)) {\n this._isTransitioning = true\n }\n\n const showEvent = $.Event(Event.SHOW, {\n relatedTarget\n })\n\n $(this._element).trigger(showEvent)\n\n if (this._isShown || showEvent.isDefaultPrevented()) {\n return\n }\n\n this._isShown = true\n\n this._checkScrollbar()\n this._setScrollbar()\n\n this._adjustDialog()\n\n $(document.body).addClass(ClassName.OPEN)\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n $(this._element).on(\n Event.CLICK_DISMISS,\n Selector.DATA_DISMISS,\n (event) => this.hide(event)\n )\n\n $(this._dialog).on(Event.MOUSEDOWN_DISMISS, () => {\n $(this._element).one(Event.MOUSEUP_DISMISS, (event) => {\n if ($(event.target).is(this._element)) {\n this._ignoreBackdropClick = true\n }\n })\n })\n\n this._showBackdrop(() => this._showElement(relatedTarget))\n }\n\n hide(event) {\n if (event) {\n event.preventDefault()\n }\n\n if (this._isTransitioning || !this._isShown) {\n return\n }\n\n const hideEvent = $.Event(Event.HIDE)\n\n $(this._element).trigger(hideEvent)\n\n if (!this._isShown || hideEvent.isDefaultPrevented()) {\n return\n }\n\n this._isShown = false\n\n const transition = Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)\n\n if (transition) {\n this._isTransitioning = true\n }\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n $(document).off(Event.FOCUSIN)\n\n $(this._element).removeClass(ClassName.SHOW)\n\n $(this._element).off(Event.CLICK_DISMISS)\n $(this._dialog).off(Event.MOUSEDOWN_DISMISS)\n\n if (transition) {\n $(this._element)\n .one(Util.TRANSITION_END, (event) => this._hideModal(event))\n .emulateTransitionEnd(TRANSITION_DURATION)\n } else {\n this._hideModal()\n }\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n\n $(window, document, this._element, this._backdrop).off(EVENT_KEY)\n\n this._config = null\n this._element = null\n this._dialog = null\n this._backdrop = null\n this._isShown = null\n this._isBodyOverflowing = null\n this._ignoreBackdropClick = null\n this._scrollbarWidth = null\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n Util.typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _showElement(relatedTarget) {\n const transition = Util.supportsTransitionEnd() &&\n $(this._element).hasClass(ClassName.FADE)\n\n if (!this._element.parentNode ||\n this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {\n // Don't move modal's DOM position\n document.body.appendChild(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.scrollTop = 0\n\n if (transition) {\n Util.reflow(this._element)\n }\n\n $(this._element).addClass(ClassName.SHOW)\n\n if (this._config.focus) {\n this._enforceFocus()\n }\n\n const shownEvent = $.Event(Event.SHOWN, {\n relatedTarget\n })\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._element.focus()\n }\n this._isTransitioning = false\n $(this._element).trigger(shownEvent)\n }\n\n if (transition) {\n $(this._dialog)\n .one(Util.TRANSITION_END, transitionComplete)\n .emulateTransitionEnd(TRANSITION_DURATION)\n } else {\n transitionComplete()\n }\n }\n\n _enforceFocus() {\n $(document)\n .off(Event.FOCUSIN) // Guard against infinite focus loop\n .on(Event.FOCUSIN, (event) => {\n if (document !== event.target &&\n this._element !== event.target &&\n $(this._element).has(event.target).length === 0) {\n this._element.focus()\n }\n })\n }\n\n _setEscapeEvent() {\n if (this._isShown && this._config.keyboard) {\n $(this._element).on(Event.KEYDOWN_DISMISS, (event) => {\n if (event.which === ESCAPE_KEYCODE) {\n event.preventDefault()\n this.hide()\n }\n })\n } else if (!this._isShown) {\n $(this._element).off(Event.KEYDOWN_DISMISS)\n }\n }\n\n _setResizeEvent() {\n if (this._isShown) {\n $(window).on(Event.RESIZE, (event) => this.handleUpdate(event))\n } else {\n $(window).off(Event.RESIZE)\n }\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._isTransitioning = false\n this._showBackdrop(() => {\n $(document.body).removeClass(ClassName.OPEN)\n this._resetAdjustments()\n this._resetScrollbar()\n $(this._element).trigger(Event.HIDDEN)\n })\n }\n\n _removeBackdrop() {\n if (this._backdrop) {\n $(this._backdrop).remove()\n this._backdrop = null\n }\n }\n\n _showBackdrop(callback) {\n const animate = $(this._element).hasClass(ClassName.FADE)\n ? ClassName.FADE : ''\n\n if (this._isShown && this._config.backdrop) {\n const doAnimate = Util.supportsTransitionEnd() && animate\n\n this._backdrop = document.createElement('div')\n this._backdrop.className = ClassName.BACKDROP\n\n if (animate) {\n $(this._backdrop).addClass(animate)\n }\n\n $(this._backdrop).appendTo(document.body)\n\n $(this._element).on(Event.CLICK_DISMISS, (event) => {\n if (this._ignoreBackdropClick) {\n this._ignoreBackdropClick = false\n return\n }\n if (event.target !== event.currentTarget) {\n return\n }\n if (this._config.backdrop === 'static') {\n this._element.focus()\n } else {\n this.hide()\n }\n })\n\n if (doAnimate) {\n Util.reflow(this._backdrop)\n }\n\n $(this._backdrop).addClass(ClassName.SHOW)\n\n if (!callback) {\n return\n }\n\n if (!doAnimate) {\n callback()\n return\n }\n\n $(this._backdrop)\n .one(Util.TRANSITION_END, callback)\n .emulateTransitionEnd(BACKDROP_TRANSITION_DURATION)\n } else if (!this._isShown && this._backdrop) {\n $(this._backdrop).removeClass(ClassName.SHOW)\n\n const callbackRemove = () => {\n this._removeBackdrop()\n if (callback) {\n callback()\n }\n }\n\n if (Util.supportsTransitionEnd() &&\n $(this._element).hasClass(ClassName.FADE)) {\n $(this._backdrop)\n .one(Util.TRANSITION_END, callbackRemove)\n .emulateTransitionEnd(BACKDROP_TRANSITION_DURATION)\n } else {\n callbackRemove()\n }\n } else if (callback) {\n callback()\n }\n }\n\n // ----------------------------------------------------------------------\n // the following methods are used to handle overflowing modals\n // todo (fat): these should probably be refactored out of modal.js\n // ----------------------------------------------------------------------\n\n _adjustDialog() {\n const isModalOverflowing =\n this._element.scrollHeight > document.documentElement.clientHeight\n\n if (!this._isBodyOverflowing && isModalOverflowing) {\n this._element.style.paddingLeft = `${this._scrollbarWidth}px`\n }\n\n if (this._isBodyOverflowing && !isModalOverflowing) {\n this._element.style.paddingRight = `${this._scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n _checkScrollbar() {\n const rect = document.body.getBoundingClientRect()\n this._isBodyOverflowing = rect.left + rect.right < window.innerWidth\n this._scrollbarWidth = this._getScrollbarWidth()\n }\n\n _setScrollbar() {\n if (this._isBodyOverflowing) {\n // Note: DOMNode.style.paddingRight returns the actual value or '' if not set\n // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set\n\n // Adjust fixed content padding\n $(Selector.FIXED_CONTENT).each((index, element) => {\n const actualPadding = $(element)[0].style.paddingRight\n const calculatedPadding = $(element).css('padding-right')\n $(element).data('padding-right', actualPadding).css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)\n })\n\n // Adjust sticky content margin\n $(Selector.STICKY_CONTENT).each((index, element) => {\n const actualMargin = $(element)[0].style.marginRight\n const calculatedMargin = $(element).css('margin-right')\n $(element).data('margin-right', actualMargin).css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`)\n })\n\n // Adjust navbar-toggler margin\n $(Selector.NAVBAR_TOGGLER).each((index, element) => {\n const actualMargin = $(element)[0].style.marginRight\n const calculatedMargin = $(element).css('margin-right')\n $(element).data('margin-right', actualMargin).css('margin-right', `${parseFloat(calculatedMargin) + this._scrollbarWidth}px`)\n })\n\n // Adjust body padding\n const actualPadding = document.body.style.paddingRight\n const calculatedPadding = $('body').css('padding-right')\n $('body').data('padding-right', actualPadding).css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)\n }\n }\n\n _resetScrollbar() {\n // Restore fixed content padding\n $(Selector.FIXED_CONTENT).each((index, element) => {\n const padding = $(element).data('padding-right')\n if (typeof padding !== 'undefined') {\n $(element).css('padding-right', padding).removeData('padding-right')\n }\n })\n\n // Restore sticky content and navbar-toggler margin\n $(`${Selector.STICKY_CONTENT}, ${Selector.NAVBAR_TOGGLER}`).each((index, element) => {\n const margin = $(element).data('margin-right')\n if (typeof margin !== 'undefined') {\n $(element).css('margin-right', margin).removeData('margin-right')\n }\n })\n\n // Restore body padding\n const padding = $('body').data('padding-right')\n if (typeof padding !== 'undefined') {\n $('body').css('padding-right', padding).removeData('padding-right')\n }\n }\n\n _getScrollbarWidth() { // thx d.walsh\n const scrollDiv = document.createElement('div')\n scrollDiv.className = ClassName.SCROLLBAR_MEASURER\n document.body.appendChild(scrollDiv)\n const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth\n document.body.removeChild(scrollDiv)\n return scrollbarWidth\n }\n\n // Static\n\n static _jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = {\n ...Modal.Default,\n ...$(this).data(),\n ...typeof config === 'object' && config\n }\n\n if (!data) {\n data = new Modal(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config](relatedTarget)\n } else if (_config.show) {\n data.show(relatedTarget)\n }\n })\n }\n }\n\n /**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\n $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {\n let target\n const selector = Util.getSelectorFromElement(this)\n\n if (selector) {\n target = $(selector)[0]\n }\n\n const config = $(target).data(DATA_KEY)\n ? 'toggle' : {\n ...$(target).data(),\n ...$(this).data()\n }\n\n if (this.tagName === 'A' || this.tagName === 'AREA') {\n event.preventDefault()\n }\n\n const $target = $(target).one(Event.SHOW, (showEvent) => {\n if (showEvent.isDefaultPrevented()) {\n // Only register focus restorer if modal will actually get shown\n return\n }\n\n $target.one(Event.HIDDEN, () => {\n if ($(this).is(':visible')) {\n this.focus()\n }\n })\n })\n\n Modal._jQueryInterface.call($(target), config, this)\n })\n\n /**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n $.fn[NAME] = Modal._jQueryInterface\n $.fn[NAME].Constructor = Modal\n $.fn[NAME].noConflict = function () {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Modal._jQueryInterface\n }\n\n return Modal\n})($)\n\nexport default Modal\n","import $ from 'jquery'\nimport Popper from 'popper.js'\nimport Util from './util'\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.0.0): tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst Tooltip = (($) => {\n /**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\n const NAME = 'tooltip'\n const VERSION = '4.0.0'\n const DATA_KEY = 'bs.tooltip'\n const EVENT_KEY = `.${DATA_KEY}`\n const JQUERY_NO_CONFLICT = $.fn[NAME]\n const TRANSITION_DURATION = 150\n const CLASS_PREFIX = 'bs-tooltip'\n const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\\\s)${CLASS_PREFIX}\\\\S+`, 'g')\n\n const DefaultType = {\n animation : 'boolean',\n template : 'string',\n title : '(string|element|function)',\n trigger : 'string',\n delay : '(number|object)',\n html : 'boolean',\n selector : '(string|boolean)',\n placement : '(string|function)',\n offset : '(number|string)',\n container : '(string|element|boolean)',\n fallbackPlacement : '(string|array)',\n boundary : '(string|element)'\n }\n\n const AttachmentMap = {\n AUTO : 'auto',\n TOP : 'top',\n RIGHT : 'right',\n BOTTOM : 'bottom',\n LEFT : 'left'\n }\n\n const Default = {\n animation : true,\n template : '
' +\n '
' +\n '
',\n trigger : 'hover focus',\n title : '',\n delay : 0,\n html : false,\n selector : false,\n placement : 'top',\n offset : 0,\n container : false,\n fallbackPlacement : 'flip',\n boundary : 'scrollParent'\n }\n\n const HoverState = {\n SHOW : 'show',\n OUT : 'out'\n }\n\n const Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n INSERTED : `inserted${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n FOCUSOUT : `focusout${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`\n }\n\n const ClassName = {\n FADE : 'fade',\n SHOW : 'show'\n }\n\n const Selector = {\n TOOLTIP : '.tooltip',\n TOOLTIP_INNER : '.tooltip-inner',\n ARROW : '.arrow'\n }\n\n const Trigger = {\n HOVER : 'hover',\n FOCUS : 'focus',\n CLICK : 'click',\n MANUAL : 'manual'\n }\n\n\n /**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\n class Tooltip {\n constructor(element, config) {\n /**\n * Check for Popper dependency\n * Popper - https://popper.js.org\n */\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap tooltips require Popper.js (https://popper.js.org)')\n }\n\n // private\n this._isEnabled = true\n this._timeout = 0\n this._hoverState = ''\n this._activeTrigger = {}\n this._popper = null\n\n // Protected\n this.element = element\n this.config = this._getConfig(config)\n this.tip = null\n\n this._setListeners()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get DATA_KEY() {\n return DATA_KEY\n }\n\n static get Event() {\n return Event\n }\n\n static get EVENT_KEY() {\n return EVENT_KEY\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle(event) {\n if (!this._isEnabled) {\n return\n }\n\n if (event) {\n const dataKey = this.constructor.DATA_KEY\n let context = $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n context._activeTrigger.click = !context._activeTrigger.click\n\n if (context._isWithActiveTrigger()) {\n context._enter(null, context)\n } else {\n context._leave(null, context)\n }\n } else {\n if ($(this.getTipElement()).hasClass(ClassName.SHOW)) {\n this._leave(null, this)\n return\n }\n\n this._enter(null, this)\n }\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n $.removeData(this.element, this.constructor.DATA_KEY)\n\n $(this.element).off(this.constructor.EVENT_KEY)\n $(this.element).closest('.modal').off('hide.bs.modal')\n\n if (this.tip) {\n $(this.tip).remove()\n }\n\n this._isEnabled = null\n this._timeout = null\n this._hoverState = null\n this._activeTrigger = null\n if (this._popper !== null) {\n this._popper.destroy()\n }\n\n this._popper = null\n this.element = null\n this.config = null\n this.tip = null\n }\n\n show() {\n if ($(this.element).css('display') === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n const showEvent = $.Event(this.constructor.Event.SHOW)\n if (this.isWithContent() && this._isEnabled) {\n $(this.element).trigger(showEvent)\n\n const isInTheDom = $.contains(\n this.element.ownerDocument.documentElement,\n this.element\n )\n\n if (showEvent.isDefaultPrevented() || !isInTheDom) {\n return\n }\n\n const tip = this.getTipElement()\n const tipId = Util.getUID(this.constructor.NAME)\n\n tip.setAttribute('id', tipId)\n this.element.setAttribute('aria-describedby', tipId)\n\n this.setContent()\n\n if (this.config.animation) {\n $(tip).addClass(ClassName.FADE)\n }\n\n const placement = typeof this.config.placement === 'function'\n ? this.config.placement.call(this, tip, this.element)\n : this.config.placement\n\n const attachment = this._getAttachment(placement)\n this.addAttachmentClass(attachment)\n\n const container = this.config.container === false ? document.body : $(this.config.container)\n\n $(tip).data(this.constructor.DATA_KEY, this)\n\n if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {\n $(tip).appendTo(container)\n }\n\n $(this.element).trigger(this.constructor.Event.INSERTED)\n\n this._popper = new Popper(this.element, tip, {\n placement: attachment,\n modifiers: {\n offset: {\n offset: this.config.offset\n },\n flip: {\n behavior: this.config.fallbackPlacement\n },\n arrow: {\n element: Selector.ARROW\n },\n preventOverflow: {\n boundariesElement: this.config.boundary\n }\n },\n onCreate: (data) => {\n if (data.originalPlacement !== data.placement) {\n this._handlePopperPlacementChange(data)\n }\n },\n onUpdate: (data) => {\n this._handlePopperPlacementChange(data)\n }\n })\n\n $(tip).addClass(ClassName.SHOW)\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n $('body').children().on('mouseover', null, $.noop)\n }\n\n const complete = () => {\n if (this.config.animation) {\n this._fixTransition()\n }\n const prevHoverState = this._hoverState\n this._hoverState = null\n\n $(this.element).trigger(this.constructor.Event.SHOWN)\n\n if (prevHoverState === HoverState.OUT) {\n this._leave(null, this)\n }\n }\n\n if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) {\n $(this.tip)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(Tooltip._TRANSITION_DURATION)\n } else {\n complete()\n }\n }\n }\n\n hide(callback) {\n const tip = this.getTipElement()\n const hideEvent = $.Event(this.constructor.Event.HIDE)\n const complete = () => {\n if (this._hoverState !== HoverState.SHOW && tip.parentNode) {\n tip.parentNode.removeChild(tip)\n }\n\n this._cleanTipClass()\n this.element.removeAttribute('aria-describedby')\n $(this.element).trigger(this.constructor.Event.HIDDEN)\n if (this._popper !== null) {\n this._popper.destroy()\n }\n\n if (callback) {\n callback()\n }\n }\n\n $(this.element).trigger(hideEvent)\n\n if (hideEvent.isDefaultPrevented()) {\n return\n }\n\n $(tip).removeClass(ClassName.SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n $('body').children().off('mouseover', null, $.noop)\n }\n\n this._activeTrigger[Trigger.CLICK] = false\n this._activeTrigger[Trigger.FOCUS] = false\n this._activeTrigger[Trigger.HOVER] = false\n\n if (Util.supportsTransitionEnd() &&\n $(this.tip).hasClass(ClassName.FADE)) {\n $(tip)\n .one(Util.TRANSITION_END, complete)\n .emulateTransitionEnd(TRANSITION_DURATION)\n } else {\n complete()\n }\n\n this._hoverState = ''\n }\n\n update() {\n if (this._popper !== null) {\n this._popper.scheduleUpdate()\n }\n }\n\n // Protected\n\n isWithContent() {\n return Boolean(this.getTitle())\n }\n\n addAttachmentClass(attachment) {\n $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)\n }\n\n getTipElement() {\n this.tip = this.tip || $(this.config.template)[0]\n return this.tip\n }\n\n setContent() {\n const $tip = $(this.getTipElement())\n this.setElementContent($tip.find(Selector.TOOLTIP_INNER), this.getTitle())\n $tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)\n }\n\n setElementContent($element, content) {\n const html = this.config.html\n if (typeof content === 'object' && (content.nodeType || content.jquery)) {\n // Content is a DOM node or a jQuery\n if (html) {\n if (!$(content).parent().is($element)) {\n $element.empty().append(content)\n }\n } else {\n $element.text($(content).text())\n }\n } else {\n $element[html ? 'html' : 'text'](content)\n }\n }\n\n getTitle() {\n let title = this.element.getAttribute('data-original-title')\n\n if (!title) {\n title = typeof this.config.title === 'function'\n ? this.config.title.call(this.element)\n : this.config.title\n }\n\n return title\n }\n\n // Private\n\n _getAttachment(placement) {\n return AttachmentMap[placement.toUpperCase()]\n }\n\n _setListeners() {\n const triggers = this.config.trigger.split(' ')\n\n triggers.forEach((trigger) => {\n if (trigger === 'click') {\n $(this.element).on(\n this.constructor.Event.CLICK,\n this.config.selector,\n (event) => this.toggle(event)\n )\n } else if (trigger !== Trigger.MANUAL) {\n const eventIn = trigger === Trigger.HOVER\n ? this.constructor.Event.MOUSEENTER\n : this.constructor.Event.FOCUSIN\n const eventOut = trigger === Trigger.HOVER\n ? this.constructor.Event.MOUSELEAVE\n : this.constructor.Event.FOCUSOUT\n\n $(this.element)\n .on(\n eventIn,\n this.config.selector,\n (event) => this._enter(event)\n )\n .on(\n eventOut,\n this.config.selector,\n (event) => this._leave(event)\n )\n }\n\n $(this.element).closest('.modal').on(\n 'hide.bs.modal',\n () => this.hide()\n )\n })\n\n if (this.config.selector) {\n this.config = {\n ...this.config,\n trigger: 'manual',\n selector: ''\n }\n } else {\n this._fixTitle()\n }\n }\n\n _fixTitle() {\n const titleType = typeof this.element.getAttribute('data-original-title')\n if (this.element.getAttribute('title') ||\n titleType !== 'string') {\n this.element.setAttribute(\n 'data-original-title',\n this.element.getAttribute('title') || ''\n )\n this.element.setAttribute('title', '')\n }\n }\n\n _enter(event, context) {\n const dataKey = this.constructor.DATA_KEY\n\n context = context || $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER\n ] = true\n }\n\n if ($(context.getTipElement()).hasClass(ClassName.SHOW) ||\n context._hoverState === HoverState.SHOW) {\n context._hoverState = HoverState.SHOW\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HoverState.SHOW\n\n if (!context.config.delay || !context.config.delay.show) {\n context.show()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HoverState.SHOW) {\n context.show()\n }\n }, context.config.delay.show)\n }\n\n _leave(event, context) {\n const dataKey = this.constructor.DATA_KEY\n\n context = context || $(event.currentTarget).data(dataKey)\n\n if (!context) {\n context = new this.constructor(\n event.currentTarget,\n this._getDelegateConfig()\n )\n $(event.currentTarget).data(dataKey, context)\n }\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER\n ] = false\n }\n\n if (context._isWithActiveTrigger()) {\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HoverState.OUT\n\n if (!context.config.delay || !context.config.delay.hide) {\n context.hide()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HoverState.OUT) {\n context.hide()\n }\n }, context.config.delay.hide)\n }\n\n _isWithActiveTrigger() {\n for (const trigger in this._activeTrigger) {\n if (this._activeTrigger[trigger]) {\n return true\n }\n }\n\n return false\n }\n\n _getConfig(config) {\n config = {\n ...this.constructor.Default,\n ...$(this.element).data(),\n ...config\n }\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n Util.typeCheckConfig(\n NAME,\n config,\n this.constructor.DefaultType\n )\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n if (this.config) {\n for (const key in this.config) {\n if (this.constructor.Default[key] !== this.config[key]) {\n config[key] = this.config[key]\n }\n }\n }\n\n return config\n }\n\n _cleanTipClass() {\n const $tip = $(this.getTipElement())\n const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)\n if (tabClass !== null && tabClass.length > 0) {\n $tip.removeClass(tabClass.join(''))\n }\n }\n\n _handlePopperPlacementChange(data) {\n this._cleanTipClass()\n this.addAttachmentClass(this._getAttachment(data.placement))\n }\n\n _fixTransition() {\n const tip = this.getTipElement()\n const initConfigAnimation = this.config.animation\n if (tip.getAttribute('x-placement') !== null) {\n return\n }\n $(tip).removeClass(ClassName.FADE)\n this.config.animation = false\n this.hide()\n this.show()\n this.config.animation = initConfigAnimation\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' && config\n\n if (!data && /dispose|hide/.test(config)) {\n return\n }\n\n if (!data) {\n data = new Tooltip(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n }\n\n /**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n $.fn[NAME] = Tooltip._jQueryInterface\n $.fn[NAME].Constructor = Tooltip\n $.fn[NAME].noConflict = function () {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Tooltip._jQueryInterface\n }\n\n return Tooltip\n})($, Popper)\n\nexport default Tooltip\n","import $ from 'jquery'\nimport Tooltip from './tooltip'\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.0.0): popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst Popover = (($) => {\n /**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\n const NAME = 'popover'\n const VERSION = '4.0.0'\n const DATA_KEY = 'bs.popover'\n const EVENT_KEY = `.${DATA_KEY}`\n const JQUERY_NO_CONFLICT = $.fn[NAME]\n const CLASS_PREFIX = 'bs-popover'\n const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\\\s)${CLASS_PREFIX}\\\\S+`, 'g')\n\n const Default = {\n ...Tooltip.Default,\n placement : 'right',\n trigger : 'click',\n content : '',\n template : '
' +\n '
' +\n '

' +\n '
'\n }\n\n const DefaultType = {\n ...Tooltip.DefaultType,\n content : '(string|element|function)'\n }\n\n const ClassName = {\n FADE : 'fade',\n SHOW : 'show'\n }\n\n const Selector = {\n TITLE : '.popover-header',\n CONTENT : '.popover-body'\n }\n\n const Event = {\n HIDE : `hide${EVENT_KEY}`,\n HIDDEN : `hidden${EVENT_KEY}`,\n SHOW : `show${EVENT_KEY}`,\n SHOWN : `shown${EVENT_KEY}`,\n INSERTED : `inserted${EVENT_KEY}`,\n CLICK : `click${EVENT_KEY}`,\n FOCUSIN : `focusin${EVENT_KEY}`,\n FOCUSOUT : `focusout${EVENT_KEY}`,\n MOUSEENTER : `mouseenter${EVENT_KEY}`,\n MOUSELEAVE : `mouseleave${EVENT_KEY}`\n }\n\n /**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\n class Popover extends Tooltip {\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get DATA_KEY() {\n return DATA_KEY\n }\n\n static get Event() {\n return Event\n }\n\n static get EVENT_KEY() {\n return EVENT_KEY\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Overrides\n\n isWithContent() {\n return this.getTitle() || this._getContent()\n }\n\n addAttachmentClass(attachment) {\n $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)\n }\n\n getTipElement() {\n this.tip = this.tip || $(this.config.template)[0]\n return this.tip\n }\n\n setContent() {\n const $tip = $(this.getTipElement())\n\n // We use append for html objects to maintain js events\n this.setElementContent($tip.find(Selector.TITLE), this.getTitle())\n let content = this._getContent()\n if (typeof content === 'function') {\n content = content.call(this.element)\n }\n this.setElementContent($tip.find(Selector.CONTENT), content)\n\n $tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)\n }\n\n // Private\n\n _getContent() {\n return this.element.getAttribute('data-content') ||\n this.config.content\n }\n\n _cleanTipClass() {\n const $tip = $(this.getTipElement())\n const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)\n if (tabClass !== null && tabClass.length > 0) {\n $tip.removeClass(tabClass.join(''))\n }\n }\n\n // Static\n\n static _jQueryInterface(config) {\n return this.each(function () {\n let data = $(this).data(DATA_KEY)\n const _config = typeof config === 'object' ? config : null\n\n if (!data && /destroy|hide/.test(config)) {\n return\n }\n\n if (!data) {\n data = new Popover(this, _config)\n $(this).data(DATA_KEY, data)\n }\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n data[config]()\n }\n })\n }\n }\n\n /**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\n $.fn[NAME] = Popover._jQueryInterface\n $.fn[NAME].Constructor = Popover\n $.fn[NAME].noConflict = function () {\n $.fn[NAME] = JQUERY_NO_CONFLICT\n return Popover._jQueryInterface\n }\n\n return Popover\n})($)\n\nexport default Popover\n","import $ from 'jquery'\nimport Util from './util'\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.0.0): scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst ScrollSpy = (($) => {\n /**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\n const NAME = 'scrollspy'\n const VERSION = '4.0.0'\n const DATA_KEY = 'bs.scrollspy'\n const EVENT_KEY = `.${DATA_KEY}`\n const DATA_API_KEY = '.data-api'\n const JQUERY_NO_CONFLICT = $.fn[NAME]\n\n const Default = {\n offset : 10,\n method : 'auto',\n target : ''\n }\n\n const DefaultType = {\n offset : 'number',\n method : 'string',\n target : '(string|element)'\n }\n\n const Event = {\n ACTIVATE : `activate${EVENT_KEY}`,\n SCROLL : `scroll${EVENT_KEY}`,\n LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`\n }\n\n const ClassName = {\n DROPDOWN_ITEM : 'dropdown-item',\n DROPDOWN_MENU : 'dropdown-menu',\n ACTIVE : 'active'\n }\n\n const Selector = {\n DATA_SPY : '[data-spy=\"scroll\"]',\n ACTIVE : '.active',\n NAV_LIST_GROUP : '.nav, .list-group',\n NAV_LINKS : '.nav-link',\n NAV_ITEMS : '.nav-item',\n LIST_ITEMS : '.list-group-item',\n DROPDOWN : '.dropdown',\n DROPDOWN_ITEMS : '.dropdown-item',\n DROPDOWN_TOGGLE : '.dropdown-toggle'\n }\n\n const OffsetMethod = {\n OFFSET : 'offset',\n POSITION : 'position'\n }\n\n /**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\n class ScrollSpy {\n constructor(element, config) {\n this._element = element\n this._scrollElement = element.tagName === 'BODY' ? window : element\n this._config = this._getConfig(config)\n this._selector = `${this._config.target} ${Selector.NAV_LINKS},` +\n `${this._config.target} ${Selector.LIST_ITEMS},` +\n `${this._config.target} ${Selector.DROPDOWN_ITEMS}`\n this._offsets = []\n this._targets = []\n this._activeTarget = null\n this._scrollHeight = 0\n\n $(this._scrollElement).on(Event.SCROLL, (event) => this._process(event))\n\n this.refresh()\n this._process()\n }\n\n // Getters\n\n static get VERSION() {\n return VERSION\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n refresh() {\n const autoMethod = this._scrollElement === this._scrollElement.window\n ? OffsetMethod.OFFSET : OffsetMethod.POSITION\n\n const offsetMethod = this._config.method === 'auto'\n ? autoMethod : this._config.method\n\n const offsetBase = offsetMethod === OffsetMethod.POSITION\n ? this._getScrollTop() : 0\n\n this._offsets = []\n this._targets = []\n\n this._scrollHeight = this._getScrollHeight()\n\n const targets = $.makeArray($(this._selector))\n\n targets\n .map((element) => {\n let target\n const targetSelector = Util.getSelectorFromElement(element)\n\n if (targetSelector) {\n target = $(targetSelector)[0]\n }\n\n if (target) {\n const targetBCR = target.getBoundingClientRect()\n if (targetBCR.width || targetBCR.height) {\n // TODO (fat): remove sketch reliance on jQuery position/offset\n return [\n $(target)[offsetMethod]().top + offsetBase,\n targetSelector\n ]\n }\n }\n return null\n })\n .filter((item) => item)\n .sort((a, b) => a[0] - b[0])\n .forEach((item) => {\n this._offsets.push(item[0])\n this._targets.push(item[1])\n })\n }\n\n dispose() {\n $.removeData(this._element, DATA_KEY)\n $(this._scrollElement).off(EVENT_KEY)\n\n this._element = null\n this._scrollElement = null\n this._config = null\n this._selector = null\n this._offsets = null\n this._targets = null\n this._activeTarget = null\n this._scrollHeight = null\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...config\n }\n\n if (typeof config.target !== 'string') {\n let id = $(config.target).attr('id')\n if (!id) {\n id = Util.getUID(NAME)\n $(config.target).attr('id', id)\n }\n config.target = `#${id}`\n }\n\n Util.typeCheckConfig(NAME, config, DefaultType)\n\n return config\n }\n\n _getScrollTop() {\n return this._scrollElement === window\n ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop\n }\n\n _getScrollHeight() {\n return this._scrollElement.scrollHeight || Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n )\n }\n\n _getOffsetHeight() {\n return this._scrollElement === window\n ? window.innerHeight : this._scrollElement.getBoundingClientRect().height\n }\n\n _process() {\n const scrollTop = this._getScrollTop() + this._config.offset\n const scrollHeight = this._getScrollHeight()\n const maxScroll = this._config.offset +\n scrollHeight -\n this._getOffsetHeight()\n\n if (this._scrollHeight !== scrollHeight) {\n this.refresh()\n }\n\n if (scrollTop >= maxScroll) {\n const target = this._targets[this._targets.length - 1]\n\n if (this._activeTarget !== target) {\n this._activate(target)\n }\n return\n }\n\n if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {\n this._activeTarget = null\n this._clear()\n return\n }\n\n for (let i = this._offsets.length; i--;) {\n const isActiveTarget = this._activeTarget !== this._targets[i] &&\n scrollTop >= this._offsets[i] &&\n (typeof this._offsets[i + 1] === 'undefined' ||\n scrollTop < this._offsets[i + 1])\n\n if (isActiveTarget) {\n this._activate(this._targets[i])\n }\n }\n }\n\n _activate(target) {\n this._activeTarget = target\n\n this._clear()\n\n let queries = this._selector.split(',')\n // eslint-disable-next-line arrow-body-style\n queries = queries.map((selector) => {\n return `${selector}[data-target=\"${target}\"],` +\n `${selector}[href=\"${target}\"]`\n })\n\n const $link = $(queries.join(','))\n\n if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {\n $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)\n $link.addClass(ClassName.ACTIVE)\n } else {\n // Set triggered link as active\n $link.addClass(ClassName.ACTIVE)\n // Set triggered links parents as active\n // With both
- + diff --git a/hwe/a_hallOfFame.php b/hwe/a_hallOfFame.php index d41a2f38..3a94afd0 100644 --- a/hwe/a_hallOfFame.php +++ b/hwe/a_hallOfFame.php @@ -44,6 +44,7 @@ else{ + diff --git a/hwe/a_npcList.php b/hwe/a_npcList.php index a56f606f..9544d96c 100644 --- a/hwe/a_npcList.php +++ b/hwe/a_npcList.php @@ -29,9 +29,14 @@ $sel[$type] = "selected"; <?=UniqueConst::$serverName?>: 빙의일람 + + + + + @@ -107,7 +112,7 @@ for ($j=0; $j < $gencount; $j++) { - + diff --git a/hwe/b_genList.php b/hwe/b_genList.php index 9704c5fb..7250156e 100644 --- a/hwe/b_genList.php +++ b/hwe/b_genList.php @@ -59,6 +59,7 @@ $sel[$type] = "selected"; + diff --git a/hwe/b_myGenInfo.php b/hwe/b_myGenInfo.php index e9075182..6b54c914 100644 --- a/hwe/b_myGenInfo.php +++ b/hwe/b_myGenInfo.php @@ -45,6 +45,7 @@ $sel = [$type => "selected"]; + @@ -185,7 +186,7 @@ for($j=0; $j < $gencount; $j++) { - + "; diff --git a/hwe/func_converter.php b/hwe/func_converter.php index 3886a0c6..b4a9678f 100644 --- a/hwe/func_converter.php +++ b/hwe/func_converter.php @@ -214,7 +214,7 @@ function getSpecialInfo(?int $type):?string{ 60 => ['돌격', '[전투] 상대 회피 불가, 공격 시 전투 페이즈 +1, 공격 시 대미지 +10%'], 61 => ['무쌍', '[전투] 대미지 +10%, 공격 시 필살 확률 +10%p'], - 62 => ['견고', '[전투] 수비 시 아군 피해 -10%, 공격 시 대미지 +10%'], + 62 => ['견고', '[전투] 수비 시 대미지 +10%, 공격 시 아군 피해 -10%'], 63 => ['위압', '[전투] 훈련/사기≥90, 병력≥1,000 일 때 첫 페이즈 위압 발동(적 공격 불가)'], 70 => ['저격', '[전투] 전투 개시 시 1/3 확률로 저격 발동'], diff --git a/hwe/js/common.js b/hwe/js/common.js index 0ac528d6..7ee16d19 100644 --- a/hwe/js/common.js +++ b/hwe/js/common.js @@ -128,4 +128,13 @@ function getIconPath(imgsvr, picture){ else{ return pathConfig.root+'/d_pic/'+picture; } -} \ No newline at end of file +} + +jQuery(function($){ + $('.obj_tooltip').tooltip({ + title:function(){ + return $(this).find('.tooltiptext').html(); + }, + html:true + }); +}); diff --git a/hwe/js/hallOfFame.js b/hwe/js/hallOfFame.js index 672d401e..ad475cfd 100644 --- a/hwe/js/hallOfFame.js +++ b/hwe/js/hallOfFame.js @@ -4,11 +4,4 @@ jQuery(function($){ $.redirect('a_hallOfFame.php', {scenarioIdx:scenarioIdx}, 'get'); }) //$.redirect("processing.php",{ commandtype: commandtype, turn: turn}, 'post'); - - $('.obj_tooltip').tooltip({ - title:function(){ - return $(this).find('.tooltiptext').html(); - }, - html:true - }) }); \ No newline at end of file diff --git a/hwe/js/main.js b/hwe/js/main.js index 2d984267..ed502349 100644 --- a/hwe/js/main.js +++ b/hwe/js/main.js @@ -82,11 +82,4 @@ jQuery(function($){ $('#mainBtnSubmit').click(function(){ }); - - $('.obj_tooltip').tooltip({ - title:function(){ - return $(this).find('.tooltiptext').html(); - }, - html:true - }) }); \ No newline at end of file diff --git a/hwe/templates/tooltip.php b/hwe/templates/tooltip.php index 9120421f..6ecbc2ed 100644 --- a/hwe/templates/tooltip.php +++ b/hwe/templates/tooltip.php @@ -2,4 +2,4 @@ - \ No newline at end of file + \ No newline at end of file -- 2.54.0 From 53c61fe2d0e02a7753500921b5028206bc19d35d Mon Sep 17 00:00:00 2001 From: hide_d Date: Wed, 11 Jul 2018 03:27:27 +0900 Subject: [PATCH 293/304] =?UTF-8?q?=EB=B9=99=EC=9D=98=20=EC=8B=9C=EC=97=90?= =?UTF-8?q?=EB=8F=84=20=EC=A0=84=ED=8A=B9=20=ED=91=9C=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/js/select_npc.js | 36 +++++++++++++++++++++++++++++++++++- hwe/select_npc.php | 24 ++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/hwe/js/select_npc.js b/hwe/js/select_npc.js index b41713ff..4393193d 100644 --- a/hwe/js/select_npc.js +++ b/hwe/js/select_npc.js @@ -3,11 +3,19 @@ var templateGeneralCard =

<%name%>

\

\

<%leader%> / <%power%> / <%intel%>
\ - <%special%> / <%special2%>

\ + <%specialText%> / <%special2Text%>

\ \ \ '; +var templateSpecial = +'<%text%>\ + \ + <%info%>\ + \ +\ +'; + function pickGeneral(){ $btn = $(this); @@ -85,12 +93,38 @@ function printGenerals(value){ $.each(pick, function(idx, cardData){ cardData.iconPath = getIconPath(cardData.imgsvr, cardData.picture); + if(cardData.special in specialInfo){ + cardData.specialText = TemplateEngine(templateSpecial, { + text:cardData.special, + info:specialInfo[cardData.special] + }); + } + else{ + cardData.specialText = special; + } + + if(cardData.special2 in specialInfo){ + cardData.special2Text = TemplateEngine(templateSpecial, { + text:cardData.special2, + info:specialInfo[cardData.special2] + }); + } + else{ + cardData.special2Text = cardData.special2; + } + var $card = $(TemplateEngine(templateGeneralCard, cardData)); console.log($card); $('.card_holder').append($card); $card.find('.select_btn').click(pickGeneral); + $card.find('.obj_tooltip').tooltip({ + title:function(){ + return $(this).find('.tooltiptext').html(); + }, + html:true + }); }); updatePickMoreTimer(); diff --git a/hwe/select_npc.php b/hwe/select_npc.php index 442fc840..cabafc1a 100644 --- a/hwe/select_npc.php +++ b/hwe/select_npc.php @@ -27,15 +27,35 @@ $nationList = $db->query('SELECT nation,`name`,color,scout,scoutmsg FROM nation - + + + - + + -- 2.54.0 From 791e8b02c364d401816b7a2886c80af7560e5e5a Mon Sep 17 00:00:00 2001 From: hide_d Date: Wed, 11 Jul 2018 17:03:47 +0900 Subject: [PATCH 294/304] =?UTF-8?q?=ED=8A=B9=EA=B8=B0=EA=B0=80=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EA=B2=BD=EC=9A=B0=EC=97=90=EB=8F=84=20tooltip=20?= =?UTF-8?q?=EB=9D=84=EC=9A=B0=EB=8F=84=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/select_npc.php | 1 + 1 file changed, 1 insertion(+) diff --git a/hwe/select_npc.php b/hwe/select_npc.php index cabafc1a..979bbf62 100644 --- a/hwe/select_npc.php +++ b/hwe/select_npc.php @@ -47,6 +47,7 @@ foreach (SpecialityConst::WAR as $id=>$values) { $text = getSpecialInfo($id); $specialAll[$name] = $text; } +$specialAll['-'] = '없음'; echo Json::encode($specialAll); ?> ; -- 2.54.0 From d165dbdca476b3cb43e8ce15873c94e531a555d5 Mon Sep 17 00:00:00 2001 From: hide_d Date: Wed, 11 Jul 2018 20:51:13 +0900 Subject: [PATCH 295/304] =?UTF-8?q?=ED=8A=B9=EA=B8=B0=20=EC=97=86=EC=9D=84?= =?UTF-8?q?=20=EB=95=8C=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/js/select_npc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hwe/js/select_npc.js b/hwe/js/select_npc.js index 4393193d..0aaab52f 100644 --- a/hwe/js/select_npc.js +++ b/hwe/js/select_npc.js @@ -100,7 +100,7 @@ function printGenerals(value){ }); } else{ - cardData.specialText = special; + cardData.specialText = cardData.special; } if(cardData.special2 in specialInfo){ -- 2.54.0 From 76786882ff0b0afc7780559932701e4e3bb039ef Mon Sep 17 00:00:00 2001 From: hide_d Date: Wed, 11 Jul 2018 20:52:46 +0900 Subject: [PATCH 296/304] =?UTF-8?q?tooltip=20html=20trim=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/js/common.js | 2 +- hwe/js/select_npc.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hwe/js/common.js b/hwe/js/common.js index 7ee16d19..b529c5cd 100644 --- a/hwe/js/common.js +++ b/hwe/js/common.js @@ -133,7 +133,7 @@ function getIconPath(imgsvr, picture){ jQuery(function($){ $('.obj_tooltip').tooltip({ title:function(){ - return $(this).find('.tooltiptext').html(); + return $.trim($(this).find('.tooltiptext').html()); }, html:true }); diff --git a/hwe/js/select_npc.js b/hwe/js/select_npc.js index 0aaab52f..773e9bf8 100644 --- a/hwe/js/select_npc.js +++ b/hwe/js/select_npc.js @@ -121,7 +121,7 @@ function printGenerals(value){ $card.find('.select_btn').click(pickGeneral); $card.find('.obj_tooltip').tooltip({ title:function(){ - return $(this).find('.tooltiptext').html(); + return $.trim($(this).find('.tooltiptext').html()); }, html:true }); -- 2.54.0 From 359da4026f06f50bfbdd9dbdd121ff5f5c5d9f16 Mon Sep 17 00:00:00 2001 From: hide_d Date: Wed, 11 Jul 2018 21:50:01 +0900 Subject: [PATCH 297/304] =?UTF-8?q?=EC=9E=A5=EC=88=98=20=EC=84=B1=EA=B2=A9?= =?UTF-8?q?,=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EC=84=A4=EB=AA=85=20?= =?UTF-8?q?=ED=88=B4=ED=8C=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hwe/a_bestGeneral.php | 2 +- hwe/a_genList.php | 2 +- hwe/a_npcList.php | 2 +- hwe/b_myGenInfo.php | 2 +- hwe/func.php | 4 +-- hwe/func_converter.php | 63 ++++++++++++++++++++++++++++++++++++++++-- hwe/func_template.php | 25 +++++++++++++++++ hwe/join.php | 17 ++++++++++-- hwe/js/join.js | 15 +++++++++- 9 files changed, 120 insertions(+), 12 deletions(-) diff --git a/hwe/a_bestGeneral.php b/hwe/a_bestGeneral.php index 47941618..c28d1b26 100644 --- a/hwe/a_bestGeneral.php +++ b/hwe/a_bestGeneral.php @@ -218,7 +218,7 @@ $itemTypes = [ ["명 마", 'horse', function($v){return getHorseName($v);}, 7, 26, []], ["명 검", 'weap', function($v){return getWeapName($v);}, 7, 26, []], ["명 서", 'book', function($v){return getBookName($v);}, 7, 26, []], - ["도 구", 'item', function($v){return getItemName($v);}, 7, 26, []], + ["도 구", 'item', function($v){return displayItemInfo($v);}, 7, 26, []], ]; $simpleItemTypes = array_map(function($itemType){return $itemType[1];}, $itemTypes); diff --git a/hwe/a_genList.php b/hwe/a_genList.php index 810c7958..1e89d1e9 100644 --- a/hwe/a_genList.php +++ b/hwe/a_genList.php @@ -170,7 +170,7 @@ for ($j=0; $j < $gencount; $j++) {
- + diff --git a/hwe/a_npcList.php b/hwe/a_npcList.php index 9544d96c..b9d3645f 100644 --- a/hwe/a_npcList.php +++ b/hwe/a_npcList.php @@ -111,7 +111,7 @@ for ($j=0; $j < $gencount; $j++) { - + diff --git a/hwe/b_myGenInfo.php b/hwe/b_myGenInfo.php index 6b54c914..0cd3bcc3 100644 --- a/hwe/b_myGenInfo.php +++ b/hwe/b_myGenInfo.php @@ -185,7 +185,7 @@ for($j=0; $j < $gencount; $j++) { - + diff --git a/hwe/func.php b/hwe/func.php index 4fe0e266..dc9b830f 100644 --- a/hwe/func.php +++ b/hwe/func.php @@ -821,7 +821,7 @@ function generalInfo($no) { $weapname = getWeapName($general['weap']); $bookname = getBookName($general['book']); $horsename = getHorseName($general['horse']); - $itemname = getItemName($general['item']); + $itemname = displayItemInfo($general['item']); if($general['injury'] > 0) { $leader = intdiv($general['leader'] * (100 - $general['injury']), 100); $power = intdiv($general['power'] * (100 - $general['injury']), 100); @@ -913,7 +913,7 @@ function generalInfo($no) { - + diff --git a/hwe/func_converter.php b/hwe/func_converter.php index b4a9678f..7f348b03 100644 --- a/hwe/func_converter.php +++ b/hwe/func_converter.php @@ -135,6 +135,28 @@ function getGenChar($type) { return $call; } +function getCharInfo(?int $type):?string { + if($type === null){ + return null; + } + + $infoText = [ + 10=>['은둔', '명성 -10%, 계급 -10%, 사기 -5, 훈련 -5, 단련 성공률 +10%'], + 9=>['안전', '사기 -5, 징·모병 비용 -20%'], + 8=>['유지', '훈련 -5, 징·모병 비용 -20%'], + 7=>['재간', '명성 -10%, 징·모병 비용 -20%'], + 6=>['출세', '명성 +10%, 징·모병 비용 +20%'], + 5=>['할거', '명성 -10%, 훈련 +5'], + 4=>['정복', '명성 -10%, 사기 +5'], + 3=>['패권', '훈련 +5, 징·모병 비용 +20%'], + 2=>['의협', '사기 +5, 징·모병 비용 +20%'], + 1=>['대의', '명성 +10%, 훈련 -5'], + 0=>['왕좌', '명성 +10%, 사기 -5'], + ]; + + return $infoText[$type][1]??null; +} + function getGenSpecial($type) { switch($type) { case 0: $call = '-'; break; @@ -198,10 +220,10 @@ function getSpecialInfo(?int $type):?string{ 20 => ['인덕', '[내정] 주민 선정·정착 장려 : 기본 보정 +10%, 성공률 +10%p, 비용 -20%'], 30 => ['거상', '이것 저것'], - 31 => ['귀모', '[내정] 화계·탈취·파괴·선동 : 성공률 +20%p'], + 31 => ['귀모', '[계략] 화계·탈취·파괴·선동 : 성공률 +20%p'], 40 => ['귀병', '[군사] 귀병 계통 징·모병비 -10%
[전투] 계략 시도 확률 +20%p'], - 41 => ['신산', '[내정] 화계·탈취·파괴·선동 : 성공률 +10%p
[전투] 계략 시도 확률 +20%p, 계략 성공 확률 +20%p '], + 41 => ['신산', '[계략] 화계·탈취·파괴·선동 : 성공률 +10%p
[전투] 계략 시도 확률 +20%p, 계략 성공 확률 +20%p '], 42 => ['환술', '[전투] 계략 시도 확률 +10%p, 계략 성공 시 대미지 +30%'], 43 => ['집중', '[전투] 계략 성공 시 대미지 +50%'], 44 => ['신중', '[전투] 계략 성공 확률 100%'], @@ -776,7 +798,7 @@ function getHorseEff($horse) : int { return $horse; } -function getItemName($item) : string { +function getItemName($item) : ?string { switch($item) { case 0: $itemname = '-'; break; case 1: $itemname = '환약(치료)'; break; @@ -806,10 +828,45 @@ function getItemName($item) : string { case 24: $itemname = '태평청령(의술)'; break; case 25: $itemname = '태평요술(회피)'; break; case 26: $itemname = '둔갑천서(회피)'; break; + default: $itemname = null; } return $itemname; } +function getItemInfo(?int $item):?string{ + $itemInfo = [ + 1=>['환약(치료)', '[군사] 턴 실행 전 부상 회복. 1회용'], + 2=>['수극(저격)', '[전투] 전투 개시 전 20% 확률로 저격 시도. 1회용'], + 3=>['탁주(사기)', '[전투] 사기 보정 +3. 1회용'], + 4=>['청주(훈련)', '[전투] 훈련 보정 +3. 1회용'], + 5=>['이추(계략)', '[계략] 화계·탈취·파괴·선동 : 성공률 +10%p. 1회용'], + 6=>['향낭(계략)', '[계략] 화계·탈취·파괴·선동 : 성공률 +20%p. 1회용'], + + 7=>['오석산(치료)', '[군사] 턴 실행 전 부상 회복.'], + 8=>['무후행군(치료)', '[군사] 턴 실행 전 부상 회복.'], + 9=>['도소연명(치료)', '[군사] 턴 실행 전 부상 회복.'], + 10=>['칠엽청점(치료)', '[군사] 턴 실행 전 부상 회복.'], + 11=>['정력견혈(치료)', '[군사] 턴 실행 전 부상 회복.'], + 12=>['과실주(훈련)', '[전투] 훈련 보정 +5'], + 13=>['이강주(훈련)', '[전투] 훈련 보정 +5'], + 14=>['의적주(사기)', '[전투] 사기 보정 +5'], + 15=>['두강주(사기)', '[전투] 사기 보정 +5'], + 16=>['보령압주(사기)', '[전투] 사기 보정 +5'], + 17=>['철벽서(훈련)', '[전투] 훈련 보정 +7'], + 18=>['단결도(훈련)', '[전투] 훈련 보정 +7'], + 19=>['춘화첩(사기)', '[전투] 사기 보정 +7'], + 20=>['초선화(사기)', '[전투] 사기 보정 +7'], + 21=>['육도(계략)', '[계략] 화계·탈취·파괴·선동 : 성공률 +20%p'], + 22=>['삼략(계략)', '[계략] 화계·탈취·파괴·선동 : 성공률 +20%p'], + 23=>['청낭서(의술)', '[군사] 매 턴마다 자신(100%)과 소속 도시 장수(적 포함 50%) 부상 회복
[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)'], + 24=>['태평청령(의술)', '[군사] 매 턴마다 자신(100%)과 소속 도시 장수(적 포함 50%) 부상 회복
[전투] 페이즈마다 20% 확률로 치료 발동(아군 피해 1/3 감소)'], + 25=>['태평요술(회피)', '[전투] 회피 확률 +20%p'], + 26=>['둔갑천서(회피)', '[전투] 회피 확률 +20%p'], + ]; + + return $itemInfo[$item][1]??null; +} + function getItemCost2($weap) : int { switch($weap) { case 0: $weapcost = 0; break; diff --git a/hwe/func_template.php b/hwe/func_template.php index 9081d141..70eb11c7 100644 --- a/hwe/func_template.php +++ b/hwe/func_template.php @@ -64,6 +64,18 @@ function CoreTurnTable() { "; } +function displayCharInfo(?int $type):string{ + $info = getCharInfo($type); + $text = getGenChar($type); + + $templates = new \League\Plates\Engine(__dir__.'/templates'); + + return $templates->render('tooltip', [ + 'text'=>$text, + 'info'=>$info, + ]); +} + function displaySpecialInfo(?int $type):string{ $info = getSpecialInfo($type); $text = getGenSpecial($type); @@ -76,6 +88,19 @@ function displaySpecialInfo(?int $type):string{ ]); } +function displayItemInfo(?int $type):string{ + $info = getItemInfo($type); + $text = getItemName($type); + + $templates = new \League\Plates\Engine(__dir__.'/templates'); + + return $templates->render('tooltip', [ + 'text'=>$text, + 'info'=>$info, + ]); +} + + function allButton() { $db = DB::db(); $gameStor = KVStorage::getStorage($db, 'game_env'); diff --git a/hwe/join.php b/hwe/join.php index 3022be2f..deb7c57e 100644 --- a/hwe/join.php +++ b/hwe/join.php @@ -38,11 +38,24 @@ $connect=$db->get(); var defaultStatTotal = ; var defaultStatMin = ; var defaultStatMax = ; + +var charInfoText = ; + + + @@ -106,7 +119,7 @@ if ($admin['show_img_level'] >= 1 && $member['grade'] >= 1 && $member['picture']
` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n font-size: $font-size-base;\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Suppress the focus outline on elements that cannot be accessed via keyboard.\n// This prevents an unwanted focus outline from appearing around elements that\n// might still respond to pointer events.\n//\n// Credit: https://github.com/suitcss/base\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Remove the bottom border in Firefox 39-.\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Duplicate behavior to the data-* attribute for our tooltip plugin\n\nabbr[title],\nabbr[data-original-title] { // 4\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 1\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic; // Add the correct font style in Android 4.3-\n}\n\n// stylelint-disable font-weight-notation\nb,\nstrong {\n font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n// stylelint-enable font-weight-notation\n\nsmall {\n font-size: 80%; // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n -webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.\n\n @include hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href)\n// which have not been made explicitly keyboard-focusable (without tabindex).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n\n @include hover-focus {\n color: inherit;\n text-decoration: none;\n }\n\n &:focus {\n outline: 0;\n }\n}\n\n\n//\n// Code\n//\n\n// stylelint-disable font-family-no-duplicate-names\npre,\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; // Correct the inheritance and scaling of font size in all browsers.\n font-size: 1em; // Correct the odd `em` font sizing in all browsers.\n}\n// stylelint-enable font-family-no-duplicate-names\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n // We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so\n // we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n -ms-overflow-style: scrollbar;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg:not(:root) {\n overflow: hidden; // Hide the overflow in IE\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `

` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\nhtml [type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `
`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","/*!\n * Bootstrap v4.1.0 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n:root {\n --blue: #007bff;\n --indigo: #6610f2;\n --purple: #6f42c1;\n --pink: #e83e8c;\n --red: #dc3545;\n --orange: #fd7e14;\n --yellow: #ffc107;\n --green: #28a745;\n --teal: #20c997;\n --cyan: #17a2b8;\n --white: #fff;\n --gray: #6c757d;\n --gray-dark: #343a40;\n --primary: #007bff;\n --secondary: #6c757d;\n --success: #28a745;\n --info: #17a2b8;\n --warning: #ffc107;\n --danger: #dc3545;\n --light: #f8f9fa;\n --dark: #343a40;\n --breakpoint-xs: 0;\n --breakpoint-sm: 576px;\n --breakpoint-md: 768px;\n --breakpoint-lg: 992px;\n --breakpoint-xl: 1200px;\n --font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: transparent;\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: 0.5rem;\n font-family: inherit;\n font-weight: 500;\n line-height: 1.2;\n color: inherit;\n}\n\nh1, .h1 {\n font-size: 2.5rem;\n}\n\nh2, .h2 {\n font-size: 2rem;\n}\n\nh3, .h3 {\n font-size: 1.75rem;\n}\n\nh4, .h4 {\n font-size: 1.5rem;\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: 6rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-2 {\n font-size: 5.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-3 {\n font-size: 4.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-4 {\n font-size: 3.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\nhr {\n margin-top: 1rem;\n margin-bottom: 1rem;\n border: 0;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n font-size: 80%;\n font-weight: 400;\n}\n\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%;\n color: #6c757d;\n}\n\n.blockquote-footer::before {\n content: \"\\2014 \\00A0\";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 90%;\n color: #6c757d;\n}\n\ncode,\nkbd,\npre,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\ncode {\n font-size: 87.5%;\n color: #e83e8c;\n word-break: break-word;\n}\n\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 87.5%;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\n\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n}\n\npre {\n display: block;\n font-size: 87.5%;\n color: #212529;\n}\n\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n.row {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n min-height: 1px;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.col-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n}\n\n.col-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n -ms-flex-order: -1;\n order: -1;\n}\n\n.order-last {\n -ms-flex-order: 13;\n order: 13;\n}\n\n.order-0 {\n -ms-flex-order: 0;\n order: 0;\n}\n\n.order-1 {\n -ms-flex-order: 1;\n order: 1;\n}\n\n.order-2 {\n -ms-flex-order: 2;\n order: 2;\n}\n\n.order-3 {\n -ms-flex-order: 3;\n order: 3;\n}\n\n.order-4 {\n -ms-flex-order: 4;\n order: 4;\n}\n\n.order-5 {\n -ms-flex-order: 5;\n order: 5;\n}\n\n.order-6 {\n -ms-flex-order: 6;\n order: 6;\n}\n\n.order-7 {\n -ms-flex-order: 7;\n order: 7;\n}\n\n.order-8 {\n -ms-flex-order: 8;\n order: 8;\n}\n\n.order-9 {\n -ms-flex-order: 9;\n order: 9;\n}\n\n.order-10 {\n -ms-flex-order: 10;\n order: 10;\n}\n\n.order-11 {\n -ms-flex-order: 11;\n order: 11;\n}\n\n.order-12 {\n -ms-flex-order: 12;\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-sm-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-sm-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-sm-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-sm-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-sm-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-sm-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-sm-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-sm-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-sm-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-sm-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-sm-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-sm-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-sm-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-sm-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-sm-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-sm-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-md-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-md-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-md-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-md-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-md-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-md-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-md-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-md-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-md-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-md-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-md-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-md-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-md-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-md-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-md-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-md-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-lg-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-lg-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-lg-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-lg-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-lg-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-lg-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-lg-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-lg-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-lg-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-lg-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-lg-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-lg-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-lg-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-lg-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-lg-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-lg-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-xl-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-xl-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-xl-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-xl-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-xl-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-xl-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-xl-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-xl-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-xl-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-xl-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-xl-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-xl-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-xl-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-xl-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-xl-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-xl-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 1rem;\n background-color: transparent;\n}\n\n.table th,\n.table td {\n padding: 0.75rem;\n vertical-align: top;\n border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n vertical-align: bottom;\n border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n border-top: 2px solid #dee2e6;\n}\n\n.table .table {\n background-color: #fff;\n}\n\n.table-sm th,\n.table-sm td {\n padding: 0.3rem;\n}\n\n.table-bordered {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n background-color: #b8daff;\n}\n\n.table-hover .table-primary:hover {\n background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n background-color: #d6d8db;\n}\n\n.table-hover .table-secondary:hover {\n background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n background-color: #c3e6cb;\n}\n\n.table-hover .table-success:hover {\n background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n background-color: #bee5eb;\n}\n\n.table-hover .table-info:hover {\n background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n background-color: #ffeeba;\n}\n\n.table-hover .table-warning:hover {\n background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n background-color: #f5c6cb;\n}\n\n.table-hover .table-danger:hover {\n background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n background-color: #fdfdfe;\n}\n\n.table-hover .table-light:hover {\n background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n background-color: #c6c8ca;\n}\n\n.table-hover .table-dark:hover {\n background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n color: #fff;\n background-color: #212529;\n border-color: #32383e;\n}\n\n.table .thead-light th {\n color: #495057;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.table-dark {\n color: #fff;\n background-color: #212529;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n border-color: #32383e;\n}\n\n.table-dark.table-bordered {\n border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-sm > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 767.98px) {\n .table-responsive-md {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-md > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-lg > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-xl > .table-bordered {\n border: 0;\n }\n}\n\n.table-responsive {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n}\n\n.table-responsive > .table-bordered {\n border: 0;\n}\n\n.form-control {\n display: block;\n width: 100%;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .form-control {\n transition: none;\n }\n}\n\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n\n.form-control:focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.form-control::-webkit-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::-moz-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:-ms-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::-ms-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n\nselect.form-control:not([size]):not([multiple]) {\n height: calc(2.25rem + 2px);\n}\n\nselect.form-control:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n margin-bottom: 0;\n line-height: 1.5;\n color: #212529;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .input-group-sm > .form-control-plaintext.form-control,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-sm > .input-group-append > .form-control-plaintext.btn, .form-control-plaintext.form-control-lg, .input-group-lg > .form-control-plaintext.form-control,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-lg > .input-group-append > .form-control-plaintext.btn {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm, .input-group-sm > .form-control,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\nselect.form-control-sm:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(1.8125rem + 2px);\n}\n\n.form-control-lg, .input-group-lg > .form-control,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\nselect.form-control-lg:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(2.875rem + 2px);\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-text {\n display: block;\n margin-top: 0.25rem;\n}\n\n.form-row {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n margin-right: -5px;\n margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.form-check {\n position: relative;\n display: block;\n padding-left: 1.25rem;\n}\n\n.form-check-input {\n position: absolute;\n margin-top: 0.3rem;\n margin-left: -1.25rem;\n}\n\n.form-check-input:disabled ~ .form-check-label {\n color: #6c757d;\n}\n\n.form-check-label {\n margin-bottom: 0;\n}\n\n.form-check-inline {\n display: -ms-inline-flexbox;\n display: inline-flex;\n -ms-flex-align: center;\n align-items: center;\n padding-left: 0;\n margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n position: static;\n margin-top: 0;\n margin-right: 0.3125rem;\n margin-left: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #28a745;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(40, 167, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid, .was-validated\n.custom-select:valid,\n.custom-select.is-valid {\n border-color: #28a745;\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated\n.custom-select:valid:focus,\n.custom-select.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-control:valid ~ .valid-feedback,\n.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback,\n.form-control.is-valid ~ .valid-tooltip, .was-validated\n.custom-select:valid ~ .valid-feedback,\n.was-validated\n.custom-select:valid ~ .valid-tooltip,\n.custom-select.is-valid ~ .valid-feedback,\n.custom-select.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n background-color: #71dd8a;\n}\n\n.was-validated .custom-control-input:valid ~ .valid-feedback,\n.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback,\n.custom-control-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label::before, .custom-file-input.is-valid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:valid ~ .valid-feedback,\n.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback,\n.custom-file-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid, .was-validated\n.custom-select:invalid,\n.custom-select.is-invalid {\n border-color: #dc3545;\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated\n.custom-select:invalid:focus,\n.custom-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control:invalid ~ .invalid-feedback,\n.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback,\n.form-control.is-invalid ~ .invalid-tooltip, .was-validated\n.custom-select:invalid ~ .invalid-feedback,\n.was-validated\n.custom-select:invalid ~ .invalid-tooltip,\n.custom-select.is-invalid ~ .invalid-feedback,\n.custom-select.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n background-color: #efa2a9;\n}\n\n.was-validated .custom-control-input:invalid ~ .invalid-feedback,\n.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback,\n.custom-control-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label::before, .custom-file-input.is-invalid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:invalid ~ .invalid-feedback,\n.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback,\n.custom-file-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.form-inline .form-check {\n width: 100%;\n}\n\n@media (min-width: 576px) {\n .form-inline label {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n margin-bottom: 0;\n }\n .form-inline .form-group {\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n -ms-flex-align: center;\n align-items: center;\n margin-bottom: 0;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-plaintext {\n display: inline-block;\n }\n .form-inline .input-group,\n .form-inline .custom-select {\n width: auto;\n }\n .form-inline .form-check {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n width: auto;\n padding-left: 0;\n }\n .form-inline .form-check-input {\n position: relative;\n margin-top: 0;\n margin-right: 0.25rem;\n margin-left: 0;\n }\n .form-inline .custom-control {\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n }\n .form-inline .custom-control-label {\n margin-bottom: 0;\n }\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .btn {\n transition: none;\n }\n}\n\n.btn:hover, .btn:focus {\n text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.btn.disabled, .btn:disabled {\n opacity: 0.65;\n}\n\n.btn:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active {\n background-image: none;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n pointer-events: none;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:hover {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0062cc;\n border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:hover {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #545b62;\n border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-success {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:hover {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #1e7e34;\n border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-info {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:hover {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n color: #fff;\n background-color: #117a8b;\n border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-warning {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:hover {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n color: #212529;\n background-color: #d39e00;\n border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:hover {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #bd2130;\n border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-light {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:hover {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n color: #212529;\n background-color: #dae0e5;\n border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-dark {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:hover {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1d2124;\n border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-primary {\n color: #007bff;\n background-color: transparent;\n background-image: none;\n border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n background-color: transparent;\n background-image: none;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n color: #28a745;\n background-color: transparent;\n background-image: none;\n border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n color: #28a745;\n background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n color: #17a2b8;\n background-color: transparent;\n background-image: none;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n color: #17a2b8;\n background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n color: #ffc107;\n background-color: transparent;\n background-image: none;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n color: #dc3545;\n background-color: transparent;\n background-image: none;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n background-color: transparent;\n background-image: none;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n color: #343a40;\n background-color: transparent;\n background-image: none;\n border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n color: #343a40;\n background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n font-weight: 400;\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-link:hover {\n color: #0056b3;\n text-decoration: underline;\n background-color: transparent;\n border-color: transparent;\n}\n\n.btn-link:focus, .btn-link.focus {\n text-decoration: underline;\n border-color: transparent;\n box-shadow: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n.btn-block + .btn-block {\n margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n\n.fade {\n transition: opacity 0.15s linear;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .fade {\n transition: none;\n }\n}\n\n.fade:not(.show) {\n opacity: 0;\n}\n\n.collapse:not(.show) {\n display: none;\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .collapsing {\n transition: none;\n }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n position: relative;\n}\n\n.dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0.125rem 0 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n\n.dropdown-menu-right {\n right: 0;\n left: auto;\n}\n\n.dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n top: 0;\n right: auto;\n left: 100%;\n margin-top: 0;\n margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n top: 0;\n right: 100%;\n left: auto;\n margin-top: 0;\n margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n display: inline-block;\n width: 0;\n height: 0;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n right: auto;\n bottom: auto;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n color: #16181b;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1.5rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.dropdown-item-text {\n display: block;\n padding: 0.25rem 1.5rem;\n color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: -ms-inline-flexbox;\n display: inline-flex;\n vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n -ms-flex: 0 1 auto;\n flex: 0 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group,\n.btn-group-vertical .btn + .btn,\n.btn-group-vertical .btn + .btn-group,\n.btn-group-vertical .btn-group + .btn,\n.btn-group-vertical .btn-group + .btn-group {\n margin-left: -1px;\n}\n\n.btn-toolbar {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n -ms-flex-direction: column;\n flex-direction: column;\n -ms-flex-align: start;\n align-items: flex-start;\n -ms-flex-pack: center;\n justify-content: center;\n}\n\n.btn-group-vertical .btn,\n.btn-group-vertical .btn-group {\n width: 100%;\n}\n\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n\n.input-group {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: stretch;\n align-items: stretch;\n width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .custom-select,\n.input-group > .custom-file {\n position: relative;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n width: 1%;\n margin-bottom: 0;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file:focus {\n z-index: 3;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n margin-left: -1px;\n}\n\n.input-group > .form-control:not(:last-child),\n.input-group > .custom-select:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label,\n.input-group > .custom-file:not(:first-child) .custom-file-label::after {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n display: -ms-flexbox;\n display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n position: relative;\n z-index: 2;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n margin-left: -1px;\n}\n\n.input-group-prepend {\n margin-right: -1px;\n}\n\n.input-group-append {\n margin-left: -1px;\n}\n\n.input-group-text {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n padding: 0.375rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n margin-top: 0;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group > .input-group-append:not(:last-child) > .btn,\n.input-group > .input-group-append:not(:last-child) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.custom-control {\n position: relative;\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5rem;\n}\n\n.custom-control-inline {\n display: -ms-inline-flexbox;\n display: inline-flex;\n margin-right: 1rem;\n}\n\n.custom-control-input {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n color: #fff;\n background-color: #007bff;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:active ~ .custom-control-label::before {\n color: #fff;\n background-color: #b3d7ff;\n}\n\n.custom-control-input:disabled ~ .custom-control-label {\n color: #6c757d;\n}\n\n.custom-control-input:disabled ~ .custom-control-label::before {\n background-color: #e9ecef;\n}\n\n.custom-control-label {\n margin-bottom: 0;\n}\n\n.custom-control-label::before {\n position: absolute;\n top: 0.25rem;\n left: 0;\n display: block;\n width: 1rem;\n height: 1rem;\n pointer-events: none;\n content: \"\";\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n background-color: #dee2e6;\n}\n\n.custom-control-label::after {\n position: absolute;\n top: 0.25rem;\n left: 0;\n display: block;\n width: 1rem;\n height: 1rem;\n content: \"\";\n background-repeat: no-repeat;\n background-position: center center;\n background-size: 50% 50%;\n}\n\n.custom-checkbox .custom-control-label::before {\n border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n vertical-align: middle;\n background: #fff url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E\") no-repeat right 0.75rem center;\n background-size: 8px 10px;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.custom-select:focus {\n border-color: #80bdff;\n outline: 0;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(128, 189, 255, 0.5);\n}\n\n.custom-select:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n height: auto;\n padding-right: 0.75rem;\n background-image: none;\n}\n\n.custom-select:disabled {\n color: #6c757d;\n background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n opacity: 0;\n}\n\n.custom-select-sm {\n height: calc(1.8125rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 75%;\n}\n\n.custom-select-lg {\n height: calc(2.875rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 125%;\n}\n\n.custom-file {\n position: relative;\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin-bottom: 0;\n}\n\n.custom-file-input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin: 0;\n opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n border-color: #80bdff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-file-input:focus ~ .custom-file-label::after {\n border-color: #80bdff;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n content: \"Browse\";\n}\n\n.custom-file-label {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.custom-file-label::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n z-index: 3;\n display: block;\n height: calc(calc(2.25rem + 2px) - 1px * 2);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n content: \"Browse\";\n background-color: #e9ecef;\n border-left: 1px solid #ced4da;\n border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n width: 100%;\n padding-left: 0;\n background-color: transparent;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.custom-range:focus {\n outline: none;\n}\n\n.custom-range::-moz-focus-outer {\n border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: -0.25rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n -webkit-appearance: none;\n appearance: none;\n}\n\n.custom-range::-webkit-slider-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-webkit-slider-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-moz-range-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n -moz-appearance: none;\n appearance: none;\n}\n\n.custom-range::-moz-range-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-range-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-ms-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-ms-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: transparent;\n border-color: transparent;\n border-width: 0.5rem;\n}\n\n.custom-range::-ms-fill-lower {\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n margin-right: 15px;\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.nav {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n text-decoration: none;\n}\n\n.nav-link.disabled {\n color: #6c757d;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-item {\n margin-bottom: -1px;\n}\n\n.nav-tabs .nav-link {\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #007bff;\n}\n\n.nav-fill .nav-item {\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified .nav-item {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n text-align: center;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: justify;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n}\n\n.navbar > .container,\n.navbar > .container-fluid {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: justify;\n justify-content: space-between;\n}\n\n.navbar-brand {\n display: inline-block;\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n text-decoration: none;\n}\n\n.navbar-nav {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n position: static;\n float: none;\n}\n\n.navbar-text {\n display: inline-block;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n -ms-flex-preferred-size: 100%;\n flex-basis: 100%;\n -ms-flex-positive: 1;\n flex-grow: 1;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n text-decoration: none;\n}\n\n.navbar-toggler:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n content: \"\";\n background: no-repeat center center;\n background-size: 100% 100%;\n}\n\n@media (max-width: 575.98px) {\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-sm .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 767.98px) {\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 768px) {\n .navbar-expand-md {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-md .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 991.98px) {\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 992px) {\n .navbar-expand-lg {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-lg .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 1199.98px) {\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-xl .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n}\n\n.navbar-expand {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n display: none;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.5);\n border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.5);\n border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-text a {\n color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n\n.card > .list-group:first-child .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.card > .list-group:last-child .list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.card-body {\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n padding: 1.25rem;\n}\n\n.card-title {\n margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n margin-top: -0.375rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link:hover {\n text-decoration: none;\n}\n\n.card-link + .card-link {\n margin-left: 1.25rem;\n}\n\n.card-header {\n padding: 0.75rem 1.25rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-header + .list-group .list-group-item:first-child {\n border-top: 0;\n}\n\n.card-footer {\n padding: 0.75rem 1.25rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.625rem;\n margin-bottom: -0.75rem;\n margin-left: -0.625rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.625rem;\n margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1.25rem;\n}\n\n.card-img {\n width: 100%;\n border-radius: calc(0.25rem - 1px);\n}\n\n.card-img-top {\n width: 100%;\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img-bottom {\n width: 100%;\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-deck {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n\n.card-deck .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-deck {\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n margin-right: -15px;\n margin-left: -15px;\n }\n .card-deck .card {\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 1 0 0%;\n flex: 1 0 0%;\n -ms-flex-direction: column;\n flex-direction: column;\n margin-right: 15px;\n margin-bottom: 0;\n margin-left: 15px;\n }\n}\n\n.card-group {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n\n.card-group > .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-group {\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n }\n .card-group > .card {\n -ms-flex: 1 0 0%;\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-top,\n .card-group > .card:first-child .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-bottom,\n .card-group > .card:first-child .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:last-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-top,\n .card-group > .card:last-child .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-bottom,\n .card-group > .card:last-child .card-footer {\n border-bottom-left-radius: 0;\n }\n .card-group > .card:only-child {\n border-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-top,\n .card-group > .card:only-child .card-header {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-bottom,\n .card-group > .card:only-child .card-footer {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) {\n border-radius: 0;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer {\n border-radius: 0;\n }\n}\n\n.card-columns .card {\n margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n .card-columns {\n -webkit-column-count: 3;\n -moz-column-count: 3;\n column-count: 3;\n -webkit-column-gap: 1.25rem;\n -moz-column-gap: 1.25rem;\n column-gap: 1.25rem;\n orphans: 1;\n widows: 1;\n }\n .card-columns .card {\n display: inline-block;\n width: 100%;\n }\n}\n\n.accordion .card:not(:first-of-type):not(:last-of-type) {\n border-bottom: 0;\n border-radius: 0;\n}\n\n.accordion .card:not(:first-of-type) .card-header:first-child {\n border-radius: 0;\n}\n\n.accordion .card:first-of-type {\n border-bottom: 0;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.accordion .card:last-of-type {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.breadcrumb {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n padding: 0.75rem 1rem;\n margin-bottom: 1rem;\n list-style: none;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n display: inline-block;\n padding-right: 0.5rem;\n color: #6c757d;\n content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: none;\n}\n\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: -ms-flexbox;\n display: flex;\n padding-left: 0;\n list-style: none;\n border-radius: 0.25rem;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: 0.5rem 0.75rem;\n margin-left: -1px;\n line-height: 1.25;\n color: #007bff;\n background-color: #fff;\n border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n z-index: 2;\n color: #0056b3;\n text-decoration: none;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.page-link:focus {\n z-index: 2;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-link:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.page-item:first-child .page-link {\n margin-left: 0;\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n z-index: 1;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n cursor: auto;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.25em 0.4em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n}\n\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.badge-pill {\n padding-right: 0.6em;\n padding-left: 0.6em;\n border-radius: 10rem;\n}\n\n.badge-primary {\n color: #fff;\n background-color: #007bff;\n}\n\n.badge-primary[href]:hover, .badge-primary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #0062cc;\n}\n\n.badge-secondary {\n color: #fff;\n background-color: #6c757d;\n}\n\n.badge-secondary[href]:hover, .badge-secondary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #545b62;\n}\n\n.badge-success {\n color: #fff;\n background-color: #28a745;\n}\n\n.badge-success[href]:hover, .badge-success[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1e7e34;\n}\n\n.badge-info {\n color: #fff;\n background-color: #17a2b8;\n}\n\n.badge-info[href]:hover, .badge-info[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #117a8b;\n}\n\n.badge-warning {\n color: #212529;\n background-color: #ffc107;\n}\n\n.badge-warning[href]:hover, .badge-warning[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #d39e00;\n}\n\n.badge-danger {\n color: #fff;\n background-color: #dc3545;\n}\n\n.badge-danger[href]:hover, .badge-danger[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #bd2130;\n}\n\n.badge-light {\n color: #212529;\n background-color: #f8f9fa;\n}\n\n.badge-light[href]:hover, .badge-light[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #dae0e5;\n}\n\n.badge-dark {\n color: #fff;\n background-color: #343a40;\n}\n\n.badge-dark[href]:hover, .badge-dark[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1d2124;\n}\n\n.jumbotron {\n padding: 2rem 1rem;\n margin-bottom: 2rem;\n background-color: #e9ecef;\n border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n .jumbotron {\n padding: 4rem 2rem;\n }\n}\n\n.jumbotron-fluid {\n padding-right: 0;\n padding-left: 0;\n border-radius: 0;\n}\n\n.alert {\n position: relative;\n padding: 0.75rem 1.25rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n position: absolute;\n top: 0;\n right: 0;\n padding: 0.75rem 1.25rem;\n color: inherit;\n}\n\n.alert-primary {\n color: #004085;\n background-color: #cce5ff;\n border-color: #b8daff;\n}\n\n.alert-primary hr {\n border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n color: #002752;\n}\n\n.alert-secondary {\n color: #383d41;\n background-color: #e2e3e5;\n border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n color: #202326;\n}\n\n.alert-success {\n color: #155724;\n background-color: #d4edda;\n border-color: #c3e6cb;\n}\n\n.alert-success hr {\n border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n color: #0b2e13;\n}\n\n.alert-info {\n color: #0c5460;\n background-color: #d1ecf1;\n border-color: #bee5eb;\n}\n\n.alert-info hr {\n border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n color: #062c33;\n}\n\n.alert-warning {\n color: #856404;\n background-color: #fff3cd;\n border-color: #ffeeba;\n}\n\n.alert-warning hr {\n border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n color: #533f03;\n}\n\n.alert-danger {\n color: #721c24;\n background-color: #f8d7da;\n border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n color: #491217;\n}\n\n.alert-light {\n color: #818182;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n\n.alert-light hr {\n border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n color: #686868;\n}\n\n.alert-dark {\n color: #1b1e21;\n background-color: #d6d8d9;\n border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n color: #040505;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n@keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n.progress {\n display: -ms-flexbox;\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n -ms-flex-pack: center;\n justify-content: center;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n background-color: #007bff;\n transition: width 0.6s ease;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .progress-bar {\n transition: none;\n }\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n -webkit-animation: progress-bar-stripes 1s linear infinite;\n animation: progress-bar-stripes 1s linear infinite;\n}\n\n.media {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: start;\n align-items: flex-start;\n}\n\n.media-body {\n -ms-flex: 1;\n flex: 1;\n}\n\n.list-group {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.75rem 1.25rem;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.list-group-item:hover, .list-group-item:focus {\n z-index: 1;\n text-decoration: none;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n background-color: #fff;\n}\n\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.list-group-flush .list-group-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n\n.list-group-flush:first-child .list-group-item:first-child {\n border-top: 0;\n}\n\n.list-group-flush:last-child .list-group-item:last-child {\n border-bottom: 0;\n}\n\n.list-group-item-primary {\n color: #004085;\n background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #004085;\n background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #004085;\n border-color: #004085;\n}\n\n.list-group-item-secondary {\n color: #383d41;\n background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #383d41;\n background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #383d41;\n border-color: #383d41;\n}\n\n.list-group-item-success {\n color: #155724;\n background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #155724;\n background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #155724;\n border-color: #155724;\n}\n\n.list-group-item-info {\n color: #0c5460;\n background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #0c5460;\n background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #0c5460;\n border-color: #0c5460;\n}\n\n.list-group-item-warning {\n color: #856404;\n background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #856404;\n background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #856404;\n border-color: #856404;\n}\n\n.list-group-item-danger {\n color: #721c24;\n background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #721c24;\n background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #721c24;\n border-color: #721c24;\n}\n\n.list-group-item-light {\n color: #818182;\n background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #818182;\n background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #818182;\n border-color: #818182;\n}\n\n.list-group-item-dark {\n color: #1b1e21;\n background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #1b1e21;\n background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #1b1e21;\n border-color: #1b1e21;\n}\n\n.close {\n float: right;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: .5;\n}\n\n.close:hover, .close:focus {\n color: #000;\n text-decoration: none;\n opacity: .75;\n}\n\n.close:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\nbutton.close {\n padding: 0;\n background-color: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n\n.modal-open {\n overflow: hidden;\n}\n\n.modal {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n display: none;\n overflow: hidden;\n outline: 0;\n}\n\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n transition: -webkit-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;\n -webkit-transform: translate(0, -25%);\n transform: translate(0, -25%);\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .modal.fade .modal-dialog {\n transition: none;\n }\n}\n\n.modal.show .modal-dialog {\n -webkit-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n\n.modal-dialog-centered {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n min-height: calc(100% - (0.5rem * 2));\n}\n\n.modal-content {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n\n.modal-backdrop.fade {\n opacity: 0;\n}\n\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: start;\n align-items: flex-start;\n -ms-flex-pack: justify;\n justify-content: space-between;\n padding: 1rem;\n border-bottom: 1px solid #e9ecef;\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n\n.modal-header .close {\n padding: 1rem;\n margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: end;\n justify-content: flex-end;\n padding: 1rem;\n border-top: 1px solid #e9ecef;\n}\n\n.modal-footer > :not(:first-child) {\n margin-left: .25rem;\n}\n\n.modal-footer > :not(:last-child) {\n margin-right: .25rem;\n}\n\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n .modal-dialog-centered {\n min-height: calc(100% - (1.75rem * 2));\n }\n .modal-sm {\n max-width: 300px;\n }\n}\n\n@media (min-width: 992px) {\n .modal-lg {\n max-width: 800px;\n }\n}\n\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n\n.tooltip.show {\n opacity: 0.9;\n}\n\n.tooltip .arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n top: 0;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n right: 0;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n bottom: 0;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n left: 0;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: block;\n max-width: 276px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n\n.popover .arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n margin-bottom: 0.5rem;\n}\n\n.bs-popover-top .arrow, .bs-popover-auto[x-placement^=\"top\"] .arrow {\n bottom: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before,\n.bs-popover-top .arrow::after, .bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0;\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before {\n bottom: 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-top .arrow::after, .bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n bottom: 1px;\n border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n margin-left: 0.5rem;\n}\n\n.bs-popover-right .arrow, .bs-popover-auto[x-placement^=\"right\"] .arrow {\n left: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before,\n.bs-popover-right .arrow::after, .bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0.5rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before {\n left: 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-right .arrow::after, .bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n left: 1px;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n margin-top: 0.5rem;\n}\n\n.bs-popover-bottom .arrow, .bs-popover-auto[x-placement^=\"bottom\"] .arrow {\n top: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before,\n.bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n border-width: 0 0.5rem 0.5rem 0.5rem;\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before {\n top: 0;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n top: 1px;\n border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n margin-right: 0.5rem;\n}\n\n.bs-popover-left .arrow, .bs-popover-auto[x-placement^=\"left\"] .arrow {\n right: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before,\n.bs-popover-left .arrow::after, .bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n border-width: 0.5rem 0 0.5rem 0.5rem;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before {\n right: 0;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-left .arrow::after, .bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n right: 1px;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n color: inherit;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 0.5rem 0.75rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n\n.carousel-item {\n position: relative;\n display: none;\n -ms-flex-align: center;\n align-items: center;\n width: 100%;\n transition: -webkit-transform 0.6s ease;\n transition: transform 0.6s ease;\n transition: transform 0.6s ease, -webkit-transform 0.6s ease;\n -webkit-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n perspective: 1000px;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .carousel-item {\n transition: none;\n }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next,\n.carousel-item-prev {\n position: absolute;\n top: 0;\n}\n\n.carousel-item-next.carousel-item-left,\n.carousel-item-prev.carousel-item-right {\n -webkit-transform: translateX(0);\n transform: translateX(0);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-item-next.carousel-item-left,\n .carousel-item-prev.carousel-item-right {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-item-next,\n.active.carousel-item-right {\n -webkit-transform: translateX(100%);\n transform: translateX(100%);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-item-next,\n .active.carousel-item-right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n }\n}\n\n.carousel-item-prev,\n.active.carousel-item-left {\n -webkit-transform: translateX(-100%);\n transform: translateX(-100%);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-item-prev,\n .active.carousel-item-left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n }\n}\n\n.carousel-fade .carousel-item {\n opacity: 0;\n transition-duration: .6s;\n transition-property: opacity;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n opacity: 0;\n}\n\n.carousel-fade .carousel-item-next,\n.carousel-fade .carousel-item-prev,\n.carousel-fade .carousel-item.active,\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-prev {\n -webkit-transform: translateX(0);\n transform: translateX(0);\n}\n\n@supports ((-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d)) {\n .carousel-fade .carousel-item-next,\n .carousel-fade .carousel-item-prev,\n .carousel-fade .carousel-item.active,\n .carousel-fade .active.carousel-item-left,\n .carousel-fade .active.carousel-item-prev {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n width: 15%;\n color: #fff;\n text-align: center;\n opacity: 0.5;\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: .9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: transparent no-repeat center center;\n background-size: 100% 100%;\n}\n\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 10px;\n left: 0;\n z-index: 15;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-pack: center;\n justify-content: center;\n padding-left: 0;\n margin-right: 15%;\n margin-left: 15%;\n list-style: none;\n}\n\n.carousel-indicators li {\n position: relative;\n -ms-flex: 0 1 auto;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n background-color: rgba(255, 255, 255, 0.5);\n}\n\n.carousel-indicators li::before {\n position: absolute;\n top: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators li::after {\n position: absolute;\n bottom: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators .active {\n background-color: #fff;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.bg-primary {\n background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n background-color: #0062cc !important;\n}\n\n.bg-secondary {\n background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n background-color: #545b62 !important;\n}\n\n.bg-success {\n background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n background-color: #1e7e34 !important;\n}\n\n.bg-info {\n background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n background-color: #117a8b !important;\n}\n\n.bg-warning {\n background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n background-color: #d39e00 !important;\n}\n\n.bg-danger {\n background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n background-color: #bd2130 !important;\n}\n\n.bg-light {\n background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n background-color: #1d2124 !important;\n}\n\n.bg-white {\n background-color: #fff !important;\n}\n\n.bg-transparent {\n background-color: transparent !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-right-0 {\n border-right: 0 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-left-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #007bff !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #28a745 !important;\n}\n\n.border-info {\n border-color: #17a2b8 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #343a40 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n border-top-left-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.d-none {\n display: none !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n}\n\n.d-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-md-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: -ms-flexbox !important;\n display: flex !important;\n }\n .d-print-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n.embed-responsive {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n}\n\n.embed-responsive::before {\n display: block;\n content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n\n.embed-responsive-21by9::before {\n padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n padding-top: 100%;\n}\n\n.flex-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n}\n\n.flex-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n}\n\n.justify-content-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n}\n\n.align-items-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n}\n\n.align-items-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n}\n\n.align-items-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n}\n\n.align-items-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n}\n\n.align-content-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n}\n\n.align-content-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n}\n\n.align-content-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n}\n\n.align-content-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n}\n\n.align-content-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n}\n\n.align-self-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n}\n\n.align-self-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n}\n\n.align-self-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n}\n\n.align-self-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n}\n\n.align-self-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-sm-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-sm-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-sm-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-sm-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-sm-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-sm-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-sm-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-sm-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-sm-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-sm-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-sm-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-md-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-md-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-md-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-md-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-md-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-md-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-md-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-md-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-md-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-md-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-md-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-md-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-md-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-md-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-md-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-md-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-md-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-md-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-md-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-lg-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-lg-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-lg-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-lg-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-lg-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-lg-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-lg-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-lg-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-lg-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-lg-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-lg-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-xl-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-xl-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-xl-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-xl-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-xl-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-xl-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-xl-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-xl-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-xl-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-xl-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-xl-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n.float-left {\n float: left !important;\n}\n\n.float-right {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-left {\n float: left !important;\n }\n .float-sm-right {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n}\n\n@media (min-width: 768px) {\n .float-md-left {\n float: left !important;\n }\n .float-md-right {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n}\n\n@media (min-width: 992px) {\n .float-lg-left {\n float: left !important;\n }\n .float-lg-right {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n}\n\n@media (min-width: 1200px) {\n .float-xl-left {\n float: left !important;\n }\n .float-xl-right {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: -webkit-sticky !important;\n position: sticky !important;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n@supports ((position: -webkit-sticky) or (position: sticky)) {\n .sticky-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip: auto;\n white-space: normal;\n}\n\n.shadow-sm {\n box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n box-shadow: none !important;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.w-auto {\n width: auto !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.h-auto {\n height: auto !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n.text-monospace {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n.text-justify {\n text-align: justify !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.text-left {\n text-align: left !important;\n}\n\n.text-right {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n@media (min-width: 576px) {\n .text-sm-left {\n text-align: left !important;\n }\n .text-sm-right {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 768px) {\n .text-md-left {\n text-align: left !important;\n }\n .text-md-right {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 992px) {\n .text-lg-left {\n text-align: left !important;\n }\n .text-lg-right {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 1200px) {\n .text-xl-left {\n text-align: left !important;\n }\n .text-xl-right {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.font-weight-light {\n font-weight: 300 !important;\n}\n\n.font-weight-normal {\n font-weight: 400 !important;\n}\n\n.font-weight-bold {\n font-weight: 700 !important;\n}\n\n.font-italic {\n font-style: italic !important;\n}\n\n.text-white {\n color: #fff !important;\n}\n\n.text-primary {\n color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n color: #0062cc !important;\n}\n\n.text-secondary {\n color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n color: #545b62 !important;\n}\n\n.text-success {\n color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n color: #1e7e34 !important;\n}\n\n.text-info {\n color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n color: #117a8b !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n color: #d39e00 !important;\n}\n\n.text-danger {\n color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n color: #bd2130 !important;\n}\n\n.text-light {\n color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n color: #dae0e5 !important;\n}\n\n.text-dark {\n color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n color: #1d2124 !important;\n}\n\n.text-body {\n color: #212529 !important;\n}\n\n.text-muted {\n color: #6c757d !important;\n}\n\n.text-black-50 {\n color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media print {\n *,\n *::before,\n *::after {\n text-shadow: none !important;\n box-shadow: none !important;\n }\n a:not(.btn) {\n text-decoration: underline;\n }\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n pre {\n white-space: pre-wrap !important;\n }\n pre,\n blockquote {\n border: 1px solid #adb5bd;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n @page {\n size: a3;\n }\n body {\n min-width: 992px !important;\n }\n .container {\n min-width: 992px !important;\n }\n .navbar {\n display: none;\n }\n .badge {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #dee2e6 !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*!\n * Bootstrap v4.1.0 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n:root {\n --blue: #007bff;\n --indigo: #6610f2;\n --purple: #6f42c1;\n --pink: #e83e8c;\n --red: #dc3545;\n --orange: #fd7e14;\n --yellow: #ffc107;\n --green: #28a745;\n --teal: #20c997;\n --cyan: #17a2b8;\n --white: #fff;\n --gray: #6c757d;\n --gray-dark: #343a40;\n --primary: #007bff;\n --secondary: #6c757d;\n --success: #28a745;\n --info: #17a2b8;\n --warning: #ffc107;\n --danger: #dc3545;\n --light: #f8f9fa;\n --dark: #343a40;\n --breakpoint-xs: 0;\n --breakpoint-sm: 576px;\n --breakpoint-md: 768px;\n --breakpoint-lg: 992px;\n --breakpoint-xl: 1200px;\n --font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: transparent;\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: 0.5rem;\n font-family: inherit;\n font-weight: 500;\n line-height: 1.2;\n color: inherit;\n}\n\nh1, .h1 {\n font-size: 2.5rem;\n}\n\nh2, .h2 {\n font-size: 2rem;\n}\n\nh3, .h3 {\n font-size: 1.75rem;\n}\n\nh4, .h4 {\n font-size: 1.5rem;\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: 6rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-2 {\n font-size: 5.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-3 {\n font-size: 4.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-4 {\n font-size: 3.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\nhr {\n margin-top: 1rem;\n margin-bottom: 1rem;\n border: 0;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n font-size: 80%;\n font-weight: 400;\n}\n\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%;\n color: #6c757d;\n}\n\n.blockquote-footer::before {\n content: \"\\2014 \\00A0\";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 90%;\n color: #6c757d;\n}\n\ncode,\nkbd,\npre,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\ncode {\n font-size: 87.5%;\n color: #e83e8c;\n word-break: break-word;\n}\n\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 87.5%;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\n\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n}\n\npre {\n display: block;\n font-size: 87.5%;\n color: #212529;\n}\n\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n.row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n min-height: 1px;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n}\n\n.col-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n order: -1;\n}\n\n.order-last {\n order: 13;\n}\n\n.order-0 {\n order: 0;\n}\n\n.order-1 {\n order: 1;\n}\n\n.order-2 {\n order: 2;\n}\n\n.order-3 {\n order: 3;\n}\n\n.order-4 {\n order: 4;\n}\n\n.order-5 {\n order: 5;\n}\n\n.order-6 {\n order: 6;\n}\n\n.order-7 {\n order: 7;\n}\n\n.order-8 {\n order: 8;\n}\n\n.order-9 {\n order: 9;\n}\n\n.order-10 {\n order: 10;\n}\n\n.order-11 {\n order: 11;\n}\n\n.order-12 {\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-sm-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n order: -1;\n }\n .order-sm-last {\n order: 13;\n }\n .order-sm-0 {\n order: 0;\n }\n .order-sm-1 {\n order: 1;\n }\n .order-sm-2 {\n order: 2;\n }\n .order-sm-3 {\n order: 3;\n }\n .order-sm-4 {\n order: 4;\n }\n .order-sm-5 {\n order: 5;\n }\n .order-sm-6 {\n order: 6;\n }\n .order-sm-7 {\n order: 7;\n }\n .order-sm-8 {\n order: 8;\n }\n .order-sm-9 {\n order: 9;\n }\n .order-sm-10 {\n order: 10;\n }\n .order-sm-11 {\n order: 11;\n }\n .order-sm-12 {\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-md-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n order: -1;\n }\n .order-md-last {\n order: 13;\n }\n .order-md-0 {\n order: 0;\n }\n .order-md-1 {\n order: 1;\n }\n .order-md-2 {\n order: 2;\n }\n .order-md-3 {\n order: 3;\n }\n .order-md-4 {\n order: 4;\n }\n .order-md-5 {\n order: 5;\n }\n .order-md-6 {\n order: 6;\n }\n .order-md-7 {\n order: 7;\n }\n .order-md-8 {\n order: 8;\n }\n .order-md-9 {\n order: 9;\n }\n .order-md-10 {\n order: 10;\n }\n .order-md-11 {\n order: 11;\n }\n .order-md-12 {\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-lg-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n order: -1;\n }\n .order-lg-last {\n order: 13;\n }\n .order-lg-0 {\n order: 0;\n }\n .order-lg-1 {\n order: 1;\n }\n .order-lg-2 {\n order: 2;\n }\n .order-lg-3 {\n order: 3;\n }\n .order-lg-4 {\n order: 4;\n }\n .order-lg-5 {\n order: 5;\n }\n .order-lg-6 {\n order: 6;\n }\n .order-lg-7 {\n order: 7;\n }\n .order-lg-8 {\n order: 8;\n }\n .order-lg-9 {\n order: 9;\n }\n .order-lg-10 {\n order: 10;\n }\n .order-lg-11 {\n order: 11;\n }\n .order-lg-12 {\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none;\n }\n .col-xl-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n order: -1;\n }\n .order-xl-last {\n order: 13;\n }\n .order-xl-0 {\n order: 0;\n }\n .order-xl-1 {\n order: 1;\n }\n .order-xl-2 {\n order: 2;\n }\n .order-xl-3 {\n order: 3;\n }\n .order-xl-4 {\n order: 4;\n }\n .order-xl-5 {\n order: 5;\n }\n .order-xl-6 {\n order: 6;\n }\n .order-xl-7 {\n order: 7;\n }\n .order-xl-8 {\n order: 8;\n }\n .order-xl-9 {\n order: 9;\n }\n .order-xl-10 {\n order: 10;\n }\n .order-xl-11 {\n order: 11;\n }\n .order-xl-12 {\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 1rem;\n background-color: transparent;\n}\n\n.table th,\n.table td {\n padding: 0.75rem;\n vertical-align: top;\n border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n vertical-align: bottom;\n border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n border-top: 2px solid #dee2e6;\n}\n\n.table .table {\n background-color: #fff;\n}\n\n.table-sm th,\n.table-sm td {\n padding: 0.3rem;\n}\n\n.table-bordered {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n background-color: #b8daff;\n}\n\n.table-hover .table-primary:hover {\n background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n background-color: #d6d8db;\n}\n\n.table-hover .table-secondary:hover {\n background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n background-color: #c3e6cb;\n}\n\n.table-hover .table-success:hover {\n background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n background-color: #bee5eb;\n}\n\n.table-hover .table-info:hover {\n background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n background-color: #ffeeba;\n}\n\n.table-hover .table-warning:hover {\n background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n background-color: #f5c6cb;\n}\n\n.table-hover .table-danger:hover {\n background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n background-color: #fdfdfe;\n}\n\n.table-hover .table-light:hover {\n background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n background-color: #c6c8ca;\n}\n\n.table-hover .table-dark:hover {\n background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n color: #fff;\n background-color: #212529;\n border-color: #32383e;\n}\n\n.table .thead-light th {\n color: #495057;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.table-dark {\n color: #fff;\n background-color: #212529;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n border-color: #32383e;\n}\n\n.table-dark.table-bordered {\n border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-sm > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 767.98px) {\n .table-responsive-md {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-md > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-lg > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n }\n .table-responsive-xl > .table-bordered {\n border: 0;\n }\n}\n\n.table-responsive {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n}\n\n.table-responsive > .table-bordered {\n border: 0;\n}\n\n.form-control {\n display: block;\n width: 100%;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .form-control {\n transition: none;\n }\n}\n\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n\n.form-control:focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n\nselect.form-control:not([size]):not([multiple]) {\n height: calc(2.25rem + 2px);\n}\n\nselect.form-control:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n margin-bottom: 0;\n line-height: 1.5;\n color: #212529;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .input-group-sm > .form-control-plaintext.form-control,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-sm > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-sm > .input-group-append > .form-control-plaintext.btn, .form-control-plaintext.form-control-lg, .input-group-lg > .form-control-plaintext.form-control,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-append > .form-control-plaintext.input-group-text,\n.input-group-lg > .input-group-prepend > .form-control-plaintext.btn,\n.input-group-lg > .input-group-append > .form-control-plaintext.btn {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm, .input-group-sm > .form-control,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\nselect.form-control-sm:not([size]):not([multiple]), .input-group-sm > select.form-control:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-sm > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-sm > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(1.8125rem + 2px);\n}\n\n.form-control-lg, .input-group-lg > .form-control,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\nselect.form-control-lg:not([size]):not([multiple]), .input-group-lg > select.form-control:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.input-group-text:not([size]):not([multiple]),\n.input-group-lg > .input-group-prepend > select.btn:not([size]):not([multiple]),\n.input-group-lg > .input-group-append > select.btn:not([size]):not([multiple]) {\n height: calc(2.875rem + 2px);\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-text {\n display: block;\n margin-top: 0.25rem;\n}\n\n.form-row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -5px;\n margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.form-check {\n position: relative;\n display: block;\n padding-left: 1.25rem;\n}\n\n.form-check-input {\n position: absolute;\n margin-top: 0.3rem;\n margin-left: -1.25rem;\n}\n\n.form-check-input:disabled ~ .form-check-label {\n color: #6c757d;\n}\n\n.form-check-label {\n margin-bottom: 0;\n}\n\n.form-check-inline {\n display: inline-flex;\n align-items: center;\n padding-left: 0;\n margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n position: static;\n margin-top: 0;\n margin-right: 0.3125rem;\n margin-left: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #28a745;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(40, 167, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid, .was-validated\n.custom-select:valid,\n.custom-select.is-valid {\n border-color: #28a745;\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus, .was-validated\n.custom-select:valid:focus,\n.custom-select.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-control:valid ~ .valid-feedback,\n.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback,\n.form-control.is-valid ~ .valid-tooltip, .was-validated\n.custom-select:valid ~ .valid-feedback,\n.was-validated\n.custom-select:valid ~ .valid-tooltip,\n.custom-select.is-valid ~ .valid-feedback,\n.custom-select.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n background-color: #71dd8a;\n}\n\n.was-validated .custom-control-input:valid ~ .valid-feedback,\n.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback,\n.custom-control-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label::before, .custom-file-input.is-valid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:valid ~ .valid-feedback,\n.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback,\n.custom-file-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: .5rem;\n margin-top: .1rem;\n font-size: .875rem;\n line-height: 1;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.8);\n border-radius: .2rem;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid, .was-validated\n.custom-select:invalid,\n.custom-select.is-invalid {\n border-color: #dc3545;\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus, .was-validated\n.custom-select:invalid:focus,\n.custom-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control:invalid ~ .invalid-feedback,\n.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback,\n.form-control.is-invalid ~ .invalid-tooltip, .was-validated\n.custom-select:invalid ~ .invalid-feedback,\n.was-validated\n.custom-select:invalid ~ .invalid-tooltip,\n.custom-select.is-invalid ~ .invalid-feedback,\n.custom-select.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n background-color: #efa2a9;\n}\n\n.was-validated .custom-control-input:invalid ~ .invalid-feedback,\n.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback,\n.custom-control-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label::before, .custom-file-input.is-invalid ~ .custom-file-label::before {\n border-color: inherit;\n}\n\n.was-validated .custom-file-input:invalid ~ .invalid-feedback,\n.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback,\n.custom-file-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n display: flex;\n flex-flow: row wrap;\n align-items: center;\n}\n\n.form-inline .form-check {\n width: 100%;\n}\n\n@media (min-width: 576px) {\n .form-inline label {\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 0;\n }\n .form-inline .form-group {\n display: flex;\n flex: 0 0 auto;\n flex-flow: row wrap;\n align-items: center;\n margin-bottom: 0;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-plaintext {\n display: inline-block;\n }\n .form-inline .input-group,\n .form-inline .custom-select {\n width: auto;\n }\n .form-inline .form-check {\n display: flex;\n align-items: center;\n justify-content: center;\n width: auto;\n padding-left: 0;\n }\n .form-inline .form-check-input {\n position: relative;\n margin-top: 0;\n margin-right: 0.25rem;\n margin-left: 0;\n }\n .form-inline .custom-control {\n align-items: center;\n justify-content: center;\n }\n .form-inline .custom-control-label {\n margin-bottom: 0;\n }\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n user-select: none;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .btn {\n transition: none;\n }\n}\n\n.btn:hover, .btn:focus {\n text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.btn.disabled, .btn:disabled {\n opacity: 0.65;\n}\n\n.btn:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.btn:not(:disabled):not(.disabled):active, .btn:not(:disabled):not(.disabled).active {\n background-image: none;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n pointer-events: none;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:hover {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0062cc;\n border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:hover {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #545b62;\n border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-success {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:hover {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #1e7e34;\n border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-info {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:hover {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n color: #fff;\n background-color: #117a8b;\n border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-warning {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:hover {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n color: #212529;\n background-color: #d39e00;\n border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:hover {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #bd2130;\n border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-light {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:hover {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n color: #212529;\n background-color: #dae0e5;\n border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-dark {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:hover {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1d2124;\n border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-primary {\n color: #007bff;\n background-color: transparent;\n background-image: none;\n border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n background-color: transparent;\n background-image: none;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n color: #28a745;\n background-color: transparent;\n background-image: none;\n border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n color: #28a745;\n background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n color: #17a2b8;\n background-color: transparent;\n background-image: none;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n color: #17a2b8;\n background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n color: #ffc107;\n background-color: transparent;\n background-image: none;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n color: #dc3545;\n background-color: transparent;\n background-image: none;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n background-color: transparent;\n background-image: none;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n color: #343a40;\n background-color: transparent;\n background-image: none;\n border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n color: #343a40;\n background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n font-weight: 400;\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-link:hover {\n color: #0056b3;\n text-decoration: underline;\n background-color: transparent;\n border-color: transparent;\n}\n\n.btn-link:focus, .btn-link.focus {\n text-decoration: underline;\n border-color: transparent;\n box-shadow: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n.btn-block + .btn-block {\n margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n\n.fade {\n transition: opacity 0.15s linear;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .fade {\n transition: none;\n }\n}\n\n.fade:not(.show) {\n opacity: 0;\n}\n\n.collapse:not(.show) {\n display: none;\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .collapsing {\n transition: none;\n }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n position: relative;\n}\n\n.dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0.125rem 0 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n\n.dropdown-menu-right {\n right: 0;\n left: auto;\n}\n\n.dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n top: 0;\n right: auto;\n left: 100%;\n margin-top: 0;\n margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n top: 0;\n right: 100%;\n left: auto;\n margin-top: 0;\n margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n display: inline-block;\n width: 0;\n height: 0;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n right: auto;\n bottom: auto;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n color: #16181b;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1.5rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.dropdown-item-text {\n display: block;\n padding: 0.25rem 1.5rem;\n color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n flex: 0 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group,\n.btn-group-vertical .btn + .btn,\n.btn-group-vertical .btn + .btn-group,\n.btn-group-vertical .btn-group + .btn,\n.btn-group-vertical .btn-group + .btn-group {\n margin-left: -1px;\n}\n\n.btn-toolbar {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.btn-group-vertical .btn,\n.btn-group-vertical .btn-group {\n width: 100%;\n}\n\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n\n.input-group {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .custom-select,\n.input-group > .custom-file {\n position: relative;\n flex: 1 1 auto;\n width: 1%;\n margin-bottom: 0;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file:focus {\n z-index: 3;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n margin-left: -1px;\n}\n\n.input-group > .form-control:not(:last-child),\n.input-group > .custom-select:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n display: flex;\n align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label,\n.input-group > .custom-file:not(:first-child) .custom-file-label::after {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n position: relative;\n z-index: 2;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n margin-left: -1px;\n}\n\n.input-group-prepend {\n margin-right: -1px;\n}\n\n.input-group-append {\n margin-left: -1px;\n}\n\n.input-group-text {\n display: flex;\n align-items: center;\n padding: 0.375rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n margin-top: 0;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group > .input-group-append:not(:last-child) > .btn,\n.input-group > .input-group-append:not(:last-child) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.custom-control {\n position: relative;\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5rem;\n}\n\n.custom-control-inline {\n display: inline-flex;\n margin-right: 1rem;\n}\n\n.custom-control-input {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n color: #fff;\n background-color: #007bff;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:active ~ .custom-control-label::before {\n color: #fff;\n background-color: #b3d7ff;\n}\n\n.custom-control-input:disabled ~ .custom-control-label {\n color: #6c757d;\n}\n\n.custom-control-input:disabled ~ .custom-control-label::before {\n background-color: #e9ecef;\n}\n\n.custom-control-label {\n margin-bottom: 0;\n}\n\n.custom-control-label::before {\n position: absolute;\n top: 0.25rem;\n left: 0;\n display: block;\n width: 1rem;\n height: 1rem;\n pointer-events: none;\n content: \"\";\n user-select: none;\n background-color: #dee2e6;\n}\n\n.custom-control-label::after {\n position: absolute;\n top: 0.25rem;\n left: 0;\n display: block;\n width: 1rem;\n height: 1rem;\n content: \"\";\n background-repeat: no-repeat;\n background-position: center center;\n background-size: 50% 50%;\n}\n\n.custom-checkbox .custom-control-label::before {\n border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::before {\n background-color: #007bff;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n vertical-align: middle;\n background: #fff url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E\") no-repeat right 0.75rem center;\n background-size: 8px 10px;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n appearance: none;\n}\n\n.custom-select:focus {\n border-color: #80bdff;\n outline: 0;\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075), 0 0 5px rgba(128, 189, 255, 0.5);\n}\n\n.custom-select:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n height: auto;\n padding-right: 0.75rem;\n background-image: none;\n}\n\n.custom-select:disabled {\n color: #6c757d;\n background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n opacity: 0;\n}\n\n.custom-select-sm {\n height: calc(1.8125rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 75%;\n}\n\n.custom-select-lg {\n height: calc(2.875rem + 2px);\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n font-size: 125%;\n}\n\n.custom-file {\n position: relative;\n display: inline-block;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin-bottom: 0;\n}\n\n.custom-file-input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: calc(2.25rem + 2px);\n margin: 0;\n opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n border-color: #80bdff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-file-input:focus ~ .custom-file-label::after {\n border-color: #80bdff;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n content: \"Browse\";\n}\n\n.custom-file-label {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n height: calc(2.25rem + 2px);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.custom-file-label::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n z-index: 3;\n display: block;\n height: calc(calc(2.25rem + 2px) - 1px * 2);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n content: \"Browse\";\n background-color: #e9ecef;\n border-left: 1px solid #ced4da;\n border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n width: 100%;\n padding-left: 0;\n background-color: transparent;\n appearance: none;\n}\n\n.custom-range:focus {\n outline: none;\n}\n\n.custom-range::-moz-focus-outer {\n border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: -0.25rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-webkit-slider-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-webkit-slider-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-moz-range-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-moz-range-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-range-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n appearance: none;\n}\n\n.custom-range::-ms-thumb:focus {\n outline: none;\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-ms-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: transparent;\n border-color: transparent;\n border-width: 0.5rem;\n}\n\n.custom-range::-ms-fill-lower {\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n margin-right: 15px;\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.nav {\n display: flex;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n text-decoration: none;\n}\n\n.nav-link.disabled {\n color: #6c757d;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-item {\n margin-bottom: -1px;\n}\n\n.nav-tabs .nav-link {\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #007bff;\n}\n\n.nav-fill .nav-item {\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified .nav-item {\n flex-basis: 0;\n flex-grow: 1;\n text-align: center;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n}\n\n.navbar > .container,\n.navbar > .container-fluid {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n}\n\n.navbar-brand {\n display: inline-block;\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n text-decoration: none;\n}\n\n.navbar-nav {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n position: static;\n float: none;\n}\n\n.navbar-text {\n display: inline-block;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n flex-basis: 100%;\n flex-grow: 1;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n text-decoration: none;\n}\n\n.navbar-toggler:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n content: \"\";\n background: no-repeat center center;\n background-size: 100% 100%;\n}\n\n@media (max-width: 575.98px) {\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-sm .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 767.98px) {\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 768px) {\n .navbar-expand-md {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-md .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 991.98px) {\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 992px) {\n .navbar-expand-lg {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-lg .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 1199.98px) {\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-xl .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n}\n\n.navbar-expand {\n flex-flow: row nowrap;\n justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n display: none;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.5);\n border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.5);\n border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E\");\n}\n\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-text a {\n color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n\n.card > .list-group:first-child .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.card > .list-group:last-child .list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.card-body {\n flex: 1 1 auto;\n padding: 1.25rem;\n}\n\n.card-title {\n margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n margin-top: -0.375rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link:hover {\n text-decoration: none;\n}\n\n.card-link + .card-link {\n margin-left: 1.25rem;\n}\n\n.card-header {\n padding: 0.75rem 1.25rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-header + .list-group .list-group-item:first-child {\n border-top: 0;\n}\n\n.card-footer {\n padding: 0.75rem 1.25rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.625rem;\n margin-bottom: -0.75rem;\n margin-left: -0.625rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.625rem;\n margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1.25rem;\n}\n\n.card-img {\n width: 100%;\n border-radius: calc(0.25rem - 1px);\n}\n\n.card-img-top {\n width: 100%;\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img-bottom {\n width: 100%;\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-deck {\n display: flex;\n flex-direction: column;\n}\n\n.card-deck .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-deck {\n flex-flow: row wrap;\n margin-right: -15px;\n margin-left: -15px;\n }\n .card-deck .card {\n display: flex;\n flex: 1 0 0%;\n flex-direction: column;\n margin-right: 15px;\n margin-bottom: 0;\n margin-left: 15px;\n }\n}\n\n.card-group {\n display: flex;\n flex-direction: column;\n}\n\n.card-group > .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-group {\n flex-flow: row wrap;\n }\n .card-group > .card {\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-top,\n .card-group > .card:first-child .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:first-child .card-img-bottom,\n .card-group > .card:first-child .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:last-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-top,\n .card-group > .card:last-child .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:last-child .card-img-bottom,\n .card-group > .card:last-child .card-footer {\n border-bottom-left-radius: 0;\n }\n .card-group > .card:only-child {\n border-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-top,\n .card-group > .card:only-child .card-header {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n }\n .card-group > .card:only-child .card-img-bottom,\n .card-group > .card:only-child .card-footer {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) {\n border-radius: 0;\n }\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header,\n .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer {\n border-radius: 0;\n }\n}\n\n.card-columns .card {\n margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n .card-columns {\n column-count: 3;\n column-gap: 1.25rem;\n orphans: 1;\n widows: 1;\n }\n .card-columns .card {\n display: inline-block;\n width: 100%;\n }\n}\n\n.accordion .card:not(:first-of-type):not(:last-of-type) {\n border-bottom: 0;\n border-radius: 0;\n}\n\n.accordion .card:not(:first-of-type) .card-header:first-child {\n border-radius: 0;\n}\n\n.accordion .card:first-of-type {\n border-bottom: 0;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.accordion .card:last-of-type {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.breadcrumb {\n display: flex;\n flex-wrap: wrap;\n padding: 0.75rem 1rem;\n margin-bottom: 1rem;\n list-style: none;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n display: inline-block;\n padding-right: 0.5rem;\n color: #6c757d;\n content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: none;\n}\n\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: flex;\n padding-left: 0;\n list-style: none;\n border-radius: 0.25rem;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: 0.5rem 0.75rem;\n margin-left: -1px;\n line-height: 1.25;\n color: #007bff;\n background-color: #fff;\n border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n z-index: 2;\n color: #0056b3;\n text-decoration: none;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.page-link:focus {\n z-index: 2;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-link:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\n.page-item:first-child .page-link {\n margin-left: 0;\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n z-index: 1;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n cursor: auto;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.25em 0.4em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n}\n\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.badge-pill {\n padding-right: 0.6em;\n padding-left: 0.6em;\n border-radius: 10rem;\n}\n\n.badge-primary {\n color: #fff;\n background-color: #007bff;\n}\n\n.badge-primary[href]:hover, .badge-primary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #0062cc;\n}\n\n.badge-secondary {\n color: #fff;\n background-color: #6c757d;\n}\n\n.badge-secondary[href]:hover, .badge-secondary[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #545b62;\n}\n\n.badge-success {\n color: #fff;\n background-color: #28a745;\n}\n\n.badge-success[href]:hover, .badge-success[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1e7e34;\n}\n\n.badge-info {\n color: #fff;\n background-color: #17a2b8;\n}\n\n.badge-info[href]:hover, .badge-info[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #117a8b;\n}\n\n.badge-warning {\n color: #212529;\n background-color: #ffc107;\n}\n\n.badge-warning[href]:hover, .badge-warning[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #d39e00;\n}\n\n.badge-danger {\n color: #fff;\n background-color: #dc3545;\n}\n\n.badge-danger[href]:hover, .badge-danger[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #bd2130;\n}\n\n.badge-light {\n color: #212529;\n background-color: #f8f9fa;\n}\n\n.badge-light[href]:hover, .badge-light[href]:focus {\n color: #212529;\n text-decoration: none;\n background-color: #dae0e5;\n}\n\n.badge-dark {\n color: #fff;\n background-color: #343a40;\n}\n\n.badge-dark[href]:hover, .badge-dark[href]:focus {\n color: #fff;\n text-decoration: none;\n background-color: #1d2124;\n}\n\n.jumbotron {\n padding: 2rem 1rem;\n margin-bottom: 2rem;\n background-color: #e9ecef;\n border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n .jumbotron {\n padding: 4rem 2rem;\n }\n}\n\n.jumbotron-fluid {\n padding-right: 0;\n padding-left: 0;\n border-radius: 0;\n}\n\n.alert {\n position: relative;\n padding: 0.75rem 1.25rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n position: absolute;\n top: 0;\n right: 0;\n padding: 0.75rem 1.25rem;\n color: inherit;\n}\n\n.alert-primary {\n color: #004085;\n background-color: #cce5ff;\n border-color: #b8daff;\n}\n\n.alert-primary hr {\n border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n color: #002752;\n}\n\n.alert-secondary {\n color: #383d41;\n background-color: #e2e3e5;\n border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n color: #202326;\n}\n\n.alert-success {\n color: #155724;\n background-color: #d4edda;\n border-color: #c3e6cb;\n}\n\n.alert-success hr {\n border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n color: #0b2e13;\n}\n\n.alert-info {\n color: #0c5460;\n background-color: #d1ecf1;\n border-color: #bee5eb;\n}\n\n.alert-info hr {\n border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n color: #062c33;\n}\n\n.alert-warning {\n color: #856404;\n background-color: #fff3cd;\n border-color: #ffeeba;\n}\n\n.alert-warning hr {\n border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n color: #533f03;\n}\n\n.alert-danger {\n color: #721c24;\n background-color: #f8d7da;\n border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n color: #491217;\n}\n\n.alert-light {\n color: #818182;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n\n.alert-light hr {\n border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n color: #686868;\n}\n\n.alert-dark {\n color: #1b1e21;\n background-color: #d6d8d9;\n border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n color: #040505;\n}\n\n@keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n.progress {\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: flex;\n flex-direction: column;\n justify-content: center;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n background-color: #007bff;\n transition: width 0.6s ease;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .progress-bar {\n transition: none;\n }\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n animation: progress-bar-stripes 1s linear infinite;\n}\n\n.media {\n display: flex;\n align-items: flex-start;\n}\n\n.media-body {\n flex: 1;\n}\n\n.list-group {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.75rem 1.25rem;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.list-group-item:hover, .list-group-item:focus {\n z-index: 1;\n text-decoration: none;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n background-color: #fff;\n}\n\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.list-group-flush .list-group-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n\n.list-group-flush:first-child .list-group-item:first-child {\n border-top: 0;\n}\n\n.list-group-flush:last-child .list-group-item:last-child {\n border-bottom: 0;\n}\n\n.list-group-item-primary {\n color: #004085;\n background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #004085;\n background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #004085;\n border-color: #004085;\n}\n\n.list-group-item-secondary {\n color: #383d41;\n background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #383d41;\n background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #383d41;\n border-color: #383d41;\n}\n\n.list-group-item-success {\n color: #155724;\n background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #155724;\n background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #155724;\n border-color: #155724;\n}\n\n.list-group-item-info {\n color: #0c5460;\n background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #0c5460;\n background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #0c5460;\n border-color: #0c5460;\n}\n\n.list-group-item-warning {\n color: #856404;\n background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #856404;\n background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #856404;\n border-color: #856404;\n}\n\n.list-group-item-danger {\n color: #721c24;\n background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #721c24;\n background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #721c24;\n border-color: #721c24;\n}\n\n.list-group-item-light {\n color: #818182;\n background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #818182;\n background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #818182;\n border-color: #818182;\n}\n\n.list-group-item-dark {\n color: #1b1e21;\n background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #1b1e21;\n background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #1b1e21;\n border-color: #1b1e21;\n}\n\n.close {\n float: right;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: .5;\n}\n\n.close:hover, .close:focus {\n color: #000;\n text-decoration: none;\n opacity: .75;\n}\n\n.close:not(:disabled):not(.disabled) {\n cursor: pointer;\n}\n\nbutton.close {\n padding: 0;\n background-color: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n\n.modal-open {\n overflow: hidden;\n}\n\n.modal {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n display: none;\n overflow: hidden;\n outline: 0;\n}\n\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n transition: transform 0.3s ease-out;\n transform: translate(0, -25%);\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .modal.fade .modal-dialog {\n transition: none;\n }\n}\n\n.modal.show .modal-dialog {\n transform: translate(0, 0);\n}\n\n.modal-dialog-centered {\n display: flex;\n align-items: center;\n min-height: calc(100% - (0.5rem * 2));\n}\n\n.modal-content {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n\n.modal-backdrop.fade {\n opacity: 0;\n}\n\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 1rem;\n border-bottom: 1px solid #e9ecef;\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n\n.modal-header .close {\n padding: 1rem;\n margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding: 1rem;\n border-top: 1px solid #e9ecef;\n}\n\n.modal-footer > :not(:first-child) {\n margin-left: .25rem;\n}\n\n.modal-footer > :not(:last-child) {\n margin-right: .25rem;\n}\n\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n .modal-dialog-centered {\n min-height: calc(100% - (1.75rem * 2));\n }\n .modal-sm {\n max-width: 300px;\n }\n}\n\n@media (min-width: 992px) {\n .modal-lg {\n max-width: 800px;\n }\n}\n\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n\n.tooltip.show {\n opacity: 0.9;\n}\n\n.tooltip .arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n top: 0;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n right: 0;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n bottom: 0;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n left: 0;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: block;\n max-width: 276px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n\n.popover .arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n margin-bottom: 0.5rem;\n}\n\n.bs-popover-top .arrow, .bs-popover-auto[x-placement^=\"top\"] .arrow {\n bottom: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before,\n.bs-popover-top .arrow::after, .bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0;\n}\n\n.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^=\"top\"] .arrow::before {\n bottom: 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-top .arrow::after, .bs-popover-auto[x-placement^=\"top\"] .arrow::after {\n bottom: 1px;\n border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n margin-left: 0.5rem;\n}\n\n.bs-popover-right .arrow, .bs-popover-auto[x-placement^=\"right\"] .arrow {\n left: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before,\n.bs-popover-right .arrow::after, .bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n border-width: 0.5rem 0.5rem 0.5rem 0;\n}\n\n.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^=\"right\"] .arrow::before {\n left: 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-right .arrow::after, .bs-popover-auto[x-placement^=\"right\"] .arrow::after {\n left: 1px;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n margin-top: 0.5rem;\n}\n\n.bs-popover-bottom .arrow, .bs-popover-auto[x-placement^=\"bottom\"] .arrow {\n top: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before,\n.bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n border-width: 0 0.5rem 0.5rem 0.5rem;\n}\n\n.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::before {\n top: 0;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-bottom .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] .arrow::after {\n top: 1px;\n border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n margin-right: 0.5rem;\n}\n\n.bs-popover-left .arrow, .bs-popover-auto[x-placement^=\"left\"] .arrow {\n right: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before,\n.bs-popover-left .arrow::after, .bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n border-width: 0.5rem 0 0.5rem 0.5rem;\n}\n\n.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^=\"left\"] .arrow::before {\n right: 0;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-left .arrow::after, .bs-popover-auto[x-placement^=\"left\"] .arrow::after {\n right: 1px;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n color: inherit;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 0.5rem 0.75rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n\n.carousel-item {\n position: relative;\n display: none;\n align-items: center;\n width: 100%;\n transition: transform 0.6s ease;\n backface-visibility: hidden;\n perspective: 1000px;\n}\n\n@media screen and (prefers-reduced-motion: reduce) {\n .carousel-item {\n transition: none;\n }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next,\n.carousel-item-prev {\n position: absolute;\n top: 0;\n}\n\n.carousel-item-next.carousel-item-left,\n.carousel-item-prev.carousel-item-right {\n transform: translateX(0);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-next.carousel-item-left,\n .carousel-item-prev.carousel-item-right {\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-item-next,\n.active.carousel-item-right {\n transform: translateX(100%);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-next,\n .active.carousel-item-right {\n transform: translate3d(100%, 0, 0);\n }\n}\n\n.carousel-item-prev,\n.active.carousel-item-left {\n transform: translateX(-100%);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-item-prev,\n .active.carousel-item-left {\n transform: translate3d(-100%, 0, 0);\n }\n}\n\n.carousel-fade .carousel-item {\n opacity: 0;\n transition-duration: .6s;\n transition-property: opacity;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n opacity: 0;\n}\n\n.carousel-fade .carousel-item-next,\n.carousel-fade .carousel-item-prev,\n.carousel-fade .carousel-item.active,\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-prev {\n transform: translateX(0);\n}\n\n@supports (transform-style: preserve-3d) {\n .carousel-fade .carousel-item-next,\n .carousel-fade .carousel-item-prev,\n .carousel-fade .carousel-item.active,\n .carousel-fade .active.carousel-item-left,\n .carousel-fade .active.carousel-item-prev {\n transform: translate3d(0, 0, 0);\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 15%;\n color: #fff;\n text-align: center;\n opacity: 0.5;\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: .9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: transparent no-repeat center center;\n background-size: 100% 100%;\n}\n\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 10px;\n left: 0;\n z-index: 15;\n display: flex;\n justify-content: center;\n padding-left: 0;\n margin-right: 15%;\n margin-left: 15%;\n list-style: none;\n}\n\n.carousel-indicators li {\n position: relative;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n background-color: rgba(255, 255, 255, 0.5);\n}\n\n.carousel-indicators li::before {\n position: absolute;\n top: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators li::after {\n position: absolute;\n bottom: -10px;\n left: 0;\n display: inline-block;\n width: 100%;\n height: 10px;\n content: \"\";\n}\n\n.carousel-indicators .active {\n background-color: #fff;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.bg-primary {\n background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n background-color: #0062cc !important;\n}\n\n.bg-secondary {\n background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n background-color: #545b62 !important;\n}\n\n.bg-success {\n background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n background-color: #1e7e34 !important;\n}\n\n.bg-info {\n background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n background-color: #117a8b !important;\n}\n\n.bg-warning {\n background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n background-color: #d39e00 !important;\n}\n\n.bg-danger {\n background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n background-color: #bd2130 !important;\n}\n\n.bg-light {\n background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n background-color: #1d2124 !important;\n}\n\n.bg-white {\n background-color: #fff !important;\n}\n\n.bg-transparent {\n background-color: transparent !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-right-0 {\n border-right: 0 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-left-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #007bff !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #28a745 !important;\n}\n\n.border-info {\n border-color: #17a2b8 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #343a40 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n border-top-left-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.d-none {\n display: none !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n}\n\n.embed-responsive {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n}\n\n.embed-responsive::before {\n display: block;\n content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n\n.embed-responsive-21by9::before {\n padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n padding-top: 100%;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n}\n\n.float-left {\n float: left !important;\n}\n\n.float-right {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-left {\n float: left !important;\n }\n .float-sm-right {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n}\n\n@media (min-width: 768px) {\n .float-md-left {\n float: left !important;\n }\n .float-md-right {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n}\n\n@media (min-width: 992px) {\n .float-lg-left {\n float: left !important;\n }\n .float-lg-right {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n}\n\n@media (min-width: 1200px) {\n .float-xl-left {\n float: left !important;\n }\n .float-xl-right {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: sticky !important;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n@supports (position: sticky) {\n .sticky-top {\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip: auto;\n white-space: normal;\n}\n\n.shadow-sm {\n box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n box-shadow: none !important;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.w-auto {\n width: auto !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.h-auto {\n height: auto !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n.text-monospace {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n.text-justify {\n text-align: justify !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.text-left {\n text-align: left !important;\n}\n\n.text-right {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n@media (min-width: 576px) {\n .text-sm-left {\n text-align: left !important;\n }\n .text-sm-right {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 768px) {\n .text-md-left {\n text-align: left !important;\n }\n .text-md-right {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 992px) {\n .text-lg-left {\n text-align: left !important;\n }\n .text-lg-right {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 1200px) {\n .text-xl-left {\n text-align: left !important;\n }\n .text-xl-right {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.font-weight-light {\n font-weight: 300 !important;\n}\n\n.font-weight-normal {\n font-weight: 400 !important;\n}\n\n.font-weight-bold {\n font-weight: 700 !important;\n}\n\n.font-italic {\n font-style: italic !important;\n}\n\n.text-white {\n color: #fff !important;\n}\n\n.text-primary {\n color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n color: #0062cc !important;\n}\n\n.text-secondary {\n color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n color: #545b62 !important;\n}\n\n.text-success {\n color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n color: #1e7e34 !important;\n}\n\n.text-info {\n color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n color: #117a8b !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n color: #d39e00 !important;\n}\n\n.text-danger {\n color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n color: #bd2130 !important;\n}\n\n.text-light {\n color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n color: #dae0e5 !important;\n}\n\n.text-dark {\n color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n color: #1d2124 !important;\n}\n\n.text-body {\n color: #212529 !important;\n}\n\n.text-muted {\n color: #6c757d !important;\n}\n\n.text-black-50 {\n color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media print {\n *,\n *::before,\n *::after {\n text-shadow: none !important;\n box-shadow: none !important;\n }\n a:not(.btn) {\n text-decoration: underline;\n }\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n pre {\n white-space: pre-wrap !important;\n }\n pre,\n blockquote {\n border: 1px solid #adb5bd;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n @page {\n size: a3;\n }\n body {\n min-width: 992px !important;\n }\n .container {\n min-width: 992px !important;\n }\n .navbar {\n display: none;\n }\n .badge {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #dee2e6 !important;\n }\n}\n\n/*# sourceMappingURL=bootstrap.css.map */","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Origally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular psuedo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover {\n &:hover { @content; }\n}\n\n@mixin hover-focus {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n","// stylelint-disable declaration-no-important, selector-list-comma-newline-after\n\n//\n// Headings\n//\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1, .h1 { font-size: $h1-font-size; }\nh2, .h2 { font-size: $h2-font-size; }\nh3, .h3 { font-size: $h3-font-size; }\nh4, .h4 { font-size: $h4-font-size; }\nh5, .h5 { font-size: $h5-font-size; }\nh6, .h6 { font-size: $h6-font-size; }\n\n.lead {\n font-size: $lead-font-size;\n font-weight: $lead-font-weight;\n}\n\n// Type display classes\n.display-1 {\n font-size: $display1-size;\n font-weight: $display1-weight;\n line-height: $display-line-height;\n}\n.display-2 {\n font-size: $display2-size;\n font-weight: $display2-weight;\n line-height: $display-line-height;\n}\n.display-3 {\n font-size: $display3-size;\n font-weight: $display3-weight;\n line-height: $display-line-height;\n}\n.display-4 {\n font-size: $display4-size;\n font-weight: $display4-weight;\n line-height: $display-line-height;\n}\n\n\n//\n// Horizontal rules\n//\n\nhr {\n margin-top: $hr-margin-y;\n margin-bottom: $hr-margin-y;\n border: 0;\n border-top: $hr-border-width solid $hr-border-color;\n}\n\n\n//\n// Emphasis\n//\n\nsmall,\n.small {\n font-size: $small-font-size;\n font-weight: $font-weight-normal;\n}\n\nmark,\n.mark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n//\n// Lists\n//\n\n.list-unstyled {\n @include list-unstyled;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n @include list-unstyled;\n}\n.list-inline-item {\n display: inline-block;\n\n &:not(:last-child) {\n margin-right: $list-inline-padding;\n }\n}\n\n\n//\n// Misc\n//\n\n// Builds on `abbr`\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n// Blockquotes\n.blockquote {\n margin-bottom: $spacer;\n font-size: $blockquote-font-size;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%; // back to default font-size\n color: $blockquote-small-color;\n\n &::before {\n content: \"\\2014 \\00A0\"; // em dash, nbsp\n }\n}\n","// Lists\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n@mixin list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n","// Responsive images (ensure images don't scale beyond their parents)\n//\n// This is purposefully opt-in via an explicit class rather than being the default for all ``s.\n// We previously tried the \"images are responsive by default\" approach in Bootstrap v2,\n// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)\n// which weren't expecting the images within themselves to be involuntarily resized.\n// See also https://github.com/twbs/bootstrap/issues/18178\n.img-fluid {\n @include img-fluid;\n}\n\n\n// Image thumbnails\n.img-thumbnail {\n padding: $thumbnail-padding;\n background-color: $thumbnail-bg;\n border: $thumbnail-border-width solid $thumbnail-border-color;\n @include border-radius($thumbnail-border-radius);\n @include box-shadow($thumbnail-box-shadow);\n\n // Keep them at most 100% wide\n @include img-fluid;\n}\n\n//\n// Figures\n//\n\n.figure {\n // Ensures the caption's text aligns with the image.\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: ($spacer / 2);\n line-height: 1;\n}\n\n.figure-caption {\n font-size: $figure-caption-font-size;\n color: $figure-caption-color;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n\n@mixin img-fluid {\n // Part 1: Set a maximum relative to the parent\n max-width: 100%;\n // Part 2: Override the height to auto, otherwise images will be stretched\n // when setting a width and height attribute on the img element.\n height: auto;\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size.\n\n// stylelint-disable indentation, media-query-list-comma-newline-after\n@mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) {\n background-image: url($file-1x);\n\n // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,\n // but doesn't convert dppx=>dpi.\n // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.\n // Compatibility info: https://caniuse.com/#feat=css-media-resolution\n @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx\n only screen and (min-resolution: 2dppx) { // Standardized\n background-image: url($file-2x);\n background-size: $width-1x $height-1x;\n }\n}\n","// Single side border-radius\n\n@mixin border-radius($radius: $border-radius) {\n @if $enable-rounded {\n border-radius: $radius;\n }\n}\n\n@mixin border-top-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-top-right-radius: $radius;\n }\n}\n\n@mixin border-right-radius($radius) {\n @if $enable-rounded {\n border-top-right-radius: $radius;\n border-bottom-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-radius($radius) {\n @if $enable-rounded {\n border-bottom-right-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n\n@mixin border-left-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n","// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: $font-family-monospace;\n}\n\n// Inline code\ncode {\n font-size: $code-font-size;\n color: $code-color;\n word-break: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n font-size: $kbd-font-size;\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n @include box-shadow($kbd-box-shadow);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: $nested-kbd-font-weight;\n @include box-shadow(none);\n }\n}\n\n// Blocks of code\npre {\n display: block;\n font-size: $code-font-size;\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: $pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-grid-classes {\n .container {\n @include make-container();\n @include make-container-max-widths();\n }\n}\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but with 100% width for\n// fluid, full width layouts.\n\n@if $enable-grid-classes {\n .container-fluid {\n @include make-container();\n }\n}\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n }\n\n // Remove the negative margin from default .row, then the horizontal padding\n // from all immediate children columns (to prevent runaway style inheritance).\n .no-gutters {\n margin-right: 0;\n margin-left: 0;\n\n > .col,\n > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n }\n }\n}\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","/// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-container() {\n width: 100%;\n padding-right: ($grid-gutter-width / 2);\n padding-left: ($grid-gutter-width / 2);\n margin-right: auto;\n margin-left: auto;\n}\n\n\n// For each breakpoint, define the maximum width of the container in a media query\n@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {\n @each $breakpoint, $container-max-width in $max-widths {\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n max-width: $container-max-width;\n }\n }\n}\n\n@mixin make-row() {\n display: flex;\n flex-wrap: wrap;\n margin-right: ($grid-gutter-width / -2);\n margin-left: ($grid-gutter-width / -2);\n}\n\n@mixin make-col-ready() {\n position: relative;\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we use `flex` values\n // later on to override this initial width.\n width: 100%;\n min-height: 1px; // Prevent collapsing\n padding-right: ($grid-gutter-width / 2);\n padding-left: ($grid-gutter-width / 2);\n}\n\n@mixin make-col($size, $columns: $grid-columns) {\n flex: 0 0 percentage($size / $columns);\n // Add a `max-width` to ensure content within each column does not blow out\n // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari\n // do not appear to require this.\n max-width: percentage($size / $columns);\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: $size / $columns;\n margin-left: if($num == 0, 0, percentage($num));\n}\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width. Null for the largest (last) breakpoint.\n// The maximum value is calculated as the minimum of the next one less 0.02px\n// to work around the limitations of `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $next: breakpoint-next($name, $breakpoints);\n @return if($next, breakpoint-min($next, $breakpoints) - .02px, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $max: breakpoint-max($name, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($name, $breakpoints) {\n @content;\n }\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n // Common properties for all breakpoints\n %grid-column {\n position: relative;\n width: 100%;\n min-height: 1px; // Prevent columns from collapsing when empty\n padding-right: ($gutter / 2);\n padding-left: ($gutter / 2);\n }\n\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n // Allow columns to stretch full width below their breakpoints\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @extend %grid-column;\n }\n }\n .col#{$infix},\n .col#{$infix}-auto {\n @extend %grid-column;\n }\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col#{$infix}-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: none; // Reset earlier grid tiers\n }\n\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n .order#{$infix}-first { order: -1; }\n\n .order#{$infix}-last { order: $columns + 1; }\n\n @for $i from 0 through $columns {\n .order#{$infix}-#{$i} { order: $i; }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n }\n}\n","//\n// Basic Bootstrap table\n//\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: $spacer;\n background-color: $table-bg; // Reset for nesting within parents with `background-color`.\n\n th,\n td {\n padding: $table-cell-padding;\n vertical-align: top;\n border-top: $table-border-width solid $table-border-color;\n }\n\n thead th {\n vertical-align: bottom;\n border-bottom: (2 * $table-border-width) solid $table-border-color;\n }\n\n tbody + tbody {\n border-top: (2 * $table-border-width) solid $table-border-color;\n }\n\n .table {\n background-color: $body-bg;\n }\n}\n\n\n//\n// Condensed table w/ half padding\n//\n\n.table-sm {\n th,\n td {\n padding: $table-cell-padding-sm;\n }\n}\n\n\n// Border versions\n//\n// Add or remove borders all around the table and between all the columns.\n\n.table-bordered {\n border: $table-border-width solid $table-border-color;\n\n th,\n td {\n border: $table-border-width solid $table-border-color;\n }\n\n thead {\n th,\n td {\n border-bottom-width: (2 * $table-border-width);\n }\n }\n}\n\n.table-borderless {\n th,\n td,\n thead th,\n tbody + tbody {\n border: 0;\n }\n}\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n tbody tr:nth-of-type(#{$table-striped-order}) {\n background-color: $table-accent-bg;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n tbody tr {\n @include hover {\n background-color: $table-hover-bg;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n@each $color, $value in $theme-colors {\n @include table-row-variant($color, theme-color-level($color, -9));\n}\n\n@include table-row-variant(active, $table-active-bg);\n\n\n// Dark styles\n//\n// Same table markup, but inverted color scheme: dark background and light text.\n\n// stylelint-disable-next-line no-duplicate-selectors\n.table {\n .thead-dark {\n th {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n border-color: $table-dark-border-color;\n }\n }\n\n .thead-light {\n th {\n color: $table-head-color;\n background-color: $table-head-bg;\n border-color: $table-border-color;\n }\n }\n}\n\n.table-dark {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n\n th,\n td,\n thead th {\n border-color: $table-dark-border-color;\n }\n\n &.table-bordered {\n border: 0;\n }\n\n &.table-striped {\n tbody tr:nth-of-type(odd) {\n background-color: $table-dark-accent-bg;\n }\n }\n\n &.table-hover {\n tbody tr {\n @include hover {\n background-color: $table-dark-hover-bg;\n }\n }\n }\n}\n\n\n// Responsive tables\n//\n// Generate series of `.table-responsive-*` classes for configuring the screen\n// size of where your table will overflow.\n\n.table-responsive {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $next: breakpoint-next($breakpoint, $grid-breakpoints);\n $infix: breakpoint-infix($next, $grid-breakpoints);\n\n &#{$infix} {\n @include media-breakpoint-down($breakpoint) {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n -ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057\n\n // Prevent double border on horizontal scroll due to use of `display: block;`\n > .table-bordered {\n border: 0;\n }\n }\n }\n }\n}\n","// Tables\n\n@mixin table-row-variant($state, $background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table-#{$state} {\n &,\n > th,\n > td {\n background-color: $background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover {\n $hover-background: darken($background, 5%);\n\n .table-#{$state} {\n @include hover {\n background-color: $hover-background;\n\n > td,\n > th {\n background-color: $hover-background;\n }\n }\n }\n }\n}\n","// stylelint-disable selector-no-qualifying-type\n\n//\n// Textual form controls\n//\n\n.form-control {\n display: block;\n width: 100%;\n padding: $input-padding-y $input-padding-x;\n font-size: $font-size-base;\n line-height: $input-line-height;\n color: $input-color;\n background-color: $input-bg;\n background-clip: padding-box;\n border: $input-border-width solid $input-border-color;\n\n // Note: This has no effect on `s in CSS.\n @if $enable-rounded {\n // Manually use the if/else instead of the mixin to account for iOS override\n border-radius: $input-border-radius;\n } @else {\n // Otherwise undo the iOS default\n border-radius: 0;\n }\n\n @include box-shadow($input-box-shadow);\n @include transition($input-transition);\n\n // Unstyle the caret on ` receives focus\n // in IE and (under certain conditions) Edge, as it looks bad and cannot be made to\n // match the appearance of the native widget.\n // See https://github.com/twbs/bootstrap/issues/19398.\n color: $input-color;\n background-color: $input-bg;\n }\n}\n\n// Make file inputs better match text inputs by forcing them to new lines.\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n\n//\n// Labels\n//\n\n// For use with horizontal and inline forms, when you need the label (or legend)\n// text to align with the form controls.\n.col-form-label {\n padding-top: calc(#{$input-padding-y} + #{$input-border-width});\n padding-bottom: calc(#{$input-padding-y} + #{$input-border-width});\n margin-bottom: 0; // Override the `
$name {$general['age']}세 ".getGenChar($general['personal'])."".getGenSpecial($general['special'])." / ".getGenSpecial($general['special2'])."".displaySpecialInfo($general['special'])." / ".displaySpecialInfo($general['special2'])." Lv ".getExpLevel($general['experience'])." {$nation} ".getHonor($general['experience'])."Lv {$general['explevel']} {$nation} ".getGenChar($general['personal'])."".getGenSpecial($general['special'])." / ".getGenSpecial($general['special2'])."".displaySpecialInfo($general['special'])." / ".displaySpecialInfo($general['special2'])." {$general['sum']} {$general['leader']} {$general['power']}{$general['gold']} {$general['rice']} ".getGenChar($general['personal'])."".getGenSpecial($general['special'])." / ".getGenSpecial($general['special2'])."".displaySpecialInfo($general['special'])." / ".displaySpecialInfo($general['special2'])." {$general['belong']} {$general['connect']}"; echo "
(".getConnect($general['connect']).")
$name {$general['age']}세".getGenChar($general['personal'])."".displayCharInfo($general['personal'])." ".displaySpecialInfo($general['special'])." / ".displaySpecialInfo($general['special2'])." Lv ".getExpLevel($general['experience'])." {$nation}{$general['name2']} Lv {$general['explevel']} {$nation}".getGenChar($general['personal'])."".displayCharInfo($general['personal'])." ".displaySpecialInfo($general['special'])." / ".displaySpecialInfo($general['special2'])." {$general['sum']} {$general['leader']}$intel {$general['gold']} {$general['rice']}".getGenChar($general['personal'])."".displayCharInfo($general['personal'])." ".displaySpecialInfo($general['special'])." / ".displaySpecialInfo($general['special2'])." {$general['belong']} {$general['connect']}"; echo "
(".getConnect($general['connect']).")
병사 {$general['crew']} 성격".getGenChar($general['personal'])."".displayCharInfo($general['personal'])."
훈련
성격 -