├── application ├── views │ ├── account │ │ ├── edit.php │ │ ├── confirm.php │ │ ├── reset.php │ │ ├── recovery.php │ │ ├── login.php │ │ ├── profile.php │ │ └── register.php │ ├── errors │ │ ├── 403.php │ │ └── 404.php │ ├── admin │ │ ├── login.php │ │ ├── history.php │ │ ├── tariffs.php │ │ └── withdraw.php │ ├── dashboard │ │ ├── history.php │ │ ├── tariffs.php │ │ ├── referrals.php │ │ └── invest.php │ ├── main │ │ └── index.php │ └── layouts │ │ ├── default.php │ │ └── admin.php ├── config │ ├── admin.php │ ├── db.php │ ├── tariffs.php │ └── routes.php ├── models │ ├── Main.php │ ├── Merchant.php │ ├── Dashboard.php │ ├── Admin.php │ └── Account.php ├── acl │ ├── main.php │ ├── merchant.php │ ├── admin.php │ ├── dashboard.php │ └── account.php ├── lib │ ├── Dev.php │ ├── Db.php │ └── Pagination.php ├── core │ ├── Model.php │ ├── View.php │ ├── Controller.php │ └── Router.php └── controllers │ ├── MainController.php │ ├── MerchantController.php │ ├── DashboardController.php │ ├── AdminController.php │ └── AccountController.php ├── README.md ├── public ├── fonts │ └── RobotoCondensed.ttf ├── styles │ ├── main.css │ └── admin.css └── scripts │ ├── form.js │ ├── popper.js │ └── bootstrap.js ├── .htaccess ├── index.php └── db.sql /application/views/account/edit.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /application/views/errors/403.php: -------------------------------------------------------------------------------- 1 | Страница 403 -------------------------------------------------------------------------------- /application/views/errors/404.php: -------------------------------------------------------------------------------- 1 | Страница 404 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-invest 2 | Simple PHP OOP invest project (HYIP) based on mvc 3 | -------------------------------------------------------------------------------- /application/config/admin.php: -------------------------------------------------------------------------------- 1 | 'admin', 5 | 'password' => '12345', 6 | ]; -------------------------------------------------------------------------------- /public/fonts/RobotoCondensed.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-youtube/php-invest/HEAD/public/fonts/RobotoCondensed.ttf -------------------------------------------------------------------------------- /application/config/db.php: -------------------------------------------------------------------------------- 1 | '', 5 | 'name' => '', 6 | 'user' => '', 7 | 'password' => '', 8 | ]; -------------------------------------------------------------------------------- /application/models/Main.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'index', 6 | ], 7 | 'authorize' => [ 8 | // 9 | ], 10 | 'guest' => [ 11 | // 12 | ], 13 | 'admin' => [ 14 | // 15 | ], 16 | ]; -------------------------------------------------------------------------------- /application/lib/Dev.php: -------------------------------------------------------------------------------- 1 | '; 8 | var_dump($str); 9 | echo ''; 10 | exit; 11 | } -------------------------------------------------------------------------------- /application/acl/merchant.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'perfectmoney', 6 | ], 7 | 'authorize' => [ 8 | // 9 | ], 10 | 'guest' => [ 11 | // 12 | ], 13 | 'admin' => [ 14 | // 15 | ], 16 | ]; -------------------------------------------------------------------------------- /application/core/Model.php: -------------------------------------------------------------------------------- 1 | db = new Db; 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /application/acl/admin.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'login', 6 | ], 7 | 'authorize' => [ 8 | // 9 | ], 10 | 'guest' => [ 11 | // 12 | ], 13 | 'admin' => [ 14 | 'withdraw', 15 | 'tariffs', 16 | 'history', 17 | 'logout', 18 | ], 19 | ]; -------------------------------------------------------------------------------- /application/acl/dashboard.php: -------------------------------------------------------------------------------- 1 | [ 5 | // 6 | ], 7 | 'authorize' => [ 8 | 'tariffs', 9 | 'invest', 10 | 'history', 11 | 'referrals', 12 | ], 13 | 'guest' => [ 14 | // 15 | ], 16 | 'admin' => [ 17 | // 18 | ], 19 | ]; -------------------------------------------------------------------------------- /application/acl/account.php: -------------------------------------------------------------------------------- 1 | [ 5 | // 6 | ], 7 | 'authorize' => [ 8 | 'profile', 9 | 'logout', 10 | ], 11 | 'guest' => [ 12 | 'register', 13 | 'login', 14 | 'recovery', 15 | 'confirm', 16 | 'reset', 17 | ], 18 | 'admin' => [ 19 | // 20 | ], 21 | ]; -------------------------------------------------------------------------------- /application/views/account/confirm.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 | 7 |
8 |
-------------------------------------------------------------------------------- /application/controllers/MainController.php: -------------------------------------------------------------------------------- 1 | $this->tariffs, 12 | ]; 13 | $this->view->render('Главная страница', $vars); 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | run(); -------------------------------------------------------------------------------- /application/views/account/reset.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 |
5 |

Новый пароль для входа:

6 | Перейти ко входу 7 |
8 |
9 |
-------------------------------------------------------------------------------- /public/styles/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 54px; 3 | } 4 | 5 | @media (min-width: 992px) { 6 | body { 7 | padding-top: 56px; 8 | } 9 | } 10 | 11 | .carousel-item { 12 | height: 65vh; 13 | min-height: 300px; 14 | background: no-repeat center center scroll; 15 | -webkit-background-size: cover; 16 | -moz-background-size: cover; 17 | -o-background-size: cover; 18 | background-size: cover; 19 | } 20 | 21 | .portfolio-item { 22 | margin-bottom: 30px; 23 | } -------------------------------------------------------------------------------- /public/scripts/form.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('form').submit(function(event) { 3 | if ($(this).attr('id') == 'no_ajax') { 4 | return; 5 | } 6 | var json; 7 | event.preventDefault(); 8 | $.ajax({ 9 | type: $(this).attr('method'), 10 | url: $(this).attr('action'), 11 | data: new FormData(this), 12 | contentType: false, 13 | cache: false, 14 | processData: false, 15 | success: function(result) { 16 | json = jQuery.parseJSON(result); 17 | if (json.url) { 18 | window.location.href = '/' + json.url; 19 | } else { 20 | alert(json.status + ' - ' + json.message); 21 | } 22 | }, 23 | }); 24 | }); 25 | }); -------------------------------------------------------------------------------- /application/views/account/recovery.php: -------------------------------------------------------------------------------- 1 |
2 |

Восстановление пароля

3 |
4 |
5 |
6 |
7 |
8 | 9 | 10 |

11 |
12 |
13 | 14 |
15 |
16 |
17 |
-------------------------------------------------------------------------------- /application/controllers/MerchantController.php: -------------------------------------------------------------------------------- 1 | view->errorCode(404); 21 | } 22 | $data = $this->model->validatePerfectMoney($_POST, $this->tariffs); 23 | if (!$data) { 24 | $this->view->errorCode(403); 25 | } 26 | $this->model->createTariff($data, $this->tariffs[$data['tid']]); 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /application/views/admin/login.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
Вход в панель Администратора
4 |
5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 |
14 | 15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /application/views/dashboard/history.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 |
5 | 6 |

История пуста

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
ДатаОписание
24 | 25 | 26 |
27 |
28 |
-------------------------------------------------------------------------------- /application/views/account/login.php: -------------------------------------------------------------------------------- 1 |
2 |

Вход

3 |
4 |
5 |
6 |
7 |
8 | 9 | 10 |

11 |
12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 |
21 |
22 |
23 |
-------------------------------------------------------------------------------- /application/lib/Db.php: -------------------------------------------------------------------------------- 1 | db = new PDO('mysql:host='.$config['host'].';dbname='.$config['name'].'', $config['user'], $config['password']); 14 | } 15 | 16 | public function query($sql, $params = []) { 17 | $stmt = $this->db->prepare($sql); 18 | if (!empty($params)) { 19 | foreach ($params as $key => $val) { 20 | if (is_int($val)) { 21 | $type = PDO::PARAM_INT; 22 | } else { 23 | $type = PDO::PARAM_STR; 24 | } 25 | $stmt->bindValue(':'.$key, $val, $type); 26 | } 27 | } 28 | $stmt->execute(); 29 | return $stmt; 30 | } 31 | 32 | public function row($sql, $params = []) { 33 | $result = $this->query($sql, $params); 34 | return $result->fetchAll(PDO::FETCH_ASSOC); 35 | } 36 | 37 | public function column($sql, $params = []) { 38 | $result = $this->query($sql, $params); 39 | return $result->fetchColumn(); 40 | } 41 | 42 | public function lastInsertId() { 43 | return $this->db->lastInsertId(); 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /application/core/View.php: -------------------------------------------------------------------------------- 1 | route = $route; 13 | $this->path = $route['controller'].'/'.$route['action']; 14 | } 15 | 16 | public function render($title, $vars = []) { 17 | extract($vars); 18 | $path = 'application/views/'.$this->path.'.php'; 19 | if (file_exists($path)) { 20 | ob_start(); 21 | require $path; 22 | $content = ob_get_clean(); 23 | require 'application/views/layouts/'.$this->layout.'.php'; 24 | } 25 | } 26 | 27 | public function redirect($url) { 28 | header('location: /'.$url); 29 | exit; 30 | } 31 | 32 | public static function errorCode($code) { 33 | http_response_code($code); 34 | $path = 'application/views/errors/'.$code.'.php'; 35 | if (file_exists($path)) { 36 | require $path; 37 | } 38 | exit; 39 | } 40 | 41 | public function message($status, $message) { 42 | exit(json_encode(['status' => $status, 'message' => $message])); 43 | } 44 | 45 | public function location($url) { 46 | exit(json_encode(['url' => $url])); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /application/config/tariffs.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'title' => 'Минимальный', 6 | 'description' => 'подойдет для новичков', 7 | 'hour' => 50, 8 | 'percent' => 5, 9 | 'min' => 100, 10 | 'max' => 1000, 11 | ], 12 | 2 => [ 13 | 'title' => 'Стартовый', 14 | 'description' => 'это старт вашей карьеры', 15 | 'hour' => 100, 16 | 'percent' => 25, 17 | 'min' => 1500, 18 | 'max' => 5000, 19 | ], 20 | 3 => [ 21 | 'title' => 'Простой', 22 | 'description' => 'проще чем ходить на работу', 23 | 'hour' => 300, 24 | 'percent' => 50, 25 | 'min' => 5000, 26 | 'max' => 10000, 27 | ], 28 | 4 => [ 29 | 'title' => 'Расширенный', 30 | 'description' => 'для ценителей ощущений', 31 | 'hour' => 100, 32 | 'percent' => 500, 33 | 'min' => 25000, 34 | 'max' => 50000, 35 | ], 36 | 5 => [ 37 | 'title' => 'Профессиональный', 38 | 'description' => 'решение для акул бизнеса', 39 | 'hour' => 5000, 40 | 'percent' => 2000, 41 | 'min' => 100000, 42 | 'max' => 300000, 43 | ], 44 | 6 => [ 45 | 'title' => 'Роскошный', 46 | 'description' => 'сюда инвестируют только киты', 47 | 'hour' => 10000, 48 | 'percent' => 5000, 49 | 'min' => 500000, 50 | 'max' => 1000000, 51 | ], 52 | ]; -------------------------------------------------------------------------------- /db.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `accounts` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT, 3 | `email` varchar(50) NOT NULL, 4 | `login` varchar(15) NOT NULL, 5 | `wallet` varchar(15) NOT NULL, 6 | `password` varchar(200) NOT NULL, 7 | `ref` int(11) NOT NULL, 8 | `refBalance` float NOT NULL, 9 | `token` varchar(30) NOT NULL, 10 | `status` int(11) NOT NULL, 11 | PRIMARY KEY (`id`) 12 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 13 | 14 | CREATE TABLE IF NOT EXISTS `history` ( 15 | `id` int(11) NOT NULL AUTO_INCREMENT, 16 | `uid` int(11) NOT NULL, 17 | `unixTime` int(11) NOT NULL, 18 | `description` text NOT NULL, 19 | PRIMARY KEY (`id`) 20 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 21 | 22 | 23 | CREATE TABLE IF NOT EXISTS `ref_withdraw` ( 24 | `id` int(11) NOT NULL AUTO_INCREMENT, 25 | `uid` int(11) NOT NULL, 26 | `unixTime` int(11) NOT NULL, 27 | `amount` float NOT NULL, 28 | PRIMARY KEY (`id`) 29 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 30 | 31 | CREATE TABLE IF NOT EXISTS `tariffs` ( 32 | `id` int(11) NOT NULL AUTO_INCREMENT, 33 | `uid` int(11) NOT NULL, 34 | `sumIn` float NOT NULL, 35 | `sumOut` float NOT NULL, 36 | `percent` float NOT NULL, 37 | `unixTimeStart` int(11) NOT NULL, 38 | `unixTimeFinish` int(11) NOT NULL, 39 | PRIMARY KEY (`id`) 40 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -------------------------------------------------------------------------------- /application/core/Controller.php: -------------------------------------------------------------------------------- 1 | route = $route; 16 | if (!$this->checkAcl()) { 17 | View::errorCode(403); 18 | } 19 | $this->view = new View($route); 20 | $this->model = $this->loadModel($route['controller']); 21 | $this->tariffs = require 'application/config/tariffs.php'; 22 | } 23 | 24 | public function loadModel($name) { 25 | $path = 'application\models\\'.ucfirst($name); 26 | if (class_exists($path)) { 27 | return new $path; 28 | } 29 | } 30 | 31 | public function checkAcl() { 32 | $this->acl = require 'application/acl/'.$this->route['controller'].'.php'; 33 | if ($this->isAcl('all')) { 34 | return true; 35 | } 36 | elseif (isset($_SESSION['account']['id']) and $this->isAcl('authorize')) { 37 | return true; 38 | } 39 | elseif (!isset($_SESSION['account']['id']) and $this->isAcl('guest')) { 40 | return true; 41 | } 42 | elseif (isset($_SESSION['admin']) and $this->isAcl('admin')) { 43 | return true; 44 | } 45 | return false; 46 | } 47 | 48 | public function isAcl($key) { 49 | return in_array($this->route['action'], $this->acl[$key]); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /application/controllers/DashboardController.php: -------------------------------------------------------------------------------- 1 | $this->tariffs[$this->route['id']], 13 | ]; 14 | $this->view->render('Инвестировать', $vars); 15 | } 16 | 17 | public function tariffsAction() { 18 | $pagination = new Pagination($this->route, $this->model->tariffsCount()); 19 | $vars = [ 20 | 'pagination' => $pagination->get(), 21 | 'list' => $this->model->tariffsList($this->route), 22 | ]; 23 | $this->view->render('Тарифы', $vars); 24 | } 25 | 26 | public function historyAction() { 27 | $pagination = new Pagination($this->route, $this->model->historyCount()); 28 | $vars = [ 29 | 'pagination' => $pagination->get(), 30 | 'list' => $this->model->historyList($this->route), 31 | ]; 32 | $this->view->render('История', $vars); 33 | } 34 | 35 | public function referralsAction() { 36 | if (!empty($_POST)) { 37 | if ($_SESSION['account']['refBalance'] <= 0) { 38 | $this->view->message('success', 'Реферальный баланс пуст'); 39 | } 40 | $this->model->creatRefWithdraw(); 41 | $this->view->message('success', 'Заявка на вывод создана'); 42 | } 43 | $pagination = new Pagination($this->route, $this->model->referralsCount()); 44 | $vars = [ 45 | 'pagination' => $pagination->get(), 46 | 'list' => $this->model->referralsList($this->route), 47 | ]; 48 | $this->view->render('Рефералы', $vars); 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /application/views/main/index.php: -------------------------------------------------------------------------------- 1 |
2 |

Тарифы

3 |
4 | $val): ?> 5 |
6 |
7 |

8 |
9 |
%
10 |
11 |
12 |
    13 |
  • Минимальная инвестиция: $
  • 14 |
  • Максимальная инвестиция: $
  • 15 |
  • Период инвестиции: ч.
  • 16 |
  • 17 | 18 | Инвестировать 19 | 20 |

    * Для покупки этого тарифа необходима авторизация

    21 | 22 |
  • 23 |
24 |
25 |
26 | 27 |
28 |
-------------------------------------------------------------------------------- /application/views/account/profile.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 |
5 |
6 |
7 |
8 | 9 | 10 |

11 |
12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 |
33 |
34 |
35 |
-------------------------------------------------------------------------------- /application/views/admin/history.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 9 |

История пуста

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
ДатаЛогинE-mailОписание
31 | 32 | 33 |
34 |
35 |
36 |
37 |
38 |
-------------------------------------------------------------------------------- /application/core/Router.php: -------------------------------------------------------------------------------- 1 | $val) { 15 | $this->add($key, $val); 16 | } 17 | } 18 | 19 | public function add($route, $params) { 20 | $route = preg_replace('/{([a-z]+):([^\}]+)}/', '(?P<\1>\2)', $route); 21 | $route = '#^'.$route.'$#'; 22 | $this->routes[$route] = $params; 23 | } 24 | 25 | public function match() { 26 | $url = trim($_SERVER['REQUEST_URI'], '/'); 27 | foreach ($this->routes as $route => $params) { 28 | if (preg_match($route, $url, $matches)) { 29 | foreach ($matches as $key => $match) { 30 | if (is_string($key)) { 31 | if (is_numeric($match)) { 32 | $match = (int) $match; 33 | } 34 | $params[$key] = $match; 35 | } 36 | } 37 | $this->params = $params; 38 | return true; 39 | } 40 | } 41 | return false; 42 | } 43 | 44 | public function run(){ 45 | if ($this->match()) { 46 | $path = 'application\controllers\\'.ucfirst($this->params['controller']).'Controller'; 47 | if (class_exists($path)) { 48 | $action = $this->params['action'].'Action'; 49 | if (method_exists($path, $action)) { 50 | $controller = new $path($this->params); 51 | $controller->$action(); 52 | } else { 53 | View::errorCode(404); 54 | } 55 | } else { 56 | View::errorCode(404); 57 | } 58 | } else { 59 | View::errorCode(404); 60 | } 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /application/views/dashboard/tariffs.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 |
5 | 6 |

Список инвестиций пуст

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 38 | 39 | 40 | 41 |
Дата стартаДата завершенияСуммаПолучаетеПроцентСтатус
$ $ % 28 | = $val['unixTimeFinish']): ?> 29 | 30 | Ожидает выплаты 31 | 32 | Закрыт 33 | 34 | 35 | Активна 36 | 37 |
42 | 43 | 44 |
45 |
46 |
-------------------------------------------------------------------------------- /application/views/dashboard/referrals.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 |
5 |
6 |
7 |
8 | 9 | 10 |

11 |
12 |
13 |
14 |
15 | 16 | 17 |

18 |
19 |
20 | 21 |
22 |
23 | 24 |

Список рефералов пуст

25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
ЛогинE-mail
42 | 43 | 44 |
45 |
46 |
-------------------------------------------------------------------------------- /application/views/account/register.php: -------------------------------------------------------------------------------- 1 |
2 |

Регистрация

3 |
4 |
5 |
6 |
7 |
8 | 9 | 10 |
11 |
12 |
13 |
14 | 15 | 16 |
17 |
18 | route['ref'])): ?> 19 |
20 |
21 | 22 | 23 |
24 |
25 | 26 | 27 | 28 |
29 |
30 | 31 | 32 |
33 |
34 |
35 |
36 | 37 | 38 |
39 |
40 | 41 |
42 |
43 |
44 |
-------------------------------------------------------------------------------- /application/controllers/AdminController.php: -------------------------------------------------------------------------------- 1 | view->layout = 'admin'; 13 | } 14 | 15 | public function loginAction() { 16 | if (isset($_SESSION['admin'])) { 17 | $this->view->redirect('admin/withdraw'); 18 | } 19 | if (!empty($_POST)) { 20 | if (!$this->model->loginValidate($_POST)) { 21 | $this->view->message('error', $this->model->error); 22 | } 23 | $_SESSION['admin'] = true; 24 | $this->view->location('admin/withdraw'); 25 | } 26 | $this->view->render('Вход'); 27 | } 28 | 29 | public function withdrawAction() { 30 | if (!empty($_POST)) { 31 | if ($_POST['type'] == 'ref') { 32 | $result = $this->model->withdrawRefComplete($_POST['id']); 33 | if ($result) { 34 | $this->view->location('admin/withdraw'); 35 | } 36 | else { 37 | $this->view->message('error', 'Ошибка обработки запроса'); 38 | } 39 | } 40 | elseif ($_POST['type'] == 'tariff') { 41 | $result = $this->model->withdrawTariffsComplete($_POST['id']); 42 | if ($result) { 43 | $this->view->location('admin/withdraw'); 44 | } 45 | else { 46 | $this->view->message('error', 'Ошибка обработки запроса'); 47 | } 48 | } 49 | } 50 | $vars = [ 51 | 'listRef' => $this->model->withdrawRefList(), 52 | 'listTariffs' => $this->model->withdrawTariffsList(), 53 | ]; 54 | $this->view->render('Заказы на вывод средств', $vars); 55 | } 56 | 57 | public function historyAction() { 58 | $pagination = new Pagination($this->route, $this->model->historyCount()); 59 | $vars = [ 60 | 'pagination' => $pagination->get(), 61 | 'list' => $this->model->historyList($this->route), 62 | ]; 63 | $this->view->render('История', $vars); 64 | } 65 | 66 | public function tariffsAction() { 67 | $pagination = new Pagination($this->route, $this->model->tariffsCount()); 68 | $vars = [ 69 | 'pagination' => $pagination->get(), 70 | 'list' => $this->model->tariffsList($this->route), 71 | ]; 72 | $this->view->render('Список инвестиций', $vars); 73 | } 74 | 75 | public function logoutAction() { 76 | unset($_SESSION['admin']); 77 | $this->view->redirect('admin/login'); 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /application/models/Merchant.php: -------------------------------------------------------------------------------- 1 | $tariff[$tid]['max'] or $amount < $tariff[$tid]['min']) { 34 | return false; 35 | } 36 | return [ 37 | 'tid' => $tid, 38 | 'uid' => $uid, 39 | 'amount' => $amount, 40 | ]; 41 | } 42 | 43 | public function createTariff($data, $tarif) { 44 | $dataRef = $this->db->column('SELECT ref FROM accounts WHERE id = :id', ['id' => $data['uid']]); 45 | if ($dataRef === false) { 46 | return false; 47 | } 48 | if ($dataRef != 0) { 49 | $refSum = round((($data['amount'] * 5) / 100), 2); 50 | $params = [ 51 | 'sum' => $refSum, 52 | 'id' => $dataRef, 53 | ]; 54 | $this->db->query('UPDATE accounts SET refBalance = refBalance + :sum WHERE id = :id', $params); 55 | $params = [ 56 | 'id' => '', 57 | 'uid' => $dataRef, 58 | 'unixTime' => time(), 59 | 'description' => 'Реферальное вознаграждение, сумма '.$refSum.' $', 60 | ]; 61 | $this->db->query('INSERT INTO history VALUES (:id, :uid, :unixTime, :description)', $params); 62 | } 63 | $params = [ 64 | 'id' => '', 65 | 'uid' => $data['uid'], 66 | 'sumIn' => round($data['amount'], 2), 67 | 'sumOut' => round($data['amount'] + (($data['amount'] * $tarif['percent']) / 100), 2), 68 | 'percent' => $tarif['percent'], 69 | 'unixTimeStart' => time(), 70 | 'unixTimeFinish' => strtotime('+ '.$tarif['hour'].' hours'), 71 | ]; 72 | $this->db->query('INSERT INTO tariffs VALUES (:id, :uid, :sumIn, :sumOut, :percent, :unixTimeStart, :unixTimeFinish)', $params); 73 | 74 | $params = [ 75 | 'id' => '', 76 | 'uid' => $data['uid'], 77 | 'unixTime' => time(), 78 | 'description' => 'Инвестиция, номер вклада # '.$this->db->lastInsertId(), 79 | ]; 80 | $this->db->query('INSERT INTO history VALUES (:id, :uid, :unixTime, :description)', $params); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /application/models/Dashboard.php: -------------------------------------------------------------------------------- 1 | $_SESSION['account']['id'], 12 | ]; 13 | return $this->db->column('SELECT COUNT(id) FROM history WHERE uid = :uid', $params); 14 | } 15 | 16 | public function historyList($route) { 17 | $max = 10; 18 | $params = [ 19 | 'max' => $max, 20 | 'start' => ((($route['page'] ?? 1) - 1) * $max), 21 | 'uid' => $_SESSION['account']['id'], 22 | ]; 23 | return $this->db->row('SELECT * FROM history WHERE uid = :uid ORDER BY id DESC LIMIT :start, :max', $params); 24 | } 25 | 26 | public function referralsCount() { 27 | $params = [ 28 | 'uid' => $_SESSION['account']['id'], 29 | ]; 30 | return $this->db->column('SELECT COUNT(id) FROM accounts WHERE ref = :uid', $params); 31 | } 32 | 33 | public function referralsList($route) { 34 | $max = 10; 35 | $params = [ 36 | 'max' => $max, 37 | 'start' => ((($route['page'] ?? 1) - 1) * $max), 38 | 'uid' => $_SESSION['account']['id'], 39 | ]; 40 | return $this->db->row('SELECT login, email FROM accounts WHERE ref = :uid ORDER BY id DESC LIMIT :start, :max', $params); 41 | } 42 | 43 | public function tariffsCount() { 44 | $params = [ 45 | 'uid' => $_SESSION['account']['id'], 46 | ]; 47 | return $this->db->column('SELECT COUNT(id) FROM tariffs WHERE uid = :uid', $params); 48 | } 49 | 50 | public function tariffsList($route) { 51 | $max = 10; 52 | $params = [ 53 | 'max' => $max, 54 | 'start' => ((($route['page'] ?? 1) - 1) * $max), 55 | 'uid' => $_SESSION['account']['id'], 56 | ]; 57 | return $this->db->row('SELECT * FROM tariffs WHERE uid = :uid ORDER BY id DESC LIMIT :start, :max', $params); 58 | } 59 | 60 | public function creatRefWithdraw() { 61 | $amount = $_SESSION['account']['refBalance']; 62 | $_SESSION['account']['refBalance'] = 0; 63 | 64 | $params = [ 65 | 'id' => $_SESSION['account']['id'], 66 | ]; 67 | $this->db->query('UPDATE accounts SET refBalance = 0 WHERE id = :id', $params); 68 | 69 | $params = [ 70 | 'id' => '', 71 | 'uid' => $_SESSION['account']['id'], 72 | 'unixTime' => time(), 73 | 'amount' => $amount, 74 | ]; 75 | $this->db->query('INSERT INTO ref_withdraw VALUES (:id, :uid, :unixTime, :amount)', $params); 76 | 77 | $params = [ 78 | 'id' => '', 79 | 'uid' => $_SESSION['account']['id'], 80 | 'unixTime' => time(), 81 | 'description' => 'Вывод реферального вознаграждения, сумма '.$amount.' $', 82 | ]; 83 | $this->db->query('INSERT INTO history VALUES (:id, :uid, :unixTime, :description)', $params); 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /application/config/routes.php: -------------------------------------------------------------------------------- 1 | [ 6 | 'controller' => 'main', 7 | 'action' => 'index', 8 | ], 9 | // MerchantController 10 | 'merchant/perfectmoney' => [ 11 | 'controller' => 'merchant', 12 | 'action' => 'perfectmoney', 13 | ], 14 | // DashboardController 15 | 'dashboard/tariffs' => [ 16 | 'controller' => 'dashboard', 17 | 'action' => 'tariffs', 18 | ], 19 | 'dashboard/tariffs/{page:\d+}' => [ 20 | 'controller' => 'dashboard', 21 | 'action' => 'tariffs', 22 | ], 23 | 'dashboard/invest/{id:\d+}' => [ 24 | 'controller' => 'dashboard', 25 | 'action' => 'invest', 26 | ], 27 | 'dashboard/history' => [ 28 | 'controller' => 'dashboard', 29 | 'action' => 'history', 30 | ], 31 | 'dashboard/history/{page:\d+}' => [ 32 | 'controller' => 'dashboard', 33 | 'action' => 'history', 34 | ], 35 | 'dashboard/referrals' => [ 36 | 'controller' => 'dashboard', 37 | 'action' => 'referrals', 38 | ], 39 | 'dashboard/referrals/{page:\d+}' => [ 40 | 'controller' => 'dashboard', 41 | 'action' => 'referrals', 42 | ], 43 | // AccountController 44 | 'account/login' => [ 45 | 'controller' => 'account', 46 | 'action' => 'login', 47 | ], 48 | 'account/register' => [ 49 | 'controller' => 'account', 50 | 'action' => 'register', 51 | ], 52 | 'account/register/{ref:\w+}' => [ 53 | 'controller' => 'account', 54 | 'action' => 'register', 55 | ], 56 | 'account/recovery' => [ 57 | 'controller' => 'account', 58 | 'action' => 'recovery', 59 | ], 60 | 'account/confirm/{token:\w+}' => [ 61 | 'controller' => 'account', 62 | 'action' => 'confirm', 63 | ], 64 | 'account/reset/{token:\w+}' => [ 65 | 'controller' => 'account', 66 | 'action' => 'reset', 67 | ], 68 | 'account/profile' => [ 69 | 'controller' => 'account', 70 | 'action' => 'profile', 71 | ], 72 | 'account/logout' => [ 73 | 'controller' => 'account', 74 | 'action' => 'logout', 75 | ], 76 | // AdminController 77 | 'admin/withdraw' => [ 78 | 'controller' => 'admin', 79 | 'action' => 'withdraw', 80 | ], 81 | 'admin/history' => [ 82 | 'controller' => 'admin', 83 | 'action' => 'history', 84 | ], 85 | 'admin/history/{page:\d+}' => [ 86 | 'controller' => 'admin', 87 | 'action' => 'history', 88 | ], 89 | 'admin/tariffs' => [ 90 | 'controller' => 'admin', 91 | 'action' => 'tariffs', 92 | ], 93 | 'admin/tariffs/{page:\d+}' => [ 94 | 'controller' => 'admin', 95 | 'action' => 'tariffs', 96 | ], 97 | 'admin/login' => [ 98 | 'controller' => 'admin', 99 | 'action' => 'login', 100 | ], 101 | 'admin/logout' => [ 102 | 'controller' => 'admin', 103 | 'action' => 'logout', 104 | ], 105 | ]; -------------------------------------------------------------------------------- /application/views/dashboard/invest.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 |
5 |
6 |
7 |
8 | 9 | 10 |
11 |
12 |
13 |
14 | 15 | 16 |
17 |
18 |
19 |
20 | 21 | 22 |
23 |
24 | 25 | 26 |
27 |
28 | 29 | 30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 |
42 |
43 |
-------------------------------------------------------------------------------- /application/lib/Pagination.php: -------------------------------------------------------------------------------- 1 | route = $route; 16 | $this->total = $total; 17 | $this->limit = $limit; 18 | $this->amount = $this->amount(); 19 | $this->setCurrentPage(); 20 | } 21 | 22 | public function get() { 23 | $links = null; 24 | $limits = $this->limits(); 25 | $html = ''; 42 | return $html; 43 | } 44 | 45 | private function generateHtml($page, $text = null) { 46 | if (!$text) { 47 | $text = $page; 48 | } 49 | return '
  • '.$text.'
  • '; 50 | } 51 | 52 | private function limits() { 53 | $left = $this->current_page - round($this->max / 2); 54 | $start = $left > 0 ? $left : 1; 55 | if ($start + $this->max <= $this->amount) { 56 | $end = $start > 1 ? $start + $this->max : $this->max; 57 | } 58 | else { 59 | $end = $this->amount; 60 | $start = $this->amount - $this->max > 0 ? $this->amount - $this->max : 1; 61 | } 62 | return array($start, $end); 63 | } 64 | 65 | private function setCurrentPage() { 66 | if (isset($this->route['page'])) { 67 | $currentPage = $this->route['page']; 68 | } else { 69 | $currentPage = 1; 70 | } 71 | $this->current_page = $currentPage; 72 | if ($this->current_page > 0) { 73 | if ($this->current_page > $this->amount) { 74 | $this->current_page = $this->amount; 75 | } 76 | } else { 77 | $this->current_page = 1; 78 | } 79 | } 80 | 81 | private function amount() { 82 | return ceil($this->total / $this->limit); 83 | } 84 | } -------------------------------------------------------------------------------- /application/views/layouts/default.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?php echo $title; ?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /application/views/layouts/admin.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <?php echo $title; ?> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | route['action'] != 'login'): ?> 16 | 46 | 47 | 48 | route['action'] != 'login'): ?> 49 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /application/views/admin/tariffs.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |
    6 |
    7 |
    8 | 9 |

    Список инвестиций пуст

    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 49 | 50 |
    Номер вкладаДата стартаДата завершенияСуммаПолучаетеПроцентЛогинE-mailСтатус
    $ $ % 37 | = $val['unixTimeFinish']): ?> 38 | 39 | Ожидает выплаты 40 | 41 | Закрыт 42 | 43 | 44 | Активна 45 | 46 |
    51 | 52 | 53 |
    54 |
    55 |
    56 |
    57 |
    58 |
    -------------------------------------------------------------------------------- /application/controllers/AccountController.php: -------------------------------------------------------------------------------- 1 | model->validate(['email', 'login', 'wallet', 'password', 'ref'], $_POST)) { 14 | $this->view->message('error', $this->model->error); 15 | } 16 | elseif ($this->model->checkEmailExists($_POST['email'])) { 17 | $this->view->message('error', 'Этот E-mail уже используется'); 18 | } 19 | elseif (!$this->model->checkLoginExists($_POST['login'])) { 20 | $this->view->message('error', $this->model->error); 21 | } 22 | $this->model->register($_POST); 23 | $this->view->message('success', 'Регистрация завершена, подтвердите свой E-mail'); 24 | } 25 | $this->view->render('Регистрация'); 26 | } 27 | 28 | public function confirmAction() { 29 | if (!$this->model->checkTokenExists($this->route['token'])) { 30 | $this->view->redirect('account/login'); 31 | } 32 | $this->model->activate($this->route['token']); 33 | $this->view->render('Аккаунт активирован'); 34 | } 35 | 36 | // Вход 37 | 38 | public function loginAction() { 39 | if (!empty($_POST)) { 40 | if (!$this->model->validate(['login', 'password'], $_POST)) { 41 | $this->view->message('error', $this->model->error); 42 | } 43 | elseif (!$this->model->checkData($_POST['login'], $_POST['password'])) { 44 | $this->view->message('error', 'Логин или пароль указан неверно'); 45 | } 46 | elseif (!$this->model->checkStatus('login', $_POST['login'])) { 47 | $this->view->message('error', $this->model->error); 48 | } 49 | $this->model->login($_POST['login']); 50 | $this->view->location('account/profile'); 51 | } 52 | $this->view->render('Вход'); 53 | } 54 | 55 | // Профиль 56 | 57 | public function profileAction() { 58 | if (!empty($_POST)) { 59 | if (!$this->model->validate(['email', 'wallet'], $_POST)) { 60 | $this->view->message('error', $this->model->error); 61 | } 62 | $id = $this->model->checkEmailExists($_POST['email']); 63 | if ($id and $id != $_SESSION['account']['id']) { 64 | $this->view->message('error', 'Этот E-mail уже используется'); 65 | } 66 | if (!empty($_POST['password']) and !$this->model->validate(['password'], $_POST)) { 67 | $this->view->message('error', $this->model->error); 68 | } 69 | $this->model->save($_POST); 70 | $this->view->message('error', 'Сохранено'); 71 | } 72 | $this->view->render('Профиль'); 73 | } 74 | 75 | public function logoutAction() { 76 | unset($_SESSION['account']); 77 | $this->view->redirect('account/login'); 78 | } 79 | 80 | // Восстановление пароля 81 | 82 | public function recoveryAction() { 83 | if (!empty($_POST)) { 84 | if (!$this->model->validate(['email'], $_POST)) { 85 | $this->view->message('error', $this->model->error); 86 | } 87 | elseif (!$this->model->checkEmailExists($_POST['email'])) { 88 | $this->view->message('error', 'Пользователь не найден'); 89 | } 90 | elseif (!$this->model->checkStatus('email', $_POST['email'])) { 91 | $this->view->message('error', $this->model->error); 92 | } 93 | $this->model->recovery($_POST); 94 | $this->view->message('success', 'Запрос на восстановление пароля отправлен на E-mail'); 95 | } 96 | $this->view->render('Восстановление пароля'); 97 | } 98 | 99 | public function resetAction() { 100 | if (!$this->model->checkTokenExists($this->route['token'])) { 101 | $this->view->redirect('account/login'); 102 | } 103 | $password = $this->model->reset($this->route['token']); 104 | $vars = [ 105 | 'password' => $password, 106 | ]; 107 | $this->view->render('Пароль сброшен', $vars); 108 | } 109 | } -------------------------------------------------------------------------------- /application/models/Admin.php: -------------------------------------------------------------------------------- 1 | error = 'Логин или пароль указан неверно'; 13 | return false; 14 | } 15 | return true; 16 | } 17 | 18 | public function historyCount() { 19 | return $this->db->column('SELECT COUNT(id) FROM history'); 20 | } 21 | 22 | public function historyList($route) { 23 | $max = 10; 24 | $params = [ 25 | 'max' => $max, 26 | 'start' => ((($route['page'] ?? 1) - 1) * $max), 27 | ]; 28 | $arr = []; 29 | $result = $this->db->row('SELECT * FROM history ORDER BY id DESC LIMIT :start, :max', $params); 30 | if (!empty($result)) { 31 | foreach ($result as $key => $val) { 32 | $arr[$key] = $val; 33 | $params = [ 34 | 'id' => $val['uid'], 35 | ]; 36 | $account = $this->db->row('SELECT login, email FROM accounts WHERE id = :id', $params)[0]; 37 | $arr[$key]['login'] = $account['login']; 38 | $arr[$key]['email'] = $account['email']; 39 | } 40 | } 41 | return $arr; 42 | } 43 | 44 | public function withdrawRefList() { 45 | $arr = []; 46 | $result = $this->db->row('SELECT * FROM ref_withdraw ORDER BY id DESC'); 47 | if (!empty($result)) { 48 | foreach ($result as $key => $val) { 49 | $arr[$key] = $val; 50 | $params = [ 51 | 'id' => $val['uid'], 52 | ]; 53 | $account = $this->db->row('SELECT login, wallet FROM accounts WHERE id = :id', $params)[0]; 54 | $arr[$key]['login'] = $account['login']; 55 | $arr[$key]['wallet'] = $account['wallet']; 56 | } 57 | } 58 | return $arr; 59 | } 60 | 61 | public function withdrawTariffsList() { 62 | $arr = []; 63 | $result = $this->db->row('SELECT * FROM tariffs WHERE UNIX_TIMESTAMP() >= unixTimeFinish AND sumOut != 0 ORDER BY id DESC'); 64 | if (!empty($result)) { 65 | foreach ($result as $key => $val) { 66 | $arr[$key] = $val; 67 | $params = [ 68 | 'id' => $val['uid'], 69 | ]; 70 | $account = $this->db->row('SELECT login, wallet FROM accounts WHERE id = :id', $params)[0]; 71 | $arr[$key]['login'] = $account['login']; 72 | $arr[$key]['wallet'] = $account['wallet']; 73 | } 74 | } 75 | return $arr; 76 | } 77 | 78 | public function withdrawRefComplete($id) { 79 | $params = [ 80 | 'id' => $id, 81 | ]; 82 | $data = $this->db->row('SELECT uid, amount FROM ref_withdraw WHERE id = :id', $params); 83 | if (!$data) { 84 | return false; 85 | } 86 | $this->db->query('DELETE FROM ref_withdraw WHERE id = :id', $params); 87 | $data = $data[0]; 88 | $params = [ 89 | 'id' => '', 90 | 'uid' => $data['uid'], 91 | 'unixTime' => time(), 92 | 'description' => 'Выплата реферального вознаграждения произведена, сумма '.$data['amount'].' $', 93 | ]; 94 | $this->db->query('INSERT INTO history VALUES (:id, :uid, :unixTime, :description)', $params); 95 | return true; 96 | } 97 | 98 | public function withdrawTariffsComplete($id) { 99 | $params = [ 100 | 'id' => $id, 101 | ]; 102 | $data = $this->db->row('SELECT uid, sumOut FROM tariffs WHERE id = :id', $params); 103 | if (!$data) { 104 | return false; 105 | } 106 | $this->db->query('UPDATE tariffs SET sumOut = 0 WHERE id = :id', $params); 107 | $data = $data[0]; 108 | $params = [ 109 | 'id' => '', 110 | 'uid' => $data['uid'], 111 | 'unixTime' => time(), 112 | 'description' => 'Выплата по тарифу # '.$id.' произведена, сумма '.$data['sumOut'].' $', 113 | ]; 114 | $this->db->query('INSERT INTO history VALUES (:id, :uid, :unixTime, :description)', $params); 115 | return true; 116 | } 117 | 118 | public function tariffsCount() { 119 | return $this->db->column('SELECT COUNT(id) FROM tariffs'); 120 | } 121 | 122 | public function tariffsList($route) { 123 | $max = 10; 124 | $params = [ 125 | 'max' => $max, 126 | 'start' => ((($route['page'] ?? 1) - 1) * $max), 127 | ]; 128 | $arr = []; 129 | $result = $this->db->row('SELECT * FROM tariffs ORDER BY id DESC LIMIT :start, :max', $params); 130 | if (!empty($result)) { 131 | foreach ($result as $key => $val) { 132 | $arr[$key] = $val; 133 | $params = [ 134 | 'id' => $val['uid'], 135 | ]; 136 | $account = $this->db->row('SELECT login, email FROM accounts WHERE id = :id', $params)[0]; 137 | $arr[$key]['login'] = $account['login']; 138 | $arr[$key]['email'] = $account['email']; 139 | } 140 | } 141 | return $arr; 142 | } 143 | 144 | } -------------------------------------------------------------------------------- /application/views/admin/withdraw.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    Заявки на вывод по тарфиам
    5 |
    6 |
    7 |
    8 | 9 |

    Список заявок на вывод по тарфиам пуст

    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 37 | 38 | 39 | 40 |
    Номер вкладаДатаСуммаЛогинКошелек
    # $ 31 |
    32 | 33 | 34 | 35 |
    36 |
    41 | 42 |
    43 |
    44 |
    45 |
    46 |
    47 |
    Заявки на вывод реферального вознаграждения
    48 |
    49 |
    50 |
    51 | 52 |

    Список заявок на вывод реферального вознаграждения пуст

    53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 78 | 79 | 80 | 81 |
    ДатаСуммаЛогинКошелек
    $ 72 |
    73 | 74 | 75 | 76 |
    77 |
    82 | 83 |
    84 |
    85 |
    86 |
    87 |
    88 |
    -------------------------------------------------------------------------------- /application/models/Account.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'pattern' => '#^([a-z0-9_.-]{1,20}+)@([a-z0-9_.-]+)\.([a-z\.]{2,10})$#', 13 | 'message' => 'E-mail адрес указан неверно', 14 | ], 15 | 'login' => [ 16 | 'pattern' => '#^[a-z0-9]{3,15}$#', 17 | 'message' => 'Логин указан неверно (разрешены только латинские буквы и цифры от 3 до 15 символов', 18 | ], 19 | 'ref' => [ 20 | 'pattern' => '#^[a-z0-9]{3,15}$#', 21 | 'message' => 'Логин пригласившего указан неверно', 22 | ], 23 | 'wallet' => [ 24 | 'pattern' => '#^[A-z0-9]{3,15}$#', 25 | 'message' => 'Кошелек Perfect Money указан неверно', 26 | ], 27 | 'password' => [ 28 | 'pattern' => '#^[a-z0-9]{10,30}$#', 29 | 'message' => 'Пароль указан неверно (разрешены только латинские буквы и цифры от 10 до 30 символов', 30 | 31 | ], 32 | ]; 33 | foreach ($input as $val) { 34 | if (!isset($post[$val]) or !preg_match($rules[$val]['pattern'], $post[$val])) { 35 | $this->error = $rules[$val]['message']; 36 | return false; 37 | } 38 | } 39 | if (isset($post['ref'])) { 40 | if ($post['login'] == $post['ref']) { 41 | $this->error = 'Регистрация невозможна'; 42 | return false; 43 | } 44 | } 45 | return true; 46 | } 47 | 48 | public function checkEmailExists($email) { 49 | $params = [ 50 | 'email' => $email, 51 | ]; 52 | return $this->db->column('SELECT id FROM accounts WHERE email = :email', $params); 53 | } 54 | 55 | public function checkLoginExists($login) { 56 | $params = [ 57 | 'login' => $login, 58 | ]; 59 | if ($this->db->column('SELECT id FROM accounts WHERE login = :login', $params)) { 60 | $this->error = 'Этот логин уже используется'; 61 | return false; 62 | } 63 | return true; 64 | } 65 | 66 | public function checkTokenExists($token) { 67 | $params = [ 68 | 'token' => $token, 69 | ]; 70 | return $this->db->column('SELECT id FROM accounts WHERE token = :token', $params); 71 | } 72 | 73 | public function activate($token) { 74 | $params = [ 75 | 'token' => $token, 76 | ]; 77 | $this->db->query('UPDATE accounts SET status = 1, token = "" WHERE token = :token', $params); 78 | } 79 | 80 | public function checkRefExists($login) { 81 | $params = [ 82 | 'login' => $login, 83 | ]; 84 | return $this->db->column('SELECT id FROM accounts WHERE login = :login', $params); 85 | } 86 | 87 | public function createToken() { 88 | return substr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstuvwxyz', 30)), 0, 30); 89 | } 90 | 91 | public function register($post) { 92 | $token = $this->createToken(); 93 | if ($post['ref'] == 'none') { 94 | $ref = 0; 95 | } 96 | else { 97 | $ref = $this->checkRefExists($post['ref']); 98 | if (!$ref) { 99 | $ref = 0; 100 | } 101 | } 102 | $params = [ 103 | 'id' => '', 104 | 'email' => $post['email'], 105 | 'login' => $post['login'], 106 | 'wallet' => $post['wallet'], 107 | 'password' => password_hash($post['password'], PASSWORD_BCRYPT), 108 | 'ref' => $ref, 109 | 'refBalance' => 0, 110 | 'token' => $token, 111 | 'status' => 0, 112 | ]; 113 | $this->db->query('INSERT INTO accounts VALUES (:id, :email, :login, :wallet, :password, :ref, :refBalance, :token, :status)', $params); 114 | mail($post['email'], 'Register', 'Confirm: '.$_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].'/account/confirm/'.$token); 115 | } 116 | 117 | public function checkData($login, $password) { 118 | $params = [ 119 | 'login' => $login, 120 | ]; 121 | $hash = $this->db->column('SELECT password FROM accounts WHERE login = :login', $params); 122 | if (!$hash or !password_verify($password, $hash)) { 123 | return false; 124 | } 125 | return true; 126 | } 127 | 128 | public function checkStatus($type, $data) { 129 | $params = [ 130 | $type => $data, 131 | ]; 132 | $status = $this->db->column('SELECT status FROM accounts WHERE '.$type.' = :'.$type, $params); 133 | if ($status != 1) { 134 | $this->error = 'Аккаунт ожидает подтверждения по E-mail'; 135 | return false; 136 | } 137 | return true; 138 | } 139 | 140 | public function login($login) { 141 | $params = [ 142 | 'login' => $login, 143 | ]; 144 | $data = $this->db->row('SELECT * FROM accounts WHERE login = :login', $params); 145 | $_SESSION['account'] = $data[0]; 146 | } 147 | 148 | public function recovery($post) { 149 | $token = $this->createToken(); 150 | $params = [ 151 | 'email' => $post['email'], 152 | 'token' => $token, 153 | ]; 154 | $this->db->query('UPDATE accounts SET token = :token WHERE email = :email', $params); 155 | mail($post['email'], 'Recovery', 'Confirm: '.$_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].'/account/reset/'.$token); 156 | } 157 | 158 | public function reset($token) { 159 | $new_password = $this->createToken(); 160 | $params = [ 161 | 'token' => $token, 162 | 'password' => password_hash($new_password, PASSWORD_BCRYPT), 163 | ]; 164 | $this->db->query('UPDATE accounts SET status = 1, token = "", password = :password WHERE token = :token', $params); 165 | return $new_password; 166 | } 167 | 168 | public function save($post) { 169 | $params = [ 170 | 'id' => $_SESSION['account']['id'], 171 | 'email' => $post['email'], 172 | 'wallet' => $post['wallet'], 173 | ]; 174 | if (!empty($post['password'])) { 175 | $params['password'] = password_hash($post['password'], PASSWORD_BCRYPT); 176 | $sql = ',password = :password'; 177 | } 178 | else { 179 | $sql = ''; 180 | } 181 | foreach ($params as $key => $val) { 182 | $_SESSION['account'][$key] = $val; 183 | } 184 | $this->db->query('UPDATE accounts SET email = :email, wallet = :wallet'.$sql.' WHERE id = :id', $params); 185 | } 186 | 187 | } -------------------------------------------------------------------------------- /public/styles/admin.css: -------------------------------------------------------------------------------- 1 | html { 2 | position: relative; 3 | min-height: 100%; } 4 | 5 | body { 6 | overflow-x: hidden; } 7 | 8 | body.sticky-footer { 9 | margin-bottom: 56px; } 10 | body.sticky-footer .content-wrapper { 11 | min-height: calc(100vh - 56px - 56px); } 12 | 13 | body.fixed-nav { 14 | padding-top: 56px; } 15 | 16 | .content-wrapper { 17 | min-height: calc(100vh - 56px); 18 | padding-top: 1rem; } 19 | 20 | .scroll-to-top { 21 | position: fixed; 22 | right: 15px; 23 | bottom: 3px; 24 | display: none; 25 | width: 50px; 26 | height: 50px; 27 | text-align: center; 28 | color: white; 29 | background: rgba(52, 58, 64, 0.5); 30 | line-height: 45px; } 31 | .scroll-to-top:focus, .scroll-to-top:hover { 32 | color: white; } 33 | .scroll-to-top:hover { 34 | background: #343a40; } 35 | .scroll-to-top i { 36 | font-weight: 800; } 37 | 38 | .smaller { 39 | font-size: 0.7rem; } 40 | 41 | .o-hidden { 42 | overflow: hidden !important; } 43 | 44 | .z-0 { 45 | z-index: 0; } 46 | 47 | .z-1 { 48 | z-index: 1; } 49 | 50 | #mainNav .navbar-collapse { 51 | overflow: auto; 52 | max-height: 75vh; } 53 | #mainNav .navbar-collapse .navbar-nav .nav-item .nav-link { 54 | cursor: pointer; } 55 | #mainNav .navbar-collapse .navbar-sidenav .nav-link-collapse:after { 56 | float: right; 57 | content: '\f107'; 58 | font-family: 'FontAwesome'; } 59 | #mainNav .navbar-collapse .navbar-sidenav .nav-link-collapse.collapsed:after { 60 | content: '\f105'; } 61 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-second-level, 62 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-third-level { 63 | padding-left: 0; } 64 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-second-level > li > a, 65 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-third-level > li > a { 66 | display: block; 67 | padding: 0.5em 0; } 68 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-second-level > li > a:focus, #mainNav .navbar-collapse .navbar-sidenav .sidenav-second-level > li > a:hover, 69 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-third-level > li > a:focus, 70 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-third-level > li > a:hover { 71 | text-decoration: none; } 72 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-second-level > li > a { 73 | padding-left: 1em; } 74 | #mainNav .navbar-collapse .navbar-sidenav .sidenav-third-level > li > a { 75 | padding-left: 2em; } 76 | #mainNav .navbar-collapse .sidenav-toggler { 77 | display: none; } 78 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link { 79 | position: relative; 80 | min-width: 45px; } 81 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link:after { 82 | float: right; 83 | width: auto; 84 | content: '\f105'; 85 | border: none; 86 | font-family: 'FontAwesome'; } 87 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link .indicator { 88 | position: absolute; 89 | top: 5px; 90 | left: 21px; 91 | font-size: 10px; } 92 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown.show > .nav-link:after { 93 | content: '\f107'; } 94 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown .dropdown-menu > .dropdown-item > .dropdown-message { 95 | overflow: hidden; 96 | max-width: none; 97 | text-overflow: ellipsis; } 98 | 99 | @media (min-width: 992px) { 100 | #mainNav .navbar-brand { 101 | width: 250px; } 102 | #mainNav .navbar-collapse { 103 | overflow: visible; 104 | max-height: none; } 105 | #mainNav .navbar-collapse .navbar-sidenav { 106 | position: absolute; 107 | top: 0; 108 | left: 0; 109 | overflow-x: hidden; 110 | overflow-y: auto; 111 | -webkit-flex-direction: column; 112 | -ms-flex-direction: column; 113 | flex-direction: column; 114 | margin-top: 56px; } 115 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item { 116 | width: 250px; 117 | padding: 0; } 118 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item > .nav-link { 119 | padding: 1em; } 120 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level, 121 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level { 122 | padding-left: 0; 123 | list-style: none; } 124 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li, 125 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li { 126 | width: 250px; } 127 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a, 128 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a { 129 | padding: 1em; } 130 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a { 131 | padding-left: 2.75em; } 132 | #mainNav .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a { 133 | padding-left: 3.75em; } 134 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link { 135 | min-width: 0; } 136 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link:after { 137 | width: 24px; 138 | text-align: center; } 139 | #mainNav .navbar-collapse .navbar-nav > .nav-item.dropdown .dropdown-menu > .dropdown-item > .dropdown-message { 140 | max-width: 300px; } } 141 | 142 | #mainNav.fixed-top .sidenav-toggler { 143 | display: none; } 144 | 145 | @media (min-width: 992px) { 146 | #mainNav.fixed-top .navbar-sidenav { 147 | height: calc(100vh - 112px); } 148 | #mainNav.fixed-top .sidenav-toggler { 149 | position: absolute; 150 | top: 0; 151 | left: 0; 152 | display: flex; 153 | overflow-x: hidden; 154 | overflow-y: auto; 155 | -webkit-flex-direction: column; 156 | -ms-flex-direction: column; 157 | flex-direction: column; 158 | margin-top: calc(100vh - 56px); } 159 | #mainNav.fixed-top .sidenav-toggler > .nav-item { 160 | width: 250px; 161 | padding: 0; } 162 | #mainNav.fixed-top .sidenav-toggler > .nav-item > .nav-link { 163 | padding: 1em; } } 164 | 165 | #mainNav.fixed-top.navbar-dark .sidenav-toggler { 166 | background-color: #212529; } 167 | #mainNav.fixed-top.navbar-dark .sidenav-toggler a i { 168 | color: #adb5bd; } 169 | 170 | #mainNav.fixed-top.navbar-light .sidenav-toggler { 171 | background-color: #dee2e6; } 172 | #mainNav.fixed-top.navbar-light .sidenav-toggler a i { 173 | color: rgba(0, 0, 0, 0.5); } 174 | 175 | body.sidenav-toggled #mainNav.fixed-top .sidenav-toggler { 176 | overflow-x: hidden; 177 | width: 55px; } 178 | body.sidenav-toggled #mainNav.fixed-top .sidenav-toggler .nav-item, 179 | body.sidenav-toggled #mainNav.fixed-top .sidenav-toggler .nav-link { 180 | width: 55px !important; } 181 | 182 | body.sidenav-toggled #mainNav.fixed-top #sidenavToggler i { 183 | -webkit-transform: scaleX(-1); 184 | -moz-transform: scaleX(-1); 185 | -o-transform: scaleX(-1); 186 | transform: scaleX(-1); 187 | filter: FlipH; 188 | -ms-filter: 'FlipH'; } 189 | 190 | #mainNav.static-top .sidenav-toggler { 191 | display: none; } 192 | 193 | @media (min-width: 992px) { 194 | #mainNav.static-top .sidenav-toggler { 195 | display: flex; } } 196 | 197 | body.sidenav-toggled #mainNav.static-top #sidenavToggler i { 198 | -webkit-transform: scaleX(-1); 199 | -moz-transform: scaleX(-1); 200 | -o-transform: scaleX(-1); 201 | transform: scaleX(-1); 202 | filter: FlipH; 203 | -ms-filter: 'FlipH'; } 204 | 205 | .content-wrapper { 206 | overflow-x: hidden; 207 | background: white; } 208 | @media (min-width: 992px) { 209 | .content-wrapper { 210 | margin-left: 250px; } } 211 | 212 | #sidenavToggler i { 213 | font-weight: 800; } 214 | 215 | .navbar-sidenav-tooltip.show { 216 | display: none; } 217 | 218 | @media (min-width: 992px) { 219 | body.sidenav-toggled .content-wrapper { 220 | margin-left: 55px; } } 221 | 222 | body.sidenav-toggled .navbar-sidenav { 223 | overflow-x: hidden; 224 | width: 55px; } 225 | body.sidenav-toggled .navbar-sidenav .nav-link-text { 226 | display: none; } 227 | body.sidenav-toggled .navbar-sidenav .nav-item, 228 | body.sidenav-toggled .navbar-sidenav .nav-link { 229 | width: 55px !important; } 230 | body.sidenav-toggled .navbar-sidenav .nav-item:after, 231 | body.sidenav-toggled .navbar-sidenav .nav-link:after { 232 | display: none; } 233 | 234 | body.sidenav-toggled .navbar-sidenav-tooltip.show { 235 | display: flex; } 236 | 237 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav .nav-link-collapse:after { 238 | color: #868e96; } 239 | 240 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item > .nav-link { 241 | color: #868e96; } 242 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item > .nav-link:hover { 243 | color: #adb5bd; } 244 | 245 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a, 246 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a { 247 | color: #868e96; } 248 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a:focus, #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a:hover, 249 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a:focus, 250 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a:hover { 251 | color: #adb5bd; } 252 | 253 | #mainNav.navbar-dark .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link:after { 254 | color: #adb5bd; } 255 | 256 | @media (min-width: 992px) { 257 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav { 258 | background: #343a40; } 259 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav li.active a { 260 | color: white !important; 261 | background-color: #495057; } 262 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav li.active a:focus, #mainNav.navbar-dark .navbar-collapse .navbar-sidenav li.active a:hover { 263 | color: white; } 264 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level, 265 | #mainNav.navbar-dark .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level { 266 | background: #343a40; } } 267 | 268 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav .nav-link-collapse:after { 269 | color: rgba(0, 0, 0, 0.5); } 270 | 271 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item > .nav-link { 272 | color: rgba(0, 0, 0, 0.5); } 273 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item > .nav-link:hover { 274 | color: rgba(0, 0, 0, 0.7); } 275 | 276 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a, 277 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a { 278 | color: rgba(0, 0, 0, 0.5); } 279 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a:focus, #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level > li > a:hover, 280 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a:focus, 281 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level > li > a:hover { 282 | color: rgba(0, 0, 0, 0.7); } 283 | 284 | #mainNav.navbar-light .navbar-collapse .navbar-nav > .nav-item.dropdown > .nav-link:after { 285 | color: rgba(0, 0, 0, 0.5); } 286 | 287 | @media (min-width: 992px) { 288 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav { 289 | background: #f8f9fa; } 290 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav li.active a { 291 | color: #000 !important; 292 | background-color: #e9ecef; } 293 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav li.active a:focus, #mainNav.navbar-light .navbar-collapse .navbar-sidenav li.active a:hover { 294 | color: #000; } 295 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-second-level, 296 | #mainNav.navbar-light .navbar-collapse .navbar-sidenav > .nav-item .sidenav-third-level { 297 | background: #f8f9fa; } } 298 | 299 | .card-body-icon { 300 | position: absolute; 301 | z-index: 0; 302 | top: -25px; 303 | right: -25px; 304 | font-size: 5rem; 305 | -webkit-transform: rotate(15deg); 306 | -ms-transform: rotate(15deg); 307 | transform: rotate(15deg); } 308 | 309 | @media (min-width: 576px) { 310 | .card-columns { 311 | column-count: 1; } } 312 | 313 | @media (min-width: 768px) { 314 | .card-columns { 315 | column-count: 2; } } 316 | 317 | @media (min-width: 1200px) { 318 | .card-columns { 319 | column-count: 2; } } 320 | 321 | .card-login { 322 | max-width: 25rem; } 323 | 324 | .card-register { 325 | max-width: 40rem; } 326 | 327 | footer.sticky-footer { 328 | position: absolute; 329 | right: 0; 330 | bottom: 0; 331 | width: 100%; 332 | height: 56px; 333 | background-color: #e9ecef; 334 | line-height: 55px; } 335 | @media (min-width: 992px) { 336 | footer.sticky-footer { 337 | width: calc(100% - 250px); } } 338 | 339 | @media (min-width: 992px) { 340 | body.sidenav-toggled footer.sticky-footer { 341 | width: calc(100% - 55px); } } -------------------------------------------------------------------------------- /public/scripts/popper.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Federico Zivolo 2017 3 | Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). 4 | */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=window.getComputedStyle(e,null);return t?o[t]:o}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e||-1!==['HTML','BODY','#document'].indexOf(e.nodeName))return window.document.body;var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll)/.test(r+s+p)?e:n(o(e))}function r(e){var o=e&&e.offsetParent,i=o&&o.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TD','TABLE'].indexOf(o.nodeName)&&'static'===t(o,'position')?r(o):o:window.document.documentElement}function p(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||r(e.firstElementChild)===e)}function s(e){return null===e.parentNode?e:s(e.parentNode)}function d(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return window.document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,i=o?e:t,n=o?t:e,a=document.createRange();a.setStart(i,0),a.setEnd(n,0);var f=a.commonAncestorContainer;if(e!==f&&t!==f||i.contains(n))return p(f)?f:r(f);var l=s(e);return l.host?d(l.host,t):d(e,s(t).host)}function a(e){var t=1=o.clientWidth&&i>=o.clientHeight}),f=0i[e]&&!t.escapeWithReference&&(n=z(p[o],i[e]-('right'===e?p.width:p.height))),pe({},o,n)}};return n.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';p=se({},p,s[t](e))}),e.offsets.popper=p,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,i=t.reference,n=e.placement.split('-')[0],r=V,p=-1!==['top','bottom'].indexOf(n),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(i[s])&&(e.offsets.popper[d]=r(i[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){if(!F(e.instance.modifiers,'arrow','keepTogether'))return e;var o=t.element;if('string'==typeof o){if(o=e.instance.popper.querySelector(o),!o)return e;}else if(!e.instance.popper.contains(o))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var i=e.placement.split('-')[0],n=e.offsets,r=n.popper,p=n.reference,s=-1!==['left','right'].indexOf(i),d=s?'height':'width',a=s?'top':'left',f=s?'left':'top',l=s?'bottom':'right',m=O(o)[d];p[l]-mr[l]&&(e.offsets.popper[a]+=p[a]+m-r[l]);var h=p[a]+p[d]/2-m/2,g=h-c(e.offsets.popper)[a];return g=_(z(r[d]-m,g),0),e.arrowElement=o,e.offsets.arrow={},e.offsets.arrow[a]=Math.round(g),e.offsets.arrow[f]='',e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=w(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement),i=e.placement.split('-')[0],n=L(i),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case fe.FLIP:p=[i,n];break;case fe.CLOCKWISE:p=K(i);break;case fe.COUNTERCLOCKWISE:p=K(i,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(i!==s||p.length===d+1)return e;i=e.placement.split('-')[0],n=L(i);var a=e.offsets.popper,f=e.offsets.reference,l=V,m='left'===i&&l(a.right)>l(f.left)||'right'===i&&l(a.left)l(f.top)||'bottom'===i&&l(a.top)l(o.right),g=l(a.top)l(o.bottom),b='left'===i&&h||'right'===i&&c||'top'===i&&g||'bottom'===i&&u,y=-1!==['top','bottom'].indexOf(i),w=!!t.flipVariations&&(y&&'start'===r&&h||y&&'end'===r&&c||!y&&'start'===r&&g||!y&&'end'===r&&u);(m||b||w)&&(e.flipped=!0,(m||b)&&(i=p[d+1]),w&&(r=j(r)),e.placement=i+(r?'-'+r:''),e.offsets.popper=se({},e.offsets.popper,S(e.instance.popper,e.offsets.reference,e.placement)),e=N(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport'},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],i=e.offsets,n=i.popper,r=i.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return n[p?'left':'top']=r[t]-(s?n[p?'width':'height']:0),e.placement=L(t),e.offsets.popper=c(n),e}},hide:{order:800,enabled:!0,fn:function(e){if(!F(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=T(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.right=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}(),function(){function t(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function e(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o=function(){function t(t,e){for(var n=0;n0?n:null}catch(t){return null}},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(e){t(e).trigger(s.end)},supportsTransitionEnd:function(){return Boolean(s)},typeCheckConfig:function(t,i,o){for(var r in o)if(o.hasOwnProperty(r)){var s=o[r],a=i[r],l=a&&n(a)?"element":e(a);if(!new RegExp(s).test(l))throw new Error(t.toUpperCase()+': Option "'+r+'" provided type "'+l+'" but expected type "'+s+'".')}}};return s=o(),t.fn.emulateTransitionEnd=r,l.supportsTransitionEnd()&&(t.event.special[l.TRANSITION_END]=i()),l}(jQuery),s=(function(t){var e="alert",i=t.fn[e],s={DISMISS:'[data-dismiss="alert"]'},a={CLOSE:"close.bs.alert",CLOSED:"closed.bs.alert",CLICK_DATA_API:"click.bs.alert.data-api"},l={ALERT:"alert",FADE:"fade",SHOW:"show"},h=function(){function e(t){n(this,e),this._element=t}return e.prototype.close=function(t){t=t||this._element;var e=this._getRootElement(t);this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.prototype.dispose=function(){t.removeData(this._element,"bs.alert"),this._element=null},e.prototype._getRootElement=function(e){var n=r.getSelectorFromElement(e),i=!1;return n&&(i=t(n)[0]),i||(i=t(e).closest("."+l.ALERT)[0]),i},e.prototype._triggerCloseEvent=function(e){var n=t.Event(a.CLOSE);return t(e).trigger(n),n},e.prototype._removeElement=function(e){var n=this;t(e).removeClass(l.SHOW),r.supportsTransitionEnd()&&t(e).hasClass(l.FADE)?t(e).one(r.TRANSITION_END,function(t){return n._destroyElement(e,t)}).emulateTransitionEnd(150):this._destroyElement(e)},e.prototype._destroyElement=function(e){t(e).detach().trigger(a.CLOSED).remove()},e._jQueryInterface=function(n){return this.each(function(){var i=t(this),o=i.data("bs.alert");o||(o=new e(this),i.data("bs.alert",o)),"close"===n&&o[n](this)})},e._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},o(e,null,[{key:"VERSION",get:function(){return"4.0.0-beta"}}]),e}();t(document).on(a.CLICK_DATA_API,s.DISMISS,h._handleDismiss(new h)),t.fn[e]=h._jQueryInterface,t.fn[e].Constructor=h,t.fn[e].noConflict=function(){return t.fn[e]=i,h._jQueryInterface}}(jQuery),function(t){var e="button",i=t.fn[e],r={ACTIVE:"active",BUTTON:"btn",FOCUS:"focus"},s={DATA_TOGGLE_CARROT:'[data-toggle^="button"]',DATA_TOGGLE:'[data-toggle="buttons"]',INPUT:"input",ACTIVE:".active",BUTTON:".btn"},a={CLICK_DATA_API:"click.bs.button.data-api",FOCUS_BLUR_DATA_API:"focus.bs.button.data-api blur.bs.button.data-api"},l=function(){function e(t){n(this,e),this._element=t}return e.prototype.toggle=function(){var e=!0,n=!0,i=t(this._element).closest(s.DATA_TOGGLE)[0];if(i){var o=t(this._element).find(s.INPUT)[0];if(o){if("radio"===o.type)if(o.checked&&t(this._element).hasClass(r.ACTIVE))e=!1;else{var a=t(i).find(s.ACTIVE)[0];a&&t(a).removeClass(r.ACTIVE)}if(e){if(o.hasAttribute("disabled")||i.hasAttribute("disabled")||o.classList.contains("disabled")||i.classList.contains("disabled"))return;o.checked=!t(this._element).hasClass(r.ACTIVE),t(o).trigger("change")}o.focus(),n=!1}}n&&this._element.setAttribute("aria-pressed",!t(this._element).hasClass(r.ACTIVE)),e&&t(this._element).toggleClass(r.ACTIVE)},e.prototype.dispose=function(){t.removeData(this._element,"bs.button"),this._element=null},e._jQueryInterface=function(n){return this.each(function(){var i=t(this).data("bs.button");i||(i=new e(this),t(this).data("bs.button",i)),"toggle"===n&&i[n]()})},o(e,null,[{key:"VERSION",get:function(){return"4.0.0-beta"}}]),e}();t(document).on(a.CLICK_DATA_API,s.DATA_TOGGLE_CARROT,function(e){e.preventDefault();var n=e.target;t(n).hasClass(r.BUTTON)||(n=t(n).closest(s.BUTTON)),l._jQueryInterface.call(t(n),"toggle")}).on(a.FOCUS_BLUR_DATA_API,s.DATA_TOGGLE_CARROT,function(e){var n=t(e.target).closest(s.BUTTON)[0];t(n).toggleClass(r.FOCUS,/^focus(in)?$/.test(e.type))}),t.fn[e]=l._jQueryInterface,t.fn[e].Constructor=l,t.fn[e].noConflict=function(){return t.fn[e]=i,l._jQueryInterface}}(jQuery),function(t){var e="carousel",s="bs.carousel",a="."+s,l=t.fn[e],h={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0},c={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean"},u={NEXT:"next",PREV:"prev",LEFT:"left",RIGHT:"right"},d={SLIDE:"slide"+a,SLID:"slid"+a,KEYDOWN:"keydown"+a,MOUSEENTER:"mouseenter"+a,MOUSELEAVE:"mouseleave"+a,TOUCHEND:"touchend"+a,LOAD_DATA_API:"load.bs.carousel.data-api",CLICK_DATA_API:"click.bs.carousel.data-api"},f={CAROUSEL:"carousel",ACTIVE:"active",SLIDE:"slide",RIGHT:"carousel-item-right",LEFT:"carousel-item-left",NEXT:"carousel-item-next",PREV:"carousel-item-prev",ITEM:"carousel-item"},p={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"]'},_=function(){function l(e,i){n(this,l),this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this._config=this._getConfig(i),this._element=t(e)[0],this._indicatorsElement=t(this._element).find(p.INDICATORS)[0],this._addEventListeners()}return l.prototype.next=function(){this._isSliding||this._slide(u.NEXT)},l.prototype.nextWhenVisible=function(){document.hidden||this.next()},l.prototype.prev=function(){this._isSliding||this._slide(u.PREV)},l.prototype.pause=function(e){e||(this._isPaused=!0),t(this._element).find(p.NEXT_PREV)[0]&&r.supportsTransitionEnd()&&(r.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},l.prototype.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))},l.prototype.to=function(e){var n=this;this._activeElement=t(this._element).find(p.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 o=e>i?u.NEXT:u.PREV;this._slide(o,this._items[e])}},l.prototype.dispose=function(){t(this._element).off(a),t.removeData(this._element,s),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},l.prototype._getConfig=function(n){return n=t.extend({},h,n),r.typeCheckConfig(e,n,c),n},l.prototype._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)}))},l.prototype._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();break;default:return}},l.prototype._getItemIndex=function(e){return this._items=t.makeArray(t(e).parent().find(p.ITEM)),this._items.indexOf(e)},l.prototype._getItemByDirection=function(t,e){var n=t===u.NEXT,i=t===u.PREV,o=this._getItemIndex(e),r=this._items.length-1;if((i&&0===o||n&&o===r)&&!this._config.wrap)return e;var s=(o+(t===u.PREV?-1:1))%this._items.length;return-1===s?this._items[this._items.length-1]:this._items[s]},l.prototype._triggerSlideEvent=function(e,n){var i=this._getItemIndex(e),o=this._getItemIndex(t(this._element).find(p.ACTIVE_ITEM)[0]),r=t.Event(d.SLIDE,{relatedTarget:e,direction:n,from:o,to:i});return t(this._element).trigger(r),r},l.prototype._setActiveIndicatorElement=function(e){if(this._indicatorsElement){t(this._indicatorsElement).find(p.ACTIVE).removeClass(f.ACTIVE);var n=this._indicatorsElement.children[this._getItemIndex(e)];n&&t(n).addClass(f.ACTIVE)}},l.prototype._slide=function(e,n){var i=this,o=t(this._element).find(p.ACTIVE_ITEM)[0],s=this._getItemIndex(o),a=n||o&&this._getItemByDirection(e,o),l=this._getItemIndex(a),h=Boolean(this._interval),c=void 0,_=void 0,g=void 0;if(e===u.NEXT?(c=f.LEFT,_=f.NEXT,g=u.LEFT):(c=f.RIGHT,_=f.PREV,g=u.RIGHT),a&&t(a).hasClass(f.ACTIVE))this._isSliding=!1;else if(!this._triggerSlideEvent(a,g).isDefaultPrevented()&&o&&a){this._isSliding=!0,h&&this.pause(),this._setActiveIndicatorElement(a);var m=t.Event(d.SLID,{relatedTarget:a,direction:g,from:s,to:l});r.supportsTransitionEnd()&&t(this._element).hasClass(f.SLIDE)?(t(a).addClass(_),r.reflow(a),t(o).addClass(c),t(a).addClass(c),t(o).one(r.TRANSITION_END,function(){t(a).removeClass(c+" "+_).addClass(f.ACTIVE),t(o).removeClass(f.ACTIVE+" "+_+" "+c),i._isSliding=!1,setTimeout(function(){return t(i._element).trigger(m)},0)}).emulateTransitionEnd(600)):(t(o).removeClass(f.ACTIVE),t(a).addClass(f.ACTIVE),this._isSliding=!1,t(this._element).trigger(m)),h&&this.cycle()}},l._jQueryInterface=function(e){return this.each(function(){var n=t(this).data(s),o=t.extend({},h,t(this).data());"object"===(void 0===e?"undefined":i(e))&&t.extend(o,e);var r="string"==typeof e?e:o.slide;if(n||(n=new l(this,o),t(this).data(s,n)),"number"==typeof e)n.to(e);else if("string"==typeof r){if(void 0===n[r])throw new Error('No method named "'+r+'"');n[r]()}else o.interval&&(n.pause(),n.cycle())})},l._dataApiClickHandler=function(e){var n=r.getSelectorFromElement(this);if(n){var i=t(n)[0];if(i&&t(i).hasClass(f.CAROUSEL)){var o=t.extend({},t(i).data(),t(this).data()),a=this.getAttribute("data-slide-to");a&&(o.interval=!1),l._jQueryInterface.call(t(i),o),a&&t(i).data(s).to(a),e.preventDefault()}}},o(l,null,[{key:"VERSION",get:function(){return"4.0.0-beta"}},{key:"Default",get:function(){return h}}]),l}();t(document).on(d.CLICK_DATA_API,p.DATA_SLIDE,_._dataApiClickHandler),t(window).on(d.LOAD_DATA_API,function(){t(p.DATA_RIDE).each(function(){var e=t(this);_._jQueryInterface.call(e,e.data())})}),t.fn[e]=_._jQueryInterface,t.fn[e].Constructor=_,t.fn[e].noConflict=function(){return t.fn[e]=l,_._jQueryInterface}}(jQuery),function(t){var e="collapse",s="bs.collapse",a=t.fn[e],l={toggle:!0,parent:""},h={toggle:"boolean",parent:"string"},c={SHOW:"show.bs.collapse",SHOWN:"shown.bs.collapse",HIDE:"hide.bs.collapse",HIDDEN:"hidden.bs.collapse",CLICK_DATA_API:"click.bs.collapse.data-api"},u={SHOW:"show",COLLAPSE:"collapse",COLLAPSING:"collapsing",COLLAPSED:"collapsed"},d={WIDTH:"width",HEIGHT:"height"},f={ACTIVES:".show, .collapsing",DATA_TOGGLE:'[data-toggle="collapse"]'},p=function(){function a(e,i){n(this,a),this._isTransitioning=!1,this._element=e,this._config=this._getConfig(i),this._triggerArray=t.makeArray(t('[data-toggle="collapse"][href="#'+e.id+'"],[data-toggle="collapse"][data-target="#'+e.id+'"]'));for(var o=t(f.DATA_TOGGLE),s=0;s0&&this._triggerArray.push(l)}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}return a.prototype.toggle=function(){t(this._element).hasClass(u.SHOW)?this.hide():this.show()},a.prototype.show=function(){var e=this;if(!this._isTransitioning&&!t(this._element).hasClass(u.SHOW)){var n=void 0,i=void 0;if(this._parent&&((n=t.makeArray(t(this._parent).children().children(f.ACTIVES))).length||(n=null)),!(n&&(i=t(n).data(s))&&i._isTransitioning)){var o=t.Event(c.SHOW);if(t(this._element).trigger(o),!o.isDefaultPrevented()){n&&(a._jQueryInterface.call(t(n),"hide"),i||t(n).data(s,null));var l=this._getDimension();t(this._element).removeClass(u.COLLAPSE).addClass(u.COLLAPSING),this._element.style[l]=0,this._triggerArray.length&&t(this._triggerArray).removeClass(u.COLLAPSED).attr("aria-expanded",!0),this.setTransitioning(!0);var h=function(){t(e._element).removeClass(u.COLLAPSING).addClass(u.COLLAPSE).addClass(u.SHOW),e._element.style[l]="",e.setTransitioning(!1),t(e._element).trigger(c.SHOWN)};if(r.supportsTransitionEnd()){var d="scroll"+(l[0].toUpperCase()+l.slice(1));t(this._element).one(r.TRANSITION_END,h).emulateTransitionEnd(600),this._element.style[l]=this._element[d]+"px"}else h()}}}},a.prototype.hide=function(){var e=this;if(!this._isTransitioning&&t(this._element).hasClass(u.SHOW)){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",r.reflow(this._element),t(this._element).addClass(u.COLLAPSING).removeClass(u.COLLAPSE).removeClass(u.SHOW),this._triggerArray.length)for(var o=0;o0},l.prototype._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:{offset:this._config.offset},flip:{enabled:this._config.flip}}};return this._inNavbar&&(t.modifiers.applyStyle={enabled:!this._inNavbar}),t},l._jQueryInterface=function(e){return this.each(function(){var n=t(this).data(s),o="object"===(void 0===e?"undefined":i(e))?e:null;if(n||(n=new l(this,o),t(this).data(s,n)),"string"==typeof e){if(void 0===n[e])throw new Error('No method named "'+e+'"');n[e]()}})},l._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=t.makeArray(t(d.DATA_TOGGLE)),i=0;i0&&r--,40===e.which&&rdocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},a.prototype._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},a.prototype._checkScrollbar=function(){this._isBodyOverflowing=document.body.clientWidth=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 o=this._offsets.length;o--;)this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&(void 0===this._offsets[o+1]||t .dropdown-menu .active"},l=function(){function e(t){n(this,e),this._element=t}return e.prototype.show=function(){var e=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&t(this._element).hasClass(s.ACTIVE)||t(this._element).hasClass(s.DISABLED))){var n=void 0,o=void 0,l=t(this._element).closest(a.NAV_LIST_GROUP)[0],h=r.getSelectorFromElement(this._element);l&&(o=t.makeArray(t(l).find(a.ACTIVE)),o=o[o.length-1]);var c=t.Event(i.HIDE,{relatedTarget:this._element}),u=t.Event(i.SHOW,{relatedTarget:o});if(o&&t(o).trigger(c),t(this._element).trigger(u),!u.isDefaultPrevented()&&!c.isDefaultPrevented()){h&&(n=t(h)[0]),this._activate(this._element,l);var d=function(){var n=t.Event(i.HIDDEN,{relatedTarget:e._element}),r=t.Event(i.SHOWN,{relatedTarget:o});t(o).trigger(n),t(e._element).trigger(r)};n?this._activate(n,n.parentNode,d):d()}}},e.prototype.dispose=function(){t.removeData(this._element,"bs.tab"),this._element=null},e.prototype._activate=function(e,n,i){var o=this,l=t(n).find(a.ACTIVE)[0],h=i&&r.supportsTransitionEnd()&&l&&t(l).hasClass(s.FADE),c=function(){return o._transitionComplete(e,l,h,i)};l&&h?t(l).one(r.TRANSITION_END,c).emulateTransitionEnd(150):c(),l&&t(l).removeClass(s.SHOW)},e.prototype._transitionComplete=function(e,n,i,o){if(n){t(n).removeClass(s.ACTIVE);var l=t(n.parentNode).find(a.DROPDOWN_ACTIVE_CHILD)[0];l&&t(l).removeClass(s.ACTIVE),n.setAttribute("aria-expanded",!1)}if(t(e).addClass(s.ACTIVE),e.setAttribute("aria-expanded",!0),i?(r.reflow(e),t(e).addClass(s.SHOW)):t(e).removeClass(s.FADE),e.parentNode&&t(e.parentNode).hasClass(s.DROPDOWN_MENU)){var h=t(e).closest(a.DROPDOWN)[0];h&&t(h).find(a.DROPDOWN_TOGGLE).addClass(s.ACTIVE),e.setAttribute("aria-expanded",!0)}o&&o()},e._jQueryInterface=function(n){return this.each(function(){var i=t(this),o=i.data("bs.tab");if(o||(o=new e(this),i.data("bs.tab",o)),"string"==typeof n){if(void 0===o[n])throw new Error('No method named "'+n+'"');o[n]()}})},o(e,null,[{key:"VERSION",get:function(){return"4.0.0-beta"}}]),e}();t(document).on(i.CLICK_DATA_API,a.DATA_TOGGLE,function(e){e.preventDefault(),l._jQueryInterface.call(t(this),"show")}),t.fn.tab=l._jQueryInterface,t.fn.tab.Constructor=l,t.fn.tab.noConflict=function(){return t.fn.tab=e,l._jQueryInterface}}(jQuery),function(t){if("undefined"==typeof Popper)throw new Error("Bootstrap tooltips require Popper.js (https://popper.js.org)");var e="tooltip",s=".bs.tooltip",a=t.fn[e],l=new RegExp("(^|\\s)bs-tooltip\\S+","g"),h={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)"},c={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"},u={animation:!0,template:'',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip"},d={SHOW:"show",OUT:"out"},f={HIDE:"hide"+s,HIDDEN:"hidden"+s,SHOW:"show"+s,SHOWN:"shown"+s,INSERTED:"inserted"+s,CLICK:"click"+s,FOCUSIN:"focusin"+s,FOCUSOUT:"focusout"+s,MOUSEENTER:"mouseenter"+s,MOUSELEAVE:"mouseleave"+s},p={FADE:"fade",SHOW:"show"},_={TOOLTIP:".tooltip",TOOLTIP_INNER:".tooltip-inner",ARROW:".arrow"},g={HOVER:"hover",FOCUS:"focus",CLICK:"click",MANUAL:"manual"},m=function(){function a(t,e){n(this,a),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()}return a.prototype.enable=function(){this._isEnabled=!0},a.prototype.disable=function(){this._isEnabled=!1},a.prototype.toggleEnabled=function(){this._isEnabled=!this._isEnabled},a.prototype.toggle=function(e){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(p.SHOW))return void this._leave(null,this);this._enter(null,this)}},a.prototype.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},a.prototype.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 o=this.getTipElement(),s=r.getUID(this.constructor.NAME);o.setAttribute("id",s),this.element.setAttribute("aria-describedby",s),this.setContent(),this.config.animation&&t(o).addClass(p.FADE);var l="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,h=this._getAttachment(l);this.addAttachmentClass(h);var c=!1===this.config.container?document.body:t(this.config.container);t(o).data(this.constructor.DATA_KEY,this),t.contains(this.element.ownerDocument.documentElement,this.tip)||t(o).appendTo(c),t(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new Popper(this.element,o,{placement:h,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:_.ARROW}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){e._handlePopperPlacementChange(t)}}),t(o).addClass(p.SHOW),"ontouchstart"in document.documentElement&&t("body").children().on("mouseover",null,t.noop);var u=function(){e.config.animation&&e._fixTransition();var n=e._hoverState;e._hoverState=null,t(e.element).trigger(e.constructor.Event.SHOWN),n===d.OUT&&e._leave(null,e)};r.supportsTransitionEnd()&&t(this.tip).hasClass(p.FADE)?t(this.tip).one(r.TRANSITION_END,u).emulateTransitionEnd(a._TRANSITION_DURATION):u()}},a.prototype.hide=function(e){var n=this,i=this.getTipElement(),o=t.Event(this.constructor.Event.HIDE),s=function(){n._hoverState!==d.SHOW&&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(o),o.isDefaultPrevented()||(t(i).removeClass(p.SHOW),"ontouchstart"in document.documentElement&&t("body").children().off("mouseover",null,t.noop),this._activeTrigger[g.CLICK]=!1,this._activeTrigger[g.FOCUS]=!1,this._activeTrigger[g.HOVER]=!1,r.supportsTransitionEnd()&&t(this.tip).hasClass(p.FADE)?t(i).one(r.TRANSITION_END,s).emulateTransitionEnd(150):s(),this._hoverState="")},a.prototype.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},a.prototype.isWithContent=function(){return Boolean(this.getTitle())},a.prototype.addAttachmentClass=function(e){t(this.getTipElement()).addClass("bs-tooltip-"+e)},a.prototype.getTipElement=function(){return this.tip=this.tip||t(this.config.template)[0]},a.prototype.setContent=function(){var e=t(this.getTipElement());this.setElementContent(e.find(_.TOOLTIP_INNER),this.getTitle()),e.removeClass(p.FADE+" "+p.SHOW)},a.prototype.setElementContent=function(e,n){var o=this.config.html;"object"===(void 0===n?"undefined":i(n))&&(n.nodeType||n.jquery)?o?t(n).parent().is(e)||e.empty().append(n):e.text(t(n).text()):e[o?"html":"text"](n)},a.prototype.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},a.prototype._getAttachment=function(t){return c[t.toUpperCase()]},a.prototype._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!==g.MANUAL){var i=n===g.HOVER?e.constructor.Event.MOUSEENTER:e.constructor.Event.FOCUSIN,o=n===g.HOVER?e.constructor.Event.MOUSELEAVE:e.constructor.Event.FOCUSOUT;t(e.element).on(i,e.config.selector,function(t){return e._enter(t)}).on(o,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=t.extend({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},a.prototype._fixTitle=function(){var t=i(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",""))},a.prototype._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?g.FOCUS:g.HOVER]=!0),t(n.getTipElement()).hasClass(p.SHOW)||n._hoverState===d.SHOW?n._hoverState=d.SHOW:(clearTimeout(n._timeout),n._hoverState=d.SHOW,n.config.delay&&n.config.delay.show?n._timeout=setTimeout(function(){n._hoverState===d.SHOW&&n.show()},n.config.delay.show):n.show())},a.prototype._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?g.FOCUS:g.HOVER]=!1),n._isWithActiveTrigger()||(clearTimeout(n._timeout),n._hoverState=d.OUT,n.config.delay&&n.config.delay.hide?n._timeout=setTimeout(function(){n._hoverState===d.OUT&&n.hide()},n.config.delay.hide):n.hide())},a.prototype._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},a.prototype._getConfig=function(n){return(n=t.extend({},this.constructor.Default,t(this.element).data(),n)).delay&&"number"==typeof n.delay&&(n.delay={show:n.delay,hide:n.delay}),n.title&&"number"==typeof n.title&&(n.title=n.title.toString()),n.content&&"number"==typeof n.content&&(n.content=n.content.toString()),r.typeCheckConfig(e,n,this.constructor.DefaultType),n},a.prototype._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},a.prototype._cleanTipClass=function(){var e=t(this.getTipElement()),n=e.attr("class").match(l);null!==n&&n.length>0&&e.removeClass(n.join(""))},a.prototype._handlePopperPlacementChange=function(t){this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},a.prototype._fixTransition=function(){var e=this.getTipElement(),n=this.config.animation;null===e.getAttribute("x-placement")&&(t(e).removeClass(p.FADE),this.config.animation=!1,this.hide(),this.show(),this.config.animation=n)},a._jQueryInterface=function(e){return this.each(function(){var n=t(this).data("bs.tooltip"),o="object"===(void 0===e?"undefined":i(e))&&e;if((n||!/dispose|hide/.test(e))&&(n||(n=new a(this,o),t(this).data("bs.tooltip",n)),"string"==typeof e)){if(void 0===n[e])throw new Error('No method named "'+e+'"');n[e]()}})},o(a,null,[{key:"VERSION",get:function(){return"4.0.0-beta"}},{key:"Default",get:function(){return u}},{key:"NAME",get:function(){return e}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return f}},{key:"EVENT_KEY",get:function(){return s}},{key:"DefaultType",get:function(){return h}}]),a}();return t.fn[e]=m._jQueryInterface,t.fn[e].Constructor=m,t.fn[e].noConflict=function(){return t.fn[e]=a,m._jQueryInterface},m}(jQuery));!function(r){var a="popover",l=".bs.popover",h=r.fn[a],c=new RegExp("(^|\\s)bs-popover\\S+","g"),u=r.extend({},s.Default,{placement:"right",trigger:"click",content:"",template:''}),d=r.extend({},s.DefaultType,{content:"(string|element|function)"}),f={FADE:"fade",SHOW:"show"},p={TITLE:".popover-header",CONTENT:".popover-body"},_={HIDE:"hide"+l,HIDDEN:"hidden"+l,SHOW:"show"+l,SHOWN:"shown"+l,INSERTED:"inserted"+l,CLICK:"click"+l,FOCUSIN:"focusin"+l,FOCUSOUT:"focusout"+l,MOUSEENTER:"mouseenter"+l,MOUSELEAVE:"mouseleave"+l},g=function(s){function h(){return n(this,h),t(this,s.apply(this,arguments))}return e(h,s),h.prototype.isWithContent=function(){return this.getTitle()||this._getContent()},h.prototype.addAttachmentClass=function(t){r(this.getTipElement()).addClass("bs-popover-"+t)},h.prototype.getTipElement=function(){return this.tip=this.tip||r(this.config.template)[0]},h.prototype.setContent=function(){var t=r(this.getTipElement());this.setElementContent(t.find(p.TITLE),this.getTitle()),this.setElementContent(t.find(p.CONTENT),this._getContent()),t.removeClass(f.FADE+" "+f.SHOW)},h.prototype._getContent=function(){return this.element.getAttribute("data-content")||("function"==typeof this.config.content?this.config.content.call(this.element):this.config.content)},h.prototype._cleanTipClass=function(){var t=r(this.getTipElement()),e=t.attr("class").match(c);null!==e&&e.length>0&&t.removeClass(e.join(""))},h._jQueryInterface=function(t){return this.each(function(){var e=r(this).data("bs.popover"),n="object"===(void 0===t?"undefined":i(t))?t:null;if((e||!/destroy|hide/.test(t))&&(e||(e=new h(this,n),r(this).data("bs.popover",e)),"string"==typeof t)){if(void 0===e[t])throw new Error('No method named "'+t+'"');e[t]()}})},o(h,null,[{key:"VERSION",get:function(){return"4.0.0-beta"}},{key:"Default",get:function(){return u}},{key:"NAME",get:function(){return a}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return _}},{key:"EVENT_KEY",get:function(){return l}},{key:"DefaultType",get:function(){return d}}]),h}(s);r.fn[a]=g._jQueryInterface,r.fn[a].Constructor=g,r.fn[a].noConflict=function(){return r.fn[a]=h,g._jQueryInterface}}(jQuery)}(); --------------------------------------------------------------------------------