diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..48253f3
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,53 @@
+AddDefaultCharset utf-8
+
+RewriteBase /
+Options -Indexes +FollowSymLinks +MultiViews
+
+RewriteEngine on
+RewriteBase /acp/
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_URI} !=/favicon.ico
+RewriteRule ^acp /acp/index.php [L]
+
+RewriteBase /
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_URI} !=/favicon.ico
+RewriteRule ^(?!acp).* index.php [L]
+
+ErrorDocument 403 /403.html
+ErrorDocument 404 /404.html
+
+#***Раскодировать, если не загружаются большие файлы:
+php_value max_execution_time 500
+php_value max_input_time 500
+php_value upload_max_filesize 30M
+php_value post_max_size 30M
+
+#***Раскодировать, если белый экран:
+#php_flag short_open_tag = On
+
+php_flag display_startup_errors on
+php_flag display_errors on
+php_flag html_errors on
+
+
+
+ ExpiresActive On
+ #кэшировать флэш и изображения на одну неделю
+ ExpiresByType image/x-icon "access plus 7 days"
+ ExpiresByType image/jpeg "access plus 7 days"
+ ExpiresByType image/png "access plus 7 days"
+ ExpiresByType image/gif "access plus 7 days"
+ ExpiresByType application/x-shockwave-flash "access plus 7 days"
+ #кэшировать css, javascript и текстовые файлы на одну неделю
+ ExpiresByType text/css "access plus 7 days"
+ ExpiresByType text/javascript "access plus 7 days"
+ ExpiresByType application/javascript "access plus 7 days"
+ ExpiresByType application/x-javascript "access plus 7 days"
+ #кэшировать html и htm файлы на один день
+ ExpiresByType text/html "access plus 1 day"
+ #кэшировать xml файлы на десять минут
+ ExpiresByType application/xhtml+xml "access plus 10 minutes"
+
\ No newline at end of file
diff --git a/403.html b/403.html
new file mode 100644
index 0000000..307f27e
--- /dev/null
+++ b/403.html
@@ -0,0 +1,58 @@
+
+
+
+ На данный момент у нас нет свободных вакансий.
');
+ else
+ $html->set('jobs', '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/engine/megp/auth.php b/system/engine/megp/auth.php
new file mode 100644
index 0000000..18b4e57
--- /dev/null
+++ b/system/engine/megp/auth.php
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/system/engine/megp/help.php b/system/engine/megp/help.php
new file mode 100644
index 0000000..9294ede
--- /dev/null
+++ b/system/engine/megp/help.php
@@ -0,0 +1,30 @@
+query('SELECT `id` FROM `users` WHERE `id`="'.$user['id'].'" AND `help`="0" LIMIT 1');
+ if(!$sql->num())
+ {
+ $html->get('noaccess', 'sections/help');
+ $html->pack('main');
+ }else{
+ // Подключение раздела
+ if(!in_array($section, array('create', 'dialog', 'open', 'close', 'notice', 'upload')))
+ include(ENG.'404.php');
+
+ $aNav = array(
+ 'help' => 'Техническая поддержка',
+ 'create' => 'Создание вопроса',
+ 'dialog' => 'Решение вопроса',
+ 'open' => 'Список открытых вопросов',
+ 'close' => 'Список закрытых вопросов'
+ );
+
+ $title = isset($aNav[$section]) ? $aNav[$section] : $section;
+
+ include(SEC.'help/'.$section.'.php');
+ }
+?>
\ No newline at end of file
diff --git a/system/engine/megp/index.php b/system/engine/megp/index.php
new file mode 100644
index 0000000..4e44c1d
--- /dev/null
+++ b/system/engine/megp/index.php
@@ -0,0 +1,25 @@
+query('SELECT `text`, `date` FROM `logs` WHERE `user`="'.$user['id'].'" ORDER BY `id` DESC LIMIT 5');
+ while($aLog = $sql->get($qLog))
+ {
+ $html->get('list', 'logs');
+ $html->set('text', $aLog['text']);
+ $html->set('date', sys::today($aLog['date'], true));
+ $html->pack('logs');
+ }
+
+ $html->get('index');
+ $html->set('ip', $uip);
+ $html->set('login', $user['login']);
+ $html->set('balance', round($user['balance'], 2));
+ $html->set('cur', $cfg['currency']);
+ $html->set('logs', isset($html->arr['main']) ? $html->arr['main'] : '');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/engine/megp/news.php b/system/engine/megp/news.php
new file mode 100644
index 0000000..10cd83a
--- /dev/null
+++ b/system/engine/megp/news.php
@@ -0,0 +1,42 @@
+query('SELECT `id`, `name`, `full_text`, `views`, `tags`, `date` FROM `news` WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ include(ENG.'404.php');
+
+ $news = $sql->get();
+
+ $title = $news['name'];
+
+ $sql->query('UPDATE `news` set `views`="'.($news['views']+1).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $html->get('news', 'sections/news');
+ $html->set('id', $news['id']);
+ $html->set('name', htmlspecialchars_decode($news['name']));
+ $html->set('text', htmlspecialchars_decode($news['full_text']));
+ $html->set('date', sys::today($news['date']));
+ $html->pack('main');
+ }else{
+ $title = 'Последние новости';
+
+ $sql->query('SELECT `id`, `name`, `text`, `views`, `tags`, `date` FROM `news` ORDER BY `id` DESC LIMIT 5');
+ while($news = $sql->get())
+ {
+ $html->get('list', 'sections/news');
+ $html->set('id', $news['id']);
+ $html->set('name', htmlspecialchars_decode($news['name']));
+ $html->set('text', htmlspecialchars_decode($news['text']));
+ $html->set('date', sys::today($news['date']));
+ $html->pack('news');
+ }
+
+ $html->get('index', 'sections/news');
+ $html->set('list', isset($html->arr['news']) ? $html->arr['news'] : '');
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/engine/megp/quit.php b/system/engine/megp/quit.php
new file mode 100644
index 0000000..d9f863e
--- /dev/null
+++ b/system/engine/megp/quit.php
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/system/engine/megp/recovery.php b/system/engine/megp/recovery.php
new file mode 100644
index 0000000..86e8950
--- /dev/null
+++ b/system/engine/megp/recovery.php
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/system/engine/megp/replenish.php b/system/engine/megp/replenish.php
new file mode 100644
index 0000000..e3a7645
--- /dev/null
+++ b/system/engine/megp/replenish.php
@@ -0,0 +1,30 @@
+query('SELECT `text`, `date` FROM `logs` WHERE `user`="'.$user['id'].'" AND `type`="replenish" ORDER BY `id` DESC LIMIT 5');
+ while($aLog = $sql->get($qLog))
+ {
+ $html->get('list', 'sections/user/replenish');
+ $html->set('text', $aLog['text']);
+ $html->set('date', sys::today($aLog['date'], true));
+ $html->pack('logs');
+ }
+
+ $html->get('replenish', 'sections/user');
+ $html->set('id', $user['id']);
+ $html->set('wmr', $cfg['webmoney_wmr']);
+ $html->set('freekassa', $cfg['freekassa_id']);
+ $html->set('unitpay', $cfg['unitpay_pubkey']);
+ $html->set('balance', round($user['balance'], 2));
+ $html->set('cur', $cfg['currency']);
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/engine/megp/servers.php b/system/engine/megp/servers.php
new file mode 100644
index 0000000..739d4e0
--- /dev/null
+++ b/system/engine/megp/servers.php
@@ -0,0 +1,76 @@
+query('SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ $owners = $sql->query('SELECT `id` FROM `owners` WHERE `user`="'.$user['id'].'" LIMIT 1');
+
+ if(!$sql->num($servers) AND !$sql->num($owners))
+ sys::back($cfg['http'].'services'); // Если нет игровых серверов отправить на страницу аренды
+ }
+
+ if($id AND !$section)
+ $section = 'index';
+
+ $title = 'Управление игровыми серверами';
+
+ // Подключение раздела
+ if(in_array($section, array('action', 'scan', 'index', 'console', 'settings', 'tarif', 'boost')))
+ {
+ if(!$id)
+ sys::back($cfg['http'].'servers');
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" AND `user`="'.$user['id'].'" LIMIT 1');
+ if(!$sql->num())
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `server`="'.$id.'" AND `user`="'.$user['id'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers');
+
+ $owner = $sql->get();
+
+ $rights = sys::b64djs($owner['rights']);
+
+ if($section == 'action')
+ {
+ if(!isset($rights[$url['action']]) || !$rights[$url['action']])
+ sys::outjs(array('e' => sys::text('error', 'ser_owner')));
+ }else{
+ if(!in_array($section, array('index', 'scan')) AND (!isset($rights[$section]) || !$rights[$section]))
+ sys::back($cfg['http'].'servers/id/'.$owner['server']);
+ }
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers'); // Если нет игрового сервера отправить на страницу списка
+
+ $file_section = file_exists(SEC.'megp/servers/'.$section.'.php');
+ if($file_section)
+ include(SEC.'megp/servers/'.$section.'.php');
+ else
+ sys::back($cfg['http'].'servers/id/'.$id);
+
+ }else{
+ if($user['group'] == 'user' AND $mcache->get('servers_'.$user['id']) != '')
+ $html->arr['main'] = $mcache->get('servers_'.$user['id']);
+ else{
+ include(SEC.'megp/servers/list.php');
+ include(SEC.'megp/servers/owners_list.php');
+
+ $html->get('servers', 'sections/servers');
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : 'У вас нет игровых серверов', true);
+ $html->set('wait_servers', $wait_servers);
+ $html->set('updates_servers', $updates_servers);
+ $html->pack('main');
+
+ $mcache->set('servers_'.$user['id'], $html->arr['main'], false, 4);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/engine/monitoring.php b/system/engine/monitoring.php
new file mode 100644
index 0000000..1314609
--- /dev/null
+++ b/system/engine/monitoring.php
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/system/engine/pages.php b/system/engine/pages.php
new file mode 100644
index 0000000..d95f8e0
--- /dev/null
+++ b/system/engine/pages.php
@@ -0,0 +1,24 @@
+query('SELECT `name`, `file` FROM `pages` WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ include(ENG.'404.php');
+
+ $page = $sql->get();
+
+ $title = $page['name'];
+
+ $html->nav($page['name']);
+
+ $html->get('page');
+
+ $html->set('content', file_get_contents(FILES.'pages/'.$page['file']));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/engine/partners.php b/system/engine/partners.php
new file mode 100644
index 0000000..5211206
--- /dev/null
+++ b/system/engine/partners.php
@@ -0,0 +1,10 @@
+nav($title);
+
+ $html->get('partners');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/engine/pluginbuy.php b/system/engine/pluginbuy.php
new file mode 100644
index 0000000..a49d1d6
--- /dev/null
+++ b/system/engine/pluginbuy.php
@@ -0,0 +1,27 @@
+query('SELECT `id` FROM `servers` WHERE `address`="'.$address.'" LIMIT 1');
+ if(!$sql->num())
+ sys::out('bad server');
+
+ $server = $sql->get();
+
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `key`="'.$url['plugin'].'" AND `server`="'.$server['id'].'" LIMIT 1');
+ if($sql->num())
+ sys::out('true');
+
+ sys::out('false');
+?>
\ No newline at end of file
diff --git a/system/engine/plugins.php b/system/engine/plugins.php
new file mode 100644
index 0000000..db45750
--- /dev/null
+++ b/system/engine/plugins.php
@@ -0,0 +1,171 @@
+ 'Counter-Strike: 1.6',
+ 'cssold' => 'Counter-Strike: Source v34',
+ 'css' => 'Counter-Strike: Source',
+ 'csgo' => 'Counter-Strike: Global Offensive'
+ );
+
+ if(!isset($url['game']) || !array_key_exists($url['game'], $aGame))
+ $url['game'] = 'cs';
+
+ $title = 'Доступные плагины для установки';
+
+ include(LIB.'games/plugins.php');
+
+ if($id)
+ {
+ $sql->query('SELECT `upd` FROM `plugins` WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'plugins/game/'.$url['game']);
+
+ $update = $sql->get();
+
+ $sqlq = '`name`, `info`, `desc`, `images`, `upd`';
+
+ // Если установленно обновление
+ if($update['upd'])
+ {
+ $sql->query('SELECT '.$sqlq.' FROM `plugins_update` WHERE `id`="'.$update['upd'].'" LIMIT 1');
+
+ if(!$sql->num())
+ $sql->query('SELECT '.$sqlq.' FROM `plugins` WHERE `id`="'.$id.'" LIMIT 1');
+ }else
+ $sql->query('SELECT '.$sqlq.' FROM `plugins` WHERE `id`="'.$id.'" LIMIT 1');
+
+ $plugin = $sql->get();
+
+ $sql->query('SELECT `id`, `file` FROM `plugins_config` WHERE (`plugin`="'.$id.'" AND `update`="0") OR (`plugin`="'.$id.'" AND `update`="'.$update['upd'].'") ORDER BY `sort`, `id` ASC');
+ while($config = $sql->get())
+ {
+ // Исключить дублирование, путем проверки массива файлов
+ if(in_array($config['file'], $aConf))
+ continue;
+
+ $aConf[] = $config['file'];
+
+ // Данные файла
+ $file = explode('/', $config['file']);
+
+ $html->get('config_list', 'sections/plugins');
+
+ $html->set('game', $url['game']);
+ $html->set('fid', $config['id']);
+ $html->set('name', end($file));
+ $html->set('file', $config['file']);
+
+ $html->pack('configs');
+ }
+
+ $images = plugins::images($plugin['images'], $pid);
+
+ $html->get('configs', 'sections/plugins');
+
+ $html->set('game', $url['game']);
+ $html->set('name', $plugin['name']);
+ $html->set('info', htmlspecialchars_decode($plugin['info']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ // Картинки
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ // Редактируемые файлы
+ if(isset($html->arr['configs']))
+ {
+ $html->set('configs', $html->arr['configs']);
+ $html->unit('configs', 1);
+ }else
+ $html->unit('configs');
+
+ $html->pack('main');
+
+ $plugin['name'] = strip_tags($plugin['name']);
+
+ $title = $plugin['name'];
+ $html->nav('Доступные плагины для установки', $cfg['http'].'plugins/game/'.$url['game']);
+ $html->nav($plugin['name']);
+ }
+
+ if(!isset($html->arr['main']))
+ {
+ $html->nav('Доступные плагины для установки');
+
+ // Если есть кеш
+ if($mcache->get('plugins_list_view_'.$url['game']) != '')
+ $html->arr['main'] = $mcache->get('plugins_list_view_'.$url['game']);
+ else{
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$url['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/plugins');
+
+ $html->set('plugin', $plugin['id']);
+ $html->set('game', $url['game']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ $html->get('plugins', 'sections/plugins');
+
+ $html->set('game', $aGame[$url['game']]);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+
+ $html->pack('main');
+
+ $mcache->set('plugins_list_view_'.$url['game'], $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/engine/replenish.php b/system/engine/replenish.php
new file mode 100644
index 0000000..3970cd3
--- /dev/null
+++ b/system/engine/replenish.php
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/system/engine/servers.php b/system/engine/servers.php
new file mode 100644
index 0000000..0afc822
--- /dev/null
+++ b/system/engine/servers.php
@@ -0,0 +1,113 @@
+query('SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ $owners = $sql->query('SELECT `id` FROM `owners` WHERE `user`="'.$user['id'].'" LIMIT 1');
+
+ if(!$sql->num($servers) AND !$sql->num($owners))
+ sys::back($cfg['http'].'services'); // Если нет игровых серверов отправить на страницу аренды
+ }
+
+ if($id AND !$section)
+ $section = 'index';
+
+ $title = 'Управление игровыми серверами';
+
+ // Подключение раздела
+ if(in_array($section, array('action', 'scan', 'index', 'console', 'settings', 'plugins', 'maps', 'owners', 'filetp', 'tarif', 'copy', 'graph', 'web', 'boost', 'rcon')))
+ {
+ if(!$id)
+ sys::back($cfg['http'].'servers');
+
+ if($user['group'] == 'admin' || ($user['group'] == 'support' AND $user['level']))
+ {
+ if($user['group'] == 'support' AND $user['level'] < 2)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" AND `user`="'.$user['id'].'" LIMIT 1');
+ if(!$sql->num())
+ {
+ $sql->query('SELECT `id` FROM `help` WHERE `type`="server" AND `service`="'.$id.'" LIMIT 1');
+ if(!$sql->num())
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `server`="'.$id.'" AND `user`="'.$user['id'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers');
+
+ $owner = $sql->get();
+
+ $rights = sys::b64djs($owner['rights']);
+
+ if($section == 'action')
+ {
+ if(!isset($rights[$url['action']]) || !$rights[$url['action']])
+ sys::outjs(array('e' => 'У вас нет доступа к данному серверу'));
+ }else{
+ if(!in_array($section, array('index', 'scan')) AND (!isset($rights[$section]) || !$rights[$section]))
+ sys::back($cfg['http'].'servers');
+ }
+ }
+ }
+ }
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ }else{
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" AND `user`="'.$user['id'].'" LIMIT 1');
+ if(!$sql->num())
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `server`="'.$id.'" AND `user`="'.$user['id'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers');
+
+ $owner = $sql->get();
+
+ $rights = sys::b64djs($owner['rights']);
+
+ if($section == 'action')
+ {
+ if(!isset($rights[$url['action']]) || !$rights[$url['action']])
+ sys::outjs(array('e' => sys::text('error', 'ser_owner')));
+ }else{
+ if(!in_array($section, array('index', 'scan')) AND (!isset($rights[$section]) || !$rights[$section]))
+ sys::back($cfg['http'].'servers/id/'.$owner['server']);
+ }
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ }
+ }
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers'); // Если нет игрового сервера отправить на страницу списка
+
+ $html->nav($title, $cfg['http'].'servers');
+
+ $file_section = file_exists(SEC.'servers/'.$section.'.php');
+ if($file_section)
+ include(SEC.'servers/'.$section.'.php');
+ else
+ sys::back($cfg['http'].'servers/id/'.$id);
+
+ }else{
+ $html->nav($title);
+
+ if($user['group'] == 'user' AND $mcache->get('servers_'.$user['id']) != '')
+ $html->arr['main'] = $mcache->get('servers_'.$user['id']);
+ else{
+ include(SEC.'servers/list.php');
+ include(SEC.'servers/owners_list.php');
+
+ $html->get('servers', 'sections/servers');
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : 'У вас нет игровых серверов', true);
+ $html->set('wait_servers', $wait_servers);
+ $html->set('updates_servers', $updates_servers);
+ $html->pack('main');
+
+ $mcache->set('servers_'.$user['id'], $html->arr['main'], false, 4);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/engine/services.php b/system/engine/services.php
new file mode 100644
index 0000000..ece74c0
--- /dev/null
+++ b/system/engine/services.php
@@ -0,0 +1,35 @@
+nav($title);
+
+ $html->get('index', 'sections/services');
+ $html->pack('main');
+ }else{
+ $aNav = array(
+ 'cs' => 'Counter-Srike: 1.6',
+ 'css' => 'Counter-Srike: Source',
+ 'csgo' => 'Counter-Srike: Global Offensive',
+ 'cssold' => 'Counter-Srike: Source v34',
+ 'mc' => 'MineCraft',
+ 'mta' => 'GTA: MTA',
+ 'samp' => 'GTA: SA-MP',
+ 'crmp' => 'GTA: CR-MP',
+ 'hosting' => 'виртуального хостинга',
+ 'privileges' => 'привилегий на игровом сервере',
+ 'control' => 'услуги "контроль"',
+ );
+
+ $title = 'Аренда '.$aNav[$section];
+
+ $html->nav('Список услуг', $cfg['http'].'services');
+ $html->nav($title);
+
+ include(SEC.'services/'.$section.'.php');
+ }
+?>
\ No newline at end of file
diff --git a/system/engine/unitpay.php b/system/engine/unitpay.php
new file mode 100644
index 0000000..c608146
--- /dev/null
+++ b/system/engine/unitpay.php
@@ -0,0 +1,130 @@
+ array('message' => 'Некорректный адрес сервера')));
+
+ $secretKey = $cfg['unitpay_key'];
+ $params = $_GET['params'];
+
+ if($params['signature'] != getSignature($_GET['method'], $params, $secretKey))
+ sys::outjs(array('error' => array('message' => 'Некорректная цифровая подпись')));
+
+ if(!in_array($_GET['method'], array('pay', 'check', 'error')))
+ sys::outjs(array('error' => array('message' => 'Некорректный метод')));
+
+ // Оплата по ключу
+ if(!sys::valid($params['account'], 'md5'))
+ {
+ $sql->query('SELECT `id`, `server`, `price` FROM `privileges_buy` WHERE `key`="'.$params['account'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('error' => array('message' => 'bad key: '.$params['account'])));
+
+ $privilege = $sql->get();
+
+ $money = round($params['sum']*$cfg['curinrub'], 2);
+
+ if($money < $privilege['price'])
+ sys::outjs(array('error' => array('message' => 'bad sum')));
+
+ $sql->query('SELECT `user` FROM `servers` WHERE `id`="'.$privilege['server'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('error' => array('message' => 'bad server')));
+
+ $server = $sql->get();
+
+ $sql->query('SELECT `id`, `balance`, `part_money` FROM `users` WHERE `id`="'.$server['user'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('error' => array('message' => 'bad owner')));
+
+ if(isset($_GET['method']) AND $_GET['method'] == 'check')
+ sys::outjs(array('result' => array('message' => 'Запрос успешно обработан')));
+
+ $user = $sql->get();
+
+ if($cfg['part_money'])
+ $sql->query('UPDATE `users` set `part_money`="'.($user['part_money']+$money).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+ else
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']+$money).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'profit'),
+ array('server' => $privilege['server'], 'money' => $money)).'", `date`="'.$start_point.'", `type`="part", `money`="'.$money.'"');
+
+ $sql->query('UPDATE `privileges_buy` set `status`="1" WHERE `id`="'.$privilege['id'].'" LIMIT 1');
+
+ sys::outjs(array('result' => array('message' => 'Запрос успешно обработан')));
+ }
+
+ switch($_GET['method'])
+ {
+ case 'pay':
+ $sum = round($params['sum'], 2);
+
+ $user = intval($params['account']);
+
+ $sql->query('SELECT `id`, `balance`, `part` FROM `users` WHERE `id`="'.$user.'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('result' => array('message' => 'Пользователь c ID: '.$user.' не найден')));
+
+ $user = $sql->get();
+
+ $money = round($user['balance']+$sum*$cfg['curinrub'], 2);
+
+ if($cfg['part'])
+ {
+ $part_sum = round($sum/100*$cfg['part_proc'], 2);
+
+ $sql->query('SELECT `balance`, `part_money` FROM `users` WHERE `id`="'.$user['part'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $part = $sql->get();
+
+ if($cfg['part_money'])
+ $sql->query('UPDATE `users` set `part_money`="'.($part['part_money']+$part_sum).'" WHERE `id`="'.$user['part'].'" LIMIT 1');
+ else
+ $sql->query('UPDATE `users` set `balance`="'.($part['balance']+$part_sum).'" WHERE `id`="'.$user['part'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['part'].'", `text`="'.sys::updtext(sys::text('logs', 'part'),
+ array('part' => $uid, 'money' => $part_sum)).'", `date`="'.$start_point.'", `type`="part", `money`="'.$part_sum.'"');
+ }
+ }
+
+ $sql->query('UPDATE `users` set `balance`="'.$money.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="Пополнение баланса на сумму: '.$sum.' '.$cfg['currency'].'", `date`="'.$start_point.'", `type`="replenish", `money`="'.$sum.'"');
+
+ sys::outjs(array('result' => array('message' => 'Запрос успешно обработан')));
+
+ case 'check':
+ $sql->query('SELECT `id` FROM `users` WHERE `id`="'.intval($params['account']).'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('result' => array('message' => 'Запрос успешно обработан')));
+
+ sys::outjs(array('jsonrpc' => "2.0", 'error' => array('code' => -32000, 'message' => 'Пользователь не найден'), 'id' => 1));
+
+ case 'error':
+ sys::outjs(array('result' => array('message' => 'Запрос успешно обработан')));
+ }
+?>
\ No newline at end of file
diff --git a/system/engine/unitpay_entity.php b/system/engine/unitpay_entity.php
new file mode 100644
index 0000000..dd779be
--- /dev/null
+++ b/system/engine/unitpay_entity.php
@@ -0,0 +1,120 @@
+ array('message' => 'Некорректный адрес сервера')));
+
+ $secretKey = $cfg['unitpay_key'];
+ $params = $_GET['params'];
+
+ if ($params['signature'] != getSha256SignatureByMethodAndParams(
+ $_REQUEST["method"], $params, $GATEWAY['SecretKey']
+ ));
+
+ if(!in_array($_GET['method'], array('pay', 'check', 'error')))
+ sys::outjs(array('error' => array('message' => 'Некорректный метод')));
+
+ // Оплата по ключу
+ if(!sys::valid($params['account'], 'md5'))
+ {
+ $sql->query('SELECT `id`, `server`, `price` FROM `privileges_buy` WHERE `key`="'.$params['account'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('error' => array('message' => 'bad key: '.$params['account'])));
+
+ $privilege = $sql->get();
+
+ $money = round($params['sum']*$cfg['curinrub'], 2);
+
+ if($money < $privilege['price'])
+ sys::outjs(array('error' => array('message' => 'bad sum')));
+
+ $sql->query('SELECT `user` FROM `servers` WHERE `id`="'.$privilege['server'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('error' => array('message' => 'bad server')));
+
+ $server = $sql->get();
+
+ $sql->query('SELECT `id`, `balance`, `part_money` FROM `users` WHERE `id`="'.$server['user'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('error' => array('message' => 'bad owner')));
+
+ if(isset($_GET['method']) AND $_GET['method'] == 'check')
+ sys::outjs(array('result' => array('message' => 'Запрос успешно обработан')));
+
+ $user = $sql->get();
+
+ if($cfg['part_money'])
+ $sql->query('UPDATE `users` set `part_money`="'.($user['part_money']+$money).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+ else
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']+$money).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'profit'),
+ array('server' => $privilege['server'], 'money' => $money)).'", `date`="'.$start_point.'", `type`="part", `money`="'.$money.'"');
+
+ $sql->query('UPDATE `privileges_buy` set `status`="1" WHERE `id`="'.$privilege['id'].'" LIMIT 1');
+
+ sys::outjs(array('result' => array('message' => 'Запрос успешно обработан')));
+ }
+
+ switch($_GET['method'])
+ {
+ case 'pay':
+ $sum = round($params['sum'], 2);
+
+ $user = intval($params['account']);
+
+ $sql->query('SELECT `id`, `balance`, `part` FROM `users` WHERE `id`="'.$user.'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('result' => array('message' => 'Пользователь c ID: '.$user.' не найден')));
+
+ $user = $sql->get();
+
+ $money = round($user['balance']+$sum*$cfg['curinrub'], 2);
+
+ if($cfg['part'])
+ {
+ $part_sum = round($sum/100*$cfg['part_proc'], 2);
+
+ $sql->query('SELECT `balance`, `part_money` FROM `users` WHERE `id`="'.$user['part'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $part = $sql->get();
+
+ if($cfg['part_money'])
+ $sql->query('UPDATE `users` set `part_money`="'.($part['part_money']+$part_sum).'" WHERE `id`="'.$user['part'].'" LIMIT 1');
+ else
+ $sql->query('UPDATE `users` set `balance`="'.($part['balance']+$part_sum).'" WHERE `id`="'.$user['part'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['part'].'", `text`="'.sys::updtext(sys::text('logs', 'part'),
+ array('part' => $uid, 'money' => $part_sum)).'", `date`="'.$start_point.'", `type`="part", `money`="'.$part_sum.'"');
+ }
+ }
+
+ $sql->query('UPDATE `users` set `balance`="'.$money.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="Пополнение баланса на сумму: '.$sum.' '.$cfg['currency'].'", `date`="'.$start_point.'", `type`="replenish", `money`="'.$sum.'"');
+
+ sys::outjs(array('result' => array('message' => 'Запрос успешно обработан')));
+
+ case 'check':
+ $sql->query('SELECT `id` FROM `users` WHERE `id`="'.intval($params['account']).'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('result' => array('message' => 'Запрос успешно обработан')));
+
+ sys::outjs(array('jsonrpc' => "2.0", 'error' => array('code' => -32000, 'message' => 'Пользователь не найден'), 'id' => 1));
+
+ case 'error':
+ sys::outjs(array('result' => array('message' => 'Запрос успешно обработан')));
+ }
+?>
\ No newline at end of file
diff --git a/system/engine/user.php b/system/engine/user.php
new file mode 100644
index 0000000..21851f8
--- /dev/null
+++ b/system/engine/user.php
@@ -0,0 +1,26 @@
+ 'Авторизация',
+ 'recovery' => 'Восстановление',
+ 'replenish' => 'Пополнение баланса',
+ 'signup' => 'Регистрация',
+ 'lk' => 'Личный кабинет',
+ 'quit' => 'Выход'
+ );
+
+ $title = $aTitle[$section];
+
+ if($section == 'lk')
+ $html->nav($title, $cfg['http'].'user/section/lk');
+ else
+ $html->nav($title);
+
+ include(SEC.'user/'.$section.'.php');
+?>
diff --git a/system/engine/webmoney.php b/system/engine/webmoney.php
new file mode 100644
index 0000000..ed15de4
--- /dev/null
+++ b/system/engine/webmoney.php
@@ -0,0 +1,97 @@
+query('SELECT `id`, `server`, `price` FROM `privileges_buy` WHERE `key`="'.$_POST['us_user'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::out('bad key');
+
+ $privilege = $sql->get();
+
+ $money = round($sum*$cfg['curinrub'], 2);
+
+ if($money < $privilege['price'])
+ sys::out('bad sum');
+
+ $sql->query('SELECT `user` FROM `servers` WHERE `id`="'.$privilege['server'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::out('bad server');
+
+ $server = $sql->get();
+
+ $sql->query('SELECT `id`, `balance`, `part_money` FROM `users` WHERE `id`="'.$server['user'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::out('bad owner');
+
+ $user = $sql->get();
+
+ if($cfg['part_money'])
+ $sql->query('UPDATE `users` set `part_money`="'.($user['part_money']+$money).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+ else
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']+$money).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'profit'),
+ array('server' => $privilege['server'], 'money' => $money)).'", `date`="'.$start_point.'", `type`="part", `money`="'.$money.'"');
+
+ $sql->query('UPDATE `privileges_buy` set `status`="1" WHERE `id`="'.$privilege['id'].'" LIMIT 1');
+
+ sys::out('success');
+ }
+
+ $user = intval($_POST['us_user']);
+
+ $sql->query('SELECT `id`, `balance`, `part` FROM `users` WHERE `id`="'.$user.'" LIMIT 1');
+ if(!$sql->num())
+ sys::out('bad user');
+
+ $user = $sql->get();
+
+ $money = round($user['balance']+$sum*$cfg['curinrub'], 2);
+
+ if($cfg['part'])
+ {
+ $part_sum = round($sum/100*$cfg['part_proc'], 2);
+
+ $sql->query('SELECT `balance`, `part_money` FROM `users` WHERE `id`="'.$user['part'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $part = $sql->get();
+
+ if($cfg['part_money'])
+ $sql->query('UPDATE `users` set `part_money`="'.($part['part_money']+$part_sum).'" WHERE `id`="'.$user['part'].'" LIMIT 1');
+ else
+ $sql->query('UPDATE `users` set `balance`="'.($part['balance']+$part_sum).'" WHERE `id`="'.$user['part'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['part'].'", `text`="'.sys::updtext(sys::text('logs', 'part'),
+ array('part' => $uid, 'money' => $part_sum)).'", `date`="'.$start_point.'", `type`="part", `money`="'.$part_sum.'"');
+ }
+ }
+
+ $sql->query('UPDATE `users` set `balance`="'.$money.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="Пополнение баланса на сумму: '.$sum.' '.$cfg['currency'].'", `date`="'.$start_point.'", `type`="replenish", `money`="'.$sum.'"');
+
+ sys::out('success');
+?>
\ No newline at end of file
diff --git a/system/engine/wiki.php b/system/engine/wiki.php
new file mode 100644
index 0000000..33b1205
--- /dev/null
+++ b/system/engine/wiki.php
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/system/library/acpsystem.php b/system/library/acpsystem.php
new file mode 100644
index 0000000..9ebbfd7
--- /dev/null
+++ b/system/library/acpsystem.php
@@ -0,0 +1,713 @@
+delete($cache);
+
+ die(json_encode($val));
+ }
+
+ public static function out($val = '', $cache = false)
+ {
+ global $mcache;
+
+ if($cache)
+ $mcache->delete($cache);
+
+ die(''.$val.'');
+ }
+
+ public static function valid($val, $type, $preg = '')
+ {
+ switch($type)
+ {
+ case 'promo':
+ if(!preg_match("/^[A-Za-z0-9]{2,20}$/", $val))
+ return true;
+
+ return false;
+
+ case 'en':
+ if(!preg_match("/^[A-Za-z0-9]$/", $val))
+ return true;
+
+ return false;
+
+ case 'ru':
+ if(!preg_match("/^[А-Яа-я]$/u", $val))
+ return true;
+
+ return false;
+
+ case 'wm':
+ if(!preg_match('/^R[0-9]{12,12}$|^Z[0-9]{12,12}$|^U[0-9]{12,12}$/m', $val))
+ return true;
+
+ return false;
+
+ case 'ip':
+ if(!preg_match("/^(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}$/", $val))
+ return true;
+
+ return false;
+
+ case 'steamid':
+ if(!preg_match("/^STEAM_[0-9]:[0-9]:[0-9]{6,12}$|^HLTV$|^STEAM_ID_LAN$|^STEAM_ID_PENDING$|^VALVE_ID_LAN$|^VALVE_ID_PENDING$|^STEAM_666:88:666$/", $val))
+ return true;
+
+ return false;
+
+ case 'steamid3':
+ if(!preg_match("/^\[U:[01]:[0-9]{3,12}\]$/i", $val))
+ return true;
+
+ return false;
+
+ case 'num':
+ if(!preg_match('/[^0-9]/', $val))
+ return true;
+
+ return false;
+
+ case 'md5':
+ if(!preg_match("/^[a-z0-9]{32,32}$/", $val))
+ return true;
+
+ return false;
+
+ case 'other':
+ if(!preg_match($preg, $val))
+ return true;
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public static function page($page, $nums, $num)
+ {
+ $ceil = ceil($nums/$num);
+
+ if($page > $ceil)
+ $page = $ceil;
+
+ $next = $page*$num;
+
+ if($next <= $nums)
+ $next = $next-$num;
+
+ if($next > $nums)
+ $next = $next-$num;
+
+ if($next < 1)
+ $next = 0;
+
+ $num_go = $next;
+ if($page == '')
+ $page = 1;
+
+ $aPage = array(
+ 'page' => $page,
+ 'num' => $num_go,
+ 'ceil' => $ceil
+ );
+
+ return $aPage;
+ }
+
+ public static function page_list($countnum, $actnum)
+ {
+ if($countnum == 0 || $countnum == 1)
+ return array();
+
+ if($countnum > 10)
+ {
+ if($actnum <= 4 || $actnum + 3 >= $countnum)
+ {
+ for($i = 0; $i <= 4; $i++)
+ $numlist[$i] = $i + 1;
+
+ $numlist[5] = '...';
+ for($j = 6, $k = 4; $j <= 10; $j+=1, $k-=1)
+ $numlist[$j] = $countnum - $k;
+ }else{
+ $numlist[0] = 1;
+ $numlist[1] = 2;
+ $numlist[2] = '...';
+ $numlist[3] = $actnum - 2;
+ $numlist[4] = $actnum - 1;
+ $numlist[5] = $actnum;
+ $numlist[6] = $actnum + 1;
+ $numlist[7] = $actnum + 2;
+ $numlist[8] = '...';
+ $numlist[9] = $countnum - 1;
+ $numlist[10] = $countnum;
+ }
+ }else
+ for($n = 0; $n < $countnum; $n+=1)
+ $numlist[$n] = $n + 1;
+
+ return $numlist;
+ }
+
+ public static function page_gen($ceil, $page, $actnum, $section)
+ {
+ global $cfg, $html;
+
+ $aNum = sys::page_list($ceil, $actnum);
+
+ $pages = '';
+
+ $html->get('pages');
+
+ if($ceil)
+ {
+ if($page != 1)
+ {
+ $next = $page-1;
+ $pages .= '(.+?)<\/code>#isUe",
+ "#\[quote\](.+?)\[\/quote\]#is",
+ "#\[url=(.+?)\](.+?)\[\/url\]#is",
+ "#(^|[\n ])([\w]+?://[\w\#$%&~/.\-;:=,?@\[\]+]*)#is"
+ );
+
+ $str_replace = array(
+ "",
+ "",
+ "\\1 ",
+ "\\1 ",
+ "",
+ "''.htmlspecialchars('$1').'
'",
+ "\\1
",
+ "\\2 ",
+ "\\2 "
+ );
+
+ $uptext = '';
+
+ foreach($lines as $line)
+ $uptext .= preg_replace($str_search, $str_replace, $line).PHP_EOL;
+
+ return $uptext;
+ }
+
+ public static function text($section, $name)
+ {
+ global $cfg, $user;
+
+ $group = isset($user['group']) ? $user['group'] : 'user';
+
+ if($section != 'error' || !$cfg['text_group'])
+ $group = 'all';
+
+ include(DATA.'text/'.$section.'.php');
+
+ return isset($text[$name][$group]) ? $text[$name][$group] : $text[$name];
+ }
+
+ public static function updtext($text, $data)
+ {
+ foreach($data as $name => $val)
+ $text = str_replace('['.$name.']', $val, $text);
+
+ return $text;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/api.php b/system/library/api.php
new file mode 100644
index 0000000..478f980
--- /dev/null
+++ b/system/library/api.php
@@ -0,0 +1,95 @@
+query('SELECT `unit`, `tarif`, `address`, `game`, `slots_start`, `online`, `players`, `status`, `name`, `map`, `pack`, `fps`, `tickrate`, `ram`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ if(!$sql->num())
+ return array('e' => 'сервер не найден');
+
+ $server = $sql->get();
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ if(!$sql->num())
+ return array('e' => 'локация не найдена');
+
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ if(!$sql->num())
+ return array('e' => 'тариф не найден');
+
+ $tarif = $sql->get();
+ $packs = sys::b64djs($tarif['packs']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ return array(
+ 'id' => $id,
+ 'address' => $server['address'],
+ 'unit' => $unit['name'],
+ 'tarif' => games::info_tarif($server['game'], $tarif['name'], array('fps' => $server['fps'], 'tickrate' => $server['tickrate'], 'ram' => $server['ram'])),
+ 'game' => $server['game'],
+ 'name' => $server['name'],
+ 'slots' => $server['slots_start'],
+ 'online' => $server['online'],
+ 'players' => $server['players'],
+ 'status' => sys::status($server['status'], $server['game'], $server['map']),
+ 'img' => sys::status($server['status'], $server['game'], $server['map'], 'img'),
+ 'time_end' => $time_end,
+ 'time' => sys::today($server['time']),
+ 'date' => sys::today($server['date']),
+ 'pack' => $packs[$server['pack']]
+ );
+ }
+
+ public function load($id)
+ {
+ global $sql, $cfg;
+
+ $sql->query('SELECT `online`, `slots_start`, `ram_use`, `cpu_use`, `hdd_use` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ if(!$sql->num())
+ return array('e' => 'сервер не найден');
+
+ $server = $sql->get();
+
+ $online = 100/$server['slots_start']*$server['online'];
+ $online = $online > 100 ? 100: $online;
+
+ return array(
+ 'id' => $id,
+ 'cpu' => $server['cpu_use'],
+ 'ram' => $server['ram_use'],
+ 'hdd' => $server['hdd_use'],
+ 'onl' => $online
+ );
+ }
+
+ public function console($id, $cmd)
+ {
+ global $sql, $cfg;
+
+ $aGames = array('cs', 'css', 'cssold', 'csgo', 'mc', 'mta');
+
+ $sql->query('SELECT `game` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ if(!$sql->num())
+ return 'сервер не найден';
+
+ $server = $sql->get();
+
+ if(!in_array($server['game'], $aGames))
+ return 'Игра не поддерживает команды';
+
+ $go = true;
+
+ $_POST['command'] = isset($cmd{0}) ? urldecode($cmd) : '';
+
+ include(SEC.'servers/'.$server['game'].'/console.php');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/captcha/text.ttf b/system/library/captcha/text.ttf
new file mode 100644
index 0000000..14dbdc9
Binary files /dev/null and b/system/library/captcha/text.ttf differ
diff --git a/system/library/control/actions.php b/system/library/control/actions.php
new file mode 100644
index 0000000..2972f05
--- /dev/null
+++ b/system/library/control/actions.php
@@ -0,0 +1,360 @@
+query('SELECT `uid`, `unit`, `game`, `address`, `name` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="off", `online`="0", `players`="", `stop`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function change($id, $map = false)
+ {
+ global $cfg, $sql, $html, $user, $mcache;
+
+ // Если в кеше есть карты
+ if($mcache->get('ctrl_server_maps_change_'.$id) != '' && !$map)
+ return array('maps' => $mcache->get('ctrl_server_maps_change_'.$id));
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `game`, `online`, `players`, `name` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ // Массив карт игрового сервера (папка "maps")
+ $aMaps = explode("\n", $ssh->get('cd /servers/'.$server['uid'].'/cstrike/maps/ && ls | grep .bsp | grep -v .bsp.'));
+
+ // Удаление пустого элемента
+ unset($aMaps[count($aMaps)-1]);
+
+ // Удаление ".bsp"
+ $aMaps = str_replace('.bsp', '', $aMaps);
+
+ // Если выбрана карта
+ if($map)
+ {
+ // Проверка наличия выбранной карты
+ if(!in_array($map, $aMaps))
+ return array('e' => sys::updtext(sys::text('servers', 'change'), array('map' => $map.'.bsp')));
+
+ // Отправка команды changelevel
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval '."'stuff \"changelevel ".sys::cmd($map)."\"\015'");
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="change" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'change', 'online' => $server['online'], 'players' => base64_decode($server['players'])));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'change', 'online' => $server['online']));
+
+ return array('s' => 'ok');
+ }
+
+ // Сортировка списка карт
+ sort($aMaps);
+ reset($aMaps);
+
+ // Генерация списка карт для выбора
+ foreach($aMaps as $map)
+ {
+ $html->get('change_list', 'sections/control/servers/games');
+ $html->set('img', file_exists(DIR.'/maps/'.$server['game'].'/'.$map.'.jpg') ? $cfg['http'].'maps/'.$server['game'].'/'.$map.'.jpg' : $cfg['http'].'template/images/status/none.jpg');
+ $html->set('name', $map);
+ $html->set('id', $server['unit']);
+ $html->set('server', $id);
+ $html->pack('maps');
+ }
+
+ // Запись карт в кеш
+ $mcache->set('ctrl_server_maps_change_'.$id, $html->arr['maps'], false, 30);
+
+ return array('maps' => $html->arr['maps']);
+ }
+
+ public static function reinstall($id)
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `address`, `game`, `name`, `pack`, `ftp`, `core_fix` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `fcpu` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if(!$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ $path = 'rm '.$server['pack'].'.zip; wget '.$cfg['control_server'].'/'.$server['pack'].'.zip; unzip '.$server['pack'].'.zip; rm '.$server['pack'].'.zip;';
+
+ if(in_array($server['game'], array('css', 'csgo')))
+ $path = 'cd '.$cfg['steamcmd'].'; ./steamcmd.sh +login anonymous +force_install_dir "/servers/'.$uid.'" +app_update '.$cfg['control_steamcmd'][$game].' +quit;';
+
+ // Директория игрового сервера
+ $install = '/servers/'.$server['uid'];
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], $unit['fcpu'], true); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => 'Не удается выполнить операцию, нет свободного потока.');
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ $ssh->set('rm -r '.$install.';' // Удаление директории игрового сервера
+ .'mkdir '.$install.';' // Создание директории
+ .'chown server'.$server['uid'].':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u server'.$server['uid'].' '.$taskset.' screen -dmS r_'.$server['uid'].' sh -c "'
+ .$path // Копирование файлов сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].'"');
+
+ // Очистка записей в базе
+ $sql->query('DELETE FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$id.'"'); // Список админов на сервере
+ $sql->query('DELETE FROM `control_plugins_install` WHERE `server`="'.$id.'"'); // Список установленных плагинов на сервере
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="reinstall", `core_use`="'.$core.'", `fastdl`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'reinstall', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'reinstall', 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function update($id)
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `address`, `game`, `name`, `pack`, `ftp` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `fcpu` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if(!$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ $path = 'rm '.$server['pack'].'_udp.zip; wget '.$cfg['control_server'].'/'.$server['pack'].'_udp.zip; unzip -u '.$server['pack'].'_udp.zip; rm '.$server['pack'].'_udp.zip;';
+
+ // Директория игрового сервера
+ $install = '/servers/'.$server['uid'];
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], $unit['fcpu'], true); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => 'Не удается выполнить операцию, нет свободного потока.');
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ $ssh->set('cd '.$install.' && sudo -u server'.$server['uid'].' '.$taskset.' screen -dmS u_'.$server['uid'].' sh -c "'.$path // Копирование файлов обвновления сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].'"');
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="update", `core_use`="'.$core.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function delete($id)
+ {
+ global $cfg, $sql, $user;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `game`, `slots`, `address` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!$server['uid'])
+ return array('e' => 'uid 404');
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@:'.$server['address'].' | awk '."'{print $2}'".' | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ // Директория игрового сервера
+ $install = '/servers/'.$server['uid'];
+
+ $copys = 'screen -dmS r_copy_'.$server['uid'].' sh -c "';
+
+ $scopy = $sql->query('SELECT `id`, `name` FROM `control_copy` WHERE `server`="'.$id.'"');
+ while($copy = $sql->get($scopy))
+ {
+ $copys .= 'rm /copy/'.$copy['name'].'.tar;';
+
+ $sql->query('DELETE FROM `control_copy` WHERE `id`="'.$copy['id'].'" LIMIT 1');
+ }
+
+ $copys .= '";';
+
+ $ssh->set($copys // Удаление резервных копий
+ .'screen -dmS r_'.$server['uid'].' sh -c "rm -r '.$install.';' // Удаление директории сервера
+ .'userdel server'.$server['uid'].'"'); // Удаление пользователя сервера c локации
+
+ // Удаление ftp доступа
+ $qSql = 'DELETE FROM users WHERE username=\''.$server['uid'].'\';';
+
+ $ssh->set('screen -dmS ftp'.$server['uid'].' mysql -P '.$unit['sql_port'].' -u'.$unit['sql_login'].' -p'.$unit['sql_passwd'].' --database '.$unit['sql_ftp'].' -e "'.$qSql.'"');
+
+ // Очистка правил FireWall
+ ctrl::iptables($id, 'remove', NULL, NULL, $server['unit'], false, $ssh);
+
+ // Удаление заданий из crontab
+ $sql->query('SELECT `address`, `passwd` FROM `panel` LIMIT 1');
+ $panel = $sql->get();
+
+ if(!$ssh->auth($panel['passwd'], $panel['address']))
+ return array('e' => 'Неудалось создать связь с панелью');
+
+ $crons = $sql->query('SELECT `id`, `cron` FROM `control_crontab` WHERE `server`="'.$id.'"');
+ while($cron = $sql->get($crons))
+ {
+ $ssh->set('echo "" >> /etc/crontab && cat /etc/crontab');
+ $crontab = str_replace($cron['cron'], '', $ssh->get());
+
+ // Временный файл
+ $temp = sys::temp($crontab);
+
+ $ssh->setfile($temp, '/etc/crontab', 0644);
+
+ $ssh->set("sed -i '/^$/d' /etc/crontab");
+ $ssh->set('crontab -u root /etc/crontab');
+
+ unlink($temp);
+
+ $sql->query('DELETE FROM `control_crontab` WHERE `id`="'.$cron['id'].'" LIMIT 1');
+ }
+
+ // Удаление различной информации игрового сервера
+ $sql->query('DELETE FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$id.'" LIMIT 1');
+ $sql->query('DELETE FROM `control_plugins_install` WHERE `server`="'.$id.'" LIMIT 1');
+ $sql->query('DELETE FROM `control_plugins_buy` WHERE `server`="'.$id.'" LIMIT 1');
+ $sql->query('DELETE FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+
+ return array('s' => 'ok');
+ }
+
+ public static function clmcache($id)
+ {
+ global $mcache;
+
+ $mcache->delete('ctrl_server_index_'.$id);
+ $mcache->delete('ctrl_server_resources_'.$id);
+ $mcache->delete('ctrl_server_status_'.$id);
+ }
+ }
\ No newline at end of file
diff --git a/system/library/control/control.php b/system/library/control/control.php
new file mode 100644
index 0000000..ad2b1a9
--- /dev/null
+++ b/system/library/control/control.php
@@ -0,0 +1,448 @@
+arr['btn'] = '';
+
+ if($status == 'working')
+ {
+ $html->get('restart', 'sections/control/buttons');
+ $html->set('id', $id);
+ $html->pack('btn');
+
+ return $html->arr['btn'];
+ }
+
+ return '';
+ }
+
+ public static function resources($id)
+ {
+ global $sql;
+
+ include(LIB.'ssh.php');
+
+ $aData = array(
+ 'cpu' => '0%',
+ 'ram' => '0%',
+ 'hdd' => '0%'
+ );
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $ctrl = $sql->get();
+
+ if(!$ssh->auth($ctrl['passwd'], $ctrl['address']))
+ sys::outjs($aData);
+
+ $data = $ssh->get('echo `cat /proc/meminfo | grep MemTotal | awk \'{print $2}\'; cat /proc/meminfo | grep MemFree | awk \'{print $2}\'; cat /proc/meminfo | grep Buffers | awk \'{print $2}\'; cat /proc/meminfo | grep Cached | grep -v SwapCached | awk \'{print $2}\'`');
+ $aData['ram'] = ceil(ctrl::ram_load($data)).'%';
+
+ $aData['hdd'] = trim($ssh->get('df -h | awk \'/rootfs/ {print $5}\''));
+
+ $aData['cpu'] = ctrl::cpu_load($ssh->get('echo "`ps -A -o pcpu | tail -n+2 | paste -sd+ | bc | awk \'{print $0}\'` `cat /proc/cpuinfo | grep processor | wc -l | awk \'{print $1}\'`"')).'%';
+
+ sys::outjs($aData);
+ }
+
+ public static function update_info($id)
+ {
+ global $sql;
+
+ include(LIB.'ssh.php');
+
+ $aData = array(
+ 'cpu' => 'произошла ошибка',
+ 'ram' => 'произошла ошибка',
+ 'hdd' => 'произошла ошибка'
+ );
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $ctrl = $sql->get();
+
+ if(!$ssh->auth($ctrl['passwd'], $ctrl['address'].':22'))
+ sys::outjs($aData);
+
+ $data = $ssh->get('cat /proc/meminfo | grep MemTotal | cut -d \' \' -f 2-');
+ $aData['ram'] = $data.' ('.(round(sys::int($data)/1024/1024, 2)).'Gb)';
+
+ $aData['hdd'] = trim($ssh->get('df -h | awk \'/rootfs/ {print $2}\''));
+
+ $aCPU = explode("\n", trim($ssh->get('cat /proc/cpuinfo | grep -c processor && cat /proc/cpuinfo | grep "MHz" | awk \'{print $4}\' | head -n 1')));
+
+ $aData['cpu'] = $aCPU[0].'x'.round($aCPU[1], 0).' MHz ['.trim($ssh->get('cat /proc/cpuinfo | grep -m 1 "model name" | cut -d \' \' -f 3-')).']';
+
+ sys::outjs($aData);
+ }
+
+ public static function update_status($id, $ssh = false)
+ {
+ global $cfg, $sql, $start_point, $mcache;
+
+ if(!$ssh)
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd`, `time`, `overdue`, `block`, `status` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $ctrl = $sql->get();
+
+ $status = $ctrl['status'];
+
+ if($ctrl['status'] == 'blocked' && $ctrl['block'] < $start_point)
+ $sql->query('UPDATE `control` set `block`="0", `status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($ctrl['status'] == 'blocked')
+ return 'blocked';
+
+ // Если аренда закончилась и сервер просрочен длительное время
+ if($ctrl['time'] < $start_point && $ctrl['status'] == 'overdue' && ($ctrl['overdue']+$cfg['control_delete']*86400) < $start_point)
+ {
+ $sql->query('UPDATE `control` set `user`="-1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ return 'delete';
+ }
+
+ // Если аренда закончилась, а услуга не просрочена
+ if($ctrl['time'] < $start_point && !in_array($ctrl['status'], array('overdue', 'blocked')))
+ {
+ $sql->query('UPDATE `control` set `status`="overdue" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $status = 'overdue';
+ }
+
+ // Если аренда не закончилась, а услуга просрочена
+ if($ctrl['time'] > $start_point && $ctrl['status'] == 'overdue')
+ $sql->query('UPDATE `control` set `status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($ctrl['status'], array('working', 'error')))
+ {
+ $status = 'working';
+ if(!$ssh->auth($ctrl['passwd'], $ctrl['address']))
+ $status = 'error';
+
+ $sql->query('UPDATE `control` set `status`="'.$status.'" WHERE `id`="'.$id.'" LIMIT 1');
+ }
+
+ if($ctrl['status'] == 'reboot' && !$mcache->get('reboot_control_'.$id))
+ {
+ if($ssh->auth($ctrl['passwd'], $ctrl['address']))
+ {
+ $sql->query('UPDATE `control` set `status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $status = 'working';
+ }
+ }
+
+ $time_end = $ctrl['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $ctrl['overdue']+$cfg['control_delete']*86400) : 'Осталось: '.sys::date('min', $ctrl['time']);
+
+ $aData = array(
+ 'time' => sys::today($ctrl['time']),
+ 'time_end' => $time_end,
+ 'buttons' => ctrl::buttons($id, $status),
+ 'status' => ctrl::status($status)
+ );
+
+ return $aData;
+ }
+
+ public static function ram_load($data)
+ {
+ $aData = explode(' ', $data);
+
+ return ceil(($aData[0]-($aData[1]+$aData[2]+$aData[3]))*100/$aData[0]);
+ }
+
+ public static function cpu_load($data)
+ {
+ $aData = explode(' ', $data);
+
+ $load = ceil($aData[0]/$aData[1]);
+
+ return $load > 100 ? 100 : $load;
+ }
+
+ public static function nav($server, $id, $sid, $active)
+ {
+ global $cfg, $html, $sql, $mcache, $start_point;
+
+ $aUnit = array('index', 'console', 'settings', 'plugins', 'filetp', 'copy', 'boost');
+
+ $html->get('gmenu', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('home', $cfg['http']);
+
+ foreach($aUnit as $unit)
+ if($unit == $active) $html->unit($unit, 1); else $html->unit($unit);
+
+ $html->pack('main');
+
+ $html->get('vmenu', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('home', $cfg['http']);
+
+ foreach($aUnit as $unit)
+ if($unit == $active) $html->unit($unit, 1); else $html->unit($unit);
+
+ $html->pack('vmenu');
+
+ return NULL;
+ }
+
+ public static function route($server, $inc, $go)
+ {
+ global $device, $start_point;
+
+ if(in_array($server['status'], array('install', 'reinstall', 'update', 'recovery')))
+ {
+ if($go)
+ sys::out('Раздел недоступен');
+
+ return SEC.'control/servers/noaccess.php';
+ }
+
+ if(!file_exists(SEC.'control/servers/'.$server['game'].'/'.$inc.'.php'))
+ return SEC.'control/servers/'.$server['game'].'/index.php';
+
+ return SEC.'control/servers/'.$server['game'].'/'.$inc.'.php';
+ }
+
+ public static function cpulist($unit, $core, $count = false)
+ {
+ global $device, $start_point;
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($count)
+ return 1;
+
+ $out = $core ? 'Автоматическое определение 1 ядро/поток ' : ''.$core.' ядро/поток Автоматическое определение ';
+
+ sys::outjs(array('core_fix' => $core));
+ }
+
+ $n = sys::int($ssh->get('cat /proc/cpuinfo | grep "cpu MHz" | wc -l'));
+
+ if($count)
+ return $n;
+
+ $list = 'Автоматическое определение ';
+
+ for($i = 1; $i <= $n; $i+=1)
+ $list .= ''.$i.' ядро/поток ';
+
+ sys::outjs(array('core_fix' => str_replace($core.'"', $core.'" selected="select"', $list)));
+ }
+
+ public static function iptables($id, $action, $source, $dest, $unit, $snw = false, $ssh = false)
+ {
+ global $cfg, $sql, $start_point;
+
+ if(!$ssh)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$unit.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('all', 'ssh'));
+ }
+
+ switch($action)
+ {
+ case 'block':
+ if(sys::valid($source, 'ip'))
+ return array('e' => sys::text('servers', 'firewall'));
+
+ // Если подсеть
+ if($snw)
+ {
+ $source = sys::whois($source);
+
+ if($source == 'не определена')
+ return array('e' => 'Не удалось определить подсеть для указанного адреса.');
+ }
+
+ $sql->query('SELECT `id` FROM `control_firewall` WHERE `sip`="'.$source.'" AND `server`="'.$id.'" LIMIT 1');
+
+ // Если такое правило уже добавлено или указан адрес сайта (ПУ)
+ if($sql->num() || ($source == $cfg['ip'] || $source == $cfg['subnet']))
+ return array('s' => 'ok');
+
+ $sql->query('INSERT INTO `control_firewall` set `sip`="'.$source.'", `dest`="'.$dest[0].':'.$dest[1].'", `server`="'.$id.'", `time`="'.$start_point.'"');
+
+ $line = $sql->id();
+
+ $rule = 'iptables -I INPUT -s '.$source.' -p udp -d '.$dest[0].' --dport '.$dest[1].' -j DROP;';
+
+ $ssh->set($rule.' echo -e "#'.$line.';\n'.$rule.'" >> /root/'.$cfg['iptables']);
+
+ return array('s' => 'ok');
+
+ case 'unblock':
+ if(!is_numeric($source) AND sys::valid($source, 'ip'))
+ return array('e' => sys::text('servers', 'firewall'));
+
+ if(is_numeric($source))
+ {
+ $sql->query('SELECT `id`, `sip` FROM `control_firewall` WHERE `id`="'.$source.'" AND `server`="'.$id.'" LIMIT 1');
+
+ // Если такое правило отсутствует
+ if(!$sql->num())
+ return array('s' => 'ok');
+ }else{
+ $sql->query('SELECT `id`, `sip` FROM `control_firewall` WHERE `sip`="'.$source.'" AND `server`="'.$id.'" LIMIT 1');
+
+ // Если одиночный адрес не найден, проверить на блокировку подсети
+ if(!$sql->num())
+ {
+ $source = sys::whois($source);
+
+ $sql->query('SELECT `id` FROM `control_firewall` WHERE `sip`="'.$source.'" AND `server`="'.$id.'" LIMIT 1');
+
+ if($sql->num())
+ {
+ $firewall = $sql->get();
+
+ return array('i' => 'Указанный адрес входит в заблокированную подсеть, разблокировать подсеть?', 'id' => $firewall['id']);
+ }
+
+ return array('s' => 'ok');
+ }
+ }
+
+ $firewall = $sql->get();
+
+ $ssh->set('iptables -D INPUT -s '.$firewall['sip'].' -p udp -d '.$dest[0].' --dport '.$dest[1].' -j DROP;'
+ .'sed "`nl '.$cfg['iptables'].' | grep \"#'.$firewall['id'].'\" | awk \'{print $1","$1+1}\'`d" '.$cfg['iptables'].' > '.$cfg['iptables'].'_temp; cat '.$cfg['iptables'].'_temp > '.$cfg['iptables'].'; rm '.$cfg['iptables'].'_temp');
+
+ $sql->query('DELETE FROM `control_firewall` WHERE `id`="'.$firewall['id'].'" LIMIT 1');
+
+ return array('s' => 'ok');
+
+ case 'remove':
+ $sql->query('SELECT `id`, `sip`, `dest` FROM `control_firewall` WHERE `server`="'.$id.'"');
+
+ $aRule = array();
+
+ while($firewall = $sql->get())
+ {
+ list($ip, $port) = explode(':', $firewall['dest']);
+
+ $aRule[$firewall['id']] = 'iptables -D INPUT -s '.$firewall['sip'].' -p udp -d '.$ip.' --dport '.$port.' -j DROP;';
+ }
+
+ $nRule = count($aRule);
+
+ if(!$nRule)
+ return NULL;
+
+ $cmd = '';
+
+ foreach($aRule as $line => $rule)
+ $cmd .= $rule.'sed "`nl '.$cfg['iptables'].' | grep "#'.$line.'" | awk \'{print $1","$1+1}\'`d" '.$cfg['iptables'].' > '.$cfg['iptables'].'_temp; cat '.$cfg['iptables'].'_temp > '.$cfg['iptables'].'; rm '.$cfg['iptables'].'_temp';
+
+ $ssh->set($cmd);
+
+ $sql->query('DELETE FROM `control_firewall` WHERE `server`="'.$id.'" LIMIT '.$nRule);
+
+ return array('s' => 'ok');
+ }
+ }
+
+ public static function crontab($data = array(), $id, $cid)
+ {
+ global $cfg;
+
+ if($data['allhour'])
+ $time = '0 * * * ';
+ else{
+ $hour = array(
+ '00', '01', '02',
+ '03', '04', '05',
+ '06', '07', '08',
+ '09', '10', '11',
+ '12', '13', '14',
+ '15', '16', '17',
+ '18', '19', '20',
+ '21', '22', '23'
+ );
+
+ $minute = array(
+ '00', '05', '10',
+ '15', '20', '25',
+ '30', '35', '40',
+ '45', '50', '55'
+ );
+
+ if(!in_array($data['hour'], $hour))
+ $data['hour'] = '00';
+
+ if(!in_array($data['minute'], $minute))
+ $data['minute'] = '00';
+
+ $time = $data['minute'].' '.$data['hour'].' * * ';
+ }
+
+ $week = array();
+ $week[1] = isset($data['week']['\'1\'']) ? 1 : 0;
+ $week[2] = isset($data['week']['\'2\'']) ? 2 : 0;
+ $week[3] = isset($data['week']['\'3\'']) ? 3 : 0;
+ $week[4] = isset($data['week']['\'4\'']) ? 4 : 0;
+ $week[5] = isset($data['week']['\'5\'']) ? 5 : 0;
+ $week[6] = isset($data['week']['\'6\'']) ? 6 : 0;
+ $week[7] = isset($data['week']['\'7\'']) ? 7 : 0;
+
+ $check = 0;
+
+ foreach($week as $index => $val)
+ $check+= $val;
+
+ if($check == 28 || !$check)
+ $week = '*';
+ else{
+ $weeks = $week[1].','.$week[2].','.$week[3].','.$week[4].','.$week[5].','.$week[6].','.$week[7];
+ $weeks = str_replace(array(',0', '0'), '', $weeks);
+ $week = $weeks{0} == ',' ? substr($weeks, 1) : $weeks;
+ }
+
+ $cron_task = $time.$week.' screen -dmS s'.$id.' bash -c \'cd /var/enginegp && php cron.php '.$cfg['cron_key'].' control_server_cron '.$id.' '.$cid.'\'';
+
+ return $cron_task;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/cs/action.php b/system/library/control/cs/action.php
new file mode 100644
index 0000000..492e689
--- /dev/null
+++ b/system/library/control/cs/action.php
@@ -0,0 +1,109 @@
+query('SELECT `uid`, `unit`, `game`, `address`, `slots`, `name`, `fps`, `map_start`, `vac`, `pingboost`, `time_start`, `core_fix` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd`, `fcpu` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if(!$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Проверка наличия стартовой карты
+ $ssh->set('cd /servers/'.$server['uid'].'/cstrike/maps/ && ls | grep .bsp | grep -v .bsp.');
+
+ if($server['map_start'] != '' AND !in_array($server['map_start'], str_replace('.bsp', '', explode("\n", $ssh->get()))))
+ return array('e' => sys::updtext(sys::text('servers', 'nomap'), array('map' => $server['map_start'].'.bsp')));
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], $unit['fcpu'], true); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Античит VAC
+ $vac = $server['vac'] == 0 ? '-insecure' : '-secure';
+
+ // Значение PingBoost
+ $pingboost = $server['pingboost'] ? '-pingboost '.$pingboost : '';
+
+ // Значение sys_ticrate (FPS)
+ $fps = $server['fps'];
+
+ // Параметры запуска
+ $bash = './hlds_run -debug -game cstrike -norestart -condebug -sys_ticrate '.$fps.' +servercfgfile server.cfg +sys_ticrate '.$fps.' +map \''.$server['map_start'].'\' +maxplayers '.$server['slots'].' +ip '.$ip.' +port '.$port.' +sv_lan 0 '.$vac.' '.$pingboost;
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd /servers/'.$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'sudo -u server'.$server['uid'].' mkdir -p cstrike/oldstart;' // Создание папки логов
+ .'cat cstrike/qconsole.log >> cstrike/oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm cstrike/qconsole.log; rm cstrike/oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 start.sh;' // Обновление владельца файла start.sh
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c ./start.sh'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/cs/rcon.php b/system/library/control/cs/rcon.php
new file mode 100644
index 0000000..08717ed
--- /dev/null
+++ b/system/library/control/cs/rcon.php
@@ -0,0 +1,107 @@
+Connect($ip, $port, 3, SourceQuery::GOLDSOURCE);
+
+ $sq->SetRconPassword(rcon::rcon_passwd($server));
+
+ $out = $sq->Rcon($cmd);
+
+ $sq->Disconnect();
+
+ return $out;
+ }
+
+ public static function players($data)
+ {
+ $aPlayers = array();
+ $n = 1;
+
+ $lines = explode("\n", $data);
+
+ foreach($lines as $line)
+ {
+ if(strpos($line, '#') === FALSE)
+ continue;
+
+ $start = strpos($line, '"')+1;
+ $end = strrpos($line, '"');
+
+ $name = htmlspecialchars(substr($line, $start, $end-$start));
+
+ $line = trim(substr($line, $end + 1));
+
+ $aData = array_values(array_diff(explode(' ', $line), array('', ' ')));
+
+ $steamid = trim($aData[1]);
+ $ip = trim(sys::first(explode(':', $aData[6])));
+
+ if(sys::valid($steamid, 'steamid') || sys::valid($ip, 'ip'))
+ continue;
+
+ $aPlayers[$n]['name'] = $name;
+ $aPlayers[$n]['steamid'] = $steamid;
+ $aPlayers[$n]['frags'] = trim($aData[2]);
+ $aPlayers[$n]['time'] = trim($aData[3]);
+ $aPlayers[$n]['ping'] = trim($aData[4]);
+ $aPlayers[$n]['ip'] = $ip;
+
+ $whois = rcon::country($ip);
+
+ $aPlayers[$n]['ico'] = $whois['ico'];
+ $aPlayers[$n]['country'] = $whois['name'];
+
+ $n+=1;
+ }
+
+ return $aPlayers;
+ }
+
+ public static function rcon_passwd($server)
+ {
+ global $cfg, $sql, $user;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ $ssh->set('cat /servers/'.$server['uid'].'/cstrike/server.cfg | grep rcon_password');
+ $get = explode(' ', str_replace('"', '', trim($ssh->get())));
+ $rcon = trim(end($get));
+
+ if(!isset($rcon{0}))
+ sys::outjs(array('r' => 'Необходимо установить rcon пароль (rcon_password).', 'url' => $cfg['http'].'control/id/'.$server['unit'].'/server/'.$server['unit'].'/section/settings/subsection/server'), $nmch);
+
+ return $rcon;
+ }
+
+ public static function country($ip)
+ {
+ global $SxGeo;
+
+ $cData = $SxGeo->getCityFull($ip);
+ $ico = sys::country($cData['country']['iso']);
+
+ return array('ico' => $ico, 'name' => empty($cData['country']['name_ru']) ? 'Не определена' : $cData['country']['name_ru']);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/cs/scan.php b/system/library/control/cs/scan.php
new file mode 100644
index 0000000..20e3fe1
--- /dev/null
+++ b/system/library/control/cs/scan.php
@@ -0,0 +1,133 @@
+get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $info = scan::info($sq, $id);
+
+ $sql->query('SELECT `unit`, `game`, `name`, `map`, `online`, `players`, `status` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!$info['status'])
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status'], $server['game'], $server['unit']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ if($players_get)
+ $players = scan::info($sq, $id, true);
+
+ $out['name'] = htmlspecialchars($info['name']);
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = $info['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working', $server['game'], $server['unit']);
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', $player['name']);
+ $html->set('score', $player['score']);
+ $html->set('time', $player['time']);
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `control_servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `control_servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function info($sq, $id, $pl = false)
+ {
+ global $sql;
+
+ $sql->query('SELECT `address` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $sq->Connect($ip, $port, 1, SourceQuery::GOLDSOURCE);
+
+ if($pl)
+ {
+ $players = $sq->GetPlayers();
+
+ $i = 1;
+ $data = array();
+
+ foreach($players as $n => $player)
+ {
+ $data[$i]['i'] = $i;
+ $data[$i]['name'] = $player['Name'] == '' ? 'Подключается' : $player['Name'];
+ $data[$i]['score'] = $player['Frags'];
+ $data[$i]['time'] = $player['TimeF'];
+
+ $i+=1;
+ }
+
+ return $data;
+ }
+
+ $data = $sq->GetInfo();
+
+ $server['name'] = $data['HostName'];
+ $server['map'] = $data['Map'];
+ $server['online'] = $data['Players'];
+ $server['status'] = strlen($server['map']) > 3 ? true : false;
+
+ return $server;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/csgo/action.php b/system/library/control/csgo/action.php
new file mode 100644
index 0000000..0232e54
--- /dev/null
+++ b/system/library/control/csgo/action.php
@@ -0,0 +1,282 @@
+query('SELECT `uid`, `unit`, `game`, `address`, `slots`, `name`, `tickrate`, `map_start`, `vac`, `time_start`, `core_fix`, `pingboost` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd`, `fcpu` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if(!$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Проверка наличия стартовой карты
+ $ssh->set('cd /servers/'.$server['uid'].'/csgo/maps/ && du -ah | grep -e "\.bsp$" | awk \'{print $2}\'');
+
+ include_once(LIB.'games/games.php');
+
+ if(games::map($server['map_start'], $ssh->get()))
+ return array('e' => sys::updtext(sys::text('servers', 'nomap'), array('map' => $server['map_start'].'.bsp')));
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], $unit['fcpu'], true); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Античит VAC
+ $vac = $server['vac'] == 0 ? '-insecure' : '-secure';
+
+ // TV
+ $tv = $server['tv'] ? '+tv_enable 1 +tv_maxclients 30 +tv_port '.($port+10000) : '-nohltv';
+
+ $check = explode('/', $server['map_start']);
+
+ // Стартовая карта
+ $map = $check[0] == 'workshop' ? '+workshop_start_map '.$check[1] : '+map \''.$server['map_start'].'\'';
+
+ // Игровой режим
+ $mods = array(
+ 1 => '+game_type 0 +game_mode 0',
+ 2 => '+game_type 0 +game_mode 1',
+ 3 => '+game_type 1 +game_mode 0',
+ 4 => '+game_type 1 +game_mode 1',
+ 5 => '+game_type 1 +game_mode 2'
+ );
+
+ $mod = !$server['pingboost'] ? $mods[2] : $mods[$server['pingboost']];
+
+ // Параметры запуска
+ $bash = './srcds_run -debug -game csgo -norestart -condebug console.log -usercon -tickrate '.$server['tickrate'].' '.$mod.' +servercfgfile server.cfg '.$map.' -maxplayers_override '.$server['slots'].' +ip '.$ip.' +net_public_adr '.$ip.' +port '.$port.' -sv_lan 0 '.$vac.' '.$tv;
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd /servers/'.$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'sudo -u server'.$server['uid'].' mkdir -p csgo/oldstart;' // Создание папки логов
+ .'cat csgo/console.log >> csgo/oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm csgo/console.log; rm csgo/oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 start.sh;' // Обновление владельца файла start.sh
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c "./start.sh"'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function change($id, $map = false)
+ {
+ global $cfg, $sql, $html, $user, $mcache;
+
+ // Если в кеше есть карты
+ if($mcache->get('ctrl_server_maps_change_'.$id) != '' AND !$map)
+ return array('maps' => $mcache->get('ctrl_server_maps_change_'.$id));
+
+ include(LIB.'ssh.php');
+
+ include(LIB.'games/games.php');
+
+ $sql->query('SELECT `uid`, `unit`, `game`, `online`, `players`, `name` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ // Массив карт игрового сервера (папка "maps")
+ $aMaps = explode("\n", $ssh->get('cd /servers/'.$server['uid'].'/csgo/maps/ && du -ah | grep -e "\.bsp$" | awk \'{print $2}\''));
+
+ // Удаление пустого элемента
+ unset($aMaps[count($aMaps)-1]);
+
+ // Удаление ".bsp"
+ $aMaps = str_ireplace(array('./', '.bsp'), '', $aMaps);
+
+ // Если выбрана карта
+ if($map)
+ {
+ $map = str_replace('|', '/', $map);
+
+ // Проверка наличия выбранной карты
+ if(games::map($map, $aMaps))
+ return array('e' => sys::updtext(sys::text('servers', 'change'), array('map' => $map.'.bsp')));
+
+ // Отправка команды changelevel
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval '."'stuff \"changelevel ".sys::cmd($map)."\"\015'");
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="change" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'change', 'online' => $server['online'], 'players' => base64_decode($server['players'])));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'change', 'online' => $server['online']));
+
+ return array('s' => 'ok');
+ }
+
+ // Сортировка списка карт
+ sort($aMaps);
+ reset($aMaps);
+
+ // Генерация списка карт для выбора
+ foreach($aMaps as $map)
+ {
+ $aName = explode('/', $map);
+ $name = end($aName);
+
+ $html->get('change_list', 'sections/control/servers/csgo');
+
+ $html->set('img', file_exists(DIR.'/maps/'.$server['game'].'/'.$name.'.jpg') ? $cfg['http'].'maps/'.$server['game'].'/'.$name.'.jpg' : $cfg['http'].'template/images/status/none.jpg');
+ $html->set('map', str_replace('/', '|', $map));
+ $html->set('name', $name);
+ $html->set('id', $server['unit']);
+ $html->set('server', $id);
+
+ if(count($aName) > 1)
+ $html->unit('workshop', true);
+ else
+ $html->unit('workshop');
+
+ $html->pack('maps');
+ }
+
+ // Запись карт в кеш
+ $mcache->set('ctrl_server_maps_change_'.$id, $html->arr['maps'], false, 60);
+
+ return array('maps' => $html->arr['maps']);
+ }
+
+ public static function update($id)
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `game`, `name`, `ftp`, `core_fix` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp`, `fcpu` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if(!$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Директория игрового сервера
+ $install = '/servers/'.$server['uid'];
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], $unit['fcpu'], true); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => 'Не удается выполнить операцию, нет свободного потока.');
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ $ssh->set('cd '.$cfg['steamcmd'].' && '.$taskset.' screen -dmS u_'.$server['uid'].' sh -c "'
+ .'./steamcmd.sh +login anonymous +force_install_dir "'.$install.'" +app_update 740 +quit;'
+ .'cd '.$install.';'
+ .'chown -R server'.$server['uid'].':servers .;'
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].'"');
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="update", `core_use`="'.$core.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/csgo/rcon.php b/system/library/control/csgo/rcon.php
new file mode 100644
index 0000000..cfaf75c
--- /dev/null
+++ b/system/library/control/csgo/rcon.php
@@ -0,0 +1,109 @@
+Connect($ip, $port, 3, SourceQuery::SOURCE);
+
+ $sq->SetRconPassword(rcon::rcon_passwd($server));
+
+ $out = $sq->Rcon($cmd);
+
+ $sq->Disconnect();
+
+ return $out;
+ }
+
+ public static function players($data)
+ {
+ $aPlayers = array();
+ $n = 1;
+
+ $lines = explode("\n", $data);
+
+ foreach($lines as $line)
+ {
+ if(strpos($line, '#') === FALSE)
+ continue;
+
+ $start = strpos($line, '"')+1;
+ $end = strrpos($line, '"');
+
+ $userid = sys::int(substr($line, 0, $start));
+
+ $name = htmlspecialchars(substr($line, $start, $end-$start));
+
+ $line = trim(substr($line, $end + 1));
+
+ $aData = array_values(array_diff(explode(' ', $line), array('', ' ')));
+
+ $steamid = trim($aData[0]);
+ $ip = trim(sys::first(explode(':', $aData[5])));
+
+ if((sys::valid($steamid, 'steamid') AND sys::valid($steamid, 'steamid3')) || sys::valid($ip, 'ip'))
+ continue;
+
+ $aPlayers[$n]['userid'] = $userid;
+ $aPlayers[$n]['name'] = $name;
+ $aPlayers[$n]['steamid'] = $steamid;
+ $aPlayers[$n]['time'] = trim($aData[1]);
+ $aPlayers[$n]['ping'] = trim($aData[2]);
+ $aPlayers[$n]['ip'] = $ip;
+
+ $whois = rcon::country($ip);
+
+ $aPlayers[$n]['ico'] = $whois['ico'];
+ $aPlayers[$n]['country'] = $whois['name'];
+
+ $n+=1;
+ }
+
+ return $aPlayers;
+ }
+
+ public static function rcon_passwd($server)
+ {
+ global $cfg, $sql, $user;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ $ssh->set('cat /servers/'.$server['uid'].'/csgo/cfg/server.cfg | grep rcon_password');
+ $get = explode(' ', str_replace('"', '', trim($ssh->get())));
+ $rcon = trim(end($get));
+
+ if(!isset($rcon{0}))
+ sys::outjs(array('r' => 'Необходимо установить rcon пароль (rcon_password).', 'url' => $cfg['http'].'control/id/'.$server['unit'].'/server/'.$server['unit'].'/section/settings/subsection/server'), $nmch);
+
+ return $rcon;
+ }
+
+ public static function country($ip)
+ {
+ global $SxGeo;
+
+ $cData = $SxGeo->getCityFull($ip);
+ $ico = sys::country($cData['country']['iso']);
+
+ return array('ico' => $ico, 'name' => empty($cData['country']['name_ru']) ? 'Не определена' : $cData['country']['name_ru']);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/csgo/scan.php b/system/library/control/csgo/scan.php
new file mode 100644
index 0000000..3c89854
--- /dev/null
+++ b/system/library/control/csgo/scan.php
@@ -0,0 +1,135 @@
+get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $info = scan::info($sq, $id);
+
+ $sql->query('SELECT `unit`, `game`, `name`, `map`, `online`, `players`, `status` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!$info['status'])
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status'], $server['game'], $server['unit']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ if($players_get)
+ $players = scan::info($sq, $id, true);
+
+ $out['name'] = htmlspecialchars($info['name']);
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = $info['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working', $server['game'], $server['unit']);
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', $player['name']);
+ $html->set('score', $player['score']);
+ $html->set('time', $player['time']);
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `control_servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `control_servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function info($sq, $id, $pl = false)
+ {
+ global $sql;
+
+ $sql->query('SELECT `address` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $sq->Connect($ip, $port, 1, SourceQuery::SOURCE);
+
+ if($pl)
+ {
+ $players = $sq->GetPlayers();
+
+ $i = 1;
+ $data = array();
+
+ foreach($players as $n => $player)
+ {
+ $data[$i]['i'] = $i;
+ $data[$i]['name'] = $player['Name'] == '' ? 'Подключается' : $player['Name'];
+ $data[$i]['score'] = $player['Frags'];
+ $data[$i]['time'] = $player['TimeF'];
+
+ $i+=1;
+ }
+
+ return $data;
+ }
+
+ $data = $sq->GetInfo();
+
+ $map = explode('/', $data['Map']);
+
+ $server['name'] = $data['HostName'];
+ $server['map'] = end($map);
+ $server['online'] = $data['Players'];
+ $server['status'] = strlen($server['map']) > 3 ? true : false;
+
+ return $server;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/css/action.php b/system/library/control/css/action.php
new file mode 100644
index 0000000..638b854
--- /dev/null
+++ b/system/library/control/css/action.php
@@ -0,0 +1,177 @@
+query('SELECT `uid`, `unit`, `game`, `address`, `slots`, `name`, `tickrate`, `map_start`, `vac`, `time_start`, `core_fix` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd`, `fcpu` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if(!$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Проверка наличия стартовой карты
+ $ssh->set('cd /servers/'.$server['uid'].'/cstrike/maps/ && ls | grep .bsp | grep -v .bsp.');
+
+ if($server['map_start'] != '' AND !in_array($server['map_start'], str_replace('.bsp', '', explode("\n", $ssh->get()))))
+ return array('e' => sys::updtext(sys::text('servers', 'nomap'), array('map' => $server['map_start'].'.bsp')));
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], $unit['fcpu'], true); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Античит VAC
+ $vac = $server['vac'] == 0 ? '-insecure' : '-secure';
+
+ // TV
+ $tv = $server['tv'] ? '+tv_enable 1 +tv_maxclients 255 +tv_port '.($port+10000) : '-nohltv';
+
+ // Параметры запуска
+ $bash = './srcds_run -debug -game cstrike -norestart -condebug console.log -tickrate '.$server['tickrate'].' +servercfgfile server.cfg +map \''.$server['map_start'].'\' +maxplayers '.$server['slots'].' +ip '.$ip.' +port '.$port.' -sv_lan 0 '.$vac.' '.$tv;
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd /servers/'.$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'sudo -u server'.$server['uid'].' mkdir -p cstrike/oldstart;' // Создание папки логов
+ .'cat cstrike/console.log >> cstrike/oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm cstrike/console.log; rm cstrike/oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 start.sh;' // Обновление владельца файла start.sh
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c "./start.sh"'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function update($id)
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `game`, `name`, `ftp`, `core_fix` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp`, `fcpu` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if(!$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Директория игрового сервера
+ $install = '/servers/'.$server['uid'];
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], $unit['fcpu'], true); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => 'Не удается выполнить операцию, нет свободного потока.');
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ $ssh->set('cd '.$cfg['steamcmd'].' && '.$taskset.' screen -dmS u_'.$server['uid'].' sh -c "'
+ .'./steamcmd.sh +login anonymous +force_install_dir "'.$install.'" +app_update 232330 +quit;'
+ .'cd '.$install.';'
+ .'chown -R server'.$server['uid'].':servers .;'
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].'"');
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="update", `core_use`="'.$core.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/css/rcon.php b/system/library/control/css/rcon.php
new file mode 100644
index 0000000..1dff675
--- /dev/null
+++ b/system/library/control/css/rcon.php
@@ -0,0 +1,109 @@
+Connect($ip, $port, 3, SourceQuery::SOURCE);
+
+ $sq->SetRconPassword(rcon::rcon_passwd($server));
+
+ $out = $sq->Rcon($cmd);
+
+ $sq->Disconnect();
+
+ return $out;
+ }
+
+ public static function players($data)
+ {
+ $aPlayers = array();
+ $n = 1;
+
+ $lines = explode("\n", $data);
+
+ foreach($lines as $line)
+ {
+ if(strpos($line, '#') === FALSE)
+ continue;
+
+ $start = strpos($line, '"')+1;
+ $end = strrpos($line, '"');
+
+ $userid = sys::int(substr($line, 0, $start));
+
+ $name = htmlspecialchars(substr($line, $start, $end-$start));
+
+ $line = trim(substr($line, $end + 1));
+
+ $aData = array_values(array_diff(explode(' ', $line), array('', ' ')));
+
+ $steamid = trim($aData[0]);
+ $ip = trim(sys::first(explode(':', $aData[5])));
+
+ if((sys::valid($steamid, 'steamid') AND sys::valid($steamid, 'steamid3')) || sys::valid($ip, 'ip'))
+ continue;
+
+ $aPlayers[$n]['userid'] = $userid;
+ $aPlayers[$n]['name'] = $name;
+ $aPlayers[$n]['steamid'] = $steamid;
+ $aPlayers[$n]['time'] = trim($aData[1]);
+ $aPlayers[$n]['ping'] = trim($aData[2]);
+ $aPlayers[$n]['ip'] = $ip;
+
+ $whois = rcon::country($ip);
+
+ $aPlayers[$n]['ico'] = $whois['ico'];
+ $aPlayers[$n]['country'] = $whois['name'];
+
+ $n+=1;
+ }
+
+ return $aPlayers;
+ }
+
+ public static function rcon_passwd($server)
+ {
+ global $cfg, $sql, $user;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ $ssh->set('cat /servers/'.$server['uid'].'/cstrike/cfg/server.cfg | grep rcon_password');
+ $get = explode(' ', str_replace('"', '', trim($ssh->get())));
+ $rcon = trim(end($get));
+
+ if(!isset($rcon{0}))
+ sys::outjs(array('r' => 'Необходимо установить rcon пароль (rcon_password).', 'url' => $cfg['http'].'control/id/'.$server['unit'].'/server/'.$server['unit'].'/section/settings/subsection/server'), $nmch);
+
+ return $rcon;
+ }
+
+ public static function country($ip)
+ {
+ global $SxGeo;
+
+ $cData = $SxGeo->getCityFull($ip);
+ $ico = sys::country($cData['country']['iso']);
+
+ return array('ico' => $ico, 'name' => empty($cData['country']['name_ru']) ? 'Не определена' : $cData['country']['name_ru']);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/css/scan.php b/system/library/control/css/scan.php
new file mode 100644
index 0000000..f736d83
--- /dev/null
+++ b/system/library/control/css/scan.php
@@ -0,0 +1,133 @@
+get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $info = scan::info($sq, $id);
+
+ $sql->query('SELECT `unit`, `game`, `name`, `map`, `online`, `players`, `status` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!$info['status'])
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status'], $server['game'], $server['unit']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ if($players_get)
+ $players = scan::info($sq, $id, true);
+
+ $out['name'] = htmlspecialchars($info['name']);
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = $info['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working', $server['game'], $server['unit']);
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', $player['name']);
+ $html->set('score', $player['score']);
+ $html->set('time', $player['time']);
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `control_servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `control_servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function info($sq, $id, $pl = false)
+ {
+ global $sql;
+
+ $sql->query('SELECT `address` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $sq->Connect($ip, $port, 1, SourceQuery::SOURCE);
+
+ if($pl)
+ {
+ $players = $sq->GetPlayers();
+
+ $i = 1;
+ $data = array();
+
+ foreach($players as $n => $player)
+ {
+ $data[$i]['i'] = $i;
+ $data[$i]['name'] = $player['Name'] == '' ? 'Подключается' : $player['Name'];
+ $data[$i]['score'] = $player['Frags'];
+ $data[$i]['time'] = $player['TimeF'];
+
+ $i+=1;
+ }
+
+ return $data;
+ }
+
+ $data = $sq->GetInfo();
+
+ $server['name'] = $data['HostName'];
+ $server['map'] = $data['Map'];
+ $server['online'] = $data['Players'];
+ $server['status'] = strlen($server['map']) > 3 ? true : false;
+
+ return $server;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/cssold/action.php b/system/library/control/cssold/action.php
new file mode 100644
index 0000000..392f4f2
--- /dev/null
+++ b/system/library/control/cssold/action.php
@@ -0,0 +1,110 @@
+query('SELECT `uid`, `unit`, `game`, `address`, `slots`, `name`, `tickrate`, `fps`, `map_start`, `vac`, `time_start`, `core_fix` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd`, `fcpu` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if(!$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Проверка наличия стартовой карты
+ $ssh->set('cd /servers/'.$server['uid'].'/cstrike/maps/ && ls | grep .bsp | grep -v .bsp.');
+
+ if($server['map_start'] != '' AND !in_array($server['map_start'], str_replace('.bsp', '', explode("\n", $ssh->get()))))
+ return array('e' => sys::updtext(sys::text('servers', 'nomap'), array('map' => $server['map_start'].'.bsp')));
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], $unit['fcpu'], true); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Античит VAC
+ $vac = $server['vac'] == 0 ? '-insecure' : '-secure';
+
+ // TV
+ $tv = $server['tv'] ? '+tv_enable 1 +tv_maxclients 255 +tv_port '.($port+10000) : '-nohltv';
+
+ // FPS
+ $fps = $server['fps'];
+
+ // Параметры запуска
+ $bash = './srcds_run -debug -game cstrike -norestart -condebug console.log -tickrate '.$server['tickrate'].' +fps_egp '.$fps.' +servercfgfile server.cfg +map \''.$server['map_start'].'\' +maxplayers '.$server['slots'].' +ip '.$ip.' +port '.$port.' +sv_lan 0 -nomaster -localcser '.$vac.' '.$tv;
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd /servers/'.$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'mv console.log oldstart.log;' // Перемещение лога предыдущего запуска в файл oldstart.log
+ .'sudo -u server'.$server['uid'].' mkdir -p cstrike/oldstart;' // Создание папки логов
+ .'cat cstrike/console.log >> cstrike/oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm cstrike/console.log; rm cstrike/oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 start.sh;' // Обновление владельца файла start.sh
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c "./start.sh"'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `control_servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/cssold/rcon.php b/system/library/control/cssold/rcon.php
new file mode 100644
index 0000000..97607e6
--- /dev/null
+++ b/system/library/control/cssold/rcon.php
@@ -0,0 +1,109 @@
+Connect($ip, $port, 3, SourceQuery::SOURCE);
+
+ $sq->SetRconPassword(rcon::rcon_passwd($server));
+
+ $out = $sq->Rcon($cmd);
+
+ $sq->Disconnect();
+
+ return $out;
+ }
+
+ public static function players($data)
+ {
+ $aPlayers = array();
+ $n = 1;
+
+ $lines = explode("\n", $data);
+
+ foreach($lines as $line)
+ {
+ if(strpos($line, '#') === FALSE)
+ continue;
+
+ $start = strpos($line, '"')+1;
+ $end = strrpos($line, '"');
+
+ $userid = sys::int(substr($line, 0, $start));
+
+ $name = htmlspecialchars(substr($line, $start, $end-$start));
+
+ $line = trim(substr($line, $end + 1));
+
+ $aData = array_values(array_diff(explode(' ', $line), array('', ' ')));
+
+ $steamid = trim($aData[0]);
+ $ip = trim(sys::first(explode(':', $aData[5])));
+
+ if(sys::valid($steamid, 'steamid') || sys::valid($ip, 'ip'))
+ continue;
+
+ $aPlayers[$n]['userid'] = $userid;
+ $aPlayers[$n]['name'] = $name;
+ $aPlayers[$n]['steamid'] = $steamid;
+ $aPlayers[$n]['time'] = trim($aData[1]);
+ $aPlayers[$n]['ping'] = trim($aData[2]);
+ $aPlayers[$n]['ip'] = $ip;
+
+ $whois = rcon::country($ip);
+
+ $aPlayers[$n]['ico'] = $whois['ico'];
+ $aPlayers[$n]['country'] = $whois['name'];
+
+ $n+=1;
+ }
+
+ return $aPlayers;
+ }
+
+ public static function rcon_passwd($server)
+ {
+ global $cfg, $sql, $user;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ $ssh->set('cat /servers/'.$server['uid'].'/cstrike/cfg/server.cfg | grep rcon_password');
+ $get = explode(' ', str_replace('"', '', trim($ssh->get())));
+ $rcon = trim(end($get));
+
+ if(!isset($rcon{0}))
+ sys::outjs(array('r' => 'Необходимо установить rcon пароль (rcon_password).', 'url' => $cfg['http'].'control/id/'.$server['unit'].'/server/'.$server['unit'].'/section/settings/subsection/server'), $nmch);
+
+ return $rcon;
+ }
+
+ public static function country($ip)
+ {
+ global $SxGeo;
+
+ $cData = $SxGeo->getCityFull($ip);
+ $ico = sys::country($cData['country']['iso']);
+
+ return array('ico' => $ico, 'name' => empty($cData['country']['name_ru']) ? 'Не определена' : $cData['country']['name_ru']);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/cssold/scan.php b/system/library/control/cssold/scan.php
new file mode 100644
index 0000000..f736d83
--- /dev/null
+++ b/system/library/control/cssold/scan.php
@@ -0,0 +1,133 @@
+get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $info = scan::info($sq, $id);
+
+ $sql->query('SELECT `unit`, `game`, `name`, `map`, `online`, `players`, `status` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!$info['status'])
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status'], $server['game'], $server['unit']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ if($players_get)
+ $players = scan::info($sq, $id, true);
+
+ $out['name'] = htmlspecialchars($info['name']);
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = $info['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working', $server['game'], $server['unit']);
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', $player['name']);
+ $html->set('score', $player['score']);
+ $html->set('time', $player['time']);
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `control_servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `control_servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function info($sq, $id, $pl = false)
+ {
+ global $sql;
+
+ $sql->query('SELECT `address` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $sq->Connect($ip, $port, 1, SourceQuery::SOURCE);
+
+ if($pl)
+ {
+ $players = $sq->GetPlayers();
+
+ $i = 1;
+ $data = array();
+
+ foreach($players as $n => $player)
+ {
+ $data[$i]['i'] = $i;
+ $data[$i]['name'] = $player['Name'] == '' ? 'Подключается' : $player['Name'];
+ $data[$i]['score'] = $player['Frags'];
+ $data[$i]['time'] = $player['TimeF'];
+
+ $i+=1;
+ }
+
+ return $data;
+ }
+
+ $data = $sq->GetInfo();
+
+ $server['name'] = $data['HostName'];
+ $server['map'] = $data['Map'];
+ $server['online'] = $data['Players'];
+ $server['status'] = strlen($server['map']) > 3 ? true : false;
+
+ return $server;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/plugins.php b/system/library/control/plugins.php
new file mode 100644
index 0000000..96700cd
--- /dev/null
+++ b/system/library/control/plugins.php
@@ -0,0 +1,181 @@
+arr['images']))
+ unset($html->arr['images']);
+
+ $aImg = explode("\n", $images);
+
+ foreach($aImg as $img)
+ {
+ $html->get('plugin_images', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $plugin);
+ $html->set('img', $img);
+
+ $html->pack('images');
+ }
+
+ return isset($html->arr['images']) ? $html->arr['images'] : '';
+ }
+
+ public static function status($status)
+ {
+ global $html;
+
+ if(!$status)
+ {
+ $html->unit('unstable');
+ $html->unit('stable', 1);
+ $html->unit('testing');
+ }elseif($status == 2){
+ $html->unit('unstable');
+ $html->unit('stable');
+ $html->unit('testing', 1);
+ }else{
+ $html->unit('unstable', 1);
+ $html->unit('stable');
+ $html->unit('testing');
+ }
+
+ return NULL;
+ }
+
+ public static function required($id, $required, $choice, $mcache)
+ {
+ global $sql;
+
+ if($required == '')
+ return NULL;
+
+ $aRequi = explode(':', $required);
+
+ foreach($aRequi as $pl)
+ {
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$pl.'" LIMIT 1');
+ if(!$sql->num())
+ {
+ $sql->query('SELECT `name` FROM `plugins` WHERE `id`="'.$pl.'" LIMIT 1');
+ $plRequi = $sql->get();
+
+ if($choice != '')
+ {
+ $aChoice = explode(' ', $choice);
+
+ foreach($aChoice as $plugins)
+ {
+ $aPlugins = explode(':', $plugins);
+
+ if(in_array($pl, $aPlugins))
+ {
+ $options = '';
+ foreach($aPlugins as $plugin)
+ {
+ $sql->query('SELECT `name`, `upd` FROM `plugins` WHERE `id`="'.$plugin.'" LIMIT 1');
+ $data = $sql->get();
+
+ if($data['upd'])
+ {
+ $sql->query('SELECT `name` FROM `plugins_update` WHERE `plugin`="'.$plugin.'" ORDER BY `id` DESC LIMIT 1');
+ $data = $sql->get();
+ }
+
+ $options .= ''.strip_tags($data['name']).' ';
+ }
+
+ if($options != '')
+ sys::outjs(array('e' => 'Для данного плагина требуется установка одного из родителя', 'required' => true, 'pid' => $pl, 'select' => $options), $mcache);
+ }
+ }
+ }
+
+ sys::outjs(array('e' => 'Для данного плагина требуется установка родителя', 'required' => true, 'pid' => $pl, 'pname' => htmlspecialchars_decode($plRequi['name'])), $mcache);
+ }
+ }
+
+ return NULL;
+ }
+
+ public static function incompatible($id, $incompatible, $mcache)
+ {
+ global $sql;
+
+ if($incompatible == '')
+ return NULL;
+
+ $aIncomp = explode(':', $incompatible);
+
+ foreach($aIncomp as $pl)
+ {
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$pl.'" LIMIT 1');
+ if($sql->num())
+ {
+ $sql->query('SELECT `name` FROM `plugins` WHERE `id`="'.$pl.'" LIMIT 1');
+ $plIncomp = $sql->get();
+
+ sys::outjs(array('e' => 'Данный плагин несовместим с уже установленным плагином', 'pid' => $pl, 'pname' => htmlspecialchars_decode($plIncomp['name'])), $mcache);
+ }
+ }
+
+ return NULL;
+ }
+
+ public static function clear($clear, $uid, $dir)
+ {
+ global $ssh;
+
+ // Если регулярное выражение
+ if(isset($clear['regex']) AND $clear['regex'])
+ {
+ $file = preg_replace($clear['text'], '', $ssh->get('sudo -u server'.$uid.' cat '.$dir.$clear['file']));
+
+ // Временный файл
+ $temp = sys::temp($file);
+
+ $ssh->setfile($temp, $dir.$clear['file'], 0644);
+
+ unlink($temp);
+
+ $query = 'chown server'.$uid.':servers '.$dir.$clear['file'].';';
+
+ }else
+ // Удаление текста из файла
+ $query = 'sudo -u server'.$uid.' sed -i '."'s/".str_replace('/', '\/', htmlspecialchars_decode($clear['text']))."//g'".' '.$dir.$clear['file'].';';
+
+ $ssh->set($query.'sudo -u server'.$uid.' sed -i '."'/./!d'".' '.$dir.$clear['file']);
+
+ return NULL;
+ }
+
+ public static function write($write, $uid, $dir)
+ {
+ global $ssh;
+
+ // Костыль (добавить пустую строку на всякий случай)
+ $query = 'sudo -u server'.$uid.' echo "" >> '.$dir.$write['file'].';';
+
+ // Исключить дублирование, путем удаления добавляемого текста
+ $query .= 'sudo -u server'.$uid.' sed -i '."'s/".str_replace('/', '\/', htmlspecialchars_decode($write['text']))."//g'".' '.$dir.$write['file'].';';
+
+ // Добавление текста в начало файла
+ if($write['top'])
+ $query .= 'sudo -u server'.$uid.' touch '.$dir.$write['file'].'; sudo -u server'.$uid.' sed -i '."'1i ".str_replace(array('/', "'", '\"'), array('\/', "\'", '"'), htmlspecialchars_decode($write['text']))."'".' '.$dir.$write['file'].';';
+ else
+ // Добавление текста в конец файла
+ $query .= 'sudo -u server'.$uid.' touch '.$dir.$write['file'].'; sudo -u server'.$uid.' echo "'.str_replace('"', '\"', htmlspecialchars_decode($write['text'])).'" >> '.$dir.$write['file'].';';
+
+ $ssh->set($query.'sudo -u server'.$uid.' sed -i '."'/./!d'".' '.$dir.$clear['file']);
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/control/scans.php b/system/library/control/scans.php
new file mode 100644
index 0000000..9b94376
--- /dev/null
+++ b/system/library/control/scans.php
@@ -0,0 +1,186 @@
+ 'hlds_',
+ 'cssold' => 'srcds_i686',
+ 'css' => 'srcds_',
+ 'csgo' => 'srcds_',
+ 'samp' => 'samp',
+ 'crmp' => 'samp',
+ 'mta' => 'mta',
+ 'mc' => 'java'
+ );
+
+ public static function resources($id)
+ {
+ global $cfg, $sql, $mcache;
+
+ $nmch = 'ctrl_server_resources_'.$id;
+
+ if(is_array($mcache->get($nmch)))
+ return $mcache->get($nmch);
+
+ $sql->query('SELECT `uid`, `unit`, `game`, `slots`, `status`, `online`, `hdd_use` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $server = $sql->get();
+
+ $resources = array(
+ 'usr' => 0,
+ 'cpu' => 0,
+ 'ram' => 0,
+ 'hdd' => $server['hdd_use']
+ );
+
+ $sql->query('SELECT `address`, `passwd`, `ram`, `hdd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return $resources;
+
+ if(!in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ return $resources;
+
+ $resources['usr'] = ceil(100/$server['slots']*$server['online']);
+ $resources['usr'] = $resources['usr'] > 100 ? 100 : $resources['usr'];
+
+ $cr = explode('|', $ssh->get('top -u '.$server['uid'].' -b -n 1 | grep '.(scans::$process[$server['game']]).' | sort | tail -1 | awk \'{print $9"|"$10}\''));
+
+ if(isset($cr[0]))
+ $resources['cpu'] = str_replace(',', '.', $cr[0]);
+
+ $resources['cpu'] = $resources['cpu'] > 100 ? 100 : round($resources['cpu']);
+
+ if(isset($cr[1]))
+ $resources['ram'] = str_replace(',', '.', $cr[1]);
+
+ // ram на сервер
+ $ram = $server['ram'] ? $server['ram'] : $server['slots']*$cfg['ram'][$server['game']];
+
+ $resources['ram'] = $unit['ram']/100*$resources['ram']/($ram/100);
+
+ $resources['ram'] = $resources['ram'] > 100 ? 100 : round($resources['ram']);
+
+ $resources['hdd'] = ceil(sys::int($ssh->get('cd /servers/'.$server['uid'].' && du -ms'))/($unit['hdd']/100));
+ $resources['hdd'] = $resources['hdd'] > 100 ? 100 : $resources['hdd'];
+
+ $sql->query('UPDATE `control_servers` set `ram_use`="'.$resources['ram'].'", `cpu_use`="'.$resources['cpu'].'", `hdd_use`="'.$resources['hdd'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $resources, false, $cfg['mcache_server_resources']);
+
+ return $resources;
+ }
+
+ public static function status($id)
+ {
+ global $start_point, $cfg, $sql, $mcache;
+
+ $nmch = 'ctrl_server_status_'.$id;
+
+ if($mcache->get($nmch))
+ return 'mcache -> system_block_operation';
+
+ $mcache->set($nmch, true, false, $cfg['mcache_server_status']);
+
+ $sql->query('SELECT `uid`, `unit`, `game`, `address`, `status`, `name`, `online`, `players` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return 'unit error connect';
+
+ switch($server['status'])
+ {
+ case 'working': case 'change': case 'start': case 'restart':
+ if(!sys::int($ssh->get('ps aux | grep s_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `control_servers` set `status`="off", `online`="0", `players`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> working -> off';
+ }
+
+ break;
+
+ case 'off':
+ if(sys::int($ssh->get('ps aux | grep s_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `control_servers` set `status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'working', 'online' => $server['online'], 'players' => $server['players']));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'working', 'online' => $server['online']));
+
+ return 'server -> off -> working';
+ }
+
+ break;
+
+ case 'reinstall':
+ if(!sys::int($ssh->get('ps aux | grep r_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `control_servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> reinstall -> end';
+ }
+
+ break;
+
+ case 'update':
+ if(!sys::int($ssh->get('ps aux | grep u_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `control_servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> update -> end';
+ }
+
+ break;
+
+ case 'install':
+ if(!sys::int($ssh->get('ps aux | grep i_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `control_servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> install -> end';
+ }
+
+ break;
+
+ case 'recovery':
+ if(!sys::int($ssh->get('ps aux | grep rec_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `control_servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('ctrl_server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('ctrl_server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> recovery -> end';
+ }
+ }
+
+ return 'server -> no change -> end scan';
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron.php b/system/library/cron.php
new file mode 100644
index 0000000..cac5f93
--- /dev/null
+++ b/system/library/cron.php
@@ -0,0 +1,77 @@
+ 0, 'group' => 'admin');
+
+ class cron
+ {
+ public static $seping = 5;
+
+ public static $process = array(
+ 'cs' => 'hlds_',
+ 'cssold' => 'srcds_i686',
+ 'css' => 'srcds_',
+ 'csgo' => 'srcds_',
+ 'samp' => 'samp',
+ 'crmp' => 'samp',
+ 'mta' => 'mta',
+ 'mc' => 'java'
+ );
+
+ public static $quakestat = array(
+ 'cs' => 'a2s',
+ 'cssold' => 'a2s',
+ 'css' => 'a2s',
+ 'csgo' => 'a2s',
+ 'mta' => 'eye'
+ );
+
+ public static $admins_file = array(
+ 'cs' => 'cstrike/addons/amxmodx/configs/users.ini',
+ 'cssold' => 'cstrike/addons/sourcemod/configs/admins_simple.ini',
+ 'css' => 'cstrike/addons/sourcemod/configs/admins_simple.ini',
+ 'csgo' => 'csgo/addons/sourcemod/configs/admins_simple.ini'
+ );
+
+ public static function thread($num, $type, $aData)
+ {
+ $threads = array();
+
+ for($n = 1; $n <= $num; $n+=1)
+ {
+ $data = '';
+
+ $i = 0;
+
+ foreach($aData as $key => $val)
+ {
+ if($i == cron::$seping)
+ break;
+
+ $data .= $val.' ';
+
+ unset($aData[$key]);
+
+ $i+=1;
+ }
+
+ $aData = array_values($aData);
+
+ $threads[] = $type.' '.substr($data, 0, -1);
+ }
+
+ return $threads;
+ }
+ }
+
+ include(CRON.$task.'.php');
+
+ new $task();
+?>
\ No newline at end of file
diff --git a/system/library/cron/control_delete.php b/system/library/cron/control_delete.php
new file mode 100644
index 0000000..3adc903
--- /dev/null
+++ b/system/library/cron/control_delete.php
@@ -0,0 +1,36 @@
+query('SELECT `id` FROM `control` WHERE `user`="-1" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $servers = $sql->query('SELECT `id` FROM `control_servers` WHERE `unit`="'.$unit['id'].'"');
+ while($server = $sql->get($servers))
+ {
+ $sql->query('DELETE FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$server['id'].'"');
+ $sql->query('DELETE FROM `control_copy` WHERE `server`="'.$server['id'].'"');
+ $sql->query('DELETE FROM `control_firewall` WHERE `server`="'.$server['id'].'"');
+ $sql->query('DELETE FROM `control_plugins_install` WHERE `server`="'.$server['id'].'"');
+ }
+
+ // Удаление различной информации игрового сервера
+ $sql->query('DELETE FROM `control_servers` WHERE `unit`="'.$unit['id'].'"');
+ $sql->query('DELETE FROM `control` WHERE `id`="'.$unit['id'].'"');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `control`="'.$unit['id'].'", `text`="Удаление подключенного сервера #'.$unit['id'].' ('.$unit['address'].') passwd: #'.$unit['passwd'].'", `time`="'.$start_point.'"');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/control_install.php b/system/library/cron/control_install.php
new file mode 100644
index 0000000..ee0f604
--- /dev/null
+++ b/system/library/cron/control_install.php
@@ -0,0 +1,35 @@
+query('SELECT `id`, `address`, `passwd` FROM `control` WHERE `status`="install" AND `install`="0" LIMIT 1');
+
+ if(!$sql->num())
+ exit('not found');
+
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ exit('error connect');
+
+ $ssh->set('apt-get update; apt-get install -y wget screen');
+
+ sleep(20);
+
+ $ssh->set('screen -dmS install bash -c "cd /tmp; rm script.sh; wget -O script.sh [home]autocontrol/action/script --no-check-certificate; chmod 500 script.sh;./script.sh"');
+
+ $sql->query('UPDATE `control` set install="1" WHERE `id`="'.$unit['id'].'" LIMIT 1');
+
+ exit('install');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/control_scan_servers.php b/system/library/cron/control_scan_servers.php
new file mode 100644
index 0000000..5657515
--- /dev/null
+++ b/system/library/cron/control_scan_servers.php
@@ -0,0 +1,105 @@
+query('SELECT `address` FROM `control` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $game = $servers[3];
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `control_servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ foreach($servers as $id)
+ {
+ $sql->query('SELECT `uid`, `address`, `status`, `stop` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ switch($server['status'])
+ {
+ case 'working': case 'change': case 'start': case 'restart':
+ if(!sys::int($ssh->get('ps aux | grep s_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `control_servers` set `status`="off", `online`="0", `players`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запуск сервера (если он был выключен не через панель)
+ if($server['stop'])
+ {
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' control_server_action start '.$game.' '.$id.'"');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `server`="'.$id.'", `text`="[Контроль] Включение сервера: сервер выключен не через панель", `time`="'.$start_point.'"');
+ }
+ }else
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' control_server_scan '.$game.' '.$id.'"');
+
+ break;
+
+ case 'off':
+ if(sys::int($ssh->get('ps aux | grep s_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `control_servers` set `status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+ else{
+ // Запуск сервера (если он был выключен не через панель)
+ if($server['stop'])
+ {
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' control_server_action start '.$game.' '.$id.'"');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `server`="'.$id.'", `text`="[Контроль] Включение сервера: сервер выключен не через панель", `time`="'.$start_point.'"');
+
+ continue;
+ }
+ }
+
+ break;
+
+ case 'reinstall':
+ if(!sys::int($ssh->get('ps aux | grep r_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `control_servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'update':
+ if(!sys::int($ssh->get('ps aux | grep u_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `control_servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'install':
+ if(!sys::int($ssh->get('ps aux | grep i_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `control_servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'recovery':
+ if(!sys::int($ssh->get('ps aux | grep rec_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `control_servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/control_scan_servers_admins.php b/system/library/cron/control_scan_servers_admins.php
new file mode 100644
index 0000000..a9c64ed
--- /dev/null
+++ b/system/library/cron/control_scan_servers_admins.php
@@ -0,0 +1,68 @@
+query('SELECT `address` FROM `control` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `control_servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ foreach($servers as $id)
+ {
+ $sql->query('SELECT `uid` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $admins = $sql->query('SELECT `id`, `text` FROM `control_admins_'.$game.'` WHERE `server`="'.$id.'" AND `active`="1" AND `time`<"'.$start_point.'"');
+
+ if(!$sql->num($admins))
+ continue;
+
+ $cmd = 'cd /servers/'.$server['uid'].';';
+
+ while($admin = $sql->get($admins))
+ {
+ $cmd .= 'sed -i -e \'s/'.escapeshellcmd(htmlspecialchars_decode($admin['text'])).'//g\' '.cron::$admins_file[$game].';';
+
+ $sql->query('UPDATE `admins_'.$game.'` set `active`="0" WHERE `id`="'.$admin['id'].'" LIMIT 1');
+ }
+
+ $cmd .= 'sed -i '."'/./!d'".' '.cron::$admins_file[$game].'; echo -e "\n" >> '.cron::$admins_file[$game].';';
+ $cmd .= 'chown server'.$server['uid'].':1000 '.cron::$admins_file[$game];
+
+ $ssh->set($cmd);
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/control_scan_servers_copy.php b/system/library/cron/control_scan_servers_copy.php
new file mode 100644
index 0000000..9a643e3
--- /dev/null
+++ b/system/library/cron/control_scan_servers_copy.php
@@ -0,0 +1,53 @@
+query('SELECT `address` FROM `control` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $game = $servers[3];
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `control_servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ foreach($servers as $id)
+ {
+ $copys = $sql->query('SELECT `id` FROM `control_copy` WHERE `status`="0"');
+ while($copy = $sql->get($copys))
+ {
+ $sql->query('SELECT `uid` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!sys::int($ssh->get('ps aux | grep copy_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `control_copy` set `status`="1" WHERE `id`="'.$copy['id'].'" LIMIT 1');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/control_scan_servers_down.php b/system/library/cron/control_scan_servers_down.php
new file mode 100644
index 0000000..6cdd074
--- /dev/null
+++ b/system/library/cron/control_scan_servers_down.php
@@ -0,0 +1,62 @@
+query('SELECT `address` FROM `control` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $game = $servers[3];
+
+ if(!array_key_exists($game, cron::$quakestat))
+ return NULL;
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `control_servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `ram` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ foreach($servers as $id)
+ {
+ $sql->query('SELECT `uid`, `address`, `status`, `autorestart` FROM `control_servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!$server['autorestart'])
+ continue;
+
+ if($server['status'] != 'working')
+ continue;
+
+ if(!in_array(trim($ssh->get('quakestat -'.(cron::$quakestat[$game]).' '.$server['address'].' -retry 5 -interval 2 | grep -v frags | tail -1 | awk \'{print $2}\'')), array('DOWN', 'no')))
+ continue;
+
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' control_server_action restart '.$game.' '.$id.'"');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `server`="'.$id.'", `text`="[Контроль] Перезагрука сервера: сервер завис", `time`="'.$start_point.'"');
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/control_scan_servers_route.php b/system/library/cron/control_scan_servers_route.php
new file mode 100644
index 0000000..2822cfa
--- /dev/null
+++ b/system/library/cron/control_scan_servers_route.php
@@ -0,0 +1,104 @@
+query('SELECT `address` FROM `control` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $game = $servers[3];
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `control_servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `fcpu` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ $first = $ssh->get('cat /proc/stat');
+
+ sleep(1);
+
+ $aCpu = sys::cpu_idle(array($first, $ssh->get('cat /proc/stat')), $unit['fcpu'], true);
+
+ array_shift($aCpu);
+
+ $idle = array();
+ $uses = array();
+
+ foreach($aCpu as $cpu => $data)
+ {
+ $core = sys::int($cpu)+1;
+
+ $sql->query('SELECT `id` FROM `control_servers` WHERE `unit`="'.$server['unit'].'" AND `core_fix`="'.$core.'" AND `core_fix`="1" LIMIT 1');
+ if($sql->num())
+ {
+ unset($aCpu[$cpu]);
+
+ continue;
+ }
+
+ if($data['idle'] > 50)
+ $idle[$core] = $data['idle'];
+ else
+ $uses[$core] = 100-$data['idle'];
+ }
+
+ if(!count($idle))
+ return NULL;
+
+ foreach($uses as $use_core => $use)
+ {
+ if(!count($idle))
+ break;
+
+ $sql->query('SELECT `id`, `uid` FROM `control_servers` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$game.'" AND `core_use`="'.$use_core.'" AND `status`="working" AND `core_fix`="0" ORDER BY `slots_start` DESC, `online` DESC LIMIT 3');
+ if($sql->num() > 1)
+ {
+ $server = $sql->get();
+
+ $core = array_search(max($idle), $idle);
+
+ $aPid = explode("\n", $ssh->get('ps aux | grep -v grep | grep '.$server['uid'].' | awk \'{print $2}\''));
+
+ if(count($aPid) < 2)
+ continue;
+
+ array_pop($aPid);
+
+ $taskset = '';
+
+ foreach($aPid as $pid)
+ $taskset .= 'taskset -cp '.($core-1).' '.$pid.';';
+
+ $ssh->set($taskset);
+
+ unset($idle[$core]);
+
+ $sql->query('UPDATE `control_servers` set `core_use`="'.$core.'" WHERE `id`="'.$server['id'].'" LIMIT 1');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/control_server_action.php b/system/library/cron/control_server_action.php
new file mode 100644
index 0000000..4582fdc
--- /dev/null
+++ b/system/library/cron/control_server_action.php
@@ -0,0 +1,55 @@
+get($nmch))
+ return NULL;
+
+ $mcache->set($nmch, true, false, 10);
+
+ if($argv[3] == 'console')
+ {
+ global $sql;
+
+ $sql->query('SELECT `uid`, `unit` FROM `control_servers` WHERE `id`="'.$argv[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ $sql->query('SELECT `commands` FROM `control_crontab` WHERE `id`="'.$argv[6].'" LIMIT 1');
+ $cron = $sql->get();
+
+ $aCmd = explode("\n", base64_decode($cron['commands']));
+
+ foreach($aCmd as $cmd)
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.sys::cmd($cmd).'"\015\'; sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ return NULL;
+ }
+
+ include(LIB.'control/'.$argv[4].'/action.php');
+
+ if($argv[3] == 'restart')
+ action::start($argv[5], 'restart');
+ else
+ action::$argv[3]($argv[5]);
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/control_server_cron.php b/system/library/cron/control_server_cron.php
new file mode 100644
index 0000000..89aa18a
--- /dev/null
+++ b/system/library/cron/control_server_cron.php
@@ -0,0 +1,24 @@
+query('SELECT `game` FROM `control_servers` WHERE `id`="'.$argv[3].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `task` FROM `control_crontab` WHERE `id`="'.$argv[4].'" LIMIT 1');
+ $cron = $sql->get();
+
+ $cmd = $cron['task'] == 'console' ? ' '.$argv[4] : '';
+
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' control_server_action '.$cron['task'].' '.$server['game'].' '.$argv[3].$cmd.'"');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/control_server_scan.php b/system/library/cron/control_server_scan.php
new file mode 100644
index 0000000..9dd6439
--- /dev/null
+++ b/system/library/cron/control_server_scan.php
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/system/library/cron/control_threads.php b/system/library/cron/control_threads.php
new file mode 100644
index 0000000..24fce18
--- /dev/null
+++ b/system/library/cron/control_threads.php
@@ -0,0 +1,63 @@
+query('SELECT `id` FROM `control` ORDER BY `id` ASC');
+
+ if(!$sql->num())
+ return NULL;
+
+ while($unit = $sql->get())
+ $aUnit[$unit['id']] = '';
+
+ $sql->query('SELECT `id` FROM `control_servers` LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $sql->query('SELECT `id`, `unit`, `game` FROM `control_servers` ORDER BY `unit` DESC');
+
+ $all = $sql->num();
+
+ while($server = $sql->get())
+ $aUnit[$server['unit']][$server['game']] .= $server['id'].' ';
+
+ if($argv[3] == 'control_scan_servers_route')
+ cron::$seping = 50;
+
+ foreach($aUnit as $unit => $aGame)
+ {
+ foreach($aGame as $game => $servers)
+ {
+ $aData = explode(' ', $servers);
+
+ $num = count($aData)-1;
+ $sep = $num > 0 ? ceil($num/cron::$seping) : 1;
+
+ unset($aData[end($aData)]);
+
+ $threads[] = cron::thread($sep, $game.' '.$unit, $aData);
+ }
+ }
+
+ $cmd = '';
+
+ foreach($threads as $thread)
+ {
+ foreach($thread as $screen => $servers)
+ $cmd .= 'sudo -u www-data screen -dmS scan_'.(sys::first(explode(' ', $servers))).'_'.$screen.' taskset -c '.$cfg['cron_taskset'].' sh -c \"cd /var/enginegp; php cron.php '.$cfg['cron_key'].' '.$argv[3].' '.$servers.'\"; sleep 1;';
+ }
+
+ exec('screen -dmS control_threads_'.date('His', $start_point).' sh -c "'.$cmd.'"');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/graph_servers_day.php b/system/library/cron/graph_servers_day.php
new file mode 100644
index 0000000..edf1fe6
--- /dev/null
+++ b/system/library/cron/graph_servers_day.php
@@ -0,0 +1,50 @@
+query('SELECT `id`, `date` FROM `servers` ORDER BY `id` ASC');
+
+ while($server = $sql->get($servers))
+ {
+ if($server['date']+86400 > $start_point)
+ continue;
+
+ $aGraph = array('online' => 0, 'cpu' => 0, 'ram' => 0, 'hdd' => 0, 'time' => 0);
+
+ $sql->query('SELECT `online`, `cpu`, `ram`, `hdd` FROM `graph_hour` WHERE `server`="'.$server['id'].'" AND `time`>"'.($start_point-86400).'" ORDER BY `id` DESC LIMIT 24');
+
+ $n = $sql->num();
+
+ if(!$n)
+ continue;
+
+ while($graph = $sql->get())
+ {
+ $aGraph['online'] += $graph['online'];
+ $aGraph['cpu'] += $graph['cpu'];
+ $aGraph['ram'] += $graph['ram'];
+ $aGraph['hdd'] += $graph['hdd'];
+ }
+
+ $aGraph['online'] = $aGraph['online']/$n;
+ $aGraph['cpu'] = $aGraph['cpu']/$n;
+ $aGraph['ram'] = $aGraph['ram']/$n;
+ $aGraph['hdd'] = $aGraph['hdd']/$n;
+
+ $sql->query('INSERT INTO `graph_day` set `server`="'.$server['id'].'",'
+ .'`online`="'.$aGraph['online'].'",'
+ .'`cpu`="'.$aGraph['cpu'].'",'
+ .'`ram`="'.$aGraph['ram'].'",'
+ .'`hdd`="'.$aGraph['hdd'].'", `time`="'.$start_point.'"');
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/graph_servers_hour.php b/system/library/cron/graph_servers_hour.php
new file mode 100644
index 0000000..06cc145
--- /dev/null
+++ b/system/library/cron/graph_servers_hour.php
@@ -0,0 +1,28 @@
+query('SELECT `id`, `online`, `ram_use`, `cpu_use`, `hdd_use`, `date` FROM `servers` ORDER BY `id` ASC');
+
+ while($server = $sql->get($servers))
+ {
+ if($server['date']+3600 > $start_point)
+ continue;
+
+ $sql->query('INSERT INTO `graph_hour` set `server`="'.$server['id'].'",'
+ .'`online`="'.$server['online'].'",'
+ .'`cpu`="'.$server['cpu_use'].'",'
+ .'`ram`="'.$server['ram_use'].'",'
+ .'`hdd`="'.$server['hdd_use'].'", `time`="'.$start_point.'"');
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/notice_help.php b/system/library/cron/notice_help.php
new file mode 100644
index 0000000..6b5065f
--- /dev/null
+++ b/system/library/cron/notice_help.php
@@ -0,0 +1,32 @@
+query('SELECT `id`, `user`, `time` FROM `help` WHERE `status`="0" AND `time`<"'.$time.'" AND `notice`="0" AND `close`="0"');
+ while($help = $sql->get($helps))
+ {
+ $sql->query('SELECT `mail` FROM `users` WHERE `id`="'.$help['user'].'" AND `time`<"'.$help['time'].'" AND `notice_help`="1" LIMIT 1');
+
+ if(!$sql->num())
+ continue;
+
+ $user = $sql->get();
+
+ if(!sys::mail('Техническая поддержка', sys::updtext(sys::text('mail', 'notice_help'), array('site' => $cfg['name'], 'url' => $cfg['http'].'help/section/dialog/id/'.$help['id'])), $user['mail']))
+ continue;
+
+ $sql->query('UPDATE `help` set `notice`="1" WHERE `id`="'.$help['id'].'" LIMIT 1');
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/notice_help_admin.php b/system/library/cron/notice_help_admin.php
new file mode 100644
index 0000000..26e391c
--- /dev/null
+++ b/system/library/cron/notice_help_admin.php
@@ -0,0 +1,37 @@
+query('SELECT `id`, `time`, `notice_admin` FROM `help` WHERE (`notice_admin`="0" OR `notice_admin`="2") AND `close`="0" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $help = $sql->get();
+
+ foreach($cfg['notice_admin'] as $id)
+ {
+ $sql->query('SELECT `mail` FROM `users` WHERE `id`="'.$id.'" LIMIT 1');
+ $admin = $sql->get();
+
+ if($help['notice_admin'] != 2)
+ {
+ if(!sys::mail('Техническая поддержка', sys::updtext(sys::text('mail', 'notice_help_admin_new'), array('url' => $cfg['http'].'help/section/dialog/id/'.$help['id'])), $admin['mail']))
+ continue;
+ }else{
+ if(!sys::mail('Техническая поддержка', sys::updtext(sys::text('mail', 'notice_help_admin'), array('url' => $cfg['http'].'help/section/dialog/id/'.$help['id'])), $admin['mail']))
+ continue;
+ }
+ }
+
+ $sql->query('UPDATE `help` set `notice_admin`="1" WHERE `id`="'.$help['id'].'" LIMIT 1');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/notice_server_overdue.php b/system/library/cron/notice_server_overdue.php
new file mode 100644
index 0000000..ca3e4c0
--- /dev/null
+++ b/system/library/cron/notice_server_overdue.php
@@ -0,0 +1,30 @@
+query('SELECT `id`, `user`, `address` FROM `servers` WHERE `time`<"'.$start_point.'" AND `mail`="0"');
+ while($server = $sql->get($servers))
+ {
+ $sql->query('SELECT `mail` FROM `users` WHERE `id`="'.$server['user'].'" LIMIT 1');
+ $user = $sql->get();
+
+ if(!sys::mail('Аренда сервера', sys::updtext(sys::text('mail', 'notice_server_overdue'), array('site' => $cfg['name'], 'id' => $server['id'], 'address' => $server['address'])), $user['mail']))
+ continue;
+
+ $sql->query('UPDATE `servers` set `mail`="1" WHERE `id`="'.$server['id'].'" LIMIT 1');
+ }
+
+ $servers = $sql->query('SELECT `id` FROM `servers` WHERE `time`>"'.$start_point.'" AND `mail`="1"');
+ while($server = $sql->get($servers))
+ $sql->query('UPDATE `servers` set `mail`="0" WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/preparing_web_delete.php b/system/library/cron/preparing_web_delete.php
new file mode 100644
index 0000000..73e240f
--- /dev/null
+++ b/system/library/cron/preparing_web_delete.php
@@ -0,0 +1,18 @@
+query('SELECT `id` FROM `web` WHERE `user`="0"');
+ while($web = $sql->get())
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' web_delete '.$web['id'].'"');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/privileges.php b/system/library/cron/privileges.php
new file mode 100644
index 0000000..df716cc
--- /dev/null
+++ b/system/library/cron/privileges.php
@@ -0,0 +1,99 @@
+query('SELECT `address` FROM `units` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return ': UNIT#'.$servers[4].' .';
+
+ $unit = $sql->get();
+
+ unset($servers[3], $servers[4]);
+
+ foreach($servers as $i => $id)
+ {
+ $sql->query('SELECT `id` FROM `privileges_buy` WHERE `server`="'.$id.'" AND `status`="1" LIMIT 1');
+ if(!$sql->num())
+ unset($servers[$i]);
+ }
+
+ if(!count($servers))
+ return NULL;
+
+ $sql->query('SELECT `unit` FROM `servers` WHERE `id`="'.end($servers).'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // ssh
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return ': UNIT#'.$server['unit'].' .';
+
+ $time = $start_point-172800;
+
+ foreach($servers as $id)
+ {
+ $sql->query('DELETE FROM `privileges_buy` WHERE `date`<"'.$time.'" AND status`="0" LIMIT 5');
+
+ $aMail = array();
+
+ $sql->query('SELECT `uid`, `tarif` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $file = $tarif['install'].$server['uid'].'/'.cron::$admins_file[$game];
+
+ $text = $ssh->get('cat '.$file);
+
+ $privileges = $sql->query('SELECT `id`, `text`, `sql`, `mail` FROM `privileges_buy` WHERE `server`="'.$id.'" AND `status`="1" LIMIT 3');
+ while($privilege = $sql->get($privileges))
+ {
+ $text .= base64_decode($privilege['text']).PHP_EOL;
+
+ $sql->query(base64_decode($privilege['sql']));
+ $sql->query('DELETE FROM `privileges_buy` WHERE `id`="'.$privilege['id'].'" LIMIT 1');
+
+ $aMail[] = $privilege['mail'];
+ }
+
+ $temp = sys::temp($text);
+
+ $ssh->setfile($temp, $file, 0644);
+
+ unlink($temp);
+
+ $cmd = $game == 'cs' ? 'amx_reloadadmins' : 'sm_reloadadmins';
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$file);
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"".$cmd."\"\015'");
+
+ foreach($aMail as $mail)
+ sys::mail(' ', sys::text('mail', 'success_privilege'), $mail);
+
+ echo 'server#'.$id.' ('.$game.') -> add privileges '.PHP_EOL;
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/scan_control.php b/system/library/cron/scan_control.php
new file mode 100644
index 0000000..583a21c
--- /dev/null
+++ b/system/library/cron/scan_control.php
@@ -0,0 +1,21 @@
+query('SELECT `id` FROM `control` ORDER BY `id` ASC');
+ while($ctrl = $sql->get())
+ ctrl::update_status($ctrl['id'], $ssh);
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/scan_servers.php b/system/library/cron/scan_servers.php
new file mode 100644
index 0000000..3836246
--- /dev/null
+++ b/system/library/cron/scan_servers.php
@@ -0,0 +1,145 @@
+query('SELECT `address`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $game = $servers[3];
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ foreach($servers as $id)
+ {
+ $sql->query('SELECT `user`, `uid`, `address`, `status`, `time`, `overdue`, `ftp`, `stop`, `block` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ // Если аренда не закончилась, а сервер просрочен
+ if($server['time'] > $start_point && $server['status'] == 'overdue')
+ {
+ $sql->query('UPDATE `servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ continue;
+ }
+
+ // Если аренда закончилась, а сервер не просрочен (и не заблокирован)
+ if($server['time'] < $start_point && !in_array($server['status'], array('overdue', 'blocked')))
+ {
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ if($server['ftp'])
+ $ssh->set("mysql -P ".$unit['sql_port']." -u".$unit['sql_login']." -p".$unit['sql_passwd']." --database ".$unit['sql_ftp']." -e \"DELETE FROM ftp WHERE user='".$server['uid']."'\"");
+
+ $sql->query('UPDATE `servers` set `status`="overdue", `online`="0", `players`="", `ftp`="0", `overdue`="'.$start_point.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ continue;
+ }
+
+ // Если аренда закончилась и сервер просрочен длительное время или поставлен на удаление
+ if($server['user'] == -1 || ($server['time'] < $start_point && ($server['overdue']+$cfg['server_delete']*86400) < $start_point))
+ {
+ if($server['user'] != -1)
+ $sql->query('UPDATE `servers` set `user`="-1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' server_delete '.$id.'"');
+
+ continue;
+ }
+
+ switch($server['status'])
+ {
+ case 'working': case 'change': case 'start': case 'restart':
+ if(!sys::int($ssh->get('ps aux | grep s_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `servers` set `status`="off", `online`="0", `players`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запуск сервера (если он был выключен не через панель)
+ if($server['stop'])
+ {
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' server_action start '.$game.' '.$id.'"');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `server`="'.$id.'", `text`="Включение сервера: сервер выключен не через панель", `time`="'.$start_point.'"');
+ }
+ }else
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' server_scan '.$game.' '.$id.'"');
+
+ break;
+
+ case 'off':
+ if(sys::int($ssh->get('ps aux | grep s_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `servers` set `status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+ else{
+ // Запуск сервера (если он был выключен не через панель)
+ if($server['stop'])
+ {
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' server_action start '.$game.' '.$id.'"');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `server`="'.$id.'", `text`="Включение сервера: сервер выключен не через панель", `time`="'.$start_point.'"');
+
+ continue;
+ }
+ }
+
+ break;
+
+ case 'reinstall':
+ if(!sys::int($ssh->get('ps aux | grep r_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'update':
+ if(!sys::int($ssh->get('ps aux | grep u_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'install':
+ if(!sys::int($ssh->get('ps aux | grep i_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'recovery':
+ if(!sys::int($ssh->get('ps aux | grep rec_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'blocked':
+ if($server['block'] < $start_point)
+ $sql->query('UPDATE `servers` set `status`="off", `block`="0" WHERE `id`="'.$id.'" LIMIT 1');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/scan_servers_admins.php b/system/library/cron/scan_servers_admins.php
new file mode 100644
index 0000000..7034e07
--- /dev/null
+++ b/system/library/cron/scan_servers_admins.php
@@ -0,0 +1,71 @@
+query('SELECT `address` FROM `units` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ foreach($servers as $id)
+ {
+ $sql->query('SELECT `uid`, `tarif` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $admins = $sql->query('SELECT `id`, `text` FROM `admins_'.$game.'` WHERE `server`="'.$id.'" AND `active`="1" AND `time`<"'.$start_point.'"');
+
+ if(!$sql->num($admins))
+ continue;
+
+ $cmd = 'cd '.$tarif['install'].$server['uid'].';';
+
+ while($admin = $sql->get($admins))
+ {
+ $cmd .= 'sed -i -e \'s/'.escapeshellcmd(htmlspecialchars_decode($admin['text'])).'//g\' '.cron::$admins_file[$game].';';
+
+ $sql->query('UPDATE `admins_'.$game.'` set `active`="0" WHERE `id`="'.$admin['id'].'" LIMIT 1');
+ }
+
+ $cmd .= 'sed -i '."'/./!d'".' '.cron::$admins_file[$game].'; echo -e "\n" >> '.cron::$admins_file[$game].';';
+ $cmd .= 'chown server'.$server['uid'].':1000 '.cron::$admins_file[$game];
+
+ $ssh->set($cmd);
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/scan_servers_copy.php b/system/library/cron/scan_servers_copy.php
new file mode 100644
index 0000000..f7259a7
--- /dev/null
+++ b/system/library/cron/scan_servers_copy.php
@@ -0,0 +1,53 @@
+query('SELECT `address` FROM `units` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $game = $servers[3];
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ foreach($servers as $id)
+ {
+ $copys = $sql->query('SELECT `id` FROM `copy` WHERE `status`="0"');
+ while($copy = $sql->get($copys))
+ {
+ $sql->query('SELECT `uid` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!sys::int($ssh->get('ps aux | grep copy_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `copy` set `status`="1" WHERE `id`="'.$copy['id'].'" LIMIT 1');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/scan_servers_down.php b/system/library/cron/scan_servers_down.php
new file mode 100644
index 0000000..043956c
--- /dev/null
+++ b/system/library/cron/scan_servers_down.php
@@ -0,0 +1,62 @@
+query('SELECT `address` FROM `units` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $game = $servers[3];
+
+ if(!array_key_exists($game, cron::$quakestat))
+ return NULL;
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `ram` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ foreach($servers as $id)
+ {
+ $sql->query('SELECT `uid`, `address`, `status`, `autorestart` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!$server['autorestart'])
+ continue;
+
+ if($server['status'] != 'working')
+ continue;
+
+ if(!in_array(trim($ssh->get('quakestat -'.(cron::$quakestat[$game]).' '.$server['address'].' -retry 5 -interval 2 | grep -v frags | tail -1 | awk \'{print $2}\'')), array('DOWN', 'no')))
+ continue;
+
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' server_action restart '.$game.' '.$id.'"');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `server`="'.$id.'", `text`="Перезагрука сервера: сервер завис", `time`="'.$start_point.'"');
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/scan_servers_load.php b/system/library/cron/scan_servers_load.php
new file mode 100644
index 0000000..a58e925
--- /dev/null
+++ b/system/library/cron/scan_servers_load.php
@@ -0,0 +1,96 @@
+query('SELECT `address` FROM `units` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $game = $servers[3];
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `ram` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ foreach($servers as $id)
+ {
+ $sql->query('SELECT `uid`, `slots`, `hdd`, `ram`, `ram_use_max`, `cpu_use_max`, `core_fix`, `core_fix_one` `status` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if($server['core_fix'] AND $server['core_fix_one'])
+ continue;
+
+ if(!in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ {
+ echo 'server#'.$id.' ('.$game.') -> load average: cpu = 0% / ram = 0% (no working)'.PHP_EOL;
+
+ continue;
+ }
+
+ $resources = array();
+
+ for($n = 0; $n <= 2; $n+=1)
+ {
+ $cr = explode('|', $ssh->get('top -u '.$server['uid'].' -b -n 1 | grep '.(cron::$process[$game]).' | awk \'{print $9"|"$10}\''));
+
+ $resources[$n]['cpu'] = isset($cr[0]) ? round(str_replace(',', '.', $cr[0])) : 0;
+
+ $resources[$n]['ram'] = isset($cr[1]) ? str_replace(',', '.', $cr[1]) : 0;
+ $ram = $server['ram'] ? $server['ram'] : $server['slots']*$cfg['ram'][$game];
+ $resources[$n]['ram'] = round($unit['ram']/100*$resources[$n]['ram']/($ram/100));
+
+ sleep(1);
+ }
+
+ $loads = array();
+
+ foreach($resources as $n => $load)
+ {
+ foreach($load as $type => $val)
+ $loads[$type] += $val;
+ }
+
+ $average_cpu = isset($loads['cpu']) ? $loads['cpu']/2 : 0;
+ $average_ram = isset($loads['ram']) ? $loads['ram']/2 : 0;
+
+ $max_cpu = $server['cpu_use_max'] ? $server['cpu_use_max'] : $cfg['cpu_use_max'][$game];
+ $max_ram = $server['ram_use_max'] ? $server['ram_use_max'] : $cfg['ram_use_max'][$game];
+
+ if($average_cpu > $max_cpu)
+ {
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' server_action restart '.$game.' '.$id.'"');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `server`="'.$id.'", `text`="Перезагрука сервера: OVERLOAD^cpu = '.$average_cpu.'%", `time`="'.$start_point.'"');
+ }elseif($average_ram > $max_ram){
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' server_action restart '.$game.' '.$id.'"');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `server`="'.$id.'", `text`="Перезагрука сервера: OVERLOAD^ram = '.$average_ram.'%", `time`="'.$start_point.'"');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/scan_servers_route.php b/system/library/cron/scan_servers_route.php
new file mode 100644
index 0000000..0c830e6
--- /dev/null
+++ b/system/library/cron/scan_servers_route.php
@@ -0,0 +1,104 @@
+query('SELECT `address` FROM `units` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $game = $servers[3];
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `ram` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ $first = $ssh->get('cat /proc/stat');
+
+ sleep(1);
+
+ $aCpu = sys::cpu_idle(array($first, $ssh->get('cat /proc/stat')), false, true);
+
+ array_shift($aCpu);
+
+ $idle = array();
+ $uses = array();
+
+ foreach($aCpu as $cpu => $data)
+ {
+ $core = sys::int($cpu)+1;
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$server['unit'].'" AND `core_fix`="'.$core.'" AND `core_fix`="1" LIMIT 1');
+ if($sql->num())
+ {
+ unset($aCpu[$cpu]);
+
+ continue;
+ }
+
+ if($data['idle'] > 50)
+ $idle[$core] = $data['idle'];
+ else
+ $uses[$core] = 100-$data['idle'];
+ }
+
+ if(!count($idle))
+ return NULL;
+
+ foreach($uses as $use_core => $use)
+ {
+ if(!count($idle))
+ break;
+
+ $sql->query('SELECT `id`, `uid` FROM `servers` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$game.'" AND `core_use`="'.$use_core.'" AND `status`="working" AND `core_fix`="0" ORDER BY `slots_start` DESC, `online` DESC LIMIT 3');
+ if($sql->num() > 1)
+ {
+ $server = $sql->get();
+
+ $core = array_search(max($idle), $idle);
+
+ $aPid = explode("\n", $ssh->get('ps aux | grep -v grep | grep '.$server['uid'].' | awk \'{print $2}\''));
+
+ if(count($aPid) < 2)
+ continue;
+
+ array_pop($aPid);
+
+ $taskset = '';
+
+ foreach($aPid as $pid)
+ $taskset .= 'taskset -cp '.($core-1).' '.$pid.';';
+
+ $ssh->set($taskset);
+
+ unset($idle[$core]);
+
+ $sql->query('UPDATE `servers` set `core_use`="'.$core.'" WHERE `id`="'.$server['id'].'" LIMIT 1');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/scan_servers_stop.php b/system/library/cron/scan_servers_stop.php
new file mode 100644
index 0000000..89b68c2
--- /dev/null
+++ b/system/library/cron/scan_servers_stop.php
@@ -0,0 +1,57 @@
+query('SELECT `address` FROM `units` WHERE `id`="'.$servers[4].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $unit = $sql->get();
+
+ $game = $servers[3];
+
+ unset($servers[3], $servers[4]);
+
+ $sql->query('SELECT `unit` FROM `servers` WHERE `id`="'.$servers[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ $autostop = $start_point-$cfg['autostop']*60;
+ $teststop = $start_point-$cfg['teststop']*60;
+
+ $sqlq = '(`test`="1" AND `time_start`<"'.$teststop.'" OR `autostop`="1" AND `time_start`<"'.$autostop.'")';
+
+ foreach($servers as $id)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" AND `status`="working" AND `online`="0" AND '.$sqlq.' LIMIT 1');
+
+ if(!$sql->num())
+ continue;
+
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' server_action stop '.$game.' '.$id.'"');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `server`="'.$id.'", `text`="Выключение сервера: на сервере нет игроков", `time`="'.$start_point.'"');
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/server_action.php b/system/library/cron/server_action.php
new file mode 100644
index 0000000..325aa71
--- /dev/null
+++ b/system/library/cron/server_action.php
@@ -0,0 +1,55 @@
+get($nmch))
+ return NULL;
+
+ $mcache->set($nmch, true, false, 10);
+
+ if($argv[3] == 'console')
+ {
+ global $sql;
+
+ $sql->query('SELECT `uid`, `unit` FROM `servers` WHERE `id`="'.$argv[5].'" LIMIT 1');
+ $server = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ $sql->query('SELECT `commands` FROM `crontab` WHERE `id`="'.$argv[6].'" LIMIT 1');
+ $cron = $sql->get();
+
+ $aCmd = explode("\n", base64_decode($cron['commands']));
+
+ foreach($aCmd as $cmd)
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.sys::cmd($cmd).'"\015\'; sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ return NULL;
+ }
+
+ include(LIB.'games/'.$argv[4].'/action.php');
+
+ if($argv[3] == 'restart')
+ action::start($argv[5], 'restart');
+ else
+ action::$argv[3]($argv[5]);
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/server_cron.php b/system/library/cron/server_cron.php
new file mode 100644
index 0000000..5825e83
--- /dev/null
+++ b/system/library/cron/server_cron.php
@@ -0,0 +1,24 @@
+query('SELECT `game` FROM `servers` WHERE `id`="'.$argv[3].'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `task` FROM `crontab` WHERE `id`="'.$argv[4].'" LIMIT 1');
+ $cron = $sql->get();
+
+ $cmd = $cron['task'] == 'console' ? ' '.$argv[4] : '';
+
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' server_action '.$cron['task'].' '.$server['game'].' '.$argv[3].$cmd.'"');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/server_delete.php b/system/library/cron/server_delete.php
new file mode 100644
index 0000000..fdac0c4
--- /dev/null
+++ b/system/library/cron/server_delete.php
@@ -0,0 +1,148 @@
+query('SELECT `id`, `uid`, `user`, `unit`, `tarif`, `game`, `slots`, `address`, `ddos` FROM `servers` WHERE `id`="'.$argv[3].'" AND `user`="-1" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $server = $sql->get();
+
+ if(!$server['uid'])
+ return NULL;
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return NULL;
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@:'.$server['address'].' | awk '."'{print $2}'".' | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ $copys = 'screen -dmS r_copy_'.$server['uid'].' sh -c "';
+
+ $scopy = $sql->query('SELECT `id`, `name` FROM `copy` WHERE `server`="'.$server['id'].'"');
+ while($copy = $sql->get($scopy))
+ {
+ $copys .= 'rm /copy/'.$copy['name'].'.tar;';
+
+ $sql->query('DELETE FROM `copy` WHERE `id`="'.$copy['id'].'" LIMIT 1');
+ }
+
+ $copys .= '";';
+
+ $ssh->set($copys // Удаление резервных копий
+ .'screen -dmS r_'.$server['uid'].' sh -c "rm -r '.$install.';' // Удаление директории сервера
+ .'userdel server'.$server['uid'].'"'); // Удаление пользователя сервера c локации
+
+ // Удаление ftp доступа
+ $qSql = 'DELETE FROM users WHERE username=\''.$server['uid'].'\';'
+ .'DELETE FROM quotalimits WHERE name=\''.$server['uid'].'\';'
+ .'DELETE FROM quotatallies WHERE name=\''.$server['uid'].'\'';
+
+ $ssh->set('screen -dmS ftp'.$server['uid'].' mysql -P '.$unit['sql_port'].' -u'.$unit['sql_login'].' -p'.$unit['sql_passwd'].' --database '.$unit['sql_ftp'].' -e "'.$qSql.'"');
+
+ include(LIB.'games/games.php');
+
+ // Очистка правил FireWall
+ games::iptables($server['id'], 'remove', NULL, NULL, NULL, false, $ssh);
+
+ // Очистка правил FireWall GEO
+ if($server['ddos'])
+ {
+ $geo = $cfg['iptables'].'_geo';
+
+ $country = $server['ddos'] == 2 ? 'AM,BY,UA,RU,KZ' : 'UA,RU';
+
+ $ssh->set('iptables -D INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country '.$country.' -j DROP;'
+ .'sed "`nl '.$geo.' | grep \"#'.$id.'\" | awk \'{print $1","$1+1}\'`d" '.$geo.' > '.$geo.'_temp; cat '.$geo.'_temp > '.$geo.'; rm '.$geo.'_temp;');
+ }
+
+ // Удаление заданий из crontab
+ $sql->query('SELECT `address`, `passwd` FROM `panel` LIMIT 1');
+ $panel = $sql->get();
+
+ if(!$ssh->auth($panel['passwd'], $panel['address']))
+ return NULL;
+
+ $crons = $sql->query('SELECT `id`, `cron` FROM `crontab` WHERE `server`="'.$server['id'].'"');
+ while($cron = $sql->get($crons))
+ {
+ $ssh->set('echo "" >> /etc/crontab && cat /etc/crontab');
+ $crontab = str_replace($cron['cron'], '', $ssh->get());
+
+ // Временный файл
+ $temp = sys::temp($crontab);
+
+ $ssh->setfile($temp, '/etc/crontab', 0644);
+
+ $ssh->set("sed -i '/^$/d' /etc/crontab");
+ $ssh->set('crontab -u root /etc/crontab');
+
+ unlink($temp);
+
+ $sql->query('DELETE FROM `crontab` WHERE `id`="'.$cron['id'].'" LIMIT 1');
+ }
+
+ // Обновление данных выделенного адреса
+ $sql->query('SELECT `id`, `aid` FROM `address_buy` WHERE `server`="'.$server['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $add = $sql->get();
+
+ $sql->query('UPDATE `address` set `buy`="0" WHERE `id`="'.$add['aid'].'" LIMIT 1');
+ $sql->query('DELETE FROM `address_buy` WHERE `id`="'.$add['id'].'" LIMIT 1');
+ }
+
+ include(DATA.'web.php');
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`!="'.$server['id'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $server_sec = $sql->get();
+
+ // Подготовка к удалению доп. услуги или обновление данных
+ $webs = $sql->query('SELECT `id`, `type` FROM `web` WHERE `server`="'.$server['id'].'"');
+ while($web = $sql->get($webs))
+ {
+ if($aWebInstall[$server['game']][$web['type']] == ('unit' || 'user'))
+ $sql->query('UPDATE `web` set `server`="'.$server_sec['id'].'" WHERE `id`="'.$web['id'].'" LIMIT 1');
+ else
+ $sql->query('UPDATE `web` set `user`="0" WHERE `id`="'.$web['id'].'" LIMIT 1');
+ }
+ }else
+ $sql->query('UPDATE `web` set `user`="0" WHERE `server`="'.$server['id'].'"');
+
+ // Удаление различной информации игрового сервера
+ $sql->query('DELETE FROM `admins_'.$server['game'].'` WHERE `server`="'.$server['id'].'"');
+ $sql->query('DELETE FROM `plugins_install` WHERE `server`="'.$server['id'].'"');
+ $sql->query('DELETE FROM `owners` WHERE `server`="'.$server['id'].'"');
+ $sql->query('DELETE FROM `graph` WHERE `server`="'.$server['id'].'"');
+ $sql->query('DELETE FROM `graph_day` WHERE `server`="'.$server['id'].'"');
+ $sql->query('DELETE FROM `graph_hour` WHERE `server`="'.$server['id'].'"');
+ $sql->query('DELETE FROM `servers` WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs_sys` set `user`="0", `server`="'.$argv[3].'", `text`="Удаление игрового сервера #'.$argv[3].' ('.$server['game'].') unit: #'.$server['unit'].', tarif: #'.$server['tarif'].', slots: #'.$server['slots'].'", `time`="'.$start_point.'"');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/server_scan.php b/system/library/cron/server_scan.php
new file mode 100644
index 0000000..d00e14c
--- /dev/null
+++ b/system/library/cron/server_scan.php
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/system/library/cron/threads.php b/system/library/cron/threads.php
new file mode 100644
index 0000000..e656b66
--- /dev/null
+++ b/system/library/cron/threads.php
@@ -0,0 +1,63 @@
+query('SELECT `id` FROM `units` ORDER BY `id` ASC');
+
+ if(!$sql->num())
+ return NULL;
+
+ while($unit = $sql->get())
+ $aUnit[$unit['id']] = '';
+
+ $sql->query('SELECT `id` FROM `servers` LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $sql->query('SELECT `id`, `unit`, `game` FROM `servers` ORDER BY `unit` DESC');
+
+ $all = $sql->num();
+
+ while($server = $sql->get())
+ $aUnit[$server['unit']][$server['game']] .= $server['id'].' ';
+
+ if($argv[3] == 'scan_servers_route')
+ cron::$seping = 50;
+
+ foreach($aUnit as $unit => $aGame)
+ {
+ foreach($aGame as $game => $servers)
+ {
+ $aData = explode(' ', $servers);
+
+ $num = count($aData)-1;
+ $sep = $num > 0 ? ceil($num/cron::$seping) : 1;
+
+ unset($aData[end($aData)]);
+
+ $threads[] = cron::thread($sep, $game.' '.$unit, $aData);
+ }
+ }
+
+ $cmd = '';
+
+ foreach($threads as $thread)
+ {
+ foreach($thread as $screen => $servers)
+ $cmd .= 'sudo -u www-data screen -dmS scan_'.(sys::first(explode(' ', $servers))).'_'.$screen.' taskset -c '.$cfg['cron_taskset'].' sh -c \"cd /var/enginegp; php cron.php '.$cfg['cron_key'].' '.$argv[3].' '.$servers.'\"; sleep 1;';
+ }
+
+ exec('screen -dmS threads_'.date('His', $start_point).' sh -c "'.$cmd.'"');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/update_address.php b/system/library/cron/update_address.php
new file mode 100644
index 0000000..7d8366b
--- /dev/null
+++ b/system/library/cron/update_address.php
@@ -0,0 +1,44 @@
+query('SELECT `id`, `aid`, `server` FROM `address_buy` WHERE `time`<"'.$start_point.'"');
+
+ while($add_buy = $sql->get($add_buys))
+ {
+ $sql->query('SELECT `unit`, `port`, `game`, `status` FROM `servers` WHERE `id`="'.$add_buy['server'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $server = $sql->get();
+
+ if(!$cfg['buy_address'][$server['game']])
+ continue;
+
+ $sql->query('SELECT `address` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'games/games.php');
+
+ // Очистка правил FireWall
+ games::iptables($add_buy['server'], 'remove', NULL, NULL, $server['unit'], false);
+
+ $sql->query('UPDATE `servers` set `address`="'.(sys::first(explode(':', $unit['address']))).':'.$server['port'].'" WHERE `id`="'.$add_buy['server'].'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ exec('sh -c "cd /var/enginegp; php cron.php '.$cfg['cron_key'].' server_action restart '.$server['game'].' '.$add_buy['server'].'"');
+ }
+
+ $sql->query('UPDATE `address` set `buy`="0" WHERE `id`="'.$add_buy['aid'].'" LIMIT 1');
+ $sql->query('DELETE FROM `address_buy` WHERE `id`="'.$add_buy['id'].'" LIMIT 1');
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/users_recovery_signup.php b/system/library/cron/users_recovery_signup.php
new file mode 100644
index 0000000..bc29f13
--- /dev/null
+++ b/system/library/cron/users_recovery_signup.php
@@ -0,0 +1,19 @@
+query('DELETE FROM `signup` WHERE `date`<"'.$time.'"');
+ $sql->query('DELETE FROM `recovery` WHERE `date`<"'.$time.'"');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/cron/web_delete.php b/system/library/cron/web_delete.php
new file mode 100644
index 0000000..d2386f9
--- /dev/null
+++ b/system/library/cron/web_delete.php
@@ -0,0 +1,36 @@
+query('SELECT `id`, `login`, `type`, `server`, `unit` FROM `web` WHERE `id`="'.$argv[3].'" LIMIT 1');
+ $web = $sql->get();
+
+ if($web['type'] == 'hosting')
+ {
+ include(DATA.'web.php');
+
+ $result = json_decode(file_get_contents(sys::updtext($aWebUnit['isp']['account']['delete'], array('login' => $web['login']))), true);
+
+ if(!isset($result['result']) || strtolower($result['result']) != 'ok')
+ continue;
+
+ $sql->query('DELETE FROM `web` WHERE `id`="'.$web['id'].'" LIMIT 1');
+ }
+
+ include(LIB.'web/free.php');
+
+ $aData = array(
+ 'type' => $web['type'],
+ 'server' => array('id' => $web['server'], 'unit' => $web['unit'], 'user' => 0, 'game' => 'system')
+ );
+
+ web::delete($aData, false);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/ftp.php b/system/library/ftp.php
new file mode 100644
index 0000000..2778dac
--- /dev/null
+++ b/system/library/ftp.php
@@ -0,0 +1,516 @@
+ 'Понедельник',
+ 'Tue' => 'Вторник',
+ 'Wed' => 'Среда',
+ 'Thu' => 'Четверг',
+ 'Fri' => 'Пятница',
+ 'Sat' => 'Суббота',
+ 'Sun' => 'Воскресенье'
+ );
+
+ var $mounthint = array(
+ 'Jan' => '01',
+ 'Feb' => '02',
+ 'Mar' => '03',
+ 'Apr' => '04',
+ 'May' => '05',
+ 'Jun' => '06',
+ 'Jul' => '07',
+ 'Aug' => '08',
+ 'Sep' => '09',
+ 'Oct' => '10',
+ 'Nov' => '11',
+ 'Dec' => '12'
+ );
+
+ var $mounthru = array(
+ 'Jan' => 'Янв',
+ 'Feb' => 'Фев',
+ 'Mar' => 'Мар',
+ 'Apr' => 'Апр',
+ 'May' => 'Май',
+ 'Jun' => 'Июн',
+ 'Jul' => 'Июл',
+ 'Aug' => 'Авг',
+ 'Sep' => 'Сен',
+ 'Oct' => 'Окт',
+ 'Nov' => 'Ноя',
+ 'Dec' => 'Дек'
+ );
+
+ var $aEdits = array(
+ 'txt',
+ 'cfg',
+ 'conf',
+ 'json',
+ 'xml',
+ 'ini',
+ 'gam',
+ 'php',
+ 'html',
+ 'inf',
+ 'js',
+ 'css',
+ 'sma',
+ 'log'
+ );
+
+ public function auth($host, $user, $password, $port = 21)
+ {
+ $ftp_connect = @ftp_connect($host, $port);
+
+ if(!$ftp_connect)
+ return false;
+
+ if(!@ftp_login($ftp_connect, $user, $password))
+ return false;
+
+ @ftp_pasv($ftp_connect, true);
+
+ $this->steck = $ftp_connect;
+
+ return true;
+ }
+
+ public function read($path)
+ {
+ $path = ($path == '') ? '/' : $path;
+
+ $path = str_replace('//', '/', $path);
+
+ $aDir = array();
+ $aFile = array();
+ $aInfo = array();
+
+ $rawlist = array();
+
+ $data = ftp_rawlist($this->steck, $path);
+
+ if(is_array($data))
+ foreach($data as $index)
+ {
+ $vinfo = preg_split('/[\s]+/', $index, 9);
+
+ if($vinfo[0] !== 'total')
+ {
+ $aInfo['chmod'] = $vinfo[0];
+ $aInfo['num'] = $vinfo[1];
+ $aInfo['owner'] = $vinfo[2];
+ $aInfo['group'] = $vinfo[3];
+ $aInfo['size'] = $vinfo[4];
+ $aInfo['month'] = $vinfo[5];
+ $aInfo['day'] = $vinfo[6];
+ $aInfo['time'] = $vinfo[7];
+ $aInfo['name'] = $vinfo[8];
+
+ $rawlist[$aInfo['name']] = $aInfo;
+ }
+ }
+
+ foreach($rawlist as $name => $data)
+ {
+ if($data['chmod']{0} == 'd')
+ $aDir[$name] = $data;
+
+ elseif($data['chmod']{0} == '-')
+ $aFile[$name] = $data;
+ }
+
+ $aData = array(
+ 'folder' => $aDir,
+ 'file' => $aFile,
+ 'path' => $path
+ );
+
+ return $aData;
+ }
+
+ public function view($view, $server)
+ {
+ global $html;
+
+ if($view['path'] != '/')
+ {
+ $html->get('filetp_back', 'sections/servers/games/filetp');
+
+ $html->set('back', $this->path($view['path']));
+
+ $html->pack('list');
+ }
+
+ foreach($view as $type => $aVal)
+ {
+ if(!is_array($aVal))
+ continue;
+
+ foreach($aVal as $name => $info)
+ {
+ $html->get('filetp_list', 'sections/servers/games/filetp');
+
+ $html->set('id', $server);
+ $html->set('name', $name);
+
+ $path = $view['path'];
+
+ if($path{0} != '/') $path = '/'.$path;
+
+ if($path != '/') $path = $path.'/';
+
+ $html->set('path', $path);
+ $html->set('chmod', $this->cti($info['chmod']).' '.$info['chmod']);
+ $html->set('owner', $info['owner']);
+ $html->set('group', $info['group']);
+
+ if($type == 'folder')
+ {
+ $html->unit('folder', 1);
+ $html->unit('file');
+ $html->set('size', '');
+ }else{
+ $type = explode('.', $name);
+
+ if(in_array(end($type), $this->aEdits))
+ $html->unit('edit', 1);
+ else
+ $html->unit('edit');
+
+ $html->unit('file', 1);
+ $html->unit('folder');
+ $html->set('size', sys::size($info['size']));
+ }
+
+ $html->set('month', $this->mounthru[$info['month']]);
+ $html->set('day', $info['day']);
+ $html->set('time', $info['time']);
+
+ $html->pack('list');
+ }
+ }
+
+ return isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ public function mkdir($path, $folders)
+ {
+ if(!@ftp_chdir($this->steck, $path))
+ sys::outjs(array('e' => 'Ошибка: не удалось создать папку'));
+
+ $aFolder = explode('/', $folders);
+
+ foreach($aFolder as $folder)
+ {
+ if($folder == '')
+ continue;
+
+ if(!@ftp_chdir($this->steck, $folder))
+ {
+ if(!@ftp_mkdir($this->steck, $folder))
+ sys::outjs(array('e' => 'Ошибка: не удалось создать папку '.$folder));
+
+ @ftp_chdir($this->steck, $folder);
+ }
+ }
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ public function touch($path, $file, $text)
+ {
+ $aData = explode('/', $file);
+
+ $path_file = '';
+
+ if(count($aData))
+ {
+ $file = end($aData);
+
+ unset($aData[count($aData)-1]);
+
+ foreach($aData as $val)
+ $path_file .= $val.'/';
+ }
+
+ $dir = str_replace('//', '', $path.'/'.$path_file);
+
+ $dir = ($dir == '') ? '/' : $dir;
+
+ if(!@ftp_chdir($this->steck, $dir))
+ sys::outjs(array('e' => 'Ошибка: не удалось создать файл'));
+
+ $temp = sys::temp($text);
+
+ if(@ftp_put($this->steck, $file, $temp, FTP_BINARY))
+ {
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ unlink($temp);
+
+ sys::outjs(array('e' => 'Ошибка: не удалось создать файл'));
+ }
+
+ public function edit_file($path, $file)
+ {
+ $name = md5(time().$file.'ftp');
+
+ if(@ftp_get($this->steck, TEMP.$name, $path.'/'.$file, FTP_BINARY))
+ {
+ $data = file_get_contents(TEMP.$name);
+
+ unlink(TEMP.$name);
+
+ sys::outjs(array('s' => $data));
+ }
+
+ sys::outjs(array('e' => 'Не удалось открыть файл'));
+ }
+
+ public function rename($path, $oldname, $newname)
+ {
+ if(@ftp_rename($this->steck, $path.'/'.$oldname, $path.'/'.$newname))
+ sys::outjs(array('s' => 'ok'));
+
+ sys::outjs(array('e' => 'Не удалось сменить имя'));
+ }
+
+ public function rmdir($path, $folder)
+ {
+ if(@ftp_rmdir($this->steck, $path.'/'.$folder))
+ sys::outjs(array('s' => 'ok'));
+
+ sys::outjs(array('e' => 'Ошибка: не удалось удалить папку.'));
+ }
+
+ public function rmfile($file)
+ {
+ if(@ftp_delete($this->steck, $file))
+ sys::outjs(array('s' => 'ok'));
+
+ sys::outjs(array('e' => 'Ошибка: не удалось удалить файл'));
+ }
+
+ public function chmod($path, $name, $chmod)
+ {
+ if(ftp_site($this->steck, 'CHMOD 0'.$chmod.' '.$path.'/'.$name))
+ sys::outjs(array('s' => 'ok'));
+
+ sys::outjs(array('e' => 'Ошибка: не удалось изменить права.'));
+ }
+
+ public function search($str, $server)
+ {
+ global $html, $mcache;
+
+ $nmch = md5($str.$server);
+
+ $cache = $mcache->get($nmch);
+
+ if(!is_array($cache))
+ {
+ $aData = ftp_rawlist($this->steck, '/', true);
+
+ if(!is_array($aData))
+ sys::out('Ничего не найдено');
+
+ // Файлы
+ $aFile = array();
+
+ // Файлы в корне
+ $end = array_search('', $aData);
+
+ for($i = 0; $i < $end; $i+=1)
+ {
+ $aInfo = preg_split('/[\s]+/', $aData[$i], 9);
+
+ $info = '';
+
+ for($n = 0; $n < 8; $n+=1)
+ $info .= $aInfo[$n].' ';
+
+ $aFile['/'][] = array('info' => $info, 'name' => $aInfo[8]);
+ }
+
+ // Перебор директорий и файлов в них
+ foreach($aData as $index)
+ {
+ $begin = array_search('', $aData);
+ unset($aData[$begin]);
+
+ $end = array_search('', $aData);
+
+ if(!$begin)
+ break;
+
+ $dir = substr($aData[$begin+1], 0, -1);
+
+ for($i = $begin+2; $i < $end; $i+=1)
+ {
+ $aInfo = preg_split('/[\s]+/', $aData[$i], 9);
+
+ $info = '';
+
+ for($n = 0; $n < 8; $n+=1)
+ $info .= $aInfo[$n].' ';
+
+ $aFile[$dir][] = array('info' => $info, 'name' => $aInfo[8]);
+ }
+ }
+
+ $mcache->set($nmch, $aFile, false, 20);
+ }else
+ $aFile = $cache;
+
+ $aFind = array();
+
+ // Поиск
+ foreach($aFile as $dir => $files)
+ {
+ foreach($files as $file)
+ {
+ $find = sys::first(explode('.', $file['name']));
+
+ if(preg_match('/'.$str.'/i', $find))
+ $aFind[] = array('dir' => $dir, 'info' => $file['info'], 'file' => $file['name'], 'find' => sys::find($file['name'], $str));
+ }
+ }
+
+ unset($aFile);
+
+ foreach($aFind as $data)
+ {
+ $info = preg_split('/[\s]+/', trim($data['info']), 8);
+
+ $html->get('filetp_find', 'sections/servers/games/filetp');
+
+ $html->set('id', $server);
+ $html->set('find', $data['find']);
+ $html->set('name', $data['file']);
+
+ $path = $data['dir'];
+
+ if($path{0} != '/') $path = '/'.$path;
+
+ if($path != '/') $path = $path.'/';
+
+ $html->set('path', $path);
+ $html->set('chmod', $this->cti($info[0]).' '.$info[0]);
+ $html->set('owner', $info[2]);
+ $html->set('group', $info[3]);
+
+ if($info[0]{0} == 'd')
+ {
+ $html->unit('folder', 1);
+ $html->unit('file');
+ $html->set('size', '');
+ }else{
+ $type = explode('.', $data['file']);
+
+ if(in_array(end($type), $this->aEdits))
+ $html->unit('edit', 1);
+ else
+ $html->unit('edit');
+
+ $html->unit('file', 1);
+ $html->unit('folder');
+ $html->set('size', sys::size($info[4]));
+ }
+
+ $html->set('month', $this->mounthru[$info[5]]);
+ $html->set('day', $info[6]);
+ $html->set('time', $info[7]);
+
+ $html->pack('list');
+ }
+
+ if(isset($html->arr['list']))
+ sys::out($html->arr['list']);
+
+ sys::out('Ничего не найдено');
+ }
+
+ public function logs($data, $uid)
+ {
+ global $html;
+
+ $aLine = explode("\n", $data);
+
+ $actions = array('i' => 'загрузка', 'o' => 'скачивание', 'd' => 'удаление');
+ $acticon = array('i' => ' ', 'o' => ' ', 'd' => ' ');
+
+ unset($aLine[count($aLine)-1]);
+
+ rsort($aLine);
+
+ foreach($aLine as $line)
+ {
+ $aData = explode('\\', $line);
+
+ $html->get('filetp_logs', 'sections/servers/games/filetp');
+
+ $html->set('month', $this->mounthint[$aData[0]]);
+ $html->set('day', $aData[1]);
+ $html->set('time', $aData[2]);
+ $html->set('year', $aData[3]);
+ $html->set('who', $this->who($aData[4]));
+ $html->set('size', sys::size($aData[5]));
+ $html->set('file', str_replace('/servers/'.$uid.'/', '', $aData[6]));
+ $html->set('action', $actions[$aData[7]]);
+ $html->set('acticon', $acticon[$aData[7]]);
+
+ $html->pack('logs');
+ }
+
+ return isset($html->arr['logs']) ? $html->arr['logs'] : 'Список логов отсутствует';
+ }
+
+ private function path($path)
+ {
+ $path = str_replace('//', '/', $path);
+
+ $path = explode('/', $path);
+
+ unset($path[count($path)-1]);
+
+ $newpath = '/';
+
+ foreach($path as $index => $val)
+ if(count($path)-1 == $index) $newpath .= $val; else $newpath .= $val.'/';
+
+ return str_replace('//', '/', $newpath);
+ }
+
+ private function cti($chmod)
+ {
+ $intchmod = array('-' => '0', 'r' => '4', 'w' => '2', 'x' => '1');
+
+ $chmod = substr(strtr($chmod, $intchmod), 1);
+
+ $split = str_split($chmod, 3);
+
+ return array_sum(str_split($split[0])).array_sum(str_split($split[1])).array_sum(str_split($split[2]));
+ }
+
+ private function who($address)
+ {
+ global $cfg, $uip;
+
+ if($address == $cfg['ip'])
+ return 'панель управления';
+
+ if($address == $uip)
+ return 'вы';
+
+ return $address;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/actions.php b/system/library/games/actions.php
new file mode 100644
index 0000000..aa1a7b8
--- /dev/null
+++ b/system/library/games/actions.php
@@ -0,0 +1,316 @@
+query('SELECT `uid`, `unit`, `game`, `address`, `name` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="off", `online`="0", `players`="", `stop`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function change($id, $map = false)
+ {
+ global $cfg, $sql, $html, $user, $mcache;
+
+ // Если в кеше есть карты
+ if($mcache->get('server_maps_change_'.$id) != '' && !$map)
+ return array('maps' => $mcache->get('server_maps_change_'.$id));
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `game`, `tarif`, `online`, `players`, `name` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ // Массив карт игрового сервера (папка "maps")
+ $aMaps = explode("\n", $ssh->get('cd '.$tarif['install'].$server['uid'].'/cstrike/maps/ && ls | grep .bsp | grep -v .bsp.'));
+
+ // Удаление пустого элемента
+ unset($aMaps[count($aMaps)-1]);
+
+ // Удаление ".bsp"
+ $aMaps = str_replace('.bsp', '', $aMaps);
+
+ // Если выбрана карта
+ if($map)
+ {
+ // Проверка наличия выбранной карты
+ if(!in_array($map, $aMaps))
+ return array('e' => sys::updtext(sys::text('servers', 'change'), array('map' => $map.'.bsp')));
+
+ // Отправка команды changelevel
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval '."'stuff \"changelevel ".sys::cmd($map)."\"\015'");
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="change" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'change', 'online' => $server['online'], 'players' => base64_decode($server['players'])));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'change', 'online' => $server['online']));
+
+ return array('s' => 'ok');
+ }
+
+ // Сортировка списка карт
+ sort($aMaps);
+ reset($aMaps);
+
+ // Генерация списка карт для выбора
+ foreach($aMaps as $map)
+ {
+ $html->get('change_list', 'sections/servers/games');
+ $html->set('img', sys::img($map, $server['game']));
+ $html->set('name', $map);
+ $html->set('id', $id);
+ $html->pack('maps');
+ }
+
+ // Запись карт в кеш
+ $mcache->set('server_maps_change_'.$id, $html->arr['maps'], false, 30);
+
+ return array('maps' => $html->arr['maps']);
+ }
+
+ public static function reinstall($id)
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `tarif`, `address`, `game`, `name`, `pack`, `plugins_use`, `ftp`, `reinstall`, `core_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ // Проверка времени переустановки
+ $reinstall = $server['reinstall']+$cfg['reinstall'][$server['game']]*60;
+
+ if($reinstall > $start_point && $user['group'] != 'admin')
+ return array('e' => sys::updtext(sys::text('servers', 'reinstall'), array('time' => sys::date('max', $reinstall))));
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `path`, `install`, `plugins_install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] && !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Директория сборки
+ $path = $tarif['path'].$server['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => 'Не удается выполнить операцию, нет свободного потока.');
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ $ssh->set('rm -r '.$install.';' // Удаление директории игрового сервера
+ .'mkdir '.$install.';' // Создание директории
+ .'chown server'.$server['uid'].':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u server'.$server['uid'].' '.$taskset.' screen -dmS r_'.$server['uid'].' sh -c "'
+ .'cp -r '.$path.'/. .;' // Копирование файлов сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].'"');
+
+ // Очистка записей в базе
+ $sql->query('DELETE FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'"'); // Список админов на сервере
+ $sql->query('DELETE FROM `plugins_install` WHERE `server`="'.$id.'"'); // Список установленных плагинов на сервере
+
+ // Запись установленных плагинов
+ if($server['plugins_use'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64djs($tarif['plugins_install']);
+
+ if(isset($aPlugins[$server['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$server['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="reinstall", `reinstall`="'.$start_point.'", `core_use`="'.$core.'", `fastdl`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Логирование
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'reinstall').'", `time`="'.$start_point.'"');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'reinstall', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'reinstall', 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function update($id)
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `tarif`, `address`, `game`, `name`, `pack`, `plugins_use`, `ftp`, `update` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ // Проверка времени обновления
+ $update = $server['update']+$cfg['update'][$server['game']]*60;
+
+ if($update > $start_point && $user['group'] != 'admin')
+ return array('e' => sys::updtext(sys::text('servers', 'update'), array('time' => sys::date('max', $update))));
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `update`, `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] && !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Директория обновлений сборки
+ $path = $tarif['update'].$server['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => 'Не удается выполнить операцию, нет свободного потока.');
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ $ssh->set('cd '.$install.' && sudo -u server'.$server['uid'].' '.$taskset.' screen -dmS u_'.$server['uid'].' sh -c "cp -rv '.$path.'/. .;' // Копирование файлов обвновления сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].'"');
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="update", `update`="'.$start_point.'", `core_use`="'.$core.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Логирование
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'update').'", `time`="'.$start_point.'"');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function clmcache($id)
+ {
+ global $mcache;
+
+ $mcache->delete('server_index_'.$id);
+ $mcache->delete('server_resources_'.$id);
+ $mcache->delete('server_status_'.$id);
+ }
+ }
\ No newline at end of file
diff --git a/system/library/games/boost.php b/system/library/games/boost.php
new file mode 100644
index 0000000..0d96409
--- /dev/null
+++ b/system/library/games/boost.php
@@ -0,0 +1,113 @@
+partner_key = $key;
+ $this->service_url = $url;
+ }
+
+ public function def($data)
+ {
+ $aData = array(
+ 'service' => 'boost',
+ 'period' => $data['period'],
+ 'address' => $data['address'],
+ 'game' => 'cs16'
+ );
+
+ $out = json_decode($this->defaultcurl(json_encode($aData)), true);
+
+ if($out['message'] == 'Услуга уже присутствует')
+ $out = json_decode($this->defaultcurl(json_encode($aData), 'prolong'), true);
+
+ if(!array_key_exists('status', $out))
+ array('error' => 'Не удалось приобрести услугу, повторите запрос позже.');
+
+ if(!$out['status'])
+ return true;
+
+ return array('error' => $out['message']);
+ }
+
+ private function defaultcurl($data, $action = 'buy')
+ {
+ if(!($curl = curl_init()))
+ array('error' => 'FAIL: curl_init().');
+
+ curl_setopt($curl, CURLOPT_URL, $this->service_url);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_POST, true);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, 'key='.$this->partner_key.'&action='.$action.'&data='.$data);
+
+ $out = curl_exec($curl);
+
+ curl_close($curl);
+
+ return $out;
+ }
+
+ public function vipms($data)
+ {
+ $aData = array(
+ 'format' => 'POST',
+ 'country' => 'RU',
+ 'hoster_id' => 1,
+ 'key' => $this->partner_key,
+ 'full_address' => $data['address'],
+ 'service_id' => $data['period']
+ );
+
+ return $this->othercurl($aData);
+ }
+
+ public function fulls($data)
+ {
+ $aData = array(
+ 'format' => 'POST',
+ 'country' => 'RU',
+ 'hoster_id' => 1,
+ 'key' => $this->partner_key,
+ 'full_address' => $data['address'],
+ 'service_id' => $data['period']
+ );
+
+ return $this->othercurl($aData);
+ }
+
+ private function othercurl($aData)
+ {
+ if(!($curl = curl_init()))
+ array('error' => 'FAIL: curl_init().');
+
+ curl_setopt($curl, CURLOPT_URL, $this->service_url.'?'.urldecode(http_build_query($aData)));
+ curl_setopt($curl, CURLOPT_POST, true);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, urldecode(http_build_query($aData)));
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+
+ $result = curl_exec($curl);
+
+ curl_close($curl);
+
+ if($result == 'OK')
+ return true;
+
+ $aErr = array(
+ 1 => 'BAD_HOSTER_ID',
+ 2 => 'HOSTER_NOT_FOUND',
+ 3 => 'BAD_HOSTER_IP',
+ 4 => 'FORM_INVALID',
+ 5 => 'BAD_SERVICE_ID'
+ );
+
+ return array('error' => $aErr[$result]);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/crmp/action.php b/system/library/games/crmp/action.php
new file mode 100644
index 0000000..9398e77
--- /dev/null
+++ b/system/library/games/crmp/action.php
@@ -0,0 +1,130 @@
+query('SELECT `uid`, `unit`, `tarif`, `game`, `address`, `slots_start`, `name`, `map_start`, `time_start`, `core_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe;');
+
+ // Временный файл
+ $temp = sys::temp(action::config($ip, $port, $server['slots_start'], $ssh->get('cat '.$tarif['install'].'/'.$server['uid'].'/server.cfg')));
+
+ // Обновление файла server.cfg
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/server.cfg', 0644);
+
+ unlink($temp);
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] AND !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $ssh->set('cat /proc/stat');
+ $proc_stat[1] = $ssh->get();
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Параметры запуска
+ $bash = './samp03svr-cr';
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd '.$tarif['install'].$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'sudo -u server'.$server['uid'].' mkdir -p oldstart;' // Создание папки логов
+ .'cat server_log.txt >> oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm server_log.txt; rm oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 server.cfg start.sh;' // Обновление владельца файлов server.cfg start.sh
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c "./start.sh"'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function config($ip, $port, $slots, $config)
+ {
+ $aLine = explode("\n", $config);
+
+ $eConfig = '';
+
+ foreach($aLine as $line)
+ {
+ $param = explode(' ', trim($line));
+
+ if(in_array(trim($param[0]), array('bind', 'port', 'maxplayers', 'query')))
+ continue;
+
+ $eConfig .= $line.PHP_EOL;
+ }
+
+ $eConfig .= 'bind '.$ip.PHP_EOL
+ .'port '.$port.PHP_EOL
+ .'maxplayers '.$slots.PHP_EOL
+ .'query 1';
+
+ return $eConfig;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/crmp/scan.php b/system/library/games/crmp/scan.php
new file mode 100644
index 0000000..5545de9
--- /dev/null
+++ b/system/library/games/crmp/scan.php
@@ -0,0 +1,115 @@
+query('SELECT `address`, `game`, `name`, `map`, `online`, `players`, `status`, `time`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ include(LIB.'games/query/SampQuery.php');
+
+ $sq = new SampQuery($ip, $port);
+
+ if($players_get)
+ $nmch = 'server_scan_mon_pl_'.$id;
+ else
+ $nmch = 'server_scan_mon_'.$id;
+
+ if(is_array($mcache->get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $out['time'] = 'Арендован до: '.date('d.m.Y - H:i', $server['time']);
+
+ if($server['status'] == 'overdue')
+ $out['time_end'] = 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400);
+ else
+ $out['time_end'] = 'Осталось: '.sys::date('min', $server['time']);
+
+ if(!$sq->connect())
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status'], $server['game']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ $info = $sq->getInfo();
+
+ if($players_get)
+ $players = scan::players($sq->getDetailedPlayers());
+
+ $info['map'] = htmlspecialchars(mb_convert_encoding($info['map'], 'UTF-8', 'WINDOWS-1251'));
+ $out['name'] = htmlspecialchars(mb_convert_encoding($info['hostname'], 'UTF-8', 'WINDOWS-1251'));
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = sys::int($info['players']);
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working', $server['game']);
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', htmlspecialchars($player['name']));
+ $html->set('ping', sys::int($player['ping']));
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function players($aPlayrs)
+ {
+ $i = 1;
+ $aData = array();
+
+ foreach($aPlayrs as $n => $player)
+ {
+ $aData[$i]['i'] = $i;
+ $aData[$i]['name'] = $player['nickname'] == '' ? 'Подключается' : htmlspecialchars($player['nickname']);
+ $aData[$i]['ping'] = sys::int($player['ping']);
+
+ $i+=1;
+ }
+
+ return $aData;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/crmp/service.php b/system/library/games/crmp/service.php
new file mode 100644
index 0000000..32e2531
--- /dev/null
+++ b/system/library/games/crmp/service.php
@@ -0,0 +1,355 @@
+query('SELECT `address`, `test` FROM `units` WHERE `id`="'.$aData['unit'].'" AND `crmp`="1" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Локация не найдена.'));
+
+ $unit = $sql->get();
+
+ // Проверка тарифа
+ $sql->query('SELECT `id` FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" AND `unit`="'.$aData['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Тариф не найден.'));
+
+ $sql->query('SELECT '
+ .'`slots_min`,'
+ .'`slots_max`,'
+ .'`port_min`,'
+ .'`port_max`,'
+ .'`hostname`,'
+ .'`packs`,'
+ .'`time`,'
+ .'`test`,'
+ .'`tests`,'
+ .'`discount`,'
+ .'`ftp`,'
+ .'`plugins`,'
+ .'`console`,'
+ .'`stats`,'
+ .'`copy`,'
+ .'`web`,'
+ .'`plugins_install`,'
+ .'`hdd`,'
+ .'`autostop`,'
+ .'`core_fix`,'
+ .'`ip`,'
+ .'`price`'
+ .' FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ $tarif = $sql->get();
+
+ // Проверка сборки
+ if(!array_key_exists($aData['pack'], sys::b64djs($tarif['packs'], true)))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ $test = 0;
+
+ // Проверка периода на тест
+ if($aData['test'])
+ {
+ if(!$tarif['test'] || !$unit['test'])
+ sys::outjs(array('e' => 'Тестовый период недоступен.'));
+
+
+ // Проверка на повторный запрос
+ $sql->query('SELECT `id`, `game` FROM `tests` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $test_info = $sql->get();
+
+ if(!$cfg['tests']['game'] || $test_info['game'] == 'crmp')
+ sys::outjs(array('e' => 'Тестовый период предоставляется один раз.'));
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" AND `test`="1" LIMIT 1');
+ if($sql->num() AND !$cfg['tests']['sametime'])
+ sys::outjs(array('e' => 'Чтобы получить тестовый период другой игры, дождитесь окончания текущего.'));
+ }
+
+ // Проверка наличия мест на локации
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$unit['test']);
+ if($sql->num() == $unit['test'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода нет.'));
+
+ // Проверка наличия мест для выбранного тарифа
+ $sql->query('SELECT `id` FROM `servers` WHERE `tarif`="'.$aData['tarif'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$tarif['tests']);
+ if($sql->num() == $tarif['tests'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода выбранного тарифа нет.'));
+
+ $test = 1;
+ }else
+ // Проверка периода
+ if(!$cfg['settlement_period'] AND !in_array($aData['time'], explode(':', $tarif['time'])))
+ sys::outjs(array('e' => 'Переданные данные периода неверны.'));
+
+ // Проверка слот
+ if($aData['slots'] < $tarif['slots_min'] || $aData['slots'] > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданные данные слот неверны.'));
+
+ // Определение суммы
+ if($cfg['settlement_period'])
+ {
+ // Цена аренды за расчетный период
+ $sum = games::define_sum($tarif['discount'], $tarif['price'], $aData['slots'], $start_point);
+
+ $aData['time'] = games::define_period('buy', params::$aDayMonth);
+ }else
+ $sum = games::define_sum($tarif['discount'], $tarif['price'], $aData['slots'], $aData['time']);
+
+ // Проверка промо-кода
+ $promo = games::define_promo(
+ $aData['promo'],
+ array(
+ 'tarif' => $aData['tarif'],
+ 'slots' => $aData['slots'],
+ 'time' => $aData['time'],
+ 'user' => $user['id']
+ ),
+ $tarif['discount'],
+ $sum
+ );
+
+ $days = $aData['time']; // Кол-во дней аренды
+
+ // Использование промо-кода
+ if(is_array($promo))
+ {
+ if(array_key_exists('sum', $promo))
+ $sum = $promo['sum'];
+ else
+ $days += $promo['days']; // Кол-во дней аренды с учетом подарочных (промо-код)
+ }
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']));
+
+ // Выделенный адрес игрового сервера
+ if(!empty($tarif['ip']))
+ {
+ $aIp = explode(':', $tarif['ip']);
+
+ $ip = false;
+ $port = params::$aDefPort['crmp'];
+
+ // Проверка наличия свободного адреса
+ foreach($aIp as $adr)
+ {
+ $adr = trim($adr);
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `address` LIKE "'.$adr.':%" LIMIT 1');
+ if(!$sql->num())
+ {
+ $ip = $adr;
+
+ break;
+ }
+ }
+ }else{
+ $ip = sys::first(explode(':', $unit['address']));
+ $port = false;
+
+ // Проверка наличия свободного порта
+ for($tarif['port_min']; $tarif['port_min'] <= $tarif['port_max']; $tarif['port_min']+=1)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND (`address`="'.$ip.':'.$tarif['port_min'].'" OR `port`="'.$tarif['port_min'].'") LIMIT 1');
+ if(!$sql->num())
+ {
+ $port = $tarif['port_min'];
+
+ break;
+ }
+ }
+ }
+
+ if(!$ip || !$port)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+
+ if($test)
+ $aData['time'] = games::time($start_point, $tarif['test']);
+ else
+ $aData['time'] = games::time($start_point, $days);
+
+ $fix_one = 0;
+ $core = 0;
+
+ if($tarif['core_fix'] != '')
+ {
+ $aCore = explode(',', $tarif['core_fix']);
+
+ foreach($aCore as $cpu)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `tarif`="'.$aData['tarif'].'" AND `core_fix`="'.$cpu.'" AND `core_fix_one`="1" LIMIT 1');
+
+ if($sql->num())
+ continue;
+
+ $fix_one = 1;
+ $core = $cpu;
+
+ break;
+ }
+
+ if(!$core)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+ }
+
+ $ram = $tarif['param_fix'] ? $aData['ram'] : $cfg['ram']['crmp']*$aSDATA['slots'];
+
+ // Массив данных
+ $aSDATA = array(
+ 'unit' => $aData['unit'], // идентификатор локации
+ 'tarif' => $aData['tarif'], // идентификатор тарифа
+ 'ram' => $ram, // значение ram
+ 'param_fix' => $tarif['param_fix'], // фиксированные параметры
+ 'pack' => $aData['pack'], // Выбранная сборка для установки
+ 'time' => $aData['time'], // Время аренды
+ 'days' => $days, // Число дней
+ 'sum' => $sum, // Сумма списания
+ 'test' => $test, // тестовый период
+ 'address' => $ip.':'.$port, // адрес игрового сервера
+ 'port' => $port, // порт игрового сервера
+ 'slots' => $aData['slots'], // Кол-во слот
+ 'autostop' => $tarif['autostop'], // Выключение при 0 онлайне
+ 'ftp' => $tarif['ftp'], // Использование ftp
+ 'plugins' => $tarif['plugins'], // Использование плагинов
+ 'console' => $tarif['console'], // Использование консоли
+ 'stats' => $tarif['stats'], // Использование графиков (ведение статистики)
+ 'copy' => $tarif['copy'], // Использование резервных копий
+ 'web' => $tarif['web'], // Использование доп услуг
+ 'plugins_install' => $tarif['plugins_install'], // Список установленных плагинов
+ 'hdd' => $tarif['hdd'], // Дисковое пространство
+ 'core_fix' => $core, // Выделенный поток
+ 'core_fix_one' => $fix_one, // Выделенный поток
+ 'promo' => $promo // Использование промо-кода
+ );
+
+ return $aSDATA;
+ }
+
+ public static function install($aSDATA = array())
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ // Массив данных локации (адрес,пароль)
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$aSDATA['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Массив данных тарифа (путь сборки,путь установки)
+ $sql->query('SELECT `path`, `install`, `hostname` FROM `tarifs` WHERE `id`="'.$aSDATA['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Получение идентификаторов игрового сервера
+ $sql->query('INSERT INTO `servers` set uid="1"');
+ $id = $sql->id();
+ $uid = $id+1000;
+
+ // Директория сборки
+ $path = $tarif['path'].$aSDATA['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$uid;
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -s /bin/false -d '.$install.' -g servers -u '.$uid.' server'.$uid.';' // Создание пользователя сервера на локации
+ .'chown server'.$uid.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u server'.$uid.' screen -dmS i_'.$uid.' sh -c "cp -r '.$path.'/. .;' // Копирование файлов сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 777 {} \;;'
+ .'chmod 500 '.params::$aFileGame['crmp'].'"');
+
+ // Запись данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `uid`="'.$uid.'",
+ `unit`="'.$aSDATA['unit'].'",
+ `tarif`="'.$aSDATA['tarif'].'",
+ `user`="'.$user['id'].'",
+ `address`="'.$aSDATA['address'].'",
+ `port`="'.$aSDATA['port'].'",
+ `game`="crmp",
+ `slots`="'.$aSDATA['slots'].'",
+ `slots_start`="'.$aSDATA['slots'].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$aSDATA['pack'].'",
+ `plugins_use`="'.$aSDATA['plugins'].'",
+ `ftp_use`="'.$aSDATA['ftp'].'",
+ `console_use`="'.$aSDATA['console'].'",
+ `stats_use`="'.$aSDATA['stats'].'",
+ `copy_use`="'.$aSDATA['copy'].'",
+ `web_use`="'.$aSDATA['web'].'",
+ `vac`="1",
+ `hdd`="'.$aSDATA['hdd'].'",
+ `time`="'.$aSDATA['time'].'",
+ `date`="'.$start_point.'",
+ `test`="'.$aSDATA['test'].'",
+ `ram`="'.$aSDATA['ram'].'",
+ `map_start`="'.sys::passwd(8).'",
+ `core_fix`="'.$aSDATA['core_fix'].'",
+ `core_fix_one`="'.$aSDATA['core_fix_one'].'",
+ `autostop`="'.$aSDATA['autostop'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($aSDATA['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64djs($aSDATA['plugins_install']);
+
+ if(isset($aPlugins[$aSDATA['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$aSDATA['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$aSDATA['sum']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Запись получения тестового периода
+ if($aSDATA['test'])
+ {
+ $sql->query('INSERT INTO `tests` set `server`="'.$id.'", `unit`="'.$aSDATA['unit'].'", `game`="crmp", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_test'), array('id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="0"');
+ }else{
+ // Реф. система
+ games::part($user['id'], $aSDATA['sum']);
+
+ // Запись логов
+ if(!is_array($aSDATA['promo']))
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ else{
+ $sql->query('UPDATE `servers` set `benefit`="'.$aSDATA['time'].'" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('INSERT INTO `promo_use` set `promo`="'.$aSDATA['promo']['id'].'", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_promo'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'promo' => $aSDATA['promo']['cod'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ }
+ }
+
+ return $id;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/crmp/tarif.php b/system/library/games/crmp/tarif.php
new file mode 100644
index 0000000..a65da45
--- /dev/null
+++ b/system/library/games/crmp/tarif.php
@@ -0,0 +1,179 @@
+get('extend', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', '');
+ $html->set('tarif', $tarif_name);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function extend_sp($server, $tarif, $sid)
+ {
+ global $cfg, $sql, $html, $start_point;
+
+ tarifs::extend_address($server['game'], $sid);
+
+ $sum = $tarif['slots'] ? $tarif['price'] : $tarif['price']*$server['slots'];
+
+ $html->get('extend_sp', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('date', $server['time'] > $start_point ? 'Сервер продлен до: '.date('d.m.Y', $server['time']) : 'Текущая дата: '.date('d.m.Y', $start_point));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', '');
+ $html->set('tarif', $tarif['name']);
+ $html->set('sum', $sum);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function plan()
+ {
+ return NULL;
+ }
+
+ public static function unit($server, $unit_name, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ if(!$cfg['change_unit'][$server['game']])
+ return NULL;
+
+ $tarifs = $sql->query('SELECT `unit` FROM `tarifs` WHERE `game`="'.$server['game'].'" AND `name`="'.$tarif_name.'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num($tarifs))
+ return NULL;
+
+ $units = 0;
+
+ $options = 'Выберете новую локацию ';
+
+ while($tarif = $sql->get($tarifs))
+ {
+ $sql->query('SELECT `id`, `name` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $unit = $sql->get();
+
+ $options .= ''.$unit['name'].' ';
+
+ $units+=1;
+ }
+
+ if(!$units)
+ return NULL;
+
+ $html->get('unit', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', '');
+ $html->set('unit', $unit_name);
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit_new($tarif, $unit, $server, $mcache)
+ {
+ global $ssh, $sql, $user, $start_point;
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Директория сборки
+ $path = $tarif['path'].$tarif['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Пользователь сервера
+ $uS = 'server'.$server['uid'];
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -d '.$install.' -g servers -u '.$server['uid'].' '.$uS.';' // Создание пользователя сервера на локации
+ .'chown '.$uS.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u '.$uS.' screen -dmS i_'.$server['uid'].' cp -r '.$path.'/. .'); // Копирование файлов сборки для сервера
+
+ $address = explode(':', $server['address']);
+
+ $fix_one = $tarif['core_fix'] ? 1 : 0;
+
+ // Обновление данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `unit`="'.$tarif['unit'].'",
+ `tarif`="'.$tarif['id'].'",
+ `address`="'.$server['address'].'",
+ `port`="'.$address[1].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$tarif['pack'].'",
+ `map_start`="'.$tarif['map'].'",
+ `hdd`="'.$tarif['hdd'].'",
+ `time`="'.$tarif['time'].'",
+ `autostop`="'.$tarif['autostop'].'",
+ `core_fix`="'.$tarif['core_fix'].'",
+ `core_fix_one`="'.$fix_one.'",
+ `reinstall`="'.$start_point.'" WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($tarif['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64js($tarif['plugins_install']);
+
+ if(isset($aPlugins[$tarif['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$tarif['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$server['id'].'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/cs/action.php b/system/library/games/cs/action.php
new file mode 100644
index 0000000..76ada82
--- /dev/null
+++ b/system/library/games/cs/action.php
@@ -0,0 +1,117 @@
+query('SELECT `uid`, `unit`, `tarif`, `game`, `address`, `slots_start`, `name`, `fps`, `map_start`, `vac`, `pingboost`, `time_start`, `core_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] AND !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Проверка наличия стартовой карты
+ $ssh->set('cd '.$tarif['install'].$server['uid'].'/cstrike/maps/ && ls | grep .bsp | grep -v .bsp.');
+
+ if($server['map_start'] != '' AND !in_array($server['map_start'], str_replace('.bsp', '', explode("\n", $ssh->get()))))
+ return array('e' => sys::updtext(sys::text('servers', 'nomap'), array('map' => $server['map_start'].'.bsp')));
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Античит VAC
+ $vac = $server['vac'] == 0 ? '-insecure' : '-secure';
+
+ // Значение PingBoost
+ $pingboost = $server['pingboost'] == 0 ? $cfg['pingboost'] : $server['pingboost'];
+
+ if(!$pingboost)
+ $pingboost = '';
+ else
+ $pingboost = '-pingboost '.$pingboost;
+
+ // Значение sys_ticrate (FPS)
+ $fps = $server['fps']+$cfg['fpsplus'];
+
+ // Параметры запуска
+ $bash = './hlds_run -debug -game cstrike -norestart -condebug -sys_ticrate '.$fps.' +servercfgfile server.cfg +sys_ticrate '.$fps.' +map \''.$server['map_start'].'\' +maxplayers '.$server['slots_start'].' +ip '.$ip.' +port '.$port.' +sv_lan 0 '.$vac.' '.$pingboost;
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd '.$tarif['install'].$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'sudo -u server'.$server['uid'].' mkdir -p cstrike/oldstart;' // Создание папки логов
+ .'cat cstrike/qconsole.log >> cstrike/oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm cstrike/qconsole.log; rm cstrike/oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 start.sh;' // Обновление владельца файла start.sh
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c ./start.sh'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/cs/rcon.php b/system/library/games/cs/rcon.php
new file mode 100644
index 0000000..0cf116a
--- /dev/null
+++ b/system/library/games/cs/rcon.php
@@ -0,0 +1,110 @@
+Connect($ip, $port, 3, SourceQuery::GOLDSOURCE);
+
+ $sq->SetRconPassword(rcon::rcon_passwd($server));
+
+ $out = $sq->Rcon($cmd);
+
+ $sq->Disconnect();
+
+ return $out;
+ }
+
+ public static function players($data)
+ {
+ $aPlayers = array();
+ $n = 1;
+
+ $lines = explode("\n", $data);
+
+ foreach($lines as $line)
+ {
+ if(strpos($line, '#') === FALSE)
+ continue;
+
+ $start = strpos($line, '"')+1;
+ $end = strrpos($line, '"');
+
+ $name = htmlspecialchars(substr($line, $start, $end-$start));
+
+ $line = trim(substr($line, $end + 1));
+
+ $aData = array_values(array_diff(explode(' ', $line), array('', ' ')));
+
+ $steamid = trim($aData[1]);
+ $ip = trim(sys::first(explode(':', $aData[6])));
+
+ if(sys::valid($steamid, 'steamid') || sys::valid($ip, 'ip'))
+ continue;
+
+ $aPlayers[$n]['name'] = $name;
+ $aPlayers[$n]['steamid'] = $steamid;
+ $aPlayers[$n]['frags'] = trim($aData[2]);
+ $aPlayers[$n]['time'] = trim($aData[3]);
+ $aPlayers[$n]['ping'] = trim($aData[4]);
+ $aPlayers[$n]['ip'] = $ip;
+
+ $whois = rcon::country($ip);
+
+ $aPlayers[$n]['ico'] = $whois['ico'];
+ $aPlayers[$n]['country'] = $whois['name'];
+
+ $n+=1;
+ }
+
+ return $aPlayers;
+ }
+
+ public static function rcon_passwd($server)
+ {
+ global $cfg, $sql, $user;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $ssh->set('cat '.$tarif['install'].$server['uid'].'/cstrike/server.cfg | grep rcon_password');
+ $get = explode(' ', str_replace('"', '', trim($ssh->get())));
+ $rcon = trim(end($get));
+
+ if(!isset($rcon{0}))
+ sys::outjs(array('r' => 'Необходимо установить rcon пароль (rcon_password).', 'url' => $cfg['http'].'servers/id/'.$server['id'].'/section/settings/subsection/server'), $nmch);
+
+ return $rcon;
+ }
+
+ public static function country($ip)
+ {
+ global $SxGeo;
+
+ $cData = $SxGeo->getCityFull($ip);
+ $ico = sys::country($cData['country']['iso']);
+
+ return array('ico' => $ico, 'name' => empty($cData['country']['name_ru']) ? 'Не определена' : $cData['country']['name_ru']);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/cs/scan.php b/system/library/games/cs/scan.php
new file mode 100644
index 0000000..59b6507
--- /dev/null
+++ b/system/library/games/cs/scan.php
@@ -0,0 +1,140 @@
+get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $info = scan::info($sq, $id);
+
+ $sql->query('SELECT `game`, `name`, `map`, `online`, `players`, `status`, `time`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $out['time'] = 'Арендован до: '.date('d.m.Y - H:i', $server['time']);
+
+ if($server['status'] == 'overdue')
+ $out['time_end'] = 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400);
+ else
+ $out['time_end'] = 'Осталось: '.sys::date('min', $server['time']);
+
+ if(!$info['status'])
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ if($players_get)
+ $players = scan::info($sq, $id, true);
+
+ $out['name'] = htmlspecialchars($info['name']);
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = $info['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working');
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', $player['name']);
+ $html->set('score', $player['score']);
+ $html->set('time', $player['time']);
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function info($sq, $id, $pl = false)
+ {
+ global $sql;
+
+ $sql->query('SELECT `address` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $sq->Connect($ip, $port, 1, SourceQuery::GOLDSOURCE);
+
+ if($pl)
+ {
+ $players = $sq->GetPlayers();
+
+ $i = 1;
+ $data = array();
+
+ foreach($players as $n => $player)
+ {
+ $data[$i]['i'] = $i;
+ $data[$i]['name'] = $player['Name'] == '' ? 'Подключается' : $player['Name'];
+ $data[$i]['score'] = $player['Frags'];
+ $data[$i]['time'] = $player['TimeF'];
+
+ $i+=1;
+ }
+
+ return $data;
+ }
+
+ $data = $sq->GetInfo();
+
+ $server['name'] = $data['HostName'];
+ $server['map'] = $data['Map'];
+ $server['online'] = $data['Players'];
+ $server['status'] = strlen($server['map']) > 3 ? true : false;
+
+ return $server;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/cs/service.php b/system/library/games/cs/service.php
new file mode 100644
index 0000000..44655e0
--- /dev/null
+++ b/system/library/games/cs/service.php
@@ -0,0 +1,368 @@
+query('SELECT `address`, `test` FROM `units` WHERE `id`="'.$aData['unit'].'" AND `cs`="1" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Локация не найдена.'));
+
+ $unit = $sql->get();
+
+ // Проверка тарифа
+ $sql->query('SELECT `id` FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" AND `unit`="'.$aData['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Тариф не найден.'));
+
+ $sql->query('SELECT '
+ .'`slots_min`,'
+ .'`slots_max`,'
+ .'`port_min`,'
+ .'`port_max`,'
+ .'`hostname`,'
+ .'`packs`,'
+ .'`fps`,'
+ .'`time`,'
+ .'`test`,'
+ .'`tests`,'
+ .'`discount`,'
+ .'`map`,'
+ .'`ftp`,'
+ .'`plugins`,'
+ .'`console`,'
+ .'`stats`,'
+ .'`copy`,'
+ .'`web`,'
+ .'`plugins_install`,'
+ .'`hdd`,'
+ .'`autostop`,'
+ .'`core_fix`,'
+ .'`ip`,'
+ .'`price`'
+ .' FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ $tarif = $sql->get();
+
+ // Проверка сборки
+ if(!array_key_exists($aData['pack'], sys::b64djs($tarif['packs'], true)))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ // Проверка FPS
+ if(!in_array($aData['fps'], explode(':', $tarif['fps'])))
+ sys::outjs(array('e' => 'Переданные данные fps неверны.'));
+
+ $test = 0;
+
+ // Проверка периода на тест
+ if($aData['test'])
+ {
+ if(!$tarif['test'] || !$unit['test'])
+ sys::outjs(array('e' => 'Тестовый период недоступен.'));
+
+ // Проверка на повторный запрос
+ $sql->query('SELECT `id`, `game` FROM `tests` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $test_info = $sql->get();
+
+ if(!$cfg['tests']['game'] || $test_info['game'] == 'cs')
+ sys::outjs(array('e' => 'Тестовый период предоставляется один раз.'));
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" AND `test`="1" LIMIT 1');
+ if($sql->num() AND !$cfg['tests']['sametime'])
+ sys::outjs(array('e' => 'Чтобы получить тестовый период другой игры, дождитесь окончания текущего.'));
+ }
+
+ // Проверка наличия мест на локации
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$unit['test']);
+ if($sql->num() == $unit['test'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода нет.'));
+
+ // Проверка наличия мест для выбранного тарифа
+ $sql->query('SELECT `id` FROM `servers` WHERE `tarif`="'.$aData['tarif'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$tarif['tests']);
+ if($sql->num() == $tarif['tests'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода выбранного тарифа нет.'));
+
+ $test = 1;
+ }else
+ // Проверка периода
+ if(!$cfg['settlement_period'] AND !in_array($aData['time'], explode(':', $tarif['time'])))
+ sys::outjs(array('e' => 'Переданные данные периода неверны.'));
+
+ // Проверка слот
+ if($aData['slots'] < $tarif['slots_min'] || $aData['slots'] > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданные данные слот неверны.'));
+
+ // Определение цены
+ $aPrice = explode(':', $tarif['price']);
+ $price = $aPrice[array_search($aData['fps'], explode(':', $tarif['fps']))];
+
+ // Определение суммы
+ if($cfg['settlement_period'])
+ {
+ // Цена аренды за расчетный период
+ $sum = games::define_sum($tarif['discount'], $price, $aData['slots'], $start_point);
+
+ $aData['time'] = games::define_period('buy', params::$aDayMonth);
+ }else
+ $sum = games::define_sum($tarif['discount'], $price, $aData['slots'], $aData['time']);
+
+ // Проверка промо-кода
+ $promo = games::define_promo(
+ $aData['promo'],
+ array(
+ 'tarif' => $aData['tarif'],
+ 'fps' => $aData['fps'],
+ 'slots' => $aData['slots'],
+ 'time' => $aData['time'],
+ 'user' => $user['id']
+ ),
+ $tarif['discount'],
+ $sum
+ );
+
+ $days = $aData['time']; // Кол-во дней аренды
+
+ // Использование промо-кода
+ if(is_array($promo))
+ {
+ if(array_key_exists('sum', $promo))
+ $sum = $promo['sum'];
+ else
+ $days += $promo['days']; // Кол-во дней аренды с учетом подарочных (промо-код)
+ }
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']));
+
+ // Выделенный адрес игрового сервера
+ if(!empty($tarif['ip']))
+ {
+ $aIp = explode(':', $tarif['ip']);
+
+ $ip = false;
+ $port = params::$aDefPort['cs'];
+
+ // Проверка наличия свободного адреса
+ foreach($aIp as $adr)
+ {
+ $adr = trim($adr);
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `address` LIKE "'.$adr.':%" LIMIT 1');
+ if(!$sql->num())
+ {
+ $ip = $adr;
+
+ break;
+ }
+ }
+ }else{
+ $ip = sys::first(explode(':', $unit['address']));
+ $port = false;
+
+ // Проверка наличия свободного порта
+ for($tarif['port_min']; $tarif['port_min'] <= $tarif['port_max']; $tarif['port_min']+=1)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND (`address`="'.$ip.':'.$tarif['port_min'].'" OR `port`="'.$tarif['port_min'].'") LIMIT 1');
+ if(!$sql->num())
+ {
+ $port = $tarif['port_min'];
+
+ break;
+ }
+ }
+ }
+
+ if(!$ip || !$port)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+
+ if($test)
+ $aData['time'] = games::time($start_point, $tarif['test']);
+ else
+ $aData['time'] = games::time($start_point, $days);
+
+ $fix_one = 0;
+ $core = 0;
+
+ if($tarif['core_fix'] != '')
+ {
+ $aCore = explode(',', $tarif['core_fix']);
+
+ foreach($aCore as $cpu)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `core_fix`="'.$cpu.'" AND `core_fix_one`="1" LIMIT 1');
+
+ if($sql->num())
+ continue;
+
+ $fix_one = 1;
+ $core = $cpu;
+
+ break;
+ }
+
+ if(!$core)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+ }
+
+ $ram = $tarif['param_fix'] ? $aData['ram'] : $cfg['ram']['cs']*$aSDATA['slots'];
+
+ // Массив данных
+ $aSDATA = array(
+ 'unit' => $aData['unit'], // идентификатор локации
+ 'tarif' => $aData['tarif'], // идентификатор тарифа
+ 'ram' => $ram, // значение ram
+ 'param_fix' => $tarif['param_fix'], // фиксированные параметры
+ 'fps' => $aData['fps'], // значение fps
+ 'pack' => $aData['pack'], // Выбранная сборка для установки
+ 'time' => $aData['time'], // Время аренды
+ 'days' => $days, // Число дней
+ 'sum' => $sum, // Сумма списания
+ 'test' => $test, // тестовый период
+ 'address' => $ip.':'.$port, // адрес игрового сервера
+ 'port' => $port, // порт игрового сервера
+ 'slots' => $aData['slots'], // Кол-во слот
+ 'map' => $tarif['map'], // Фиксированное значение слот
+ 'autostop' => $tarif['autostop'], // Выключение при 0 онлайне
+ 'ftp' => $tarif['ftp'], // Использование ftp
+ 'plugins' => $tarif['plugins'], // Использование плагинов
+ 'console' => $tarif['console'], // Использование консоли
+ 'stats' => $tarif['stats'], // Использование графиков (ведение статистики)
+ 'copy' => $tarif['copy'], // Использование резервных копий
+ 'web' => $tarif['web'], // Использование доп услуг
+ 'plugins_install' => $tarif['plugins_install'], // Список установленных плагинов
+ 'hdd' => $tarif['hdd'], // Дисковое пространство
+ 'core_fix' => $core, // Выделенный поток
+ 'core_fix_one' => $fix_one, // Выделенный поток
+ 'promo' => $promo // Использование промо-кода
+ );
+
+ return $aSDATA;
+ }
+
+ public static function install($aSDATA = array())
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ // Массив данных локации (адрес,пароль)
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$aSDATA['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Массив данных тарифа (путь сборки,путь установки)
+ $sql->query('SELECT `path`, `install`, `hostname` FROM `tarifs` WHERE `id`="'.$aSDATA['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Получение идентификаторов игрового сервера
+ $sql->query('INSERT INTO `servers` set uid="1"');
+ $id = $sql->id();
+ $uid = $id+1000;
+
+ // Директория сборки
+ $path = $tarif['path'].$aSDATA['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$uid;
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -s /bin/false -d '.$install.' -g servers -u '.$uid.' server'.$uid.';' // Создание пользователя сервера на локации
+ .'chown server'.$uid.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u server'.$uid.' screen -dmS i_'.$uid.' sh -c "cp -r '.$path.'/. .;' // Копирование файлов сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame['cs'].'"');
+
+ // Запись данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `uid`="'.$uid.'",
+ `unit`="'.$aSDATA['unit'].'",
+ `tarif`="'.$aSDATA['tarif'].'",
+ `user`="'.$user['id'].'",
+ `address`="'.$aSDATA['address'].'",
+ `port`="'.$aSDATA['port'].'",
+ `game`="cs",
+ `slots`="'.$aSDATA['slots'].'",
+ `slots_start`="'.$aSDATA['slots'].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$aSDATA['pack'].'",
+ `plugins_use`="'.$aSDATA['plugins'].'",
+ `ftp_use`="'.$aSDATA['ftp'].'",
+ `console_use`="'.$aSDATA['console'].'",
+ `stats_use`="'.$aSDATA['stats'].'",
+ `copy_use`="'.$aSDATA['copy'].'",
+ `web_use`="'.$aSDATA['web'].'",
+ `fps`="'.$aSDATA['fps'].'",
+ `map_start`="'.$aSDATA['map'].'",
+ `vac`="1",
+ `hdd`="'.$aSDATA['hdd'].'",
+ `time`="'.$aSDATA['time'].'",
+ `date`="'.$start_point.'",
+ `test`="'.$aSDATA['test'].'",
+ `ram`="'.$aSDATA['ram'].'",
+ `core_fix`="'.$aSDATA['core_fix'].'",
+ `core_fix_one`="'.$aSDATA['core_fix_one'].'",
+ `autostop`="'.$aSDATA['autostop'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($aSDATA['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64djs($aSDATA['plugins_install']);
+
+ if(isset($aPlugins[$aSDATA['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$aSDATA['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$aSDATA['sum']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Запись получения тестового периода
+ if($aSDATA['test'])
+ {
+ $sql->query('INSERT INTO `tests` set `server`="'.$id.'", `unit`="'.$aSDATA['unit'].'", `game`="cs", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_test'), array('id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="0"');
+ }else{
+ // Реф. система
+ games::part($user['id'], $aSDATA['sum']);
+
+ // Запись логов
+ if(!is_array($aSDATA['promo']))
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ else{
+ $sql->query('UPDATE `servers` set `benefit`="'.$aSDATA['time'].'" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('INSERT INTO `promo_use` set `promo`="'.$aSDATA['promo']['id'].'", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_promo'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'promo' => $aSDATA['promo']['cod'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ }
+ }
+
+ return $id;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/cs/tarif.php b/system/library/games/cs/tarif.php
new file mode 100644
index 0000000..c571bc8
--- /dev/null
+++ b/system/library/games/cs/tarif.php
@@ -0,0 +1,225 @@
+get('extend', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['fps'].' FPS');
+ $html->set('tarif', $tarif_name);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function extend_sp($server, $tarif, $sid)
+ {
+ global $cfg, $sql, $html, $start_point;
+
+ tarifs::extend_address($server['game'], $sid);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aFPS = explode(':', $tarif['fps']);
+
+ $sum = $tarif['slots'] ? $aPrice[array_search($server['fps'], $aFPS)] : $aPrice[array_search($server['fps'], $aFPS)]*$server['slots'];
+
+ $html->get('extend_sp', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('date', $server['time'] > $start_point ? 'Сервер продлен до: '.date('d.m.Y', $server['time']) : 'Текущая дата: '.date('d.m.Y', $start_point));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['fps'].' FPS');
+ $html->set('tarif', $tarif['name']);
+ $html->set('sum', $sum);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function plan($server, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ $sql->query('SELECT `fps`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $tarif = $sql->get();
+
+ $options = 'Выберете тарифный план ';
+
+ $aPrice = explode(':', $tarif['price']);
+ $aFps = explode(':', $tarif['fps']);
+
+ // Если есть выбор
+ if(count($aFps) > 1)
+ {
+ // Удалить при наличии fps сервера из выбора
+ if(in_array($server['fps'], $aFps))
+ unset($aFps[array_search($server['fps'], $aFps)]);
+
+ foreach($aFps as $index => $fps)
+ $options .= ''
+ .$fps.' FPS '
+ .'('.$aPrice[$index].' '.$cfg['currency'].'/слот | '
+ .($aPrice[$index]*$server['slots']).' '.$cfg['currency'].'/месяц)'
+ .' ';
+ }else
+ return NULL;
+
+ $html->get('plan', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('info', $server['fps'].' FPS');
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit($server, $unit_name, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ if(!$cfg['change_unit'][$server['game']])
+ return NULL;
+
+ $tarifs = $sql->query('SELECT `unit`, `fps` FROM `tarifs` WHERE `game`="'.$server['game'].'" AND `name`="'.$tarif_name.'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num($tarifs))
+ return NULL;
+
+ $units = 0;
+
+ $options = 'Выберете новую локацию ';
+
+ while($tarif = $sql->get($tarifs))
+ {
+ if(!in_array($server['fps'], explode(':', $tarif['fps'])))
+ continue;
+
+ $sql->query('SELECT `id`, `name` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $unit = $sql->get();
+
+ $options .= ''.$unit['name'].' ';
+
+ $units+=1;
+ }
+
+ if(!$units)
+ return NULL;
+
+ $html->get('unit', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['fps'].' FPS');
+ $html->set('unit', $unit_name);
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit_new($tarif, $unit, $server, $mcache)
+ {
+ global $ssh, $sql, $user, $start_point;
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Директория сборки
+ $path = $tarif['path'].$tarif['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Пользователь сервера
+ $uS = 'server'.$server['uid'];
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -d '.$install.' -g servers -u '.$server['uid'].' '.$uS.';' // Создание пользователя сервера на локации
+ .'chown '.$uS.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u '.$uS.' screen -dmS i_'.$server['uid'].' cp -r '.$path.'/. .'); // Копирование файлов сборки для сервера
+
+ $address = explode(':', $server['address']);
+
+ $fix_one = $tarif['core_fix'] ? 1 : 0;
+
+ // Обновление данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `unit`="'.$tarif['unit'].'",
+ `tarif`="'.$tarif['id'].'",
+ `address`="'.$server['address'].'",
+ `port`="'.$address[1].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$tarif['pack'].'",
+ `map_start`="'.$tarif['map'].'",
+ `vac`="1",
+ `hdd`="'.$tarif['hdd'].'",
+ `time`="'.$tarif['time'].'",
+ `autostop`="'.$tarif['autostop'].'",
+ `core_fix`="'.$tarif['core_fix'].'",
+ `core_fix_one`="'.$fix_one.'",
+ `reinstall`="'.$start_point.'" WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($tarif['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64js($tarif['plugins_install']);
+
+ if(isset($aPlugins[$tarif['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$tarif['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$server['id'].'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/csgo/action.php b/system/library/games/csgo/action.php
new file mode 100644
index 0000000..ab3c467
--- /dev/null
+++ b/system/library/games/csgo/action.php
@@ -0,0 +1,302 @@
+query('SELECT `uid`, `unit`, `tarif`, `game`, `address`, `slots_start`, `name`, `tickrate`, `map_start`, `vac`, `time_start`, `core_fix`, `pingboost` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] AND !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Проверка наличия стартовой карты
+ $ssh->set('cd '.$tarif['install'].$server['uid'].'/csgo/maps/ && du -ah | grep -e "\.bsp$" | awk \'{print $2}\'');
+
+ include_once(LIB.'games/games.php');
+
+ if(games::map($server['map_start'], $ssh->get()))
+ return array('e' => sys::updtext(sys::text('servers', 'nomap'), array('map' => $server['map_start'].'.bsp')));
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Античит VAC
+ $vac = $server['vac'] == 0 ? '-insecure' : '-secure';
+
+ // Боты
+ $bots = $cfg['bots'][$server['game']] ? '' : '-nobots';
+
+ // TV
+ $tv = $server['tv'] ? '+tv_enable 1 +tv_maxclients 30 +tv_port '.($port+10000) : '-nohltv';
+
+ $check = explode('/', $server['map_start']);
+
+ // Стартовая карта
+ $map = $check[0] == 'workshop' ? '+workshop_start_map '.$check[1] : '+map \''.$server['map_start'].'\'';
+
+ // Игровой режим
+ $mods = array(
+ 1 => '+game_type 0 +game_mode 0',
+ 2 => '+game_type 0 +game_mode 1',
+ 3 => '+game_type 1 +game_mode 0',
+ 4 => '+game_type 1 +game_mode 1',
+ 5 => '+game_type 1 +game_mode 2'
+ );
+
+ $mod = !$server['pingboost'] ? $mods[2] : $mods[$server['pingboost']];
+
+ // Параметры запуска
+ $bash = './srcds_run -debug -game csgo -norestart -condebug console.log -usercon -tickrate '.$server['tickrate'].' '.$mod.' +servercfgfile server.cfg '.$map.' -maxplayers_override '.$server['slots_start'].' +ip '.$ip.' +net_public_adr '.$ip.' +port '.$port.' -sv_lan 0 '.$vac.' '.$bots.' '.$tv;
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd '.$tarif['install'].$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'sudo -u server'.$server['uid'].' mkdir -p csgo/oldstart;' // Создание папки логов
+ .'cat csgo/console.log >> csgo/oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm csgo/console.log; rm csgo/oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 start.sh;' // Обновление владельца файла start.sh
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c "./start.sh"'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function change($id, $map = false)
+ {
+ global $cfg, $sql, $html, $user, $mcache;
+
+ // Если в кеше есть карты
+ if($mcache->get('server_maps_change_'.$id) != '' AND !$map)
+ return array('maps' => $mcache->get('server_maps_change_'.$id));
+
+ include(LIB.'ssh.php');
+
+ include(LIB.'games/games.php');
+
+ $sql->query('SELECT `uid`, `unit`, `game`, `tarif`, `online`, `players`, `name` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ // Массив карт игрового сервера (папка "maps")
+ $aMaps = explode("\n", $ssh->get('cd '.$tarif['install'].$server['uid'].'/csgo/maps/ && du -ah | grep -e "\.bsp$" | awk \'{print $2}\''));
+
+ // Удаление пустого элемента
+ unset($aMaps[count($aMaps)-1]);
+
+ // Удаление ".bsp"
+ $aMaps = str_ireplace(array('./', '.bsp'), '', $aMaps);
+
+ // Если выбрана карта
+ if($map)
+ {
+ $map = str_replace('|', '/', $map);
+
+ // Проверка наличия выбранной карты
+ if(games::map($map, $aMaps))
+ return array('e' => sys::updtext(sys::text('servers', 'change'), array('map' => $map.'.bsp')));
+
+ // Отправка команды changelevel
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval '."'stuff \"changelevel ".sys::cmd($map)."\"\015'");
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="change" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'change', 'online' => $server['online'], 'players' => base64_decode($server['players'])));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'change', 'online' => $server['online']));
+
+ return array('s' => 'ok');
+ }
+
+ // Сортировка списка карт
+ sort($aMaps);
+ reset($aMaps);
+
+ // Генерация списка карт для выбора
+ foreach($aMaps as $map)
+ {
+ $aName = explode('/', $map);
+ $name = end($aName);
+
+ $html->get('change_list', 'sections/servers/csgo');
+
+ $html->set('img', file_exists(DIR.'/maps/'.$server['game'].'/'.$name.'.jpg') ? $cfg['http'].'maps/'.$server['game'].'/'.$name.'.jpg' : $cfg['http'].'template/images/status/none.jpg');
+ $html->set('map', str_replace('/', '|', $map));
+ $html->set('name', $name);
+ $html->set('id', $id);
+
+ if(count($aName) > 1)
+ $html->unit('workshop', true);
+ else
+ $html->unit('workshop');
+
+ $html->pack('maps');
+ }
+
+ // Запись карт в кеш
+ $mcache->set('server_maps_change_'.$id, $html->arr['maps'], false, 60);
+
+ return array('maps' => $html->arr['maps']);
+ }
+
+ public static function update($id)
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `tarif`, `game`, `name`, `ftp`, `update`, `core_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ // Проверка времени обновления
+ $update = $server['update']+$cfg['update'][$server['game']]*60;
+
+ if($update > $start_point AND $user['group'] != 'admin')
+ return array('e' => sys::updtext(sys::text('servers', 'update'), array('time' => sys::date('max', $update))));
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install`, `plugins_install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] AND !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => 'Не удается выполнить операцию, нет свободного потока.');
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ $ssh->set('cd '.$cfg['steamcmd'].' && '.$taskset.' screen -dmS u_'.$server['uid'].' sh -c "'
+ .'./steamcmd.sh +login anonymous +force_install_dir "'.$install.'" +app_update 740 +quit;'
+ .'cd '.$install.';'
+ .'chown -R server'.$server['uid'].':servers .;'
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].'"');
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="update", `update`="'.$start_point.'", `core_use`="'.$core.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Логирование
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'update').'", `time`="'.$start_point.'"');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/csgo/rcon.php b/system/library/games/csgo/rcon.php
new file mode 100644
index 0000000..74182de
--- /dev/null
+++ b/system/library/games/csgo/rcon.php
@@ -0,0 +1,112 @@
+Connect($ip, $port, 3, SourceQuery::SOURCE);
+
+ $sq->SetRconPassword(rcon::rcon_passwd($server));
+
+ $out = $sq->Rcon($cmd);
+
+ $sq->Disconnect();
+
+ return $out;
+ }
+
+ public static function players($data)
+ {
+ $aPlayers = array();
+ $n = 1;
+
+ $lines = explode("\n", $data);
+
+ foreach($lines as $line)
+ {
+ if(strpos($line, '#') === FALSE)
+ continue;
+
+ $start = strpos($line, '"')+1;
+ $end = strrpos($line, '"');
+
+ $userid = sys::int(substr($line, 0, $start));
+
+ $name = htmlspecialchars(substr($line, $start, $end-$start));
+
+ $line = trim(substr($line, $end + 1));
+
+ $aData = array_values(array_diff(explode(' ', $line), array('', ' ')));
+
+ $steamid = trim($aData[0]);
+ $ip = trim(sys::first(explode(':', $aData[5])));
+
+ if((sys::valid($steamid, 'steamid') AND sys::valid($steamid, 'steamid3')) || sys::valid($ip, 'ip'))
+ continue;
+
+ $aPlayers[$n]['userid'] = $userid;
+ $aPlayers[$n]['name'] = $name;
+ $aPlayers[$n]['steamid'] = $steamid;
+ $aPlayers[$n]['time'] = trim($aData[1]);
+ $aPlayers[$n]['ping'] = trim($aData[2]);
+ $aPlayers[$n]['ip'] = $ip;
+
+ $whois = rcon::country($ip);
+
+ $aPlayers[$n]['ico'] = $whois['ico'];
+ $aPlayers[$n]['country'] = $whois['name'];
+
+ $n+=1;
+ }
+
+ return $aPlayers;
+ }
+
+ public static function rcon_passwd($server)
+ {
+ global $cfg, $sql, $user;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $ssh->set('cat '.$tarif['install'].'/'.$server['uid'].'/csgo/cfg/server.cfg | grep rcon_password');
+ $get = explode(' ', str_replace('"', '', trim($ssh->get())));
+ $rcon = trim(end($get));
+
+ if(!isset($rcon{0}))
+ sys::outjs(array('r' => 'Необходимо установить rcon пароль (rcon_password).', 'url' => $cfg['http'].'servers/id/'.$server['id'].'/section/settings/subsection/server'), $nmch);
+
+ return $rcon;
+ }
+
+ public static function country($ip)
+ {
+ global $SxGeo;
+
+ $cData = $SxGeo->getCityFull($ip);
+ $ico = sys::country($cData['country']['iso']);
+
+ return array('ico' => $ico, 'name' => empty($cData['country']['name_ru']) ? 'Не определена' : $cData['country']['name_ru']);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/csgo/scan.php b/system/library/games/csgo/scan.php
new file mode 100644
index 0000000..5e498aa
--- /dev/null
+++ b/system/library/games/csgo/scan.php
@@ -0,0 +1,142 @@
+get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $info = scan::info($sq, $id);
+
+ $sql->query('SELECT `game`, `name`, `map`, `online`, `players`, `status`, `time`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $out['time'] = 'Арендован до: '.date('d.m.Y - H:i', $server['time']);
+
+ if($server['status'] == 'overdue')
+ $out['time_end'] = 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400);
+ else
+ $out['time_end'] = 'Осталось: '.sys::date('min', $server['time']);
+
+ if(!$info['status'])
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ if($players_get)
+ $players = scan::info($sq, $id, true);
+
+ $out['name'] = htmlspecialchars($info['name']);
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = $info['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working');
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', $player['name']);
+ $html->set('score', $player['score']);
+ $html->set('time', $player['time']);
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function info($sq, $id, $pl = false)
+ {
+ global $sql;
+
+ $sql->query('SELECT `address` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $sq->Connect($ip, $port, 1, SourceQuery::SOURCE);
+
+ if($pl)
+ {
+ $players = $sq->GetPlayers();
+
+ $i = 1;
+ $data = array();
+
+ foreach($players as $n => $player)
+ {
+ $data[$i]['i'] = $i;
+ $data[$i]['name'] = $player['Name'] == '' ? 'Подключается' : $player['Name'];
+ $data[$i]['score'] = $player['Frags'];
+ $data[$i]['time'] = $player['TimeF'];
+
+ $i+=1;
+ }
+
+ return $data;
+ }
+
+ $data = $sq->GetInfo();
+
+ $map = explode('/', $data['Map']);
+
+ $server['name'] = $data['HostName'];
+ $server['map'] = end($map);
+ $server['online'] = $data['Players'];
+ $server['status'] = strlen($server['map']) > 3 ? true : false;
+
+ return $server;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/csgo/service.php b/system/library/games/csgo/service.php
new file mode 100644
index 0000000..6f3729c
--- /dev/null
+++ b/system/library/games/csgo/service.php
@@ -0,0 +1,369 @@
+query('SELECT `address`, `test` FROM `units` WHERE `id`="'.$aData['unit'].'" AND `csgo`="1" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Локация не найдена.'));
+
+ $unit = $sql->get();
+
+ // Проверка тарифа
+ $sql->query('SELECT `id` FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" AND `unit`="'.$aData['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Тариф не найден.'));
+
+ $sql->query('SELECT '
+ .'`slots_min`,'
+ .'`slots_max`,'
+ .'`port_min`,'
+ .'`port_max`,'
+ .'`hostname`,'
+ .'`packs`,'
+ .'`tickrate`,'
+ .'`time`,'
+ .'`test`,'
+ .'`tests`,'
+ .'`discount`,'
+ .'`map`,'
+ .'`ftp`,'
+ .'`plugins`,'
+ .'`console`,'
+ .'`stats`,'
+ .'`copy`,'
+ .'`web`,'
+ .'`plugins_install`,'
+ .'`hdd`,'
+ .'`autostop`,'
+ .'`core_fix`,'
+ .'`ip`,'
+ .'`price`'
+ .' FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ $tarif = $sql->get();
+
+ // Проверка сборки
+ if(!array_key_exists($aData['pack'], sys::b64djs($tarif['packs'], true)))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ // Проверка TickRate
+ if(!in_array($aData['tickrate'], explode(':', $tarif['tickrate'])))
+ sys::outjs(array('e' => 'Переданные данные tickrate неверны.'));
+
+ $test = 0;
+
+ // Проверка периода на тест
+ if($aData['test'])
+ {
+ if(!$tarif['test'] || !$unit['test'])
+ sys::outjs(array('e' => 'Тестовый период недоступен.'));
+
+
+ // Проверка на повторный запрос
+ $sql->query('SELECT `id`, `game` FROM `tests` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $test_info = $sql->get();
+
+ if(!$cfg['tests']['game'] || $test_info['game'] == 'csgo')
+ sys::outjs(array('e' => 'Тестовый период предоставляется один раз.'));
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" AND `test`="1" LIMIT 1');
+ if($sql->num() AND !$cfg['tests']['sametime'])
+ sys::outjs(array('e' => 'Чтобы получить тестовый период другой игры, дождитесь окончания текущего.'));
+ }
+
+ // Проверка наличия мест на локации
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$unit['test']);
+ if($sql->num() == $unit['test'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода нет.'));
+
+ // Проверка наличия мест для выбранного тарифа
+ $sql->query('SELECT `id` FROM `servers` WHERE `tarif`="'.$aData['tarif'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$tarif['tests']);
+ if($sql->num() == $tarif['tests'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода выбранного тарифа нет.'));
+
+ $test = 1;
+ }else
+ // Проверка периода
+ if(!$cfg['settlement_period'] AND !in_array($aData['time'], explode(':', $tarif['time'])))
+ sys::outjs(array('e' => 'Переданные данные периода неверны.'));
+
+ // Проверка слот
+ if($aData['slots'] < $tarif['slots_min'] || $aData['slots'] > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданные данные слот неверны.'));
+
+ // Определение цены
+ $aPrice = explode(':', $tarif['price']);
+ $price = $aPrice[array_search($aData['tickrate'], explode(':', $tarif['tickrate']))];
+
+ // Определение суммы
+ if($cfg['settlement_period'])
+ {
+ // Цена аренды за расчетный период
+ $sum = games::define_sum($tarif['discount'], $price, $aData['slots'], $start_point);
+
+ $aData['time'] = games::define_period('buy', params::$aDayMonth);
+ }else
+ $sum = games::define_sum($tarif['discount'], $price, $aData['slots'], $aData['time']);
+
+ // Проверка промо-кода
+ $promo = games::define_promo(
+ $aData['promo'],
+ array(
+ 'tarif' => $aData['tarif'],
+ 'tickrate' => $aData['tickrate'],
+ 'slots' => $aData['slots'],
+ 'time' => $aData['time'],
+ 'user' => $user['id']
+ ),
+ $tarif['discount'],
+ $sum
+ );
+
+ $days = $aData['time']; // Кол-во дней аренды
+
+ // Использование промо-кода
+ if(is_array($promo))
+ {
+ if(array_key_exists('sum', $promo))
+ $sum = $promo['sum'];
+ else
+ $days += $promo['days']; // Кол-во дней аренды с учетом подарочных (промо-код)
+ }
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']));
+
+ // Выделенный адрес игрового сервера
+ if(!empty($tarif['ip']))
+ {
+ $aIp = explode(':', $tarif['ip']);
+
+ $ip = false;
+ $port = params::$aDefPort['csgo'];
+
+ // Проверка наличия свободного адреса
+ foreach($aIp as $adr)
+ {
+ $adr = trim($adr);
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `address` LIKE "'.$adr.':%" LIMIT 1');
+ if(!$sql->num())
+ {
+ $ip = $adr;
+
+ break;
+ }
+ }
+ }else{
+ $ip = sys::first(explode(':', $unit['address']));
+ $port = false;
+
+ // Проверка наличия свободного порта
+ for($tarif['port_min']; $tarif['port_min'] <= $tarif['port_max']; $tarif['port_min']+=1)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND (`address`="'.$ip.':'.$tarif['port_min'].'" OR `port`="'.$tarif['port_min'].'") LIMIT 1');
+ if(!$sql->num())
+ {
+ $port = $tarif['port_min'];
+
+ break;
+ }
+ }
+ }
+
+ if(!$ip || !$port)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+
+ if($test)
+ $aData['time'] = games::time($start_point, $tarif['test']);
+ else
+ $aData['time'] = games::time($start_point, $days);
+
+ $fix_one = 0;
+ $core = 0;
+
+ if($tarif['core_fix'] != '')
+ {
+ $aCore = explode(',', $tarif['core_fix']);
+
+ foreach($aCore as $cpu)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `tarif`="'.$aData['tarif'].'" AND `core_fix`="'.$cpu.'" AND `core_fix_one`="1" LIMIT 1');
+
+ if($sql->num())
+ continue;
+
+ $fix_one = 1;
+ $core = $cpu;
+
+ break;
+ }
+
+ if(!$core)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+ }
+
+ $ram = $tarif['param_fix'] ? $aData['ram'] : $cfg['ram']['csgo']*$aSDATA['slots'];
+
+ // Массив данных
+ $aSDATA = array(
+ 'unit' => $aData['unit'], // идентификатор локации
+ 'tarif' => $aData['tarif'], // идентификатор тарифа
+ 'ram' => $ram, // значение ram
+ 'param_fix' => $tarif['param_fix'], // фиксированные параметры
+ 'tickrate' => $aData['tickrate'], // значение tickrate
+ 'pack' => $aData['pack'], // Выбранная сборка для установки
+ 'time' => $aData['time'], // Время аренды
+ 'days' => $days, // Число дней
+ 'sum' => $sum, // Сумма списания
+ 'test' => $test, // тестовый период
+ 'address' => $ip.':'.$port, // адрес игрового сервера
+ 'port' => $port, // порт игрового сервера
+ 'slots' => $aData['slots'], // Кол-во слот
+ 'map' => $tarif['map'], // Фиксированное значение слот
+ 'autostop' => $tarif['autostop'], // Выключение при 0 онлайне
+ 'ftp' => $tarif['ftp'], // Использование ftp
+ 'plugins' => $tarif['plugins'], // Использование плагинов
+ 'console' => $tarif['console'], // Использование консоли
+ 'stats' => $tarif['stats'], // Использование графиков (ведение статистики)
+ 'copy' => $tarif['copy'], // Использование резервных копий
+ 'web' => $tarif['web'], // Использование доп услуг
+ 'plugins_install' => $tarif['plugins_install'], // Список установленных плагинов
+ 'hdd' => $tarif['hdd'], // Дисковое пространство
+ 'core_fix' => $core, // Выделенный поток
+ 'core_fix_one' => $fix_one, // Выделенный поток
+ 'promo' => $promo // Использование промо-кода
+ );
+
+ return $aSDATA;
+ }
+
+ public static function install($aSDATA = array())
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ // Массив данных локации (адрес,пароль)
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$aSDATA['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Массив данных тарифа (путь сборки,путь установки)
+ $sql->query('SELECT `path`, `install`, `hostname` FROM `tarifs` WHERE `id`="'.$aSDATA['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Получение идентификаторов игрового сервера
+ $sql->query('INSERT INTO `servers` set uid="1"');
+ $id = $sql->id();
+ $uid = $id+1000;
+
+ // Директория сборки
+ $path = $tarif['path'].$aSDATA['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$uid;
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -s /bin/false -d '.$install.' -g servers -u '.$uid.' server'.$uid.';' // Создание пользователя сервера на локации
+ .'chown server'.$uid.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u server'.$uid.' screen -dmS i_'.$uid.' sh -c "cp -r '.$path.'/. .;' // Копирование файлов сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame['csgo'].'"');
+
+ // Запись данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `uid`="'.$uid.'",
+ `unit`="'.$aSDATA['unit'].'",
+ `tarif`="'.$aSDATA['tarif'].'",
+ `user`="'.$user['id'].'",
+ `address`="'.$aSDATA['address'].'",
+ `port`="'.$aSDATA['port'].'",
+ `game`="csgo",
+ `slots`="'.$aSDATA['slots'].'",
+ `slots_start`="'.$aSDATA['slots'].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$aSDATA['pack'].'",
+ `plugins_use`="'.$aSDATA['plugins'].'",
+ `ftp_use`="'.$aSDATA['ftp'].'",
+ `console_use`="'.$aSDATA['console'].'",
+ `stats_use`="'.$aSDATA['stats'].'",
+ `copy_use`="'.$aSDATA['copy'].'",
+ `web_use`="'.$aSDATA['web'].'",
+ `tickrate`="'.$aSDATA['tickrate'].'",
+ `map_start`="'.$aSDATA['map'].'",
+ `vac`="1",
+ `hdd`="'.$aSDATA['hdd'].'",
+ `time`="'.$aSDATA['time'].'",
+ `date`="'.$start_point.'",
+ `test`="'.$aSDATA['test'].'",
+ `ram`="'.$aSDATA['ram'].'",
+ `core_fix`="'.$aSDATA['core_fix'].'",
+ `core_fix_one`="'.$aSDATA['core_fix_one'].'",
+ `autostop`="'.$aSDATA['autostop'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($aSDATA['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64djs($aSDATA['plugins_install']);
+
+ if(isset($aPlugins[$aSDATA['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$aSDATA['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$aSDATA['sum']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Запись получения тестового периода
+ if($aSDATA['test'])
+ {
+ $sql->query('INSERT INTO `tests` set `server`="'.$id.'", `unit`="'.$aSDATA['unit'].'", `game`="csgo", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_test'), array('id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="0"');
+ }else{
+ // Реф. система
+ games::part($user['id'], $aSDATA['sum']);
+
+ // Запись логов
+ if(!is_array($aSDATA['promo']))
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ else{
+ $sql->query('UPDATE `servers` set `benefit`="'.$aSDATA['time'].'" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('INSERT INTO `promo_use` set `promo`="'.$aSDATA['promo']['id'].'", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_promo'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'promo' => $aSDATA['promo']['cod'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ }
+ }
+
+ return $id;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/csgo/tarif.php b/system/library/games/csgo/tarif.php
new file mode 100644
index 0000000..0aba2f5
--- /dev/null
+++ b/system/library/games/csgo/tarif.php
@@ -0,0 +1,225 @@
+get('extend', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['tickrate'].' TickRate');
+ $html->set('tarif', $tarif_name);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function extend_sp($server, $tarif, $sid)
+ {
+ global $cfg, $sql, $html, $start_point;
+
+ tarifs::extend_address($server['game'], $sid);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ $sum = $tarif['slots'] ? $aPrice[array_search($server['tickrate'], $aTICK)] : $aPrice[array_search($server['tickrate'], $aTICK)]*$server['slots'];
+
+ $html->get('extend_sp', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('date', $server['time'] > $start_point ? 'Сервер продлен до: '.date('d.m.Y', $server['time']) : 'Текущая дата: '.date('d.m.Y', $start_point));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['tickrate'].' TickRate');
+ $html->set('tarif', $tarif['name']);
+ $html->set('sum', $sum);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function plan($server, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ $sql->query('SELECT `tickrate`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $tarif = $sql->get();
+
+ $options = 'Выберете тарифный план ';
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTick = explode(':', $tarif['tickrate']);
+
+ // Если есть выбор
+ if(count($aTick) > 1)
+ {
+ // Удалить при наличии tickrate сервера из выбора
+ if(in_array($server['tickrate'], $aTick))
+ unset($aTick[array_search($server['tickrate'], $aTick)]);
+
+ foreach($aTick as $index => $tickrate)
+ $options .= ''
+ .$tickrate.' TickRate '
+ .'('.$aPrice[$index].' '.$cfg['currency'].'/слот | '
+ .($aPrice[$index]*$server['slots']).' '.$cfg['currency'].'/месяц)'
+ .' ';
+ }else
+ return NULL;
+
+ $html->get('plan', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('info', $server['tickrate'].' TickRate');
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit($server, $unit_name, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ if(!$cfg['change_unit'][$server['game']])
+ return NULL;
+
+ $tarifs = $sql->query('SELECT `unit`, `tickrate` FROM `tarifs` WHERE `game`="'.$server['game'].'" AND `name`="'.$tarif_name.'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num($tarifs))
+ return NULL;
+
+ $units = 0;
+
+ $options = 'Выберете новую локацию ';
+
+ while($tarif = $sql->get($tarifs))
+ {
+ if(!in_array($server['tickrate'], explode(':', $tarif['tickrate'])))
+ continue;
+
+ $sql->query('SELECT `id`, `name` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $unit = $sql->get();
+
+ $options .= ''.$unit['name'].' ';
+
+ $units+=1;
+ }
+
+ if(!$units)
+ return NULL;
+
+ $html->get('unit', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['tickrate'].' TickRate');
+ $html->set('unit', $unit_name);
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit_new($tarif, $unit, $server, $mcache)
+ {
+ global $ssh, $sql, $user, $start_point;
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Директория сборки
+ $path = $tarif['path'].$tarif['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Пользователь сервера
+ $uS = 'server'.$server['uid'];
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -d '.$install.' -g servers -u '.$server['uid'].' '.$uS.';' // Создание пользователя сервера на локации
+ .'chown '.$uS.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u '.$uS.' screen -dmS i_'.$server['uid'].' cp -r '.$path.'/. .'); // Копирование файлов сборки для сервера
+
+ $address = explode(':', $server['address']);
+
+ $fix_one = $tarif['core_fix'] ? 1 : 0;
+
+ // Обновление данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `unit`="'.$tarif['unit'].'",
+ `tarif`="'.$tarif['id'].'",
+ `address`="'.$server['address'].'",
+ `port`="'.$address[1].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$tarif['pack'].'",
+ `map_start`="'.$tarif['map'].'",
+ `vac`="1",
+ `hdd`="'.$tarif['hdd'].'",
+ `time`="'.$tarif['time'].'",
+ `autostop`="'.$tarif['autostop'].'",
+ `core_fix`="'.$tarif['core_fix'].'",
+ `core_fix_one`="'.$fix_one.'",
+ `reinstall`="'.$start_point.'" WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($tarif['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64js($tarif['plugins_install']);
+
+ if(isset($aPlugins[$tarif['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$tarif['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$server['id'].'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/css/action.php b/system/library/games/css/action.php
new file mode 100644
index 0000000..334800b
--- /dev/null
+++ b/system/library/games/css/action.php
@@ -0,0 +1,189 @@
+query('SELECT `uid`, `unit`, `tarif`, `game`, `address`, `slots_start`, `name`, `tickrate`, `map_start`, `vac`, `time_start`, `core_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] AND !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Проверка наличия стартовой карты
+ $ssh->set('cd '.$tarif['install'].$server['uid'].'/cstrike/maps/ && ls | grep .bsp | grep -v .bsp.');
+
+ if($server['map_start'] != '' AND !in_array($server['map_start'], str_replace('.bsp', '', explode("\n", $ssh->get()))))
+ return array('e' => sys::updtext(sys::text('servers', 'nomap'), array('map' => $server['map_start'].'.bsp')));
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Античит VAC
+ $vac = $server['vac'] == 0 ? '-insecure' : '-secure';
+
+ // Боты
+ $bots = $cfg['bots'][$server['game']] ? '' : '-nobots';
+
+ // TV
+ $tv = $server['tv'] ? '+tv_enable 1 +tv_maxclients 30 +tv_port '.($port+10000) : '-nohltv';
+
+ // Параметры запуска
+ $bash = './srcds_run -debug -game cstrike -norestart -condebug console.log -tickrate '.$server['tickrate'].' +servercfgfile server.cfg +map \''.$server['map_start'].'\' +maxplayers '.$server['slots_start'].' +ip '.$ip.' +port '.$port.' -sv_lan 0 '.$vac.' '.$bots.' '.$tv;
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd '.$tarif['install'].$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'sudo -u server'.$server['uid'].' mkdir -p cstrike/oldstart;' // Создание папки логов
+ .'cat cstrike/console.log >> cstrike/oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm cstrike/console.log; rm cstrike/oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 start.sh;' // Обновление владельца файла start.sh
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c "./start.sh"'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function update($id)
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `uid`, `unit`, `tarif`, `game`, `name`, `ftp`, `update`, `core_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ // Проверка времени обновления
+ $update = $server['update']+$cfg['update'][$server['game']]*60;
+
+ if($update > $start_point AND $user['group'] != 'admin')
+ return array('e' => sys::updtext(sys::text('servers', 'update'), array('time' => sys::date('max', $update))));
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install`, `plugins_install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] AND !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => 'Не удается выполнить операцию, нет свободного потока.');
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ $ssh->set('cd '.$cfg['steamcmd'].' && sudo -u server'.$server['uid'].' '.$taskset.' sh -c "screen -dmS u_'.$server['uid'].' ./steamcmd.sh +login anonymous +force_install_dir "'.$install.'" +app_update 232330 +quit"');
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="update", `update`="'.$start_point.'", `core_use`="'.$core.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Логирование
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'update').'", `time`="'.$start_point.'"');
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'update', 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/css/rcon.php b/system/library/games/css/rcon.php
new file mode 100644
index 0000000..6a97e06
--- /dev/null
+++ b/system/library/games/css/rcon.php
@@ -0,0 +1,112 @@
+Connect($ip, $port, 3, SourceQuery::SOURCE);
+
+ $sq->SetRconPassword(rcon::rcon_passwd($server));
+
+ $out = $sq->Rcon($cmd);
+
+ $sq->Disconnect();
+
+ return $out;
+ }
+
+ public static function players($data)
+ {
+ $aPlayers = array();
+ $n = 1;
+
+ $lines = explode("\n", $data);
+
+ foreach($lines as $line)
+ {
+ if(strpos($line, '#') === FALSE)
+ continue;
+
+ $start = strpos($line, '"')+1;
+ $end = strrpos($line, '"');
+
+ $userid = sys::int(substr($line, 0, $start));
+
+ $name = htmlspecialchars(substr($line, $start, $end-$start));
+
+ $line = trim(substr($line, $end + 1));
+
+ $aData = array_values(array_diff(explode(' ', $line), array('', ' ')));
+
+ $steamid = trim($aData[0]);
+ $ip = trim(sys::first(explode(':', $aData[5])));
+
+ if((sys::valid($steamid, 'steamid') AND sys::valid($steamid, 'steamid3')) || sys::valid($ip, 'ip'))
+ continue;
+
+ $aPlayers[$n]['userid'] = $userid;
+ $aPlayers[$n]['name'] = $name;
+ $aPlayers[$n]['steamid'] = $steamid;
+ $aPlayers[$n]['time'] = trim($aData[1]);
+ $aPlayers[$n]['ping'] = trim($aData[2]);
+ $aPlayers[$n]['ip'] = $ip;
+
+ $whois = rcon::country($ip);
+
+ $aPlayers[$n]['ico'] = $whois['ico'];
+ $aPlayers[$n]['country'] = $whois['name'];
+
+ $n+=1;
+ }
+
+ return $aPlayers;
+ }
+
+ public static function rcon_passwd($server)
+ {
+ global $cfg, $sql, $user;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $ssh->set('cat '.$tarif['install'].'/'.$server['uid'].'/cstrike/cfg/server.cfg | grep rcon_password');
+ $get = explode(' ', str_replace('"', '', trim($ssh->get())));
+ $rcon = trim(end($get));
+
+ if(!isset($rcon{0}))
+ sys::outjs(array('r' => 'Необходимо установить rcon пароль (rcon_password).', 'url' => $cfg['http'].'servers/id/'.$server['id'].'/section/settings/subsection/server'), $nmch);
+
+ return $rcon;
+ }
+
+ public static function country($ip)
+ {
+ global $SxGeo;
+
+ $cData = $SxGeo->getCityFull($ip);
+ $ico = sys::country($cData['country']['iso']);
+
+ return array('ico' => $ico, 'name' => empty($cData['country']['name_ru']) ? 'Не определена' : $cData['country']['name_ru']);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/css/scan.php b/system/library/games/css/scan.php
new file mode 100644
index 0000000..ca76ba3
--- /dev/null
+++ b/system/library/games/css/scan.php
@@ -0,0 +1,140 @@
+get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $info = scan::info($sq, $id);
+
+ $sql->query('SELECT `game`, `name`, `map`, `online`, `players`, `status`, `time`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $out['time'] = 'Арендован до: '.date('d.m.Y - H:i', $server['time']);
+
+ if($server['status'] == 'overdue')
+ $out['time_end'] = 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400);
+ else
+ $out['time_end'] = 'Осталось: '.sys::date('min', $server['time']);
+
+ if(!$info['status'])
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ if($players_get)
+ $players = scan::info($sq, $id, true);
+
+ $out['name'] = htmlspecialchars($info['name']);
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = $info['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working');
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', $player['name']);
+ $html->set('score', $player['score']);
+ $html->set('time', $player['time']);
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function info($sq, $id, $pl = false)
+ {
+ global $sql;
+
+ $sql->query('SELECT `address` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $sq->Connect($ip, $port, 1, SourceQuery::SOURCE);
+
+ if($pl)
+ {
+ $players = $sq->GetPlayers();
+
+ $i = 1;
+ $data = array();
+
+ foreach($players as $n => $player)
+ {
+ $data[$i]['i'] = $i;
+ $data[$i]['name'] = $player['Name'] == '' ? 'Подключается' : $player['Name'];
+ $data[$i]['score'] = $player['Frags'];
+ $data[$i]['time'] = $player['TimeF'];
+
+ $i+=1;
+ }
+
+ return $data;
+ }
+
+ $data = $sq->GetInfo();
+
+ $server['name'] = $data['HostName'];
+ $server['map'] = $data['Map'];
+ $server['online'] = $data['Players'];
+ $server['status'] = strlen($server['map']) > 3 ? true : false;
+
+ return $server;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/css/service.php b/system/library/games/css/service.php
new file mode 100644
index 0000000..efc9a89
--- /dev/null
+++ b/system/library/games/css/service.php
@@ -0,0 +1,369 @@
+query('SELECT `address`, `test` FROM `units` WHERE `id`="'.$aData['unit'].'" AND `css`="1" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Локация не найдена.'));
+
+ $unit = $sql->get();
+
+ // Проверка тарифа
+ $sql->query('SELECT `id` FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" AND `unit`="'.$aData['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Тариф не найден.'));
+
+ $sql->query('SELECT '
+ .'`slots_min`,'
+ .'`slots_max`,'
+ .'`port_min`,'
+ .'`port_max`,'
+ .'`hostname`,'
+ .'`packs`,'
+ .'`tickrate`,'
+ .'`time`,'
+ .'`test`,'
+ .'`tests`,'
+ .'`discount`,'
+ .'`map`,'
+ .'`ftp`,'
+ .'`plugins`,'
+ .'`console`,'
+ .'`stats`,'
+ .'`copy`,'
+ .'`web`,'
+ .'`plugins_install`,'
+ .'`hdd`,'
+ .'`autostop`,'
+ .'`core_fix`,'
+ .'`ip`,'
+ .'`price`'
+ .' FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ $tarif = $sql->get();
+
+ // Проверка сборки
+ if(!array_key_exists($aData['pack'], sys::b64djs($tarif['packs'], true)))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ // Проверка TickRate
+ if(!in_array($aData['tickrate'], explode(':', $tarif['tickrate'])))
+ sys::outjs(array('e' => 'Переданные данные tickrate неверны.'));
+
+ $test = 0;
+
+ // Проверка периода на тест
+ if($aData['test'])
+ {
+ if(!$tarif['test'] || !$unit['test'])
+ sys::outjs(array('e' => 'Тестовый период недоступен.'));
+
+
+ // Проверка на повторный запрос
+ $sql->query('SELECT `id`, `game` FROM `tests` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $test_info = $sql->get();
+
+ if(!$cfg['tests']['game'] || $test_info['game'] == 'css')
+ sys::outjs(array('e' => 'Тестовый период предоставляется один раз.'));
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" AND `test`="1" LIMIT 1');
+ if($sql->num() AND !$cfg['tests']['sametime'])
+ sys::outjs(array('e' => 'Чтобы получить тестовый период другой игры, дождитесь окончания текущего.'));
+ }
+
+ // Проверка наличия мест на локации
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$unit['test']);
+ if($sql->num() == $unit['test'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода нет.'));
+
+ // Проверка наличия мест для выбранного тарифа
+ $sql->query('SELECT `id` FROM `servers` WHERE `tarif`="'.$aData['tarif'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$tarif['tests']);
+ if($sql->num() == $tarif['tests'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода выбранного тарифа нет.'));
+
+ $test = 1;
+ }else
+ // Проверка периода
+ if(!$cfg['settlement_period'] AND !in_array($aData['time'], explode(':', $tarif['time'])))
+ sys::outjs(array('e' => 'Переданные данные периода неверны.'));
+
+ // Проверка слот
+ if($aData['slots'] < $tarif['slots_min'] || $aData['slots'] > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданные данные слот неверны.'));
+
+ // Определение цены
+ $aPrice = explode(':', $tarif['price']);
+ $price = $aPrice[array_search($aData['tickrate'], explode(':', $tarif['tickrate']))];
+
+ // Определение суммы
+ if($cfg['settlement_period'])
+ {
+ // Цена аренды за расчетный период
+ $sum = games::define_sum($tarif['discount'], $price, $aData['slots'], $start_point);
+
+ $aData['time'] = games::define_period('buy', params::$aDayMonth);
+ }else
+ $sum = games::define_sum($tarif['discount'], $price, $aData['slots'], $aData['time']);
+
+ // Проверка промо-кода
+ $promo = games::define_promo(
+ $aData['promo'],
+ array(
+ 'tarif' => $aData['tarif'],
+ 'tickrate' => $aData['tickrate'],
+ 'slots' => $aData['slots'],
+ 'time' => $aData['time'],
+ 'user' => $user['id']
+ ),
+ $tarif['discount'],
+ $sum
+ );
+
+ $days = $aData['time']; // Кол-во дней аренды
+
+ // Использование промо-кода
+ if(is_array($promo))
+ {
+ if(array_key_exists('sum', $promo))
+ $sum = $promo['sum'];
+ else
+ $days += $promo['days']; // Кол-во дней аренды с учетом подарочных (промо-код)
+ }
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']));
+
+ // Выделенный адрес игрового сервера
+ if(!empty($tarif['ip']))
+ {
+ $aIp = explode(':', $tarif['ip']);
+
+ $ip = false;
+ $port = params::$aDefPort['css'];
+
+ // Проверка наличия свободного адреса
+ foreach($aIp as $adr)
+ {
+ $adr = trim($adr);
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `address` LIKE "'.$adr.':%" LIMIT 1');
+ if(!$sql->num())
+ {
+ $ip = $adr;
+
+ break;
+ }
+ }
+ }else{
+ $ip = sys::first(explode(':', $unit['address']));
+ $port = false;
+
+ // Проверка наличия свободного порта
+ for($tarif['port_min']; $tarif['port_min'] <= $tarif['port_max']; $tarif['port_min']+=1)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND (`address`="'.$ip.':'.$tarif['port_min'].'" OR `port`="'.$tarif['port_min'].'") LIMIT 1');
+ if(!$sql->num())
+ {
+ $port = $tarif['port_min'];
+
+ break;
+ }
+ }
+ }
+
+ if(!$ip || !$port)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+
+ if($test)
+ $aData['time'] = games::time($start_point, $tarif['test']);
+ else
+ $aData['time'] = games::time($start_point, $days);
+
+ $fix_one = 0;
+ $core = 0;
+
+ if($tarif['core_fix'] != '')
+ {
+ $aCore = explode(',', $tarif['core_fix']);
+
+ foreach($aCore as $cpu)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `tarif`="'.$aData['tarif'].'" AND `core_fix`="'.$cpu.'" AND `core_fix_one`="1" LIMIT 1');
+
+ if($sql->num())
+ continue;
+
+ $fix_one = 1;
+ $core = $cpu;
+
+ break;
+ }
+
+ if(!$core)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+ }
+
+ $ram = $tarif['param_fix'] ? $aData['ram'] : $cfg['ram']['css']*$aSDATA['slots'];
+
+ // Массив данных
+ $aSDATA = array(
+ 'unit' => $aData['unit'], // идентификатор локации
+ 'tarif' => $aData['tarif'], // идентификатор тарифа
+ 'ram' => $ram, // значение ram
+ 'param_fix' => $tarif['param_fix'], // фиксированные параметры
+ 'tickrate' => $aData['tickrate'], // значение tickrate
+ 'pack' => $aData['pack'], // Выбранная сборка для установки
+ 'time' => $aData['time'], // Время аренды
+ 'days' => $days, // Число дней
+ 'sum' => $sum, // Сумма списания
+ 'test' => $test, // тестовый период
+ 'address' => $ip.':'.$port, // адрес игрового сервера
+ 'port' => $port, // порт игрового сервера
+ 'slots' => $aData['slots'], // Кол-во слот
+ 'map' => $tarif['map'], // Фиксированное значение слот
+ 'autostop' => $tarif['autostop'], // Выключение при 0 онлайне
+ 'ftp' => $tarif['ftp'], // Использование ftp
+ 'plugins' => $tarif['plugins'], // Использование плагинов
+ 'console' => $tarif['console'], // Использование консоли
+ 'stats' => $tarif['stats'], // Использование графиков (ведение статистики)
+ 'copy' => $tarif['copy'], // Использование резервных копий
+ 'web' => $tarif['web'], // Использование доп услуг
+ 'plugins_install' => $tarif['plugins_install'], // Список установленных плагинов
+ 'hdd' => $tarif['hdd'], // Дисковое пространство
+ 'core_fix' => $core, // Выделенный поток
+ 'core_fix_one' => $fix_one, // Выделенный поток
+ 'promo' => $promo // Использование промо-кода
+ );
+
+ return $aSDATA;
+ }
+
+ public static function install($aSDATA = array())
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ // Массив данных локации (адрес,пароль)
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$aSDATA['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Массив данных тарифа (путь сборки,путь установки)
+ $sql->query('SELECT `path`, `install`, `hostname` FROM `tarifs` WHERE `id`="'.$aSDATA['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Получение идентификаторов игрового сервера
+ $sql->query('INSERT INTO `servers` set uid="1"');
+ $id = $sql->id();
+ $uid = $id+1000;
+
+ // Директория сборки
+ $path = $tarif['path'].$aSDATA['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$uid;
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -s /bin/false -d '.$install.' -g servers -u '.$uid.' server'.$uid.';' // Создание пользователя сервера на локации
+ .'chown server'.$uid.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u server'.$uid.' screen -dmS i_'.$uid.' sh -c "cp -r '.$path.'/. .;' // Копирование файлов сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame['css'].'"');
+
+ // Запись данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `uid`="'.$uid.'",
+ `unit`="'.$aSDATA['unit'].'",
+ `tarif`="'.$aSDATA['tarif'].'",
+ `user`="'.$user['id'].'",
+ `address`="'.$aSDATA['address'].'",
+ `port`="'.$aSDATA['port'].'",
+ `game`="css",
+ `slots`="'.$aSDATA['slots'].'",
+ `slots_start`="'.$aSDATA['slots'].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$aSDATA['pack'].'",
+ `plugins_use`="'.$aSDATA['plugins'].'",
+ `ftp_use`="'.$aSDATA['ftp'].'",
+ `console_use`="'.$aSDATA['console'].'",
+ `stats_use`="'.$aSDATA['stats'].'",
+ `copy_use`="'.$aSDATA['copy'].'",
+ `web_use`="'.$aSDATA['web'].'",
+ `tickrate`="'.$aSDATA['tickrate'].'",
+ `map_start`="'.$aSDATA['map'].'",
+ `vac`="1",
+ `hdd`="'.$aSDATA['hdd'].'",
+ `time`="'.$aSDATA['time'].'",
+ `date`="'.$start_point.'",
+ `test`="'.$aSDATA['test'].'",
+ `ram`="'.$aSDATA['ram'].'",
+ `core_fix`="'.$aSDATA['core_fix'].'",
+ `core_fix_one`="'.$aSDATA['core_fix_one'].'",
+ `autostop`="'.$aSDATA['autostop'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($aSDATA['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64djs($aSDATA['plugins_install']);
+
+ if(isset($aPlugins[$aSDATA['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$aSDATA['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$aSDATA['sum']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Запись получения тестового периода
+ if($aSDATA['test'])
+ {
+ $sql->query('INSERT INTO `tests` set `server`="'.$id.'", `unit`="'.$aSDATA['unit'].'", `game`="css", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_test'), array('id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="0"');
+ }else{
+ // Реф. система
+ games::part($user['id'], $aSDATA['sum']);
+
+ // Запись логов
+ if(!is_array($aSDATA['promo']))
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ else{
+ $sql->query('UPDATE `servers` set `benefit`="'.$aSDATA['time'].'" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('INSERT INTO `promo_use` set `promo`="'.$aSDATA['promo']['id'].'", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_promo'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'promo' => $aSDATA['promo']['cod'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ }
+ }
+
+ return $id;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/css/tarif.php b/system/library/games/css/tarif.php
new file mode 100644
index 0000000..0aba2f5
--- /dev/null
+++ b/system/library/games/css/tarif.php
@@ -0,0 +1,225 @@
+get('extend', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['tickrate'].' TickRate');
+ $html->set('tarif', $tarif_name);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function extend_sp($server, $tarif, $sid)
+ {
+ global $cfg, $sql, $html, $start_point;
+
+ tarifs::extend_address($server['game'], $sid);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ $sum = $tarif['slots'] ? $aPrice[array_search($server['tickrate'], $aTICK)] : $aPrice[array_search($server['tickrate'], $aTICK)]*$server['slots'];
+
+ $html->get('extend_sp', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('date', $server['time'] > $start_point ? 'Сервер продлен до: '.date('d.m.Y', $server['time']) : 'Текущая дата: '.date('d.m.Y', $start_point));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['tickrate'].' TickRate');
+ $html->set('tarif', $tarif['name']);
+ $html->set('sum', $sum);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function plan($server, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ $sql->query('SELECT `tickrate`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $tarif = $sql->get();
+
+ $options = 'Выберете тарифный план ';
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTick = explode(':', $tarif['tickrate']);
+
+ // Если есть выбор
+ if(count($aTick) > 1)
+ {
+ // Удалить при наличии tickrate сервера из выбора
+ if(in_array($server['tickrate'], $aTick))
+ unset($aTick[array_search($server['tickrate'], $aTick)]);
+
+ foreach($aTick as $index => $tickrate)
+ $options .= ''
+ .$tickrate.' TickRate '
+ .'('.$aPrice[$index].' '.$cfg['currency'].'/слот | '
+ .($aPrice[$index]*$server['slots']).' '.$cfg['currency'].'/месяц)'
+ .' ';
+ }else
+ return NULL;
+
+ $html->get('plan', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('info', $server['tickrate'].' TickRate');
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit($server, $unit_name, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ if(!$cfg['change_unit'][$server['game']])
+ return NULL;
+
+ $tarifs = $sql->query('SELECT `unit`, `tickrate` FROM `tarifs` WHERE `game`="'.$server['game'].'" AND `name`="'.$tarif_name.'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num($tarifs))
+ return NULL;
+
+ $units = 0;
+
+ $options = 'Выберете новую локацию ';
+
+ while($tarif = $sql->get($tarifs))
+ {
+ if(!in_array($server['tickrate'], explode(':', $tarif['tickrate'])))
+ continue;
+
+ $sql->query('SELECT `id`, `name` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $unit = $sql->get();
+
+ $options .= ''.$unit['name'].' ';
+
+ $units+=1;
+ }
+
+ if(!$units)
+ return NULL;
+
+ $html->get('unit', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['tickrate'].' TickRate');
+ $html->set('unit', $unit_name);
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit_new($tarif, $unit, $server, $mcache)
+ {
+ global $ssh, $sql, $user, $start_point;
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Директория сборки
+ $path = $tarif['path'].$tarif['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Пользователь сервера
+ $uS = 'server'.$server['uid'];
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -d '.$install.' -g servers -u '.$server['uid'].' '.$uS.';' // Создание пользователя сервера на локации
+ .'chown '.$uS.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u '.$uS.' screen -dmS i_'.$server['uid'].' cp -r '.$path.'/. .'); // Копирование файлов сборки для сервера
+
+ $address = explode(':', $server['address']);
+
+ $fix_one = $tarif['core_fix'] ? 1 : 0;
+
+ // Обновление данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `unit`="'.$tarif['unit'].'",
+ `tarif`="'.$tarif['id'].'",
+ `address`="'.$server['address'].'",
+ `port`="'.$address[1].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$tarif['pack'].'",
+ `map_start`="'.$tarif['map'].'",
+ `vac`="1",
+ `hdd`="'.$tarif['hdd'].'",
+ `time`="'.$tarif['time'].'",
+ `autostop`="'.$tarif['autostop'].'",
+ `core_fix`="'.$tarif['core_fix'].'",
+ `core_fix_one`="'.$fix_one.'",
+ `reinstall`="'.$start_point.'" WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($tarif['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64js($tarif['plugins_install']);
+
+ if(isset($aPlugins[$tarif['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$tarif['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$server['id'].'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/cssold/action.php b/system/library/games/cssold/action.php
new file mode 100644
index 0000000..3786d72
--- /dev/null
+++ b/system/library/games/cssold/action.php
@@ -0,0 +1,116 @@
+query('SELECT `uid`, `unit`, `tarif`, `game`, `address`, `slots_start`, `name`, `tickrate`, `fps`, `map_start`, `vac`, `time_start`, `core_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] AND !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Проверка наличия стартовой карты
+ $ssh->set('cd '.$tarif['install'].$server['uid'].'/cstrike/maps/ && ls | grep .bsp | grep -v .bsp.');
+
+ if($server['map_start'] != '' AND !in_array($server['map_start'], str_replace('.bsp', '', explode("\n", $ssh->get()))))
+ return array('e' => sys::updtext(sys::text('servers', 'nomap'), array('map' => $server['map_start'].'.bsp')));
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Античит VAC
+ $vac = $server['vac'] == 0 ? '-insecure' : '-secure';
+
+ // Боты
+ $bots = $cfg['bots'][$server['game']] ? '' : '-nobots';
+
+ // TV
+ $tv = $server['tv'] ? '+tv_enable 1 +tv_maxclients 30 +tv_port '.($port+10000) : '-nohltv';
+
+ // FPS
+ $fps = $server['fps']+$cfg['fpsplus'];
+
+ // Параметры запуска
+ $bash = './srcds_run -debug -game cstrike -norestart -condebug console.log -tickrate '.$server['tickrate'].' +fps_egp '.$fps.' +servercfgfile server.cfg +map \''.$server['map_start'].'\' +maxplayers '.$server['slots_start'].' +ip '.$ip.' +port '.$port.' +sv_lan 0 -nomaster -localcser '.$vac.' '.$bots.' '.$tv;
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd '.$tarif['install'].$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'mv console.log oldstart.log;' // Перемещение лога предыдущего запуска в файл oldstart.log
+ .'sudo -u server'.$server['uid'].' mkdir -p cstrike/oldstart;' // Создание папки логов
+ .'cat cstrike/console.log >> cstrike/oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm cstrike/console.log; rm cstrike/oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 start.sh;' // Обновление владельца файла start.sh
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c "./start.sh"'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/cssold/rcon.php b/system/library/games/cssold/rcon.php
new file mode 100644
index 0000000..40627de
--- /dev/null
+++ b/system/library/games/cssold/rcon.php
@@ -0,0 +1,112 @@
+Connect($ip, $port, 3, SourceQuery::SOURCE);
+
+ $sq->SetRconPassword(rcon::rcon_passwd($server));
+
+ $out = $sq->Rcon($cmd);
+
+ $sq->Disconnect();
+
+ return $out;
+ }
+
+ public static function players($data)
+ {
+ $aPlayers = array();
+ $n = 1;
+
+ $lines = explode("\n", $data);
+
+ foreach($lines as $line)
+ {
+ if(strpos($line, '#') === FALSE)
+ continue;
+
+ $start = strpos($line, '"')+1;
+ $end = strrpos($line, '"');
+
+ $userid = sys::int(substr($line, 0, $start));
+
+ $name = htmlspecialchars(substr($line, $start, $end-$start));
+
+ $line = trim(substr($line, $end + 1));
+
+ $aData = array_values(array_diff(explode(' ', $line), array('', ' ')));
+
+ $steamid = trim($aData[0]);
+ $ip = trim(sys::first(explode(':', $aData[5])));
+
+ if(sys::valid($steamid, 'steamid') || sys::valid($ip, 'ip'))
+ continue;
+
+ $aPlayers[$n]['userid'] = $userid;
+ $aPlayers[$n]['name'] = $name;
+ $aPlayers[$n]['steamid'] = $steamid;
+ $aPlayers[$n]['time'] = trim($aData[1]);
+ $aPlayers[$n]['ping'] = trim($aData[2]);
+ $aPlayers[$n]['ip'] = $ip;
+
+ $whois = rcon::country($ip);
+
+ $aPlayers[$n]['ico'] = $whois['ico'];
+ $aPlayers[$n]['country'] = $whois['name'];
+
+ $n+=1;
+ }
+
+ return $aPlayers;
+ }
+
+ public static function rcon_passwd($server)
+ {
+ global $cfg, $sql, $user;
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $ssh->set('cat '.$tarif['install'].'/'.$server['uid'].'/cstrike/cfg/server.cfg | grep rcon_password');
+ $get = explode(' ', str_replace('"', '', trim($ssh->get())));
+ $rcon = trim(end($get));
+
+ if(!isset($rcon{0}))
+ sys::outjs(array('r' => 'Необходимо установить rcon пароль (rcon_password).', 'url' => $cfg['http'].'servers/id/'.$server['id'].'/section/settings/subsection/server'), $nmch);
+
+ return $rcon;
+ }
+
+ public static function country($ip)
+ {
+ global $SxGeo;
+
+ $cData = $SxGeo->getCityFull($ip);
+ $ico = sys::country($cData['country']['iso']);
+
+ return array('ico' => $ico, 'name' => empty($cData['country']['name_ru']) ? 'Не определена' : $cData['country']['name_ru']);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/cssold/scan.php b/system/library/games/cssold/scan.php
new file mode 100644
index 0000000..ca76ba3
--- /dev/null
+++ b/system/library/games/cssold/scan.php
@@ -0,0 +1,140 @@
+get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $info = scan::info($sq, $id);
+
+ $sql->query('SELECT `game`, `name`, `map`, `online`, `players`, `status`, `time`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $out['time'] = 'Арендован до: '.date('d.m.Y - H:i', $server['time']);
+
+ if($server['status'] == 'overdue')
+ $out['time_end'] = 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400);
+ else
+ $out['time_end'] = 'Осталось: '.sys::date('min', $server['time']);
+
+ if(!$info['status'])
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ if($players_get)
+ $players = scan::info($sq, $id, true);
+
+ $out['name'] = htmlspecialchars($info['name']);
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = $info['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working');
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', $player['name']);
+ $html->set('score', $player['score']);
+ $html->set('time', $player['time']);
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function info($sq, $id, $pl = false)
+ {
+ global $sql;
+
+ $sql->query('SELECT `address` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $sq->Connect($ip, $port, 1, SourceQuery::SOURCE);
+
+ if($pl)
+ {
+ $players = $sq->GetPlayers();
+
+ $i = 1;
+ $data = array();
+
+ foreach($players as $n => $player)
+ {
+ $data[$i]['i'] = $i;
+ $data[$i]['name'] = $player['Name'] == '' ? 'Подключается' : $player['Name'];
+ $data[$i]['score'] = $player['Frags'];
+ $data[$i]['time'] = $player['TimeF'];
+
+ $i+=1;
+ }
+
+ return $data;
+ }
+
+ $data = $sq->GetInfo();
+
+ $server['name'] = $data['HostName'];
+ $server['map'] = $data['Map'];
+ $server['online'] = $data['Players'];
+ $server['status'] = strlen($server['map']) > 3 ? true : false;
+
+ return $server;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/cssold/service.php b/system/library/games/cssold/service.php
new file mode 100644
index 0000000..80cab04
--- /dev/null
+++ b/system/library/games/cssold/service.php
@@ -0,0 +1,378 @@
+query('SELECT `address`, `test` FROM `units` WHERE `id`="'.$aData['unit'].'" AND `cssold`="1" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Локация не найдена.'));
+
+ $unit = $sql->get();
+
+ // Проверка тарифа
+ $sql->query('SELECT `id` FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" AND `unit`="'.$aData['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Тариф не найден.'));
+
+ $sql->query('SELECT '
+ .'`slots_min`,'
+ .'`slots_max`,'
+ .'`port_min`,'
+ .'`port_max`,'
+ .'`hostname`,'
+ .'`packs`,'
+ .'`fps`,'
+ .'`tickrate`,'
+ .'`time`,'
+ .'`test`,'
+ .'`tests`,'
+ .'`discount`,'
+ .'`map`,'
+ .'`ftp`,'
+ .'`plugins`,'
+ .'`console`,'
+ .'`stats`,'
+ .'`copy`,'
+ .'`web`,'
+ .'`plugins_install`,'
+ .'`hdd`,'
+ .'`autostop`,'
+ .'`core_fix`,'
+ .'`ip`,'
+ .'`price`'
+ .' FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ $tarif = $sql->get();
+
+ // Проверка сборки
+ if(!array_key_exists($aData['pack'], sys::b64djs($tarif['packs'], true)))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ // Проверка FPS
+ if(!in_array($aData['fps'], explode(':', $tarif['fps'])))
+ sys::outjs(array('e' => 'Переданные данные fps неверны.'));
+
+ // Проверка TickRate
+ if(!in_array($aData['tickrate'], explode(':', $tarif['tickrate'])))
+ sys::outjs(array('e' => 'Переданные данные tickrate неверны.'));
+
+ $test = 0;
+
+ // Проверка периода на тест
+ if($aData['test'])
+ {
+ if(!$tarif['test'] || !$unit['test'])
+ sys::outjs(array('e' => 'Тестовый период недоступен.'));
+
+
+ // Проверка на повторный запрос
+ $sql->query('SELECT `id`, `game` FROM `tests` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $test_info = $sql->get();
+
+ if(!$cfg['tests']['game'] || $test_info['game'] == 'cssold')
+ sys::outjs(array('e' => 'Тестовый период предоставляется один раз.'));
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" AND `test`="1" LIMIT 1');
+ if($sql->num() AND !$cfg['tests']['sametime'])
+ sys::outjs(array('e' => 'Чтобы получить тестовый период другой игры, дождитесь окончания текущего.'));
+ }
+
+ // Проверка наличия мест на локации
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$unit['test']);
+ if($sql->num() == $unit['test'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода нет.'));
+
+ // Проверка наличия мест для выбранного тарифа
+ $sql->query('SELECT `id` FROM `servers` WHERE `tarif`="'.$aData['tarif'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$tarif['tests']);
+ if($sql->num() == $tarif['tests'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода выбранного тарифа нет.'));
+
+ $test = 1;
+ }else
+ // Проверка периода
+ if(!$cfg['settlement_period'] AND !in_array($aData['time'], explode(':', $tarif['time'])))
+ sys::outjs(array('e' => 'Переданные данные периода неверны.'));
+
+ // Проверка слот
+ if($aData['slots'] < $tarif['slots_min'] || $aData['slots'] > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданные данные слот неверны.'));
+
+ $aPrice = sys::b64djs($tarif['price'], true);
+
+ // Определение цены
+ $price = $aPrice[$aData['tickrate'].'_'.$aData['fps']];
+
+ // Определение суммы
+ if($cfg['settlement_period'])
+ {
+ // Цена аренды за расчетный период
+ $sum = games::define_sum($tarif['discount'], $price, $aData['slots'], $start_point);
+
+ $aData['time'] = games::define_period('buy', params::$aDayMonth);
+ }else
+ $sum = games::define_sum($tarif['discount'], $price, $aData['slots'], $aData['time']);
+
+ // Проверка промо-кода
+ $promo = games::define_promo(
+ $aData['promo'],
+ array(
+ 'tarif' => $aData['tarif'],
+ 'tickrate' => $aData['tickrate'],
+ 'fps' => $aData['fps'],
+ 'slots' => $aData['slots'],
+ 'time' => $aData['time'],
+ 'user' => $user['id']
+ ),
+ $tarif['discount'],
+ $sum
+ );
+
+ $days = $aData['time']; // Кол-во дней аренды
+
+ // Использование промо-кода
+ if(is_array($promo))
+ {
+ if(array_key_exists('sum', $promo))
+ $sum = $promo['sum'];
+ else
+ $days += $promo['days']; // Кол-во дней аренды с учетом подарочных (промо-код)
+ }
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']));
+
+ // Выделенный адрес игрового сервера
+ if(!empty($tarif['ip']))
+ {
+ $aIp = explode(':', $tarif['ip']);
+
+ $ip = false;
+ $port = params::$aDefPort['cssold'];
+
+ // Проверка наличия свободного адреса
+ foreach($aIp as $adr)
+ {
+ $adr = trim($adr);
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `address` LIKE "'.$adr.':%" LIMIT 1');
+ if(!$sql->num())
+ {
+ $ip = $adr;
+
+ break;
+ }
+ }
+ }else{
+ $ip = sys::first(explode(':', $unit['address']));
+ $port = false;
+
+ // Проверка наличия свободного порта
+ for($tarif['port_min']; $tarif['port_min'] <= $tarif['port_max']; $tarif['port_min']+=1)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND (`address`="'.$ip.':'.$tarif['port_min'].'" OR `port`="'.$tarif['port_min'].'") LIMIT 1');
+ if(!$sql->num())
+ {
+ $port = $tarif['port_min'];
+
+ break;
+ }
+ }
+ }
+
+ if(!$ip || !$port)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+
+ if($test)
+ $aData['time'] = games::time($start_point, $tarif['test']);
+ else
+ $aData['time'] = games::time($start_point, $days);
+
+ $fix_one = 0;
+ $core = 0;
+
+ if($tarif['core_fix'] != '')
+ {
+ $aCore = explode(',', $tarif['core_fix']);
+
+ foreach($aCore as $cpu)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `tarif`="'.$aData['tarif'].'" AND `core_fix`="'.$cpu.'" AND `core_fix_one`="1" LIMIT 1');
+
+ if($sql->num())
+ continue;
+
+ $fix_one = 1;
+ $core = $cpu;
+
+ break;
+ }
+
+ if(!$core)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+ }
+
+ $ram = $tarif['param_fix'] ? $aData['ram'] : $cfg['ram']['cssold']*$aSDATA['slots'];
+
+ // Массив данных
+ $aSDATA = array(
+ 'unit' => $aData['unit'], // идентификатор локации
+ 'tarif' => $aData['tarif'], // идентификатор тарифа
+ 'ram' => $ram, // значение ram
+ 'param_fix' => $tarif['param_fix'], // фиксированные параметры
+ 'tickrate' => $aData['tickrate'], // значение tickrate
+ 'fps' => $aData['fps'], // значение fps
+ 'pack' => $aData['pack'], // Выбранная сборка для установки
+ 'time' => $aData['time'], // Время аренды
+ 'days' => $days, // Число дней
+ 'sum' => $sum, // Сумма списания
+ 'test' => $test, // тестовый период
+ 'address' => $ip.':'.$port, // адрес игрового сервера
+ 'port' => $port, // порт игрового сервера
+ 'slots' => $aData['slots'], // Кол-во слот
+ 'map' => $tarif['map'], // Фиксированное значение слот
+ 'autostop' => $tarif['autostop'], // Выключение при 0 онлайне
+ 'ftp' => $tarif['ftp'], // Использование ftp
+ 'plugins' => $tarif['plugins'], // Использование плагинов
+ 'console' => $tarif['console'], // Использование консоли
+ 'stats' => $tarif['stats'], // Использование графиков (ведение статистики)
+ 'copy' => $tarif['copy'], // Использование резервных копий
+ 'web' => $tarif['web'], // Использование доп услуг
+ 'plugins_install' => $tarif['plugins_install'], // Список установленных плагинов
+ 'hdd' => $tarif['hdd'], // Дисковое пространство
+ 'core_fix' => $core, // Выделенный поток
+ 'core_fix_one' => $fix_one, // Выделенный поток
+ 'promo' => $promo // Использование промо-кода
+ );
+
+ return $aSDATA;
+ }
+
+ public static function install($aSDATA = array())
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ // Массив данных локации (адрес,пароль)
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$aSDATA['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Массив данных тарифа (путь сборки,путь установки)
+ $sql->query('SELECT `path`, `install`, `hostname` FROM `tarifs` WHERE `id`="'.$aSDATA['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Получение идентификаторов игрового сервера
+ $sql->query('INSERT INTO `servers` set uid="1"');
+ $id = $sql->id();
+ $uid = $id+1000;
+
+ // Директория сборки
+ $path = $tarif['path'].$aSDATA['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$uid;
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -s /bin/false -d '.$install.' -g servers -u '.$uid.' server'.$uid.';' // Создание пользователя сервера на локации
+ .'chown server'.$uid.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u server'.$uid.' screen -dmS i_'.$uid.' sh -c "cp -r '.$path.'/. .;' // Копирование файлов сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame['cssold'].'"');
+
+ // Запись данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `uid`="'.$uid.'",
+ `unit`="'.$aSDATA['unit'].'",
+ `tarif`="'.$aSDATA['tarif'].'",
+ `user`="'.$user['id'].'",
+ `address`="'.$aSDATA['address'].'",
+ `port`="'.$aSDATA['port'].'",
+ `game`="cssold",
+ `slots`="'.$aSDATA['slots'].'",
+ `slots_start`="'.$aSDATA['slots'].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$aSDATA['pack'].'",
+ `plugins_use`="'.$aSDATA['plugins'].'",
+ `ftp_use`="'.$aSDATA['ftp'].'",
+ `console_use`="'.$aSDATA['console'].'",
+ `stats_use`="'.$aSDATA['stats'].'",
+ `copy_use`="'.$aSDATA['copy'].'",
+ `web_use`="'.$aSDATA['web'].'",
+ `tickrate`="'.$aSDATA['tickrate'].'",
+ `fps`="'.$aSDATA['fps'].'",
+ `map_start`="'.$aSDATA['map'].'",
+ `vac`="1",
+ `hdd`="'.$aSDATA['hdd'].'",
+ `time`="'.$aSDATA['time'].'",
+ `date`="'.$start_point.'",
+ `test`="'.$aSDATA['test'].'",
+ `ram`="'.$aSDATA['ram'].'",
+ `core_fix`="'.$aSDATA['core_fix'].'",
+ `core_fix_one`="'.$aSDATA['core_fix_one'].'",
+ `autostop`="'.$aSDATA['autostop'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($aSDATA['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64djs($aSDATA['plugins_install']);
+
+ if(isset($aPlugins[$aSDATA['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$aSDATA['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$aSDATA['sum']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Запись получения тестового периода
+ if($aSDATA['test'])
+ {
+ $sql->query('INSERT INTO `tests` set `server`="'.$id.'", `unit`="'.$aSDATA['unit'].'", `game`="cssold", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_test'), array('id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="0"');
+ }else{
+ // Реф. система
+ games::part($user['id'], $aSDATA['sum']);
+
+ // Запись логов
+ if(!is_array($aSDATA['promo']))
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ else{
+ $sql->query('UPDATE `servers` set `benefit`="'.$aSDATA['time'].'" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('INSERT INTO `promo_use` set `promo`="'.$aSDATA['promo']['id'].'", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_promo'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'promo' => $aSDATA['promo']['cod'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ }
+ }
+
+ return $id;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/cssold/tarif.php b/system/library/games/cssold/tarif.php
new file mode 100644
index 0000000..c98df60
--- /dev/null
+++ b/system/library/games/cssold/tarif.php
@@ -0,0 +1,244 @@
+get('extend', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['tickrate'].' TickRate / '.$server['fps'].' FPS');
+ $html->set('tarif', $tarif_name);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function extend_sp($server, $tarif, $sid)
+ {
+ global $cfg, $sql, $html, $start_point;
+
+ tarifs::extend_address($server['game'], $sid);
+
+ $aPrice = sys::b64djs($tarif['price']);
+
+ $sum = $tarif['slots'] ? $aPrice[$server['tickrate'].'_'.$server['fps']] : $aPrice[$server['tickrate'].'_'.$server['fps']]*$server['slots'];
+
+ $html->get('extend_sp', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('date', $server['time'] > $start_point ? 'Сервер продлен до: '.date('d.m.Y', $server['time']) : 'Текущая дата: '.date('d.m.Y', $start_point));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['tickrate'].' TickRate / '.$server['fps'].' FPS');
+ $html->set('tarif', $tarif['name']);
+ $html->set('sum', $sum);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function plan($server, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ $sql->query('SELECT `fps`, `tickrate`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $tarif = $sql->get();
+
+ $options = '';
+
+ $aPrice = sys::b64djs($tarif['price']);
+
+ unset($aPrice[$server['tickrate'].'_'.$server['fps']]);
+
+ foreach($aPrice as $param => $price)
+ {
+ list($tickrate, $fps) = explode('_', $param);
+
+ $options .= ''
+ .$tickrate.' TickRate / '
+ .$fps.' FPS '
+ .'('.$price.' '.$cfg['currency'].'/слот | '
+ .($price*$server['slots']).' '.$cfg['currency'].'/месяц)'
+ .' ';
+ }
+
+ if($options == '')
+ return NULL;
+
+ $html->get('plan', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', 'Выберете тарифный план '.$options);
+ $html->set('info', $server['tickrate'].' TickRate / '.$server['fps'].' FPS');
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit($server, $unit_name, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ if(!$cfg['change_unit'][$server['game']])
+ return NULL;
+
+ $tarifs = $sql->query('SELECT `unit`, `fps`, `tickrate`, `price` FROM `tarifs` WHERE `game`="'.$server['game'].'" AND `name`="'.$tarif_name.'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num($tarifs))
+ return NULL;
+
+ $units = 0;
+
+ $options = 'Выберете новую локацию ';
+
+ while($tarif = $sql->get($tarifs))
+ {
+ if(!array_key_exists($server['tickrate'].'_'.$server['fps'], sys::b64djs($tarif['price'])))
+ continue;
+
+ $sql->query('SELECT `id`, `name` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $unit = $sql->get();
+
+ $options .= ''.$unit['name'].' ';
+
+ $units+=1;
+ }
+
+ if(!$units)
+ return NULL;
+
+ $html->get('unit', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['tickrate'].' TickRate / '.$server['fps'].' FPS');
+ $html->set('unit', $unit_name);
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit_new($tarif, $unit, $server, $mcache)
+ {
+ global $ssh, $sql, $user, $start_point;
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Директория сборки
+ $path = $tarif['path'].$tarif['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Пользователь сервера
+ $uS = 'server'.$server['uid'];
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -d '.$install.' -g servers -u '.$server['uid'].' '.$uS.';' // Создание пользователя сервера на локации
+ .'chown '.$uS.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u '.$uS.' screen -dmS i_'.$server['uid'].' cp -r '.$path.'/. .'); // Копирование файлов сборки для сервера
+
+ $address = explode(':', $server['address']);
+
+ $fix_one = $tarif['core_fix'] ? 1 : 0;
+
+ // Обновление данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `unit`="'.$tarif['unit'].'",
+ `tarif`="'.$tarif['id'].'",
+ `address`="'.$server['address'].'",
+ `port`="'.$address[1].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$tarif['pack'].'",
+ `map_start`="'.$tarif['map'].'",
+ `vac`="1",
+ `hdd`="'.$tarif['hdd'].'",
+ `time`="'.$tarif['time'].'",
+ `autostop`="'.$tarif['autostop'].'",
+ `core_fix`="'.$tarif['core_fix'].'",
+ `core_fix_one`="'.$fix_one.'",
+ `reinstall`="'.$start_point.'" WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($tarif['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64js($tarif['plugins_install']);
+
+ if(isset($aPlugins[$tarif['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$tarif['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$server['id'].'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ return NULL;
+ }
+
+ public static function price($plan)
+ {
+ $aPrice = array_values(sys::b64djs($plan));
+
+ $check = $aPrice[0];
+
+ unset($aPrice[0]);
+
+ if(!count($aPrice))
+ return false;
+
+ foreach($aPrice as $price)
+ {
+ if($check != $price)
+ return true;
+ }
+
+ return false;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/games.php b/system/library/games/games.php
new file mode 100644
index 0000000..ee0c2d2
--- /dev/null
+++ b/system/library/games/games.php
@@ -0,0 +1,810 @@
+ 'устанавливается',
+ 'reinstall' => 'переустанавливается',
+ 'update' => 'обновляется',
+ 'recovery' => 'восстанавливается',
+ 'overdue' => 'не оплачен',
+ 'blocked' => 'заблокирован'
+ );
+
+ $msg = sys::updtext(sys::text('servers', 'determine'), array('status' => $aText[$status]));
+
+ if($go)
+ sys::out($msg);
+
+ $html->get('informer');
+
+ $html->set('[class]', 'info_red');
+ $html->set('[text]', $msg);
+
+ $html->pack($tpl);
+
+ return false;
+ }
+
+ public static function crontab_week($week)
+ {
+ $aWeek = array();
+ $aWeek[1] = isset($week['\'1\'']) ? 'Пн., ' : '';
+ $aWeek[2] = isset($week['\'2\'']) ? 'Вт., ' : '';
+ $aWeek[3] = isset($week['\'3\'']) ? 'Ср., ' : '';
+ $aWeek[4] = isset($week['\'4\'']) ? 'Чт., ' : '';
+ $aWeek[5] = isset($week['\'5\'']) ? 'Пт., ' : '';
+ $aWeek[6] = isset($week['\'6\'']) ? 'Сб., ' : '';
+ $aWeek[7] = isset($week['\'7\'']) ? 'Вс., ' : '';
+
+ $days = '';
+
+ foreach($aWeek as $index => $val)
+ {
+ if($val == '')
+ continue;
+
+ $days .= $val;
+ }
+
+ $days = substr($days, 0, -2);
+
+ if($days == '')
+ $days = 'Пн., Вт., Ср., Чт., Пт., Сб., Вс.';
+
+ return $days;
+ }
+
+ public static function crontab_time($allhour, $hour, $minute)
+ {
+ if($allhour)
+ return 'Каждый час';
+
+ $aHour = array(
+ '00', '01', '02',
+ '03', '04', '05',
+ '06', '07', '08',
+ '09', '10', '11',
+ '12', '13', '14',
+ '15', '16', '17',
+ '18', '19', '20',
+ '21', '22', '23'
+ );
+
+ $aMinute = array(
+ '00', '05', '10',
+ '15', '20', '25',
+ '30', '35', '40',
+ '45', '50', '55'
+ );
+
+ if(!in_array($hour, $aHour))
+ $hour = '00';
+
+ if(!in_array($minute, $aMinute))
+ $minute = '00';
+
+ return $hour.':'.$minute;
+ }
+
+ public static function crontab($data = array(), $id, $cid)
+ {
+ global $cfg;
+
+ if($data['allhour'])
+ $time = '0 * * * ';
+ else{
+ $hour = array(
+ '00', '01', '02',
+ '03', '04', '05',
+ '06', '07', '08',
+ '09', '10', '11',
+ '12', '13', '14',
+ '15', '16', '17',
+ '18', '19', '20',
+ '21', '22', '23'
+ );
+
+ $minute = array(
+ '00', '05', '10',
+ '15', '20', '25',
+ '30', '35', '40',
+ '45', '50', '55'
+ );
+
+ if(!in_array($data['hour'], $hour))
+ $data['hour'] = '00';
+
+ if(!in_array($data['minute'], $minute))
+ $data['minute'] = '00';
+
+ $time = $data['minute'].' '.$data['hour'].' * * ';
+ }
+
+ $week = array();
+ $week[1] = isset($data['week']['\'1\'']) ? 1 : 0;
+ $week[2] = isset($data['week']['\'2\'']) ? 2 : 0;
+ $week[3] = isset($data['week']['\'3\'']) ? 3 : 0;
+ $week[4] = isset($data['week']['\'4\'']) ? 4 : 0;
+ $week[5] = isset($data['week']['\'5\'']) ? 5 : 0;
+ $week[6] = isset($data['week']['\'6\'']) ? 6 : 0;
+ $week[7] = isset($data['week']['\'7\'']) ? 7 : 0;
+
+ $check = 0;
+
+ foreach($week as $index => $val)
+ $check+= $val;
+
+ if($check == 28 || !$check)
+ $week = '*';
+ else{
+ $weeks = $week[1].','.$week[2].','.$week[3].','.$week[4].','.$week[5].','.$week[6].','.$week[7];
+ $weeks = str_replace(array(',0', '0'), '', $weeks);
+ $week = $weeks{0} == ',' ? substr($weeks, 1) : $weeks;
+ }
+
+ $cron_task = $time.$week.' screen -dmS s'.$id.' bash -c \'cd /var/enginegp && php cron.php '.$cfg['cron_key'].' server_cron '.$id.' '.$cid.'\'';
+
+ return $cron_task;
+ }
+
+ public static function parse_tarif($aTarif = array(), $aUnit = array())
+ {
+ global $cfg, $mcache;
+
+ $nmch = 'parse_tarif_'.$aTarif['id'];
+
+ $cache = $mcache->get($nmch);
+
+ if(is_array($cache))
+ return $cache;
+
+ if(isset($aTarif['fps']))
+ $aFPS = explode(':', $aTarif['fps']);
+
+ if(isset($aTarif['tickrate']))
+ $aTICKRATE = explode(':', $aTarif['tickrate']);
+
+ if(isset($aTarif['ram']))
+ $aRAM = explode(':', $aTarif['ram']);
+
+ $fps = '';
+
+ if(isset($aFPS))
+ foreach($aFPS as $value)
+ $fps .= ''.$value.' FPS ';
+
+ $tickrate = '';
+
+ if(isset($aTICKRATE))
+ foreach($aTICKRATE as $value)
+ $tickrate .= ''.$value.' TickRate ';
+
+ $ram = '';
+
+ if(isset($aRAM))
+ {
+ if($aTarif['param_fix'])
+ foreach($aRAM as $value)
+ $ram .= ''.$value.' Ram ';
+ else
+ foreach($aRAM as $value)
+ $ram .= ''.$value.' Ram/Слот ';
+ }
+
+ $packs = '';
+ $aPack = sys::b64djs($aTarif['packs'], true);
+
+ if(is_array($aPack))
+ foreach($aPack as $index => $name)
+ $packs .= ''.$name.' ';
+
+ $slots = '';
+
+ for($i = $aTarif['slots_min']; $i <= $aTarif['slots_max']; $i+=1)
+ $slots .= ''.$i.' шт. ';
+
+ $aTime = explode(':', $aTarif['time']);
+
+ $time = games::parse_time($aTime, $aTarif['discount'], $aTarif['id']);
+
+ if($aTarif['test'] AND $aUnit['test'])
+ $time .= 'Тестовый период '.games::parse_day($aTarif['test']).' ';
+
+ $data = array(
+ 'packs' => $packs,
+ 'slots' => $slots,
+ 'time' => $time,
+ 'fps' => $fps,
+ 'tickrate' => $tickrate,
+ 'ram' => $ram
+ );
+
+ $mcache->set($nmch, $data, false, 60);
+
+ return $data;
+ }
+
+ public static function parse_time($aTime = array(), $discount, $tarif, $type = 'buy')
+ {
+ global $cfg;
+
+ $time = '';
+
+ $arr = isset(params::$disconunt['service'][$tarif]) ? $tarif : 'time';
+
+ foreach($aTime as $value)
+ {
+ if(array_key_exists($value, params::$disconunt['service'][$arr][$type]) AND $discount)
+ {
+ $data = explode(':', params::$disconunt['service'][$arr][$type][$value]);
+
+ // Если наценка
+ if($data[0] == '+')
+ {
+ // Если значение в процентах
+ if(substr($data[1], -1) == '%')
+ $time .= ''.games::parse_day($value).' (Наценка '.$data[1].') ';
+ else
+ $time .= ''.games::parse_day($value).' (Наценка '.sys::int($data[1]).' '.$cfg['currency'].') ';
+ }else{
+ // Если значение в процентах
+ if(substr($data[1], -1) == '%')
+ $time .= ''.games::parse_day($value).' (Скидка '.$data[1].') ';
+ else
+ $time .= ''.games::parse_day($value).' (Скидка '.sys::int($data[1]).' '.$cfg['currency'].') ';
+ }
+ }else
+ $time .= ''.games::parse_day($value).' ';
+ }
+
+ return $time;
+ }
+
+ public static function define_period($type, $aD_M, $time = 0)
+ {
+ global $start_point;
+
+ if($time < $start_point)
+ $time = $start_point;
+
+ $day = $type == 'extend' ? date('d', $time) : date('d', $start_point);
+ $month = $type == 'extend' ? date('n', $time) : date('n', $start_point);
+
+ $period = $aD_M[$month]-$day;
+
+ if($day > 15)
+ $period += $month != 12 ? $aD_M[$month+1] : $aD_M[1];
+
+ return $period+1;
+ }
+
+ public static function define_sum($discount, $price, $slots, $time, $type = 'buy')
+ {
+ global $sql, $user, $cfg, $start_point;
+
+ if($cfg['settlement_period'])
+ {
+ if($time < $start_point)
+ $time = $start_point;
+
+ $day = $type == 'extend' ? date('d', $time) : date('d', $start_point);
+ $month = $type == 'extend' ? date('n', $time) : date('n', $start_point);
+
+ $period = params::$aDayMonth[$month]+1-$day;
+
+ $new_month_sum = 0;
+
+ if($day > 15)
+ $new_month_sum = ceil($price*$slots);
+
+ $sum = params::$aDayMonth[$month] == $period ? $price*$slots : floor($price*$slots/30*$period)+$new_month_sum;
+ }else{
+ $sum = floor($price*$slots/30*$time);
+
+ if(array_key_exists($time, params::$disconunt['service']['time'][$type]) AND $discount)
+ {
+ $data = explode(':', params::$disconunt['service']['time'][$type][$time]);
+
+ // Если наценка
+ if($data[0] == '+')
+ {
+ // Если значение в процентах
+ if(substr($data[1], -1) == '%')
+ $sum = ceil($sum+$sum/100*intval($data[1]));
+ else
+ $sum = $sum+intval($data[1]);
+ }else{
+ // Если значение в процентах
+ if(substr($data[1], -1) == '%')
+ $sum = ceil($sum-$sum/100*intval($data[1]));
+ else
+ $sum = $sum-intval($data[1]);
+ }
+ }
+ }
+
+ $sel = $type == 'buy' ? 'rental' : 'extend';
+
+ $sql->query('SELECT `'.$sel.'` FROM `users` WHERE `id`="'.$user['id'].'" LIMIT 1');
+ $user = array_merge($user, $sql->get());
+
+ $sum = strpos($user[$sel], '%') ? $sum-$sum/100*$user[$sel] : $sum-$user[$sel];
+
+ if($sum < 0)
+ sys::outjs(array('e' => 'Ошибка: сумма за услугу неверна'));
+
+ return $sum;
+ }
+
+ public static function define_promo($cod, $data = array(), $discount, $sum, $type = 'buy')
+ {
+ global $cfg, $sql, $go, $start_point;
+
+ // Проверка формата кода
+ if(sys::valid($cod, 'promo'))
+ {
+ if(!$go)
+ sys::outjs(array('e' => 'Промо-код имеет неверный формат.'));
+
+ return NULL;
+ }
+
+ $sql->query('SELECT `id`, `value`, `discount`, `data`, `hits`, `use`, `extend`, `user`, `server` FROM `promo` WHERE `cod`="'.$cod.'" AND `tarif`="'.$data['tarif'].'" AND `time`>"'.$start_point.'" LIMIT 1');
+
+ // Проверка наличия промо-кода
+ if(!$sql->num())
+ {
+ if(!$go)
+ sys::outjs(array('e' => 'Промо-код не найден.'));
+
+ return NULL;
+ }
+
+ $promo = $sql->get();
+
+ // Проверка типа при аренде
+ if($type == 'buy' AND $promo['extend'])
+ {
+ if(!$go)
+ sys::outjs(array('e' => 'Промо-код для продления игрового сервера.'));
+
+ return NULL;
+ }
+
+ // Проверка типа при продлении
+ if($type != 'buy' AND !$promo['extend'])
+ {
+ if(!$go)
+ sys::outjs(array('e' => 'Промо-код для аренды нового игрового сервера.'));
+
+ return NULL;
+ }
+
+ // Проверка доступности на пользователя
+ if($promo['user'] AND $data['user'] != $promo['user'])
+ {
+ if(!$go)
+ sys::outjs(array('e' => 'Промо-код не найден.'));
+
+ return NULL;
+ }
+
+ // Проверка доступности на сервер
+ if($promo['server'] AND $data['server'] != $promo['server'])
+ {
+ if(!$go)
+ sys::outjs(array('e' => 'Промо-код не найден.'));
+
+ return NULL;
+ }
+
+ $use = $promo['use'] < 1 ? '1' : $promo['use'];
+
+ // Проверка доступности
+ $sql->query('SELECT `id` FROM `promo_use` WHERE `promo`="'.$promo['id'].'" LIMIT '.$use);
+ if($sql->num() >= $promo['use'])
+ {
+ if(!$go)
+ sys::outjs(array('e' => 'Промо-код использован максимальное количество раз.'));
+
+ return NULL;
+ }
+
+ // Данные для сравнения
+ $data_promo = sys::b64djs($promo['data'], true);
+
+ $check = 0;
+
+ // Проверка периода
+ if(in_array($data['time'], explode(':', $data_promo['time'])))
+ $check = 1;
+
+ // Проверка значения FPS
+ if((isset($data['fps']) AND isset($data_promo['fps'])) AND in_array($data['fps'], explode(':', $data_promo['fps'])))
+ $check+= 1;
+
+ // Проверка значения TICKRATE
+ if((isset($data['tickrate']) AND isset($data_promo['tickrate'])) AND in_array($data['tickrate'], explode(':', $data_promo['tickrate'])))
+ $check+= 1;
+
+ // Проверка значения RAM
+ if((isset($data['ram']) AND isset($data_promo['ram'])) AND in_array($data['ram'], explode(':', $data_promo['ram'])))
+ $check+= 1;
+
+ // Проверка кол-ва слот
+ if(isset($data_promo['slots']))
+ {
+ // Если совпало по перечислению слот (через число:число:число ...)
+ if(in_array($data['slots'], explode(':', $data_promo['slots'])))
+ $check+= 1;
+ else{
+ // Если указан диапозон слот
+ $aSl = explode('-', $data_promo['slots']);
+ if(count($aSl) == 2 AND ($data['slots'] >= $aSl[0] AND $data['slots'] <= $aSl[1]))
+ $check+= 1;
+ }
+ }
+
+ // Проверка совпадений
+ if($check < $promo['hits'])
+ {
+ if(!$go)
+ sys::outjs(array('e' => 'Условия для данного промо-кода не выполнены.'));
+
+ return NULL;
+ }
+
+ // Если скидка
+ if($promo['discount'])
+ {
+ // Если не суммировать скидки
+ if(!$cfg['promo_discount'])
+ {
+ if(array_key_exists($data['time'], params::$disconunt['service']['time'][$type]) AND $discount)
+ {
+ $data = explode(':', params::$disconunt['service']['time'][$type][$data['time']]);
+
+ // Если скидка
+ if($data[0] == '-')
+ {
+ // Если значение в процентах
+ if(substr($data[1], -1) == '%')
+ $sum = ceil($sum+$sum/100*intval($data[1]));
+ else
+ $sum = $sum+intval($data[1]);
+ }
+ }
+ }
+
+ // Пересчет суммы
+ if(substr($promo['value'], -1) == '%')
+ $sum = $sum-ceil($sum/100*intval($promo['value']));
+ else
+ $sum = $sum-intval($promo['value']);
+
+ if(!$go)
+ sys::outjs(array('sum' => $sum, 'discount' => 1, 'cur' => $cfg['currency']));
+
+ return array('id' => $promo['id'], 'cod' => $cod, 'sum' => $sum);
+
+ }
+
+ // Подарочные дни
+ $days = intval($promo['value']);
+
+ if(!$go)
+ sys::outjs(array('days' => games::parse_day($days)));
+
+ return array('id' => $promo['id'], 'cod' => $cod, 'days' => $days);
+ }
+
+ public static function info_tarif($game, $tarif, $param)
+ {
+ if($game == 'cs')
+ return $tarif.' / '.$param['fps'].' FPS';
+
+ if($game == 'mc')
+ return $tarif.' / '.$param['ram'].' RAM';
+
+ if($game == 'cssold')
+ return $tarif.' / '.$param['fps'].' FPS / '.$param['tickrate'].' TickRate';
+
+ if(in_array($game, array('css', 'csgo')))
+ return $tarif.' / '.$param['tickrate'].' TickRate';
+
+ return $tarif;
+ }
+
+ public static function maplist($id, $unit, $folder, $map, $go, $mcache = '', $ctrl = false)
+ {
+ global $user, $sql;
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $mcache);
+
+ sys::outjs(array('maps', 'unknown '));
+ }
+
+ // Генерация списка карт
+ $aMaps = array_diff(explode("\n", $ssh->get('cd '.$folder.' && du -ah | grep -e "\.bsp$" | awk \'{print $2}\'')), array(''));
+
+ // Удаление ".bsp"
+ $aMaps = str_ireplace(array('./', '.bsp'), '', $aMaps);
+
+ if($go)
+ {
+ $map = str_replace('|', '/', urldecode($map));
+
+ $sqlq = $ctrl ? 'control_' : '';
+
+ // Проверка наличия выбранной карты
+ if(in_array($map, $aMaps))
+ $sql->query('UPDATE `'.$sqlq.'servers` set `map_start`="'.$map.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $mcache);
+ }
+
+ sort($aMaps);
+ reset($aMaps);
+
+ $ismap = in_array($map, $aMaps);
+ $maps = $ismap ? ''.$map.' ' : 'Указанная ранее карта "'.$map.'" не найдена ';
+
+ // Удаление стартовой карты
+ if($ismap)
+ unset($aMaps[array_search($map, $aMaps)]);
+
+ foreach($aMaps as $map)
+ $maps .= ''.$map.' ';
+
+ sys::outjs(array('maps' => $maps));
+ }
+
+ public static function owners($aRights)
+ {
+ if(array_search(0, $aRights))
+ return 'Есть ограничения в доступе.';
+
+ return 'Выданы все права.';
+ }
+
+ public static function part($uid, $money)
+ {
+ global $cfg, $sql, $start_point;
+
+ if($cfg['part'])
+ return NULL;
+
+ $sql->query('SELECT `part` FROM `users` WHERE `id`="'.$uid.'" LIMIT 1');
+ $user = $sql->get();
+
+ if(!$user['part'])
+ return NULL;
+
+ $sql->query('SELECT `balance`, `part_money` FROM `users` WHERE `id`="'.$user['part'].'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $user = array_merge($user, $sql->get());
+
+ $sum = round($money/100*$cfg['part_proc'], 2);
+
+ if($cfg['part_money'])
+ $sql->query('UPDATE `users` set `part_money`="'.($user['part_money']+$sum).'" WHERE `id`="'.$user['part'].'" LIMIT 1');
+ else
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']+$sum).'" WHERE `id`="'.$user['part'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['part'].'", `text`="'.sys::updtext(sys::text('logs', 'part'),
+ array('part' => $uid, 'money' => $sum)).'", `date`="'.$start_point.'", `type`="part", `money`="'.$sum.'"');
+
+ return NULL;
+ }
+
+ public static function map($map, $aMaps)
+ {
+ if(!is_array($aMaps))
+ $aMaps = explode("\n", str_ireplace(array('./', '.bsp'), '', $aMaps));
+
+ if(in_array($map, $aMaps))
+ return false;
+
+ return true;
+ }
+
+ public static function mapsql($arr = array())
+ {
+ $sql = 'AND (';
+
+ foreach($arr as $map)
+ $sql .= ' `name` REGEXP FROM_BASE64(\''.base64_encode('^'.$map.'\_').'\') OR';
+
+ return $sql == 'AND (' ? '' : substr($sql, 0, -3).')';
+ }
+
+ public static function iptables_whois($mcache)
+ {
+ $address = isset($_POST['address']) ? trim($_POST['address']) : sys::outjs(array('info' => 'Не удалось получить информацию.'), $mcache);
+
+ if(sys::valid($address, 'ip'))
+ sys::outjs(array('e' => sys::text('servers', 'firewall')), $mcache);
+
+ include(LIB.'geo.php');
+
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $data = $SxGeo->getCityFull($address);
+
+ $info = 'Информация об IP адресе:';
+
+ if($data['country']['name_ru'] != '')
+ {
+ $info .= 'Страна: '.$data['country']['name_ru'];
+
+ if($data['city']['name_ru'] != '')
+ $info .= '
Город: '.$data['city']['name_ru'];
+
+ $info .= '
Подсеть: '.sys::whois($address);
+
+ }else
+ $info = 'Не удалось получить информацию.';
+
+ sys::outjs(array('info' => $info), $mcache);
+ }
+
+ public static function iptables($id, $action, $source, $dest, $unit, $snw = false, $ssh = false)
+ {
+ global $cfg, $sql, $start_point;
+
+ if(!$ssh)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$unit.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('all', 'ssh'));
+ }
+
+ switch($action)
+ {
+ case 'block':
+ if(sys::valid($source, 'ip'))
+ return array('e' => sys::text('servers', 'firewall'));
+
+ // Если подсеть
+ if($snw)
+ {
+ $source = sys::whois($source);
+
+ if($source == 'не определена')
+ return array('e' => 'Не удалось определить подсеть для указанного адреса.');
+ }
+
+ $sql->query('SELECT `id` FROM `firewall` WHERE `sip`="'.$source.'" AND `server`="'.$id.'" LIMIT 1');
+
+ // Если такое правило уже добавлено или указан адрес сайта (ПУ)
+ if($sql->num() || ($source == $cfg['ip'] || $source == $cfg['subnet']))
+ return array('s' => 'ok');
+
+ $sql->query('INSERT INTO `firewall` set `sip`="'.$source.'", `dest`="'.$dest[0].':'.$dest[1].'", `server`="'.$id.'", `time`="'.$start_point.'"');
+
+ $line = $sql->id();
+
+ $rule = 'iptables -I INPUT -s '.$source.' -p udp -d '.$dest[0].' --dport '.$dest[1].' -j DROP;';
+
+ $ssh->set($rule.' echo -e "#'.$line.';\n'.$rule.'" >> /root/'.$cfg['iptables']);
+
+ return array('s' => 'ok');
+
+ case 'unblock':
+ if(!is_numeric($source) AND sys::valid($source, 'ip'))
+ return array('e' => sys::text('servers', 'firewall'));
+
+ if(is_numeric($source))
+ {
+ $sql->query('SELECT `id`, `sip` FROM `firewall` WHERE `id`="'.$source.'" AND `server`="'.$id.'" LIMIT 1');
+
+ // Если такое правило отсутствует
+ if(!$sql->num())
+ return array('s' => 'ok');
+ }else{
+ $sql->query('SELECT `id`, `sip` FROM `firewall` WHERE `sip`="'.$source.'" AND `server`="'.$id.'" LIMIT 1');
+
+ // Если одиночный адрес не найден, проверить на блокировку подсети
+ if(!$sql->num())
+ {
+ $source = sys::whois($source);
+
+ $sql->query('SELECT `id` FROM `firewall` WHERE `sip`="'.$source.'" AND `server`="'.$id.'" LIMIT 1');
+
+ if($sql->num())
+ {
+ $firewall = $sql->get();
+
+ return array('i' => 'Указанный адрес входит в заблокированную подсеть, разблокировать подсеть?', 'id' => $firewall['id']);
+ }
+
+ return array('s' => 'ok');
+ }
+ }
+
+ $firewall = $sql->get();
+
+ $ssh->set('iptables -D INPUT -s '.$firewall['sip'].' -p udp -d '.$dest[0].' --dport '.$dest[1].' -j DROP;'
+ .'sed "`nl '.$cfg['iptables'].' | grep \"#'.$firewall['id'].'\" | awk \'{print $1","$1+1}\'`d" '.$cfg['iptables'].' > '.$cfg['iptables'].'_temp; cat '.$cfg['iptables'].'_temp > '.$cfg['iptables'].'; rm '.$cfg['iptables'].'_temp');
+
+ $sql->query('DELETE FROM `firewall` WHERE `id`="'.$firewall['id'].'" LIMIT 1');
+
+ return array('s' => 'ok');
+
+ case 'remove':
+ $sql->query('SELECT `id`, `sip`, `dest` FROM `firewall` WHERE `server`="'.$id.'"');
+
+ $aRule = array();
+
+ while($firewall = $sql->get())
+ {
+ list($ip, $port) = explode(':', $firewall['dest']);
+
+ $aRule[$firewall['id']] = 'iptables -D INPUT -s '.$firewall['sip'].' -p udp -d '.$ip.' --dport '.$port.' -j DROP;';
+ }
+
+ $nRule = count($aRule);
+
+ if(!$nRule)
+ return NULL;
+
+ $cmd = '';
+
+ foreach($aRule as $line => $rule)
+ $cmd .= $rule.'sed "`nl '.$cfg['iptables'].' | grep "#'.$line.'" | awk \'{print $1","$1+1}\'`d" '.$cfg['iptables'].' > '.$cfg['iptables'].'_temp; cat '.$cfg['iptables'].'_temp > '.$cfg['iptables'].'; rm '.$cfg['iptables'].'_temp';
+
+ $ssh->set($cmd);
+
+ $sql->query('DELETE FROM `firewall` WHERE `server`="'.$id.'" LIMIT '.$nRule);
+
+ return array('s' => 'ok');
+ }
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/graph.php b/system/library/games/graph.php
new file mode 100644
index 0000000..493141a
--- /dev/null
+++ b/system/library/games/graph.php
@@ -0,0 +1,404 @@
+addPoints($aOnline, 'ONLINE');
+
+ // CPU
+ $MyData->addPoints($aCPU, 'CPU');
+
+ // RAM
+ $MyData->addPoints($aRAM, 'RAM');
+
+ // HDD
+ $MyData->addPoints($aHDD, 'HDD');
+
+ // Время
+ $MyData->addPoints($aData['time'], 'TIME');
+
+ $MyData->setSerieOnAxis('CPU', 1);
+ $MyData->setSerieOnAxis('RAM', 1);
+ $MyData->setSerieOnAxis('HDD', 1);
+ $MyData->setAxisPosition(1, AXIS_POSITION_RIGHT);
+
+ $MyData->setAxisName(0, 'Онлайн');
+ $MyData->setAxisName(1, 'Нагрузка %');
+
+ $MyData->setAbscissa('TIME');
+
+ // Сечение линии
+ $MyData->setSerieTicks('ONLINE', 4);
+
+ // Цвет линий
+ $MyData->setPalette('ONLINE', array('R' => 68, 'G' => 148, 'B' => 224));
+ $MyData->setPalette('CPU', array('R' => 216, 'G' => 65, 'B' => 65));
+ $MyData->setPalette('RAM', array('R' => 26, 'G' => 150, 'B' => 38));
+ $MyData->setPalette('HDD', array('R' => 205, 'G' => 196, 'B' => 37));
+
+ $myPicture = new pImage(896, 220, $MyData);
+
+ $myPicture->drawFilledRectangle(0, 0, 896, 220, array('R' => 255, 'G' => 255, 'B' => 255));
+
+ $myPicture->drawRectangle(0, 0, 895, 219, array('R' => 221, 'G' => 221, 'B' => 221));
+
+ $myPicture->setFontProperties(array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8));
+ $myPicture->setGraphArea(40, 20, 616, 190);
+ $myPicture->drawFilledRectangle(40, 20, 616, 190, array('R' => 240, 'G' => 242, 'B' => 242, 'Alpha' => 100));
+ $myPicture->drawScale(array('XMargin' => 5, 'YMargin' => 5, 'GridR' => 76, 'GridG' => 109, 'GridB' => 120, 'LabelSkip' => 0, 'DrawSubTicks' => TRUE, 'Mode' => SCALE_MODE_MANUAL, 'Factors' => array(0 => array($slots), 1 => array(25, 50, 75, 100)), 'ManualScale' => array(0 => array('Min' => 0, 'Max' => $slots), 1 => array('Min' => 0, 'Max' => 100))));
+
+ $myPicture->drawText(676, 34, 'Средний онлайн: '.graph::average($aOnline), array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB.'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT));
+ $myPicture->drawText(676, 54, 'Средняя нагрузка (CPU): '.graph::average($aCPU).'%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB.'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT));
+ $myPicture->drawText(676, 74, 'Средняя нагрузка (RAM): '.graph::average($aRAM).'%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB.'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT));
+ $myPicture->drawText(676, 94, 'Средняя нагрузка (HDD): '.graph::average($aHDD).'%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB.'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT));
+
+ $myPicture->drawText(676, 129, 'Максимальный онлайн: '.max($aOnline), array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB.'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT));
+ $myPicture->drawText(676, 153, 'Максимальная нагрузка (CPU): '.max($aCPU).'%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB.'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT));
+ $myPicture->drawText(676, 173, 'Максимальная нагрузка (RAM): '.max($aRAM).'%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB.'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT));
+ $myPicture->drawText(676, 193, 'Максимальная нагрузка (HDD): '.max($aHDD).'%', array('R' => 25, 'G' => 25, 'B' => 25, 'FontName' => LIB.'games/graph/fonts/arianamu.ttf', 'FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMLEFT));
+
+ $myPicture->setFontProperties(array('FontName'=> LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 7));
+ $myPicture->drawSplineChart();
+ $myPicture->setShadow(FALSE);
+
+ $myPicture->drawLegend(466, 10, array('Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_HORIZONTAL));
+
+ $myPicture->render(TEMP.(md5($key.'full_'.$time)).'.png');
+
+ unset($MyData, $myPicture);
+
+ return NULL;
+ }
+
+ public static function first($server, $aPoints, $aStyle, $style, $key)
+ {
+ global $cfg, $aGname;
+
+ $MyData = new pData();
+
+ // Значения
+ $MyData->addPoints($aPoints, 'ONLINE');
+
+ $MyData->addPoints(array(VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID), 'NONE');
+ $MyData->setAbscissa('NONE');
+
+ // Цвет линии
+ $MyData->setPalette('ONLINE', $aStyle[$style]['line']);
+
+ // Размер баннера
+ $myPicture = new pImage(160, 248, $MyData);
+
+ // Цвет фона
+ $myPicture->drawFilledRectangle(0, 0, 160, 248, $aStyle[$style]['fon']);
+
+ // Обводка
+ $myPicture->drawRectangle(0, 0, 159, 247, $aStyle[$style]['border']);
+
+ // Шрифт текста
+ $myPicture->setFontProperties(array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 5)));
+
+ // Размер графика
+ $myPicture->setGraphArea(35, 160, 150, 210);
+
+ // Цвет фона графика
+ $myPicture->drawFilledRectangle(35, 160, 150, 210, $aStyle[$style]['graph']);
+
+ // График
+ $myPicture->drawScale(array('XMargin' => 5, 'YMargin' => 5, 'CycleBackground' => TRUE, 'LabelSkip' => 0, 'DrawSubTicks' => TRUE, 'Mode' => SCALE_MODE_MANUAL, 'Factors' => array(0 => array($server['slots_start'])), 'ManualScale' => array(0 => array('Min' => 0, 'Max' => $server['slots_start']))));
+
+ // Название игрового сервера
+ $myPicture->drawFilledRectangle(0, 0, 18, 248, $aStyle[$style]['leftbox']);
+ $myPicture->drawText(14, 245, $server['name'], array_merge($aStyle[$style]['boxcolor'], array('Angle' => 90, 'FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 10)));
+ $myPicture->drawFilledRectangle(0, 0, 18, 5, $aStyle[$style]['leftbox']);
+
+ // Адрес игрового сервера
+ $myPicture->drawFilledRectangle(25, 5, 153, 18, $aStyle[$style]['box']);
+ $myPicture->drawText(28, 17, 'Адрес сервера', array_merge($aStyle[$style]['boxcolor'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+ $myPicture->drawText(26, 30, $server['address'], array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+
+ // Статус игрового сервера
+ $myPicture->drawFilledRectangle(25, 35, 153, 48, $aStyle[$style]['box']);
+ $myPicture->drawText(28, 47, 'Состояние сервера', array_merge($aStyle[$style]['boxcolor'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+ $myPicture->drawText(26, 62, graph::status($server['status'], $server['map']), array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+
+ // Игроки на игровом сервере
+ $myPicture->drawFilledRectangle(25, 65, 153, 78, $aStyle[$style]['box']);
+ $myPicture->drawText(28, 77, 'Игроки на сервере', array_merge($aStyle[$style]['boxcolor'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+ $myPicture->drawText(26, 92, $server['online'].' / '.$server['slots_start'], array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+
+ // Полоска загрузки
+ $myPicture->drawProgress(83, 82, ceil($server['slots_start']/100*$server['online']), array_merge($aStyle[$style]['progress'], array('Width' => 70, 'Height' => 8)));
+
+ // Тип игрового сервера
+ $myPicture->drawFilledRectangle(25, 95, 153, 108, $aStyle[$style]['box']);
+ $myPicture->drawText(28, 107, 'Тип сервера', array_merge($aStyle[$style]['boxcolor'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+ $myPicture->drawText(26, 122, $aGname[$server['game']], array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+
+ // Хостинг игровых серверов
+ $myPicture->drawText(89, 230, $cfg['graph'], array_merge($aStyle[$style]['color'], array('FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE)));
+ $myPicture->drawText(89, 245, 'Хостинг игровых серверов', array_merge($aStyle[$style]['color'], array('FontSize' => 8, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE)));
+
+ $myPicture->drawSplineChart();
+
+ $myPicture->render(TEMP.(md5($key.$style.'first')).'.png');
+
+ unset($MyData, $myPicture);
+
+ return NULL;
+ }
+
+ public static function second($server, $aPoints, $aStyle, $style, $key)
+ {
+ global $cfg, $aGname;
+
+ $MyData = new pData();
+
+ // Значения
+ $MyData->addPoints($aPoints, 'ONLINE');
+
+ $MyData->addPoints(array(VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID, VOID), 'NONE');
+ $MyData->setAbscissa('NONE');
+
+ // Цвет линии
+ $MyData->setPalette('ONLINE', $aStyle[$style]['line']);
+
+ // Размер баннера
+ $myPicture = new pImage(560, 95, $MyData);
+
+ // Цвет фона
+ $myPicture->drawFilledRectangle(0, 0, 560, 95, $aStyle[$style]['fon']);
+
+ // Название игрового сервера
+ $myPicture->drawFilledRectangle(5, 5, 410, 18, $aStyle[$style]['box']);
+ $myPicture->drawText(8, 17, 'Название сервера', array_merge($aStyle[$style]['boxcolor'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+ $myPicture->drawText(6, 31, $server['name'], array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+
+ // Костыль для "обрезания" названия сервера
+ $myPicture->drawFilledRectangle(405, 19, 560, 35, $aStyle[$style]['fon']);
+
+ // Обводка
+ $myPicture->drawRectangle(0, 0, 559, 94, $aStyle[$style]['border']);
+
+ // Шрифт текста
+ $myPicture->setFontProperties(array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 5)));
+
+ // Размер графика
+ $myPicture->setGraphArea(430, 5, 554, 60);
+
+ // Цвет фона графика
+ $myPicture->drawFilledRectangle(430, 5, 554, 60, $aStyle[$style]['graph']);
+
+ // График
+ $myPicture->drawScale(array('XMargin' => 5, 'YMargin' => 5, 'CycleBackground' => TRUE, 'LabelSkip' => 0, 'DrawSubTicks' => TRUE, 'Mode' => SCALE_MODE_MANUAL, 'Factors' => array(0 => array($server['slots_start'])), 'ManualScale' => array(0 => array('Min' => 0, 'Max' => $server['slots_start']))));
+
+ // Адрес игрового сервера
+ $myPicture->drawFilledRectangle(5, 36, 210, 49, $aStyle[$style]['box']);
+ $myPicture->drawText(8, 48, 'Адрес сервера', array_merge($aStyle[$style]['boxcolor'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+ $myPicture->drawText(6, 62, $server['address'], array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+
+ // Статус игрового сервера
+ $myPicture->drawFilledRectangle(215, 36, 410, 49, $aStyle[$style]['box']);
+ $myPicture->drawText(218, 48, 'Состояние сервера', array_merge($aStyle[$style]['boxcolor'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+ $myPicture->drawText(216, 62, graph::status($server['status'], $server['map']), array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+
+ // Игроки на игровом сервере
+ $myPicture->drawFilledRectangle(5, 67, 210, 80, $aStyle[$style]['box']);
+ $myPicture->drawText(8, 79, 'Игроки на сервере', array_merge($aStyle[$style]['boxcolor'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+ $myPicture->drawText(6, 93, $server['online'].' / '.$server['slots_start'], array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+
+ // Полоска загрузки
+ $myPicture->drawProgress(90, 83, ceil($server['slots_start']/100*$server['online']), array_merge($aStyle[$style]['progress'], array('Width' => 120, 'Height' => 8)));
+
+ // Тип игрового сервера
+ $myPicture->drawFilledRectangle(215, 67, 410, 80, $aStyle[$style]['box']);
+ $myPicture->drawText(218, 79, 'Тип сервера', array_merge($aStyle[$style]['boxcolor'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+ $myPicture->drawText(216, 91, $aGname[$server['game']], array_merge($aStyle[$style]['color'], array('FontName' => LIB.'games/graph/fonts/tahoma.ttf', 'FontSize' => 8)));
+
+ // Хостинг игровых серверов
+ $myPicture->drawText(490, 77, $cfg['graph'], array('FontSize' => 10, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE));
+ $myPicture->drawText(490, 90, 'Хостинг игровых серверов', array('FontSize' => 8, 'Align' => TEXT_ALIGN_BOTTOMMIDDLE));
+
+ $myPicture->drawSplineChart();
+
+ $myPicture->render(TEMP.(md5($key.$style.'second')).'.png');
+
+ unset($MyData, $myPicture);
+
+ return NULL;
+ }
+
+ public static function online_day($server, $max)
+ {
+ global $sql, $start_point;
+
+ $time = $start_point-86400;
+
+ $aOnline = array();
+
+ $aSel = array();
+
+ $sql->query('SELECT `online` FROM `graph_hour` WHERE `server`="'.$server.'" AND `time`>"'.$time.'" ORDER BY `id` DESC LIMIT 24');
+ while($value = $sql->get())
+ $aSel[] = $value['online'];
+
+ $n = count($aSel);
+
+ $n = $n > 1 ? $n-1 : 0;
+
+ if($n)
+ {
+ for($i = $n; $i >= 0; $i-=1)
+ $aOnline[] = $aSel[$i] > $max ? $max : $aSel[$i];
+ }
+
+ $add = count($aOnline);
+
+ if($add < 24)
+ {
+ for($i = $n; $i <= 23; $i+=1)
+ $aOnline[] = 0;
+ }
+
+ return $aOnline;
+ }
+
+ private static function graph_data($period, $server, $max)
+ {
+ global $sql, $start_point;
+
+ $aData = array(
+ 'limit' => array(
+ 'day' => 24,
+ 'week' => 7,
+ 'month' => 30
+ ),
+
+ 'for' => array(
+ 'day' => 23,
+ 'week' => 6,
+ 'month' => 29
+ ),
+
+ 'from' => array(
+ 'day' => 'graph_hour',
+ 'week' => 'graph_day',
+ 'month' => 'graph_day'
+ ),
+
+ 'time' => array(
+ 'day' => 86400,
+ 'week' => 604800,
+ 'month' => 2592000
+ )
+ );
+
+ $time = $start_point-$aData['time'][$period];
+
+ $aOnline = array();
+
+ $aCPU = array();
+ $aRAM = array();
+ $aHDD = array();
+ $aTime = array();
+
+ $aSel = array();
+
+ $sql->query('SELECT `online`, `cpu`, `ram`, `hdd`, `time` FROM `'.$aData['from'][$period].'` WHERE `server`="'.$server.'" AND `time`>"'.$time.'" ORDER BY `id` DESC LIMIT '.$aData['limit'][$period]);
+ while($value = $sql->get())
+ $aSel[] = array('online' => $value['online'], 'cpu' => $value['cpu'], 'ram' => $value['ram'], 'hdd' => $value['hdd'], 'time' => $value['time']);
+
+ $n = count($aSel);
+
+ $n = $n > 1 ? $n-1 : 0;
+
+ $next = true;
+
+ if(isset($aSel[$n]['online']))
+ {
+ for($i = $n; $i >= 0; $i-=1)
+ {
+ $aOnline[] = $aSel[$i]['online'] > $max ? $max : $aSel[$i]['online'];
+
+ $aCPU[] = $aSel[$i]['cpu'];
+ $aRAM[] = $aSel[$i]['ram'];
+ $aHDD[] = $aSel[$i]['hdd'];
+
+ if($next)
+ {
+ $aTime[] = VOID;
+
+ $next = false;
+ }else{
+ $aTime[] = $period == 'day' ? date('H:i', $aSel[$i]['time']) : date('d.m', $aSel[$i]['time']);
+
+ $next = true;
+ }
+ }
+ }
+
+ $add = count($aOnline);
+
+ for($i = $add; $i <= $aData['for'][$period]; $i+=1)
+ {
+ $aOnline[] = 0;
+
+ $aCPU[] = 0;
+ $aRAM[] = 0;
+ $aHDD[] = 0;
+
+ $aTime[] = VOID;
+ }
+
+ return array('online' => $aOnline, 'cpu' => $aCPU, 'ram' => $aRAM, 'hdd' => $aHDD, 'time' => $aTime);
+ }
+
+ private static function status($status, $map)
+ {
+ switch($status)
+ {
+ case 'working':
+ return 'Карта: '.$map;
+ case 'off':
+ return 'Статус: выключен';
+ case 'start':
+ return 'Статус: запускается';
+ case 'restart':
+ return 'Статус: перезапускается';
+ case 'change':
+ return 'Статус: меняется карта';
+ case 'install':
+ return 'Статус: устанавливается';
+ case 'reinstall':
+ return 'Статус: переустанавливается';
+ case 'update':
+ return 'Статус: обновляется';
+ case 'recovery':
+ return 'Статус: восстанавливается';
+ case 'overdue':
+ return 'Статус: просрочен';
+ case 'blocked':
+ return 'Статус: заблокирован';
+ }
+ }
+
+ private static function average($arr)
+ {
+ return !count($arr) ? 0 : ceil(array_sum($arr)/count($arr));
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/graph/fonts/arianamu.ttf b/system/library/games/graph/fonts/arianamu.ttf
new file mode 100644
index 0000000..96499aa
Binary files /dev/null and b/system/library/games/graph/fonts/arianamu.ttf differ
diff --git a/system/library/games/graph/fonts/tahoma.ttf b/system/library/games/graph/fonts/tahoma.ttf
new file mode 100644
index 0000000..bd62e60
Binary files /dev/null and b/system/library/games/graph/fonts/tahoma.ttf differ
diff --git a/system/library/games/graph/pData.php b/system/library/games/graph/pData.php
new file mode 100644
index 0000000..dc79cb9
--- /dev/null
+++ b/system/library/games/graph/pData.php
@@ -0,0 +1,875 @@
+array('R'=>188,'G'=>224,'B'=>46,'Alpha'=>100),
+ '1'=>array('R'=>224,'G'=>100,'B'=>46,'Alpha'=>100),
+ '2'=>array('R'=>224,'G'=>214,'B'=>46,'Alpha'=>100),
+ '3'=>array('R'=>46,'G'=>151,'B'=>224,'Alpha'=>100),
+ '4'=>array('R'=>176,'G'=>46,'B'=>224,'Alpha'=>100),
+ '5'=>array('R'=>224,'G'=>46,'B'=>117,'Alpha'=>100),
+ '6'=>array('R'=>92,'G'=>224,'B'=>46,'Alpha'=>100),
+ '7'=>array('R'=>224,'G'=>176,'B'=>46,'Alpha'=>100));
+
+ function pData()
+ {
+ $this->Data = '';
+ $this->Data['XAxisDisplay'] = AXIS_FORMAT_DEFAULT;
+ $this->Data['XAxisFormat'] = NULL;
+ $this->Data['XAxisName'] = NULL;
+ $this->Data['XAxisUnit'] = NULL;
+ $this->Data['Abscissa'] = NULL;
+ $this->Data['AbsicssaPosition'] = AXIS_POSITION_BOTTOM;
+ $this->Data['Axis'][0]['Display'] = AXIS_FORMAT_DEFAULT;
+ $this->Data['Axis'][0]['Position'] = AXIS_POSITION_LEFT;
+ $this->Data['Axis'][0]['Identity'] = AXIS_Y;
+ }
+
+ function addPoints($Values, $SerieName = 'Serie1')
+ {
+ if(!isset($this->Data['Series'][$SerieName]))
+ $this->initialise($SerieName);
+
+ if(is_array($Values))
+ {
+ foreach($Values as $Key => $Value)
+ $this->Data['Series'][$SerieName]['Data'][] = $Value;
+ }else
+ $this->Data['Series'][$SerieName]['Data'][] = $Values;
+
+ if($Values != VOID)
+ {
+ $StrippedData = $this->stripVOID($this->Data['Series'][$SerieName]['Data']);
+
+ if(empty($StrippedData))
+ {
+ $this->Data['Series'][$SerieName]['Max'] = 0;
+ $this->Data['Series'][$SerieName]['Min'] =0;
+ return(0);
+ }
+
+ $this->Data['Series'][$SerieName]['Max'] = max($StrippedData);
+ $this->Data['Series'][$SerieName]['Min'] = min($StrippedData);
+ }
+ }
+
+ function stripVOID($Values)
+ {
+ if(!is_array($Values))
+ return(array());
+
+ $Result = array();
+
+ foreach($Values as $Key => $Value)
+ {
+ if($Value != VOID)
+ $Result[] = $Value;
+ }
+
+ return($Result);
+ }
+
+ function getSerieCount($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]['Data']))
+ return(sizeof($this->Data['Series'][$Serie]['Data']));
+
+ return(0);
+ }
+
+ function removeSerie($Series)
+ {
+ if(!is_array($Series))
+ $Series = $this->convertToArray($Series);
+
+ foreach($Series as $Key => $Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ unset($this->Data['Series'][$Serie]);
+ }
+ }
+
+ function getValueAt($Serie,$Index=0)
+ {
+ if(isset($this->Data['Series'][$Serie]['Data'][$Index]))
+ return($this->Data['Series'][$Serie]['Data'][$Index]);
+
+ return(NULL);
+ }
+
+ function getValues($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]['Data']))
+ return($this->Data['Series'][$Serie]['Data']);
+
+ return(NULL);
+ }
+
+ function reverseSerie($Series)
+ {
+ if(!is_array($Series))
+ $Series = $this->convertToArray($Series);
+
+ foreach($Series as $Key => $Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]['Data']))
+ $this->Data['Series'][$Serie]['Data'] = array_reverse($this->Data['Series'][$Serie]['Data']);
+ }
+ }
+
+ function getSum($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ return(array_sum($this->Data['Series'][$Serie]['Data']));
+
+ return(NULL);
+ }
+
+ function getMax($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]['Max']))
+ return($this->Data['Series'][$Serie]['Max']);
+
+ return(NULL);
+ }
+
+ function getMin($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]['Min']))
+ return($this->Data['Series'][$Serie]['Min']);
+
+ return(NULL);
+ }
+
+ function setSerieShape($Series, $Shape = SERIE_SHAPE_FILLEDCIRCLE)
+ {
+ if(!is_array($Series))
+ $Series = $this->convertToArray($Series);
+
+ foreach($Series as $Key => $Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ $this->Data['Series'][$Serie]['Shape'] = $Shape;
+ }
+ }
+
+ function setSerieDescription($Series, $Description = 'My serie')
+ {
+ if(!is_array($Series))
+ $Series = $this->convertToArray($Series);
+
+ foreach($Series as $Key => $Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ $this->Data['Series'][$Serie]['Description'] = $Description;
+ }
+ }
+
+ function setSerieDrawable($Series, $Drawable = TRUE)
+ {
+ if(!is_array($Series))
+ $Series = $this->convertToArray($Series);
+
+ foreach($Series as $Key => $Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ $this->Data['Series'][$Serie]['isDrawable'] = $Drawable;
+ }
+ }
+
+ function setSeriePicture($Series,$Picture=NULL)
+ {
+ if(!is_array($Series))
+ $Series = $this->convertToArray($Series);
+
+ foreach($Series as $Key => $Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ $this->Data['Series'][$Serie]['Picture'] = $Picture;
+ }
+ }
+
+ function setXAxisName($Name)
+ {
+ $this->Data['XAxisName'] = $Name;
+ }
+
+ function setXAxisDisplay($Mode, $Format = NULL)
+ {
+ $this->Data['XAxisDisplay'] = $Mode;
+ $this->Data['XAxisFormat'] = $Format;
+ }
+
+ function setXAxisUnit($Unit)
+ {
+ $this->Data['XAxisUnit'] = $Unit;
+ }
+
+ function setAbscissa($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ $this->Data['Abscissa'] = $Serie;
+ }
+
+ function setAbsicssaPosition($Position = AXIS_POSITION_BOTTOM)
+ {
+ $this->Data['AbsicssaPosition'] = $Position;
+ }
+
+ function setAbscissaName($Name)
+ {
+ $this->Data['AbscissaName'] = $Name;
+ }
+
+ function setScatterSerie($SerieX, $SerieY, $ID = 0)
+ {
+ if(isset($this->Data['Series'][$SerieX]) && isset($this->Data['Series'][$SerieY]))
+ {
+ $this->initScatterSerie($ID);
+ $this->Data['ScatterSeries'][$ID]['X'] = $SerieX;
+ $this->Data['ScatterSeries'][$ID]['Y'] = $SerieY;
+ }
+ }
+
+ function setScatterSerieShape($ID, $Shape = SERIE_SHAPE_FILLEDCIRCLE)
+ {
+ if(isset($this->Data['ScatterSeries'][$ID]))
+ $this->Data['ScatterSeries'][$ID]['Shape'] = $Shape;
+ }
+
+ function setScatterSerieDescription($ID, $Description = 'My serie')
+ {
+ if(isset($this->Data['ScatterSeries'][$ID]))
+ $this->Data['ScatterSeries'][$ID]['Description'] = $Description;
+ }
+
+ function setScatterSeriePicture($ID, $Picture = NULL)
+ {
+ if(isset($this->Data['ScatterSeries'][$ID]))
+ $this->Data['ScatterSeries'][$ID]['Picture'] = $Picture;
+ }
+
+ function setScatterSerieDrawable($ID, $Drawable = TRUE)
+ {
+ if(isset($this->Data['ScatterSeries'][$ID]))
+ $this->Data['ScatterSeries'][$ID]['isDrawable'] = $Drawable;
+ }
+
+ function setScatterSerieTicks($ID, $Width = 0)
+ {
+ if(isset($this->Data['ScatterSeries'][$ID]))
+ $this->Data['ScatterSeries'][$ID]['Ticks'] = $Width;
+ }
+
+ function setScatterSerieWeight($ID, $Weight = 0)
+ {
+ if(isset($this->Data['ScatterSeries'][$ID]))
+ $this->Data['ScatterSeries'][$ID]['Weight'] = $Weight;
+ }
+
+ function setScatterSerieColor($ID, $Format)
+ {
+ $R = isset($Format['R']) ? $Format['R'] : 0;
+ $G = isset($Format['G']) ? $Format['G'] : 0;
+ $B = isset($Format['B']) ? $Format['B'] : 0;
+ $Alpha = isset($Format['Alpha']) ? $Format['Alpha'] : 100;
+
+ if(isset($this->Data['ScatterSeries'][$ID]))
+ {
+ $this->Data['ScatterSeries'][$ID]['Color']['R'] = $R;
+ $this->Data['ScatterSeries'][$ID]['Color']['G'] = $G;
+ $this->Data['ScatterSeries'][$ID]['Color']['B'] = $B;
+ $this->Data['ScatterSeries'][$ID]['Color']['Alpha'] = $Alpha;
+ }
+ }
+
+ function limits()
+ {
+ $GlobalMin = ABSOLUTE_MAX;
+ $GlobalMax = ABSOLUTE_MIN;
+
+ foreach($this->Data['Series'] as $Key => $Value)
+ {
+ if($this->Data['Abscissa'] != $Key && $this->Data['Series'][$Key]['isDrawable'] == TRUE)
+ {
+ if($GlobalMin > $this->Data['Series'][$Key]['Min'])
+ $GlobalMin = $this->Data['Series'][$Key]['Min'];
+
+ if($GlobalMax < $this->Data['Series'][$Key]['Max'])
+ $GlobalMax = $this->Data['Series'][$Key]['Max'];
+ }
+ }
+
+ $this->Data['Min'] = $GlobalMin;
+ $this->Data['Max'] = $GlobalMax;
+
+ return(array($GlobalMin,$GlobalMax));
+ }
+
+ function drawAll()
+ {
+ foreach($this->Data['Series'] as $Key => $Value)
+ {
+ if($this->Data['Abscissa'] != $Key)
+ $this->Data['Series'][$Key]['isDrawable'] = TRUE;
+ }
+ }
+
+ function getSerieAverage($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ {
+ $SerieData = $this->stripVOID($this->Data['Series'][$Serie]['Data']);
+
+ return(array_sum($SerieData)/sizeof($SerieData));
+ }
+
+ return(NULL);
+ }
+
+ function getGeometricMean($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ {
+ $SerieData = $this->stripVOID($this->Data['Series'][$Serie]['Data']);
+ $Seriesum = 1;
+
+ foreach($SerieData as $Key => $Value)
+ $Seriesum = $Seriesum * $Value;
+
+ return(pow($Seriesum,1/sizeof($SerieData)));
+ }
+
+ return(NULL);
+ }
+
+ function getHarmonicMean($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ {
+ $SerieData = $this->stripVOID($this->Data['Series'][$Serie]['Data']);
+ $Seriesum = 0;
+
+ foreach($SerieData as $Key => $Value)
+ $Seriesum = $Seriesum + 1/$Value;
+
+ return(sizeof($SerieData)/$Seriesum);
+ }
+
+ return(NULL);
+ }
+
+ function getStandardDeviation($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ {
+ $Average = $this->getSerieAverage($Serie);
+ $SerieData = $this->stripVOID($this->Data['Series'][$Serie]['Data']);
+
+ $DeviationSum = 0;
+
+ foreach($SerieData as $Key => $Value)
+ $DeviationSum = $DeviationSum + ($Value-$Average)*($Value-$Average);
+
+ $Deviation = sqrt($DeviationSum/count($SerieData));
+
+ return($Deviation);
+ }
+
+ return(NULL);
+ }
+
+ function getCoefficientOfVariation($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ {
+ $Average = $this->getSerieAverage($Serie);
+ $StandardDeviation = $this->getStandardDeviation($Serie);
+
+ if($StandardDeviation != 0)
+ return($StandardDeviation/$Average);
+ }
+
+ return(NULL);
+ }
+
+ function getSerieMedian($Serie)
+ {
+ if(isset($this->Data['Series'][$Serie]))
+ {
+ $SerieData = $this->stripVOID($this->Data['Series'][$Serie]['Data']);
+
+ sort($SerieData);
+
+ $SerieCenter = floor(sizeof($SerieData)/2);
+
+ if(isset($SerieData[$SerieCenter]))
+ return($SerieData[$SerieCenter]);
+ }
+
+ return(NULL);
+ }
+
+ function getSeriePercentile($Serie = 'Serie1', $Percentil = 95)
+ {
+ if(!isset($this->Data['Series'][$Serie]['Data']))
+ return(NULL);
+
+ $Values = count($this->Data['Series'][$Serie]['Data'])-1;
+
+ if($Values < 0)
+ $Values = 0;
+
+ $PercentilID = floor(($Values/100)*$Percentil+.5);
+ $SortedValues = $this->Data['Series'][$Serie]['Data'];
+
+ sort($SortedValues);
+
+ if(is_numeric($SortedValues[$PercentilID]))
+ return($SortedValues[$PercentilID]);
+
+ return(NULL);
+ }
+
+ function addRandomValues($SerieName = 'Serie1', $Options = '')
+ {
+ $Values = isset($Options['Values']) ? $Options['Values'] : 20;
+ $Min = isset($Options['Min']) ? $Options['Min'] : 0;
+ $Max = isset($Options['Max']) ? $Options['Max'] : 100;
+ $withFloat = isset($Options['withFloat']) ? $Options['withFloat'] : FALSE;
+
+ for($i = 0; $i <= $Values; $i+=1)
+ {
+ if($withFloat)
+ $Value = rand($Min*100,$Max*100)/100;
+ else
+ $Value = rand($Min, $Max);
+
+ $this->addPoints($Value,$SerieName);
+ }
+ }
+
+ function containsData()
+ {
+ if(!isset($this->Data['Series']))
+ return(FALSE);
+
+ $Result = FALSE;
+
+ foreach($this->Data['Series'] as $Key => $Value)
+ {
+ if($this->Data['Abscissa'] != $Key && $this->Data['Series'][$Key]['isDrawable'] == TRUE)
+ $Result=TRUE;
+ }
+
+ return($Result);
+ }
+
+ function setAxisDisplay($AxisID, $Mode = AXIS_FORMAT_DEFAULT, $Format = NULL)
+ {
+ if(isset($this->Data['Axis'][$AxisID]))
+ {
+ $this->Data['Axis'][$AxisID]['Display'] = $Mode;
+
+ if($Format != NULL)
+ $this->Data['Axis'][$AxisID]['Format'] = $Format;
+ }
+ }
+
+ function setAxisPosition($AxisID, $Position = AXIS_POSITION_LEFT)
+ {
+ if(isset($this->Data['Axis'][$AxisID]))
+ {
+ $this->Data['Axis'][$AxisID]['Position'] = $Position;
+ }
+ }
+
+ /* Associate an unit to an axis */
+ function setAxisUnit($AxisID,$Unit)
+ { if ( isset($this->Data['Axis'][$AxisID] ) ) { $this->Data['Axis'][$AxisID]['Unit'] = $Unit; } }
+
+ /* Associate a name to an axis */
+ function setAxisName($AxisID,$Name)
+ { if ( isset($this->Data['Axis'][$AxisID] ) ) { $this->Data['Axis'][$AxisID]['Name'] = $Name; } }
+
+ /* Associate a color to an axis */
+ function setAxisColor($AxisID,$Format)
+ {
+ $R = isset($Format['R']) ? $Format['R'] : 0;
+ $G = isset($Format['G']) ? $Format['G'] : 0;
+ $B = isset($Format['B']) ? $Format['B'] : 0;
+ $Alpha = isset($Format['Alpha']) ? $Format['Alpha'] : 100;
+
+ if ( isset($this->Data['Axis'][$AxisID] ) )
+ {
+ $this->Data['Axis'][$AxisID]['Color']['R'] = $R;
+ $this->Data['Axis'][$AxisID]['Color']['G'] = $G;
+ $this->Data['Axis'][$AxisID]['Color']['B'] = $B;
+ $this->Data['Axis'][$AxisID]['Color']['Alpha'] = $Alpha;
+ }
+ }
+
+
+ /* Design an axis as X or Y member */
+ function setAxisXY($AxisID,$Identity=AXIS_Y)
+ { if ( isset($this->Data['Axis'][$AxisID] ) ) { $this->Data['Axis'][$AxisID]['Identity'] = $Identity; } }
+
+ /* Associate one data serie with one axis */
+ function setSerieOnAxis($Series,$AxisID)
+ {
+ if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); }
+ foreach($Series as $Key => $Serie)
+ {
+ $PreviousAxis = $this->Data['Series'][$Serie]['Axis'];
+
+ /* Create missing axis */
+ if ( !isset($this->Data['Axis'][$AxisID] ) )
+ { $this->Data['Axis'][$AxisID]['Position'] = AXIS_POSITION_LEFT; $this->Data['Axis'][$AxisID]['Identity'] = AXIS_Y;}
+
+ $this->Data['Series'][$Serie]['Axis'] = $AxisID;
+
+ /* Cleanup unused axis */
+ $Found = FALSE;
+ foreach($this->Data['Series'] as $SerieName => $Values) { if ( $Values['Axis'] == $PreviousAxis ) { $Found = TRUE; } }
+ if (!$Found) { unset($this->Data['Axis'][$PreviousAxis]); }
+ }
+ }
+
+ /* define if a serie should be draw with ticks */
+ function setSerieTicks($Series,$Width=0)
+ {
+ if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); }
+ foreach($Series as $Key => $Serie) { if ( isset($this->Data['Series'][$Serie]) ) { $this->Data['Series'][$Serie]['Ticks'] = $Width; } }
+ }
+
+ /* define if a serie should be draw with a special weight */
+ function setSerieWeight($Series,$Weight=0)
+ {
+ if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); }
+ foreach($Series as $Key => $Serie) { if ( isset($this->Data['Series'][$Serie]) ) { $this->Data['Series'][$Serie]['Weight'] = $Weight; } }
+ }
+
+ /* Returns the palette of the given serie */
+ function getSeriePalette($Serie)
+ {
+ if ( !isset($this->Data['Series'][$Serie]) ) { return(NULL); }
+
+ $Result = '';
+ $Result['R'] = $this->Data['Series'][$Serie]['Color']['R'];
+ $Result['G'] = $this->Data['Series'][$Serie]['Color']['G'];
+ $Result['B'] = $this->Data['Series'][$Serie]['Color']['B'];
+ $Result['Alpha'] = $this->Data['Series'][$Serie]['Color']['Alpha'];
+
+ return($Result);
+ }
+
+ /* Set the color of one serie */
+ function setPalette($Series,$Format=NULL)
+ {
+ if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); }
+
+ foreach($Series as $Key => $Serie)
+ {
+ $R = isset($Format['R']) ? $Format['R'] : 0;
+ $G = isset($Format['G']) ? $Format['G'] : 0;
+ $B = isset($Format['B']) ? $Format['B'] : 0;
+ $Alpha = isset($Format['Alpha']) ? $Format['Alpha'] : 100;
+
+ if ( isset($this->Data['Series'][$Serie]) )
+ {
+ $OldR = $this->Data['Series'][$Serie]['Color']['R']; $OldG = $this->Data['Series'][$Serie]['Color']['G']; $OldB = $this->Data['Series'][$Serie]['Color']['B'];
+ $this->Data['Series'][$Serie]['Color']['R'] = $R;
+ $this->Data['Series'][$Serie]['Color']['G'] = $G;
+ $this->Data['Series'][$Serie]['Color']['B'] = $B;
+ $this->Data['Series'][$Serie]['Color']['Alpha'] = $Alpha;
+
+ /* Do reverse processing on the internal palette array */
+ foreach ($this->Palette as $Key => $Value)
+ { if ($Value['R'] == $OldR && $Value['G'] == $OldG && $Value['B'] == $OldB) { $this->Palette[$Key]['R'] = $R; $this->Palette[$Key]['G'] = $G; $this->Palette[$Key]['B'] = $B; $this->Palette[$Key]['Alpha'] = $Alpha;} }
+ }
+ }
+ }
+
+ /* Load a palette file */
+ function loadPalette($FileName,$Overwrite=FALSE)
+ {
+ if ( !file_exists($FileName) ) { return(-1); }
+ if ( $Overwrite ) { $this->Palette = ''; }
+
+ $fileHandle = @fopen($FileName, 'r');
+ if (!$fileHandle) { return(-1); }
+ while (!feof($fileHandle))
+ {
+ $buffer = fgets($fileHandle, 4096);
+ if ( preg_match('/,/',$buffer) )
+ {
+ list($R,$G,$B,$Alpha) = preg_split('/,/',$buffer);
+ if ( $this->Palette == '' ) { $ID = 0; } else { $ID = count($this->Palette); }
+ $this->Palette[$ID] = array('R'=>$R,'G'=>$G,'B'=>$B,'Alpha'=>$Alpha);
+ }
+ }
+ fclose($fileHandle);
+
+ /* Apply changes to current series */
+ $ID = 0;
+ if ( isset($this->Data['Series']))
+ {
+ foreach($this->Data['Series'] as $Key => $Value)
+ {
+ if ( !isset($this->Palette[$ID]) )
+ $this->Data['Series'][$Key]['Color'] = array('R'=>0,'G'=>0,'B'=>0,'Alpha'=>0);
+ else
+ $this->Data['Series'][$Key]['Color'] = $this->Palette[$ID];
+ $ID++;
+ }
+ }
+ }
+
+ /* Initialise a given scatter serie */
+ function initScatterSerie($ID)
+ {
+ if ( isset($this->Data['ScatterSeries'][$ID]) ) { return(0); }
+
+ $this->Data['ScatterSeries'][$ID]['Description'] = 'Scatter '.$ID;
+ $this->Data['ScatterSeries'][$ID]['isDrawable'] = TRUE;
+ $this->Data['ScatterSeries'][$ID]['Picture'] = NULL;
+ $this->Data['ScatterSeries'][$ID]['Ticks'] = 0;
+ $this->Data['ScatterSeries'][$ID]['Weight'] = 0;
+
+ if ( isset($this->Palette[$ID]) )
+ $this->Data['ScatterSeries'][$ID]['Color'] = $this->Palette[$ID];
+ else
+ {
+ $this->Data['ScatterSeries'][$ID]['Color']['R'] = rand(0,255);
+ $this->Data['ScatterSeries'][$ID]['Color']['G'] = rand(0,255);
+ $this->Data['ScatterSeries'][$ID]['Color']['B'] = rand(0,255);
+ $this->Data['ScatterSeries'][$ID]['Color']['Alpha'] = 100;
+ }
+ }
+
+ /* Initialise a given serie */
+ function initialise($Serie)
+ {
+ if ( isset($this->Data['Series']) ) { $ID = count($this->Data['Series']); } else { $ID = 0; }
+
+ $this->Data['Series'][$Serie]['Description'] = $Serie;
+ $this->Data['Series'][$Serie]['isDrawable'] = TRUE;
+ $this->Data['Series'][$Serie]['Picture'] = NULL;
+ $this->Data['Series'][$Serie]['Max'] = NULL;
+ $this->Data['Series'][$Serie]['Min'] = NULL;
+ $this->Data['Series'][$Serie]['Axis'] = 0;
+ $this->Data['Series'][$Serie]['Ticks'] = 0;
+ $this->Data['Series'][$Serie]['Weight'] = 0;
+ $this->Data['Series'][$Serie]['Shape'] = SERIE_SHAPE_FILLEDCIRCLE;
+
+ if ( isset($this->Palette[$ID]) )
+ $this->Data['Series'][$Serie]['Color'] = $this->Palette[$ID];
+ else
+ {
+ $this->Data['Series'][$Serie]['Color']['R'] = rand(0,255);
+ $this->Data['Series'][$Serie]['Color']['G'] = rand(0,255);
+ $this->Data['Series'][$Serie]['Color']['B'] = rand(0,255);
+ $this->Data['Series'][$Serie]['Color']['Alpha'] = 100;
+ }
+ }
+
+ function normalize($NormalizationFactor=100,$UnitChange=NULL,$Round=1)
+ {
+ $Abscissa = $this->Data['Abscissa'];
+
+ $SelectedSeries = '';
+ $MaxVal = 0;
+ foreach($this->Data['Axis'] as $AxisID => $Axis)
+ {
+ if ( $UnitChange != NULL ) { $this->Data['Axis'][$AxisID]['Unit'] = $UnitChange; }
+
+ foreach($this->Data['Series'] as $SerieName => $Serie)
+ {
+ if ($Serie['Axis'] == $AxisID && $Serie['isDrawable'] == TRUE && $SerieName != $Abscissa)
+ {
+ $SelectedSeries[$SerieName] = $SerieName;
+
+ if ( count($Serie['Data'] ) > $MaxVal ) { $MaxVal = count($Serie['Data']); }
+ }
+ }
+ }
+
+ for($i=0;$i<=$MaxVal-1;$i++)
+ {
+ $Factor = 0;
+ foreach ($SelectedSeries as $Key => $SerieName )
+ {
+ $Value = $this->Data['Series'][$SerieName]['Data'][$i];
+ if ( $Value != VOID )
+ $Factor = $Factor + abs($Value);
+ }
+
+ if ( $Factor != 0 )
+ {
+ $Factor = $NormalizationFactor / $Factor;
+
+ foreach ($SelectedSeries as $Key => $SerieName )
+ {
+ $Value = $this->Data['Series'][$SerieName]['Data'][$i];
+
+ if ( $Value != VOID && $Factor != $NormalizationFactor )
+ $this->Data['Series'][$SerieName]['Data'][$i] = round(abs($Value)*$Factor,$Round);
+ elseif ( $Value == VOID || $Value == 0 )
+ $this->Data['Series'][$SerieName]['Data'][$i] = VOID;
+ elseif ( $Factor == $NormalizationFactor )
+ $this->Data['Series'][$SerieName]['Data'][$i] = $NormalizationFactor;
+ }
+ }
+ }
+
+ foreach ($SelectedSeries as $Key => $SerieName )
+ {
+ $this->Data['Series'][$SerieName]['Max'] = max($this->stripVOID($this->Data['Series'][$SerieName]['Data']));
+ $this->Data['Series'][$SerieName]['Min'] = min($this->stripVOID($this->Data['Series'][$SerieName]['Data']));
+ }
+ }
+
+ /* Load data from a CSV (or similar) data source */
+ function importFromCSV($FileName,$Options='')
+ {
+ $Delimiter = isset($Options['Delimiter']) ? $Options['Delimiter'] : ',';
+ $GotHeader = isset($Options['GotHeader']) ? $Options['GotHeader'] : FALSE;
+ $SkipColumns = isset($Options['SkipColumns']) ? $Options['SkipColumns'] : array(-1);
+ $DefaultSerieName = isset($Options['DefaultSerieName']) ? $Options['DefaultSerieName'] : 'Serie';
+
+ $Handle = @fopen($FileName,'r');
+ if ($Handle)
+ {
+ $HeaderParsed = FALSE; $SerieNames = '';
+ while (!feof($Handle))
+ {
+ $Buffer = fgets($Handle, 4096);
+ $Buffer = str_replace(chr(10),'',$Buffer);
+ $Buffer = str_replace(chr(13),'',$Buffer);
+ $Values = preg_split('/'.$Delimiter.'/',$Buffer);
+
+ if ( $Buffer != '' )
+ {
+ if ( $GotHeader && !$HeaderParsed )
+ {
+ foreach($Values as $Key => $Name) { if ( !in_array($Key,$SkipColumns) ) { $SerieNames[$Key] = $Name; } }
+ $HeaderParsed = TRUE;
+ }
+ else
+ {
+ if ($SerieNames == '' ) { foreach($Values as $Key => $Name) { if ( !in_array($Key,$SkipColumns) ) { $SerieNames[$Key] = $DefaultSerieName.$Key; } } }
+ foreach($Values as $Key => $Value) { if ( !in_array($Key,$SkipColumns) ) { $this->addPoints($Value,$SerieNames[$Key]); } }
+ }
+ }
+ }
+ fclose($Handle);
+ }
+ }
+
+ /* Create a dataset based on a formula */
+ function createFunctionSerie($SerieName,$Formula='',$Options='')
+ {
+ $MinX = isset($Options['MinX']) ? $Options['MinX'] : -10;
+ $MaxX = isset($Options['MaxX']) ? $Options['MaxX'] : 10;
+ $XStep = isset($Options['XStep']) ? $Options['XStep'] : 1;
+ $AutoDescription = isset($Options['AutoDescription']) ? $Options['AutoDescription'] : FALSE;
+ $RecordAbscissa = isset($Options['RecordAbscissa']) ? $Options['RecordAbscissa'] : FALSE;
+ $AbscissaSerie = isset($Options['AbscissaSerie']) ? $Options['AbscissaSerie'] : 'Abscissa';
+
+ if ( $Formula == '' ) { return(0); }
+
+ $Result = ''; $Abscissa = '';
+ for($i=$MinX; $i<=$MaxX; $i=$i+$XStep)
+ {
+ $Expression = '\$return = \'!\'.('.str_replace('z',$i,$Formula).');';
+ if ( @eval($Expression) === FALSE ) { $return = VOID; }
+ if ( $return == '!' ) { $return = VOID; } else { $return = $this->right($return,strlen($return)-1); }
+ if ( $return == 'NAN' ) { $return = VOID; }
+ if ( $return == 'INF' ) { $return = VOID; }
+ if ( $return == '-INF' ) { $return = VOID; }
+
+ $Abscissa[] = $i;
+ $Result[] = $return;
+ }
+
+ $this->addPoints($Result,$SerieName);
+ if ( $AutoDescription ) { $this->setSerieDescription($SerieName,$Formula); }
+ if ( $RecordAbscissa ) { $this->addPoints($Abscissa,$AbscissaSerie); }
+ }
+
+ function negateValues($Series)
+ {
+ if ( !is_array($Series) ) { $Series = $this->convertToArray($Series); }
+ foreach($Series as $Key => $SerieName)
+ {
+ if (isset($this->Data['Series'][$SerieName]))
+ {
+ $Data = '';
+ foreach($this->Data['Series'][$SerieName]['Data'] as $Key => $Value)
+ { if ( $Value == VOID ) { $Data[] = VOID; } else { $Data[] = -$Value; } }
+ $this->Data['Series'][$SerieName]['Data'] = $Data;
+
+ $this->Data['Series'][$SerieName]['Max'] = max($this->stripVOID($this->Data['Series'][$SerieName]['Data']));
+ $this->Data['Series'][$SerieName]['Min'] = min($this->stripVOID($this->Data['Series'][$SerieName]['Data']));
+ }
+ }
+ }
+
+ function getData()
+ { return($this->Data); }
+
+ function savePalette($ID,$Color)
+ { $this->Palette[$ID] = $Color; }
+
+ function getPalette()
+ { return($this->Palette); }
+
+ function saveAxisConfig($Axis) { $this->Data['Axis']=$Axis; }
+
+ function saveYMargin($Value) { $this->Data['YMargin']=$Value; }
+
+ function saveExtendedData($Tag,$Values) { $this->Data['Extended'][$Tag]=$Values; }
+
+ function saveOrientation($Orientation) { $this->Data['Orientation']=$Orientation; }
+
+ function convertToArray($Value)
+ { $Values = ''; $Values[] = $Value; return($Values); }
+
+ function __toString()
+ { return('pData object.'); }
+
+ function left($value,$NbChar) { return substr($value,0,$NbChar); }
+ function right($value,$NbChar) { return substr($value,strlen($value)-$NbChar,$NbChar); }
+ function mid($value,$Depart,$NbChar) { return substr($value,$Depart-1,$NbChar); }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/graph/pDraw.php b/system/library/games/graph/pDraw.php
new file mode 100644
index 0000000..cc49e57
--- /dev/null
+++ b/system/library/games/graph/pDraw.php
@@ -0,0 +1,6205 @@
+DataSet->getData();
+
+ foreach($Data["Series"] as $SerieName => $Serie)
+ { if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] ) { $Results++; } }
+
+ return($Results);
+ }
+
+ /* Fix box coordinates */
+ function fixBoxCoordinates($Xa,$Ya,$Xb,$Yb)
+ {
+ $X1 = min($Xa,$Xb); $Y1 = min($Ya,$Yb);
+ $X2 = max($Xa,$Xb); $Y2 = max($Ya,$Yb);
+
+ return(array($X1,$Y1,$X2,$Y2));
+ }
+
+ /* Draw a polygon */
+ function drawPolygon($Points,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : FALSE;
+ $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : FALSE;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
+ $BorderAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $Alpha / 2;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+ $SkipX = isset($Format["SkipX"]) ? $Format["SkipX"] : OUT_OF_SIGHT;
+ $SkipY = isset($Format["SkipY"]) ? $Format["SkipY"] : OUT_OF_SIGHT;
+
+ /* Calling the ImageFilledPolygon() function over the $Points array will round it */
+ $Backup = $Points;
+
+ if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; }
+
+ if ( $SkipX != OUT_OF_SIGHT ) { $SkipX = floor($SkipX); }
+ if ( $SkipY != OUT_OF_SIGHT ) { $SkipY = floor($SkipY); }
+
+ $RestoreShadow = $this->Shadow;
+ if ( !$NoFill )
+ {
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $this->Shadow = FALSE;
+ for($i=0;$i<=count($Points)-1;$i=$i+2)
+ { $Shadow[] = $Points[$i] + $this->ShadowX; $Shadow[] = $Points[$i+1] + $this->ShadowY; }
+ $this->drawPolygon($Shadow,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"NoBorder"=>TRUE));
+ }
+
+ $FillColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+
+ if ( count($Points) >= 6 )
+ { ImageFilledPolygon($this->Picture,$Points,count($Points)/2,$FillColor); }
+ }
+
+ if ( !$NoBorder )
+ {
+ $Points = $Backup;
+
+ if ( $NoFill )
+ $BorderSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha);
+ else
+ $BorderSettings = array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha);
+
+ for($i=0;$i<=count($Points)-1;$i=$i+2)
+ {
+ if ( isset($Points[$i+2]) )
+ {
+ if ( !($Points[$i] == $Points[$i+2] && $Points[$i] == $SkipX ) && !($Points[$i+1] == $Points[$i+3] && $Points[$i+1] == $SkipY ) )
+ $this->drawLine($Points[$i],$Points[$i+1],$Points[$i+2],$Points[$i+3],$BorderSettings);
+ }
+ else
+ {
+ if ( !($Points[$i] == $Points[0] && $Points[$i] == $SkipX ) && !($Points[$i+1] == $Points[1] && $Points[$i+1] == $SkipY ) )
+ $this->drawLine($Points[$i],$Points[$i+1],$Points[0],$Points[1],$BorderSettings);
+ }
+ }
+ }
+
+ $this->Shadow = $RestoreShadow;
+ }
+
+ /* Apply AALias correction to the rounded box boundaries */
+ function offsetCorrection($Value,$Mode)
+ {
+ $Value = round($Value,1);
+
+ if ( $Value == 0 && $Mode == 1 ) { return(.9); }
+ if ( $Value == 0 ) { return(0); }
+
+ if ( $Mode == 1)
+ { if ( $Value == 1 ) { return(.9); }; if ( $Value == .1 ) { return(.9); }; if ( $Value == .2 ) { return(.8); }; if ( $Value == .3 ) { return(.8); }; if ( $Value == .4 ) { return(.7); }; if ( $Value == .5 ) { return(.5); }; if ( $Value == .6 ) { return(.8); }; if ( $Value == .7 ) { return(.7); }; if ( $Value == .8 ) { return(.6); }; if ( $Value == .9 ) { return(.9); }; }
+
+ if ( $Mode == 2)
+ { if ( $Value == 1 ) { return(.9); }; if ( $Value == .1 ) { return(.1); }; if ( $Value == .2 ) { return(.2); }; if ( $Value == .3 ) { return(.3); }; if ( $Value == .4 ) { return(.4); }; if ( $Value == .5 ) { return(.5); }; if ( $Value == .6 ) { return(.8); }; if ( $Value == .7 ) { return(.7); }; if ( $Value == .8 ) { return(.8); }; if ( $Value == .9 ) { return(.9); }; }
+
+ if ( $Mode == 3)
+ { if ( $Value == 1 ) { return(.1); }; if ( $Value == .1 ) { return(.1); }; if ( $Value == .2 ) { return(.2); }; if ( $Value == .3 ) { return(.3); }; if ( $Value == .4 ) { return(.4); }; if ( $Value == .5 ) { return(.9); }; if ( $Value == .6 ) { return(.6); }; if ( $Value == .7 ) { return(.7); }; if ( $Value == .8 ) { return(.4); }; if ( $Value == .9 ) { return(.5); }; }
+
+ if ( $Mode == 4)
+ { if ( $Value == 1 ) { return(-1); }; if ( $Value == .1 ) { return(.1); }; if ( $Value == .2 ) { return(.2); }; if ( $Value == .3 ) { return(.3); }; if ( $Value == .4 ) { return(.1); }; if ( $Value == .5 ) { return(-.1); }; if ( $Value == .6 ) { return(.8); }; if ( $Value == .7 ) { return(.1); }; if ( $Value == .8 ) { return(.1); }; if ( $Value == .9 ) { return(.1); }; }
+ }
+
+ /* Draw a rectangle with rounded corners */
+ function drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+
+ list($X1,$Y1,$X2,$Y2) = $this->fixBoxCoordinates($X1,$Y1,$X2,$Y2);
+
+ if ( $X2 - $X1 < $Radius ) { $Radius = floor((($X2-$X1))/2); }
+ if ( $Y2 - $Y1 < $Radius ) { $Radius = floor((($Y2-$Y1))/2); }
+
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"NoBorder"=>TRUE);
+
+ if ( $Radius <= 0 ) { $this->drawRectangle($X1,$Y1,$X2,$Y2,$Color); return(0); }
+
+ if ( $this->Antialias )
+ {
+ $this->drawLine($X1+$Radius,$Y1,$X2-$Radius,$Y1,$Color);
+ $this->drawLine($X2,$Y1+$Radius,$X2,$Y2-$Radius,$Color);
+ $this->drawLine($X2-$Radius,$Y2,$X1+$Radius,$Y2,$Color);
+ $this->drawLine($X1,$Y1+$Radius,$X1,$Y2-$Radius,$Color);
+ }
+ else
+ {
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ imageline($this->Picture,$X1+$Radius,$Y1,$X2-$Radius,$Y1,$Color);
+ imageline($this->Picture,$X2,$Y1+$Radius,$X2,$Y2-$Radius,$Color);
+ imageline($this->Picture,$X2-$Radius,$Y2,$X1+$Radius,$Y2,$Color);
+ imageline($this->Picture,$X1,$Y1+$Radius,$X1,$Y2-$Radius,$Color);
+ }
+
+ $Step = 360 / (2 * PI * $Radius);
+ for($i=0;$i<=90;$i=$i+$Step)
+ {
+ $X = cos(($i+180)*PI/180) * $Radius + $X1 + $Radius;
+ $Y = sin(($i+180)*PI/180) * $Radius + $Y1 + $Radius;
+ $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+
+ $X = cos(($i+90)*PI/180) * $Radius + $X1 + $Radius;
+ $Y = sin(($i+90)*PI/180) * $Radius + $Y2 - $Radius;
+ $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+
+ $X = cos($i*PI/180) * $Radius + $X2 - $Radius;
+ $Y = sin($i*PI/180) * $Radius + $Y2 - $Radius;
+ $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+
+ $X = cos(($i+270)*PI/180) * $Radius + $X2 - $Radius;
+ $Y = sin(($i+270)*PI/180) * $Radius + $Y1 + $Radius;
+ $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ }
+ }
+
+ /* Draw a rectangle with rounded corners */
+ function drawRoundedFilledRectangle($X1,$Y1,$X2,$Y2,$Radius,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+
+ /* Temporary fix for AA issue */
+ $Y1 = floor($Y1); $Y2 = floor($Y2); $X1 = floor($X1); $X2 = floor($X2);
+
+ if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; }
+ if ( $BorderR == -1 ) { $BorderR = $R; $BorderG = $G; $BorderB = $B; }
+
+ list($X1,$Y1,$X2,$Y2) = $this->fixBoxCoordinates($X1,$Y1,$X2,$Y2);
+
+ if ( $X2 - $X1 < $Radius*2 ) { $Radius = floor((($X2-$X1))/4); }
+ if ( $Y2 - $Y1 < $Radius*2 ) { $Radius = floor((($Y2-$Y1))/4); }
+
+ $RestoreShadow = $this->Shadow;
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $this->Shadow = FALSE;
+ $this->drawRoundedFilledRectangle($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,$Radius,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa));
+ }
+
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"NoBorder"=>TRUE);
+
+ if ( $Radius <= 0 ) { $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Color); return(0); }
+
+ $YTop = $Y1+$Radius;
+ $YBottom = $Y2-$Radius;
+
+ $Step = 360 / (2 * PI * $Radius);
+ $Positions = ""; $Radius--; $MinY = ""; $MaxY = "";
+ for($i=0;$i<=90;$i=$i+$Step)
+ {
+ $Xp1 = cos(($i+180)*PI/180) * $Radius + $X1 + $Radius;
+ $Xp2 = cos(((90-$i)+270)*PI/180) * $Radius + $X2 - $Radius;
+ $Yp = floor(sin(($i+180)*PI/180) * $Radius + $YTop);
+ if ( $MinY == "" || $Yp > $MinY ) { $MinY = $Yp; }
+
+ if ( $Xp1 <= floor($X1) ) { $Xp1++; }
+ if ( $Xp2 >= floor($X2) ) { $Xp2--; }
+ $Xp1++;
+
+ if ( !isset($Positions[$Yp]) )
+ { $Positions[$Yp]["X1"] = $Xp1; $Positions[$Yp]["X2"] = $Xp2; }
+ else
+ { $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"]+$Xp1)/2; $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"]+$Xp2)/2; }
+
+ $Xp1 = cos(($i+90)*PI/180) * $Radius + $X1 + $Radius;
+ $Xp2 = cos((90-$i)*PI/180) * $Radius + $X2 - $Radius;
+ $Yp = floor(sin(($i+90)*PI/180) * $Radius + $YBottom);
+ if ( $MaxY == "" || $Yp < $MaxY ) { $MaxY = $Yp; }
+
+ if ( $Xp1 <= floor($X1) ) { $Xp1++; }
+ if ( $Xp2 >= floor($X2) ) { $Xp2--; }
+ $Xp1++;
+
+ if ( !isset($Positions[$Yp]) )
+ { $Positions[$Yp]["X1"] = $Xp1; $Positions[$Yp]["X2"] = $Xp2; }
+ else
+ { $Positions[$Yp]["X1"] = ($Positions[$Yp]["X1"]+$Xp1)/2; $Positions[$Yp]["X2"] = ($Positions[$Yp]["X2"]+$Xp2)/2; }
+ }
+
+ $ManualColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ foreach($Positions as $Yp => $Bounds)
+ {
+ $X1 = $Bounds["X1"]; $X1Dec = $this->getFirstDecimal($X1); if ( $X1Dec != 0 ) { $X1 = floor($X1)+1; }
+ $X2 = $Bounds["X2"]; $X2Dec = $this->getFirstDecimal($X2); if ( $X2Dec != 0 ) { $X2 = floor($X2)-1; }
+ imageline($this->Picture,$X1,$Yp,$X2,$Yp,$ManualColor);
+ }
+ $this->drawFilledRectangle($X1,$MinY+1,floor($X2),$MaxY-1,$Color);
+
+ $Radius++;
+ $this->drawRoundedRectangle($X1,$Y1,$X2+1,$Y2-1,$Radius,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha));
+
+ $this->Shadow = $RestoreShadow;
+ }
+
+ /* Draw a rectangle with rounded corners */
+ function drawRoundedFilledRectangle_deprecated($X1,$Y1,$X2,$Y2,$Radius,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+
+ if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; }
+ if ( $BorderR == -1 ) { $BorderR = $R; $BorderG = $G; $BorderB = $B; }
+
+ list($X1,$Y1,$X2,$Y2) = $this->fixBoxCoordinates($X1,$Y1,$X2,$Y2);
+
+ if ( $X2 - $X1 < $Radius ) { $Radius = floor((($X2-$X1)+2)/2); }
+ if ( $Y2 - $Y1 < $Radius ) { $Radius = floor((($Y2-$Y1)+2)/2); }
+
+ $RestoreShadow = $this->Shadow;
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $this->Shadow = FALSE;
+ $this->drawRoundedFilledRectangle($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,$Radius,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa));
+ }
+
+ if ( $this->getFirstDecimal($X2) >= 5 ) { $XOffset2 = 1; } else { $XOffset2 = 0; }
+ if ( $this->getFirstDecimal($X1) <= 5 ) { $XOffset1 = 1; } else { $XOffset1 = 0; }
+
+ if ( !$this->Antialias ) { $XOffset1 = 1; $XOffset2 = 1; }
+
+ $YTop = floor($Y1+$Radius);
+ $YBottom = floor($Y2-$Radius);
+
+ $this->drawFilledRectangle($X1-$XOffset1,$YTop,$X2+$XOffset2,$YBottom,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"NoBorder"=>TRUE));
+
+ $Step = 360 / (2 * PI * $Radius);
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ $Color2 = $this->allocateColor($this->Picture,255,0,0,$Alpha);
+ $Drawn = "";
+
+ if ( $Alpha < 100 ) { $Drawn[$YTop] = FALSE; }
+ if ( $Alpha < 100 ) { $Drawn[$YBottom] = TRUE; }
+
+ for($i=0;$i<=90;$i=$i+$Step)
+ {
+ $Xp1 = cos(($i+180)*PI/180) * $Radius + $X1 + $Radius;
+ $Xp2 = cos(((90-$i)+270)*PI/180) * $Radius + $X2 - $Radius;
+ $Yp = sin(($i+180)*PI/180) * $Radius + $YTop;
+
+ if ( $this->getFirstDecimal($Xp1) > 5 ) { $XOffset1 = 1; } else { $XOffset1 = 0; }
+ if ( $this->getFirstDecimal($Xp2) > 5 ) { $XOffset2 = 1; } else { $XOffset2 = 0; }
+ if ( $this->getFirstDecimal($Yp) > 5 ) { $YOffset = 1; } else { $YOffset = 0; }
+
+ if ( !isset($Drawn[$Yp+$YOffset]) || $Alpha == 100 )
+ imageline($this->Picture,$Xp1+$XOffset1,$Yp+$YOffset,$Xp2+$XOffset2,$Yp+$YOffset,$Color);
+
+ $Drawn[$Yp+$YOffset] = $Xp2;
+
+ $Xp1 = cos(($i+90)*PI/180) * $Radius + $X1 + $Radius;
+ $Xp2 = cos((90-$i)*PI/180) * $Radius + $X2 - $Radius;
+ $Yp = sin(($i+90)*PI/180) * $Radius + $YBottom;
+
+ if ( $this->getFirstDecimal($Xp1) > 7 ) { $XOffset1 = 1; } else { $XOffset1 = 0; }
+ if ( $this->getFirstDecimal($Xp2) > 7 ) { $XOffset2 = 1; } else { $XOffset2 = 0; }
+ if ( $this->getFirstDecimal($Yp) > 5 ) { $YOffset = 1; } else { $YOffset = 0; }
+
+ if ( !isset($Drawn[$Yp+$YOffset]) || $Alpha == 100 )
+ imageline($this->Picture,$Xp1+$XOffset1,$Yp+$YOffset,$Xp2+$XOffset2,$Yp+$YOffset,$Color);
+
+ $Drawn[$Yp+$YOffset] = $Xp2;
+ }
+
+ $this->drawRoundedRectangle($X1,$Y1,$X2,$Y2,$Radius,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha));
+
+ $this->Shadow = $RestoreShadow;
+ }
+
+ /* Draw a rectangle */
+ function drawRectangle($X1,$Y1,$X2,$Y2,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL;
+ $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : FALSE;
+
+ if ($X1 > $X2) { list($X1, $X2) = array($X2, $X1); }
+ if ($Y1 > $Y2) { list($Y1, $Y2) = array($Y2, $Y1); }
+
+ if ( $this->Antialias )
+ {
+ if ( $NoAngle )
+ {
+ $this->drawLine($X1+1,$Y1,$X2-1,$Y1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ $this->drawLine($X2,$Y1+1,$X2,$Y2-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ $this->drawLine($X2-1,$Y2,$X1+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ $this->drawLine($X1,$Y1+1,$X1,$Y2-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ }
+ else
+ {
+ $this->drawLine($X1+1,$Y1,$X2-1,$Y1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ $this->drawLine($X2,$Y1,$X2,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ $this->drawLine($X2-1,$Y2,$X1+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ $this->drawLine($X1,$Y1,$X1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ }
+ }
+ else
+ {
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ imagerectangle($this->Picture,$X1,$Y1,$X2,$Y2,$Color);
+ }
+ }
+
+ /* Draw a filled rectangle */
+ function drawFilledRectangle($X1,$Y1,$X2,$Y2,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
+ $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL;
+ $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : NULL;
+ $Dash = isset($Format["Dash"]) ? $Format["Dash"] : FALSE;
+ $DashStep = isset($Format["DashStep"]) ? $Format["DashStep"] : 4;
+ $DashR = isset($Format["DashR"]) ? $Format["DashR"] : 0;
+ $DashG = isset($Format["DashG"]) ? $Format["DashG"] : 0;
+ $DashB = isset($Format["DashB"]) ? $Format["DashB"] : 0;
+ $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : FALSE;
+
+ if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; }
+
+ if ($X1 > $X2) { list($X1, $X2) = array($X2, $X1); }
+ if ($Y1 > $Y2) { list($Y1, $Y2) = array($Y2, $Y1); }
+
+ $RestoreShadow = $this->Shadow;
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $this->Shadow = FALSE;
+ $this->drawFilledRectangle($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Ticks"=>$Ticks,"NoAngle"=>$NoAngle));
+ }
+
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ if ( $NoAngle )
+ {
+ imagefilledrectangle($this->Picture,ceil($X1)+1,ceil($Y1),floor($X2)-1,floor($Y2),$Color);
+ imageline($this->Picture,ceil($X1),ceil($Y1)+1,ceil($X1),floor($Y2)-1,$Color);
+ imageline($this->Picture,floor($X2),ceil($Y1)+1,floor($X2),floor($Y2)-1,$Color);
+ }
+ else
+ imagefilledrectangle($this->Picture,ceil($X1),ceil($Y1),floor($X2),floor($Y2),$Color);
+
+ if ( $Dash )
+ {
+ if ( $BorderR != -1 ) { $iX1=$X1+1; $iY1=$Y1+1; $iX2=$X2-1; $iY2=$Y2-1; } else { $iX1=$X1; $iY1=$Y1; $iX2=$X2; $iY2=$Y2; }
+
+ $Color = $this->allocateColor($this->Picture,$DashR,$DashG,$DashB,$Alpha);
+ $Y=$iY1-$DashStep;
+ for($X=$iX1; $X<=$iX2+($iY2-$iY1); $X=$X+$DashStep)
+ {
+ $Y=$Y+$DashStep;
+ if ( $X > $iX2 ) { $Xa = $X-($X-$iX2); $Ya = $iY1+($X-$iX2); } else { $Xa = $X; $Ya = $iY1; }
+ if ( $Y > $iY2 ) { $Xb = $iX1+($Y-$iY2); $Yb = $Y-($Y-$iY2); } else { $Xb = $iX1; $Yb = $Y; }
+ imageline($this->Picture,$Xa,$Ya,$Xb,$Yb,$Color);
+ }
+ }
+
+ if ( $this->Antialias && !$NoBorder )
+ {
+ if ( $X1 < ceil($X1) )
+ {
+ $AlphaA = $Alpha * (ceil($X1) - $X1);
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA);
+ imageline($this->Picture,ceil($X1)-1,ceil($Y1),ceil($X1)-1,floor($Y2),$Color);
+ }
+
+ if ( $Y1 < ceil($Y1) )
+ {
+ $AlphaA = $Alpha * (ceil($Y1) - $Y1);
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA);
+ imageline($this->Picture,ceil($X1),ceil($Y1)-1,floor($X2),ceil($Y1)-1,$Color);
+ }
+
+ if ( $X2 > floor($X2) )
+ {
+ $AlphaA = $Alpha * (.5-($X2 - floor($X2)));
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA);
+ imageline($this->Picture,floor($X2)+1,ceil($Y1),floor($X2)+1,floor($Y2),$Color);
+ }
+
+ if ( $Y2 > floor($Y2) )
+ {
+ $AlphaA = $Alpha * (.5-($Y2 - floor($Y2)));
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$AlphaA);
+ imageline($this->Picture,ceil($X1),floor($Y2)+1,floor($X2),floor($Y2)+1,$Color);
+ }
+ }
+
+ if ( $BorderR != -1 )
+ $this->drawRectangle($X1,$Y1,$X2,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$Ticks,"NoAngle"=>$NoAngle));
+
+ $this->Shadow = $RestoreShadow;
+ }
+
+ /* Draw a rectangular marker of the specified size */
+ function drawRectangleMarker($X,$Y,$Format="")
+ {
+ $Size = isset($Format["Size"]) ? $Format["Size"] : 4;
+
+ $HalfSize = floor($Size/2);
+ $this->drawFilledRectangle($X-$HalfSize,$Y-$HalfSize,$X+$HalfSize,$Y+$HalfSize,$Format);
+ }
+
+ /* Drawn a spline based on the bezier function */
+ function drawSpline($Coordinates,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $Force = isset($Format["Force"]) ? $Format["Force"] : 30;
+ $Forces = isset($Format["Forces"]) ? $Format["Forces"] : NULL;
+ $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : FALSE;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL;
+ $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : FALSE;
+ $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL;
+
+ $Cpt = NULL; $Mode = NULL; $Result = "";
+ for($i=1;$i<=count($Coordinates)-1;$i++)
+ {
+ $X1 = $Coordinates[$i-1][0]; $Y1 = $Coordinates[$i-1][1];
+ $X2 = $Coordinates[$i][0]; $Y2 = $Coordinates[$i][1];
+
+ if ( $Forces != NULL ) { $Force = $Forces[$i]; }
+
+ /* First segment */
+ if ( $i == 1 )
+ { $Xv1 = $X1; $Yv1 = $Y1; }
+ else
+ {
+ $Angle1 = $this->getAngle($XLast,$YLast,$X1,$Y1);
+ $Angle2 = $this->getAngle($X1,$Y1,$X2,$Y2);
+ $XOff = cos($Angle2 * PI / 180) * $Force + $X1;
+ $YOff = sin($Angle2 * PI / 180) * $Force + $Y1;
+
+ $Xv1 = cos($Angle1 * PI / 180) * $Force + $XOff;
+ $Yv1 = sin($Angle1 * PI / 180) * $Force + $YOff;
+ }
+
+ /* Last segment */
+ if ( $i == count($Coordinates)-1 )
+ { $Xv2 = $X2; $Yv2 = $Y2; }
+ else
+ {
+ $Angle1 = $this->getAngle($X2,$Y2,$Coordinates[$i+1][0],$Coordinates[$i+1][1]);
+ $Angle2 = $this->getAngle($X1,$Y1,$X2,$Y2);
+ $XOff = cos(($Angle2+180) * PI / 180) * $Force + $X2;
+ $YOff = sin(($Angle2+180) * PI / 180) * $Force + $Y2;
+
+ $Xv2 = cos(($Angle1+180) * PI / 180) * $Force + $XOff;
+ $Yv2 = sin(($Angle1+180) * PI / 180) * $Force + $YOff;
+ }
+
+ $Path = $this->drawBezier($X1,$Y1,$X2,$Y2,$Xv1,$Yv1,$Xv2,$Yv2,$Format);
+ if ($PathOnly) { $Result[] = $Path; }
+
+ $XLast = $X1; $YLast = $Y1;
+ }
+
+ return($Result);
+ }
+
+ /* Draw a bezier curve with two controls points */
+ function drawBezier($X1,$Y1,$X2,$Y2,$Xv1,$Yv1,$Xv2,$Yv2,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $ShowC = isset($Format["ShowControl"]) ? $Format["ShowControl"] : FALSE;
+ $Segments = isset($Format["Segments"]) ? $Format["Segments"] : NULL;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL;
+ $NoDraw = isset($Format["NoDraw"]) ? $Format["NoDraw"] : FALSE;
+ $PathOnly = isset($Format["PathOnly"]) ? $Format["PathOnly"] : FALSE;
+ $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL;
+ $DrawArrow = isset($Format["DrawArrow"]) ? $Format["DrawArrow"] : FALSE;
+ $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 10;
+ $ArrowRatio = isset($Format["ArrowRatio"]) ? $Format["ArrowRatio"] : .5;
+ $ArrowTwoHeads = isset($Format["ArrowTwoHeads"]) ? $Format["ArrowTwoHeads"] : FALSE;
+
+ if ( $Segments == NULL )
+ {
+ $Length = $this->getLength($X1,$Y1,$X2,$Y2);
+ $Precision = ($Length*125)/1000;
+ }
+ else
+ $Precision = $Segments;
+
+ $P[0]["X"] = $X1; $P[0]["Y"] = $Y1;
+ $P[1]["X"] = $Xv1; $P[1]["Y"] = $Yv1;
+ $P[2]["X"] = $Xv2; $P[2]["Y"] = $Yv2;
+ $P[3]["X"] = $X2; $P[3]["Y"] = $Y2;
+
+ /* Compute the bezier points */
+ $Q = ""; $ID = 0; $Path = "";
+ for($i=0;$i<=$Precision;$i=$i+1)
+ {
+ $u = $i / $Precision;
+
+ $C = "";
+ $C[0] = (1 - $u) * (1 - $u) * (1 - $u);
+ $C[1] = ($u * 3) * (1 - $u) * (1 - $u);
+ $C[2] = 3 * $u * $u * (1 - $u);
+ $C[3] = $u * $u * $u;
+
+ for($j=0;$j<=3;$j++)
+ {
+ if ( !isset($Q[$ID]) ) { $Q[$ID] = ""; }
+ if ( !isset($Q[$ID]["X"]) ) { $Q[$ID]["X"] = 0; }
+ if ( !isset($Q[$ID]["Y"]) ) { $Q[$ID]["Y"] = 0; }
+
+ $Q[$ID]["X"] = $Q[$ID]["X"] + $P[$j]["X"] * $C[$j];
+ $Q[$ID]["Y"] = $Q[$ID]["Y"] + $P[$j]["Y"] * $C[$j];
+ }
+ $ID++;
+ }
+ $Q[$ID]["X"] = $X2; $Q[$ID]["Y"] = $Y2;
+
+ if ( !$NoDraw )
+ {
+ /* Display the control points */
+ if ( $ShowC && !$PathOnly )
+ {
+ $Xv1 = floor($Xv1); $Yv1 = floor($Yv1); $Xv2 = floor($Xv2); $Yv2 = floor($Yv2);
+
+ $this->drawLine($X1,$Y1,$X2,$Y2,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>30));
+
+ $MyMarkerSettings = array("R"=>255,"G"=>0,"B"=>0,"BorderR"=>255,"BorderB"=>255,"BorderG"=>255,"Size"=>4);
+ $this->drawRectangleMarker($Xv1,$Yv1,$MyMarkerSettings);
+ $this->drawText($Xv1+4,$Yv1,"v1");
+ $MyMarkerSettings = array("R"=>0,"G"=>0,"B"=>255,"BorderR"=>255,"BorderB"=>255,"BorderG"=>255,"Size"=>4);
+ $this->drawRectangleMarker($Xv2,$Yv2,$MyMarkerSettings);
+ $this->drawText($Xv2+4,$Yv2,"v2");
+ }
+
+ /* Draw the bezier */
+ $LastX = NULL; $LastY = NULL; $Cpt = NULL; $Mode = NULL; $ArrowS = NULL;
+ foreach ($Q as $Key => $Point)
+ {
+ $X = $Point["X"]; $Y = $Point["Y"];
+
+ /* Get the first segment */
+ if ( $ArrowS == NULL && $LastX != NULL && $LastY != NULL )
+ { $ArrowS["X2"] = $LastX; $ArrowS["Y2"] = $LastY; $ArrowS["X1"] = $X; $ArrowS["Y1"] = $Y; }
+
+ if ( $LastX != NULL && $LastY != NULL && !$PathOnly)
+ list($Cpt,$Mode) = $this->drawLine($LastX,$LastY,$X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Cpt"=>$Cpt,"Mode"=>$Mode,"Weight"=>$Weight));
+
+ /* Get the last segment */
+ $ArrowE["X1"] = $LastX; $ArrowE["Y1"] = $LastY; $ArrowE["X2"] = $X; $ArrowE["Y2"] = $Y;
+
+ $LastX = $X; $LastY = $Y;
+ }
+
+ if ( $DrawArrow && !$PathOnly )
+ {
+ $ArrowSettings = array("FillR"=>$R,"FillG"=>$G,"FillB"=>$B,"Alpha"=>$Alpha,"Size"=>$ArrowSize,"Ratio"=>$ArrowRatio);
+ if ( $ArrowTwoHeads )
+ $this->drawArrow($ArrowS["X1"],$ArrowS["Y1"],$ArrowS["X2"],$ArrowS["Y2"],$ArrowSettings);
+
+ $this->drawArrow($ArrowE["X1"],$ArrowE["Y1"],$ArrowE["X2"],$ArrowE["Y2"],$ArrowSettings);
+ }
+ }
+ return($Q);
+ }
+
+ /* Draw a line between two points */
+ function drawLine($X1,$Y1,$X2,$Y2,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL;
+ $Cpt = isset($Format["Cpt"]) ? $Format["Cpt"] : 1;
+ $Mode = isset($Format["Mode"]) ? $Format["Mode"] : 1;
+ $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL;
+ $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL;
+
+ if ( $this->Antialias == FALSE && $Ticks == NULL )
+ {
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$this->Shadowa);
+ imageline($this->Picture,$X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,$ShadowColor);
+ }
+
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ imageline($this->Picture,$X1,$Y1,$X2,$Y2,$Color);
+ return(0);
+ }
+
+ $Distance = sqrt(($X2-$X1)*($X2-$X1)+($Y2-$Y1)*($Y2-$Y1));
+ if ( $Distance == 0 ) { return(-1); }
+
+ /* Derivative algorithm for overweighted lines, re-route to polygons primitives */
+ if ( $Weight != NULL )
+ {
+ $Angle = $this->getAngle($X1,$Y1,$X2,$Y2);
+ $PolySettings = array ("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderAlpha"=>$Alpha);
+
+ if ( $Ticks == NULL )
+ {
+ $Points = "";
+ $Points[] = cos(deg2rad($Angle-90)) * $Weight + $X1; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Y1;
+ $Points[] = cos(deg2rad($Angle+90)) * $Weight + $X1; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Y1;
+ $Points[] = cos(deg2rad($Angle+90)) * $Weight + $X2; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Y2;
+ $Points[] = cos(deg2rad($Angle-90)) * $Weight + $X2; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Y2;
+
+ $this->drawPolygon($Points,$PolySettings);
+ }
+ else
+ {
+ for($i=0;$i<=$Distance;$i=$i+$Ticks*2)
+ {
+ $Xa = (($X2-$X1)/$Distance) * $i + $X1; $Ya = (($Y2-$Y1)/$Distance) * $i + $Y1;
+ $Xb = (($X2-$X1)/$Distance) * ($i+$Ticks) + $X1; $Yb = (($Y2-$Y1)/$Distance) * ($i+$Ticks) + $Y1;
+
+ $Points = "";
+ $Points[] = cos(deg2rad($Angle-90)) * $Weight + $Xa; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Ya;
+ $Points[] = cos(deg2rad($Angle+90)) * $Weight + $Xa; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Ya;
+ $Points[] = cos(deg2rad($Angle+90)) * $Weight + $Xb; $Points[] = sin(deg2rad($Angle+90)) * $Weight + $Yb;
+ $Points[] = cos(deg2rad($Angle-90)) * $Weight + $Xb; $Points[] = sin(deg2rad($Angle-90)) * $Weight + $Yb;
+
+ $this->drawPolygon($Points,$PolySettings);
+ }
+ }
+
+ return(1);
+ }
+
+ $XStep = ($X2-$X1) / $Distance;
+ $YStep = ($Y2-$Y1) / $Distance;
+
+ for($i=0;$i<=$Distance;$i++)
+ {
+ $X = $i * $XStep + $X1;
+ $Y = $i * $YStep + $Y1;
+
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha);
+
+ if ( $Threshold != NULL )
+ {
+ foreach($Threshold as $Key => $Parameters)
+ {
+ if ( $Y <= $Parameters["MinX"] && $Y >= $Parameters["MaxX"])
+ {
+ if ( isset($Parameters["R"]) ) { $RT = $Parameters["R"]; } else { $RT = 0; }
+ if ( isset($Parameters["G"]) ) { $GT = $Parameters["G"]; } else { $GT = 0; }
+ if ( isset($Parameters["B"]) ) { $BT = $Parameters["B"]; } else { $BT = 0; }
+ if ( isset($Parameters["Alpha"]) ) { $AlphaT = $Parameters["Alpha"]; } else { $AlphaT = 0; }
+ $Color = array("R"=>$RT,"G"=>$GT,"B"=>$BT,"Alpha"=>$AlphaT);
+ }
+ }
+ }
+
+ if ( $Ticks != NULL )
+ {
+ if ( $Cpt % $Ticks == 0 )
+ { $Cpt = 0; if ( $Mode == 1 ) { $Mode = 0; } else { $Mode = 1; } }
+
+ if ( $Mode == 1 )
+ $this->drawAntialiasPixel($X,$Y,$Color);
+
+ $Cpt++;
+ }
+ else
+ $this->drawAntialiasPixel($X,$Y,$Color);
+ }
+
+ return(array($Cpt,$Mode));
+ }
+
+ /* Draw a circle */
+ function drawCircle($Xc,$Yc,$Height,$Width,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL;
+
+ $Height = abs($Height);
+ $Width = abs($Width);
+
+ if ( $Height == 0 ) { $Height = 1; }
+ if ( $Width == 0 ) { $Width = 1; }
+ $Xc = floor($Xc); $Yc = floor($Yc);
+
+ $RestoreShadow = $this->Shadow;
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $this->Shadow = FALSE;
+ $this->drawCircle($Xc+$this->ShadowX,$Yc+$this->ShadowY,$Height,$Width,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Ticks"=>$Ticks));
+ }
+
+ if ( $Width == 0 ) { $Width = $Height; }
+ if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
+ if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
+ if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
+
+ $Step = 360 / (2 * PI * max($Width,$Height));
+ $Mode = 1; $Cpt = 1;
+ for($i=0;$i<=360;$i=$i+$Step)
+ {
+ $X = cos($i*PI/180) * $Height + $Xc;
+ $Y = sin($i*PI/180) * $Width + $Yc;
+
+ if ( $Ticks != NULL )
+ {
+ if ( $Cpt % $Ticks == 0 )
+ { $Cpt = 0; if ( $Mode == 1 ) { $Mode = 0; } else { $Mode = 1; } }
+
+ if ( $Mode == 1 )
+ $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+
+ $Cpt++;
+ }
+ else
+ $this->drawAntialiasPixel($X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+
+ }
+ $this->Shadow = $RestoreShadow;
+ }
+
+ /* Draw a filled circle */
+ function drawFilledCircle($X,$Y,$Radius,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
+ $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+
+ if ( $Radius == 0 ) { $Radius = 1; }
+ if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; }
+ $X = floor($X); $Y = floor($Y);
+
+ $Radius = abs($Radius);
+
+ $RestoreShadow = $this->Shadow;
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $this->Shadow = FALSE;
+ $this->drawFilledCircle($X+$this->ShadowX,$Y+$this->ShadowY,$Radius,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Ticks"=>$Ticks));
+ }
+
+ $this->Mask = "";
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ for ($i=0; $i<=$Radius*2; $i++)
+ {
+ $Slice = sqrt($Radius * $Radius - ($Radius - $i) * ($Radius - $i));
+ $XPos = floor($Slice);
+ $YPos = $Y + $i - $Radius;
+ $AAlias = $Slice - floor($Slice);
+
+ $this->Mask[$X-$XPos][$YPos] = TRUE;
+ $this->Mask[$X+$XPos][$YPos] = TRUE;
+ imageline($this->Picture,$X-$XPos,$YPos,$X+$XPos,$YPos,$Color);
+ }
+ if ( $this->Antialias )
+ $this->drawCircle($X,$Y,$Radius,$Radius,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+
+ $this->Mask = "";
+
+ if ( $BorderR != -1 )
+ $this->drawCircle($X,$Y,$Radius,$Radius,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$Ticks));
+
+ $this->Shadow = $RestoreShadow;
+ }
+
+ /* Write text */
+ function drawText($X,$Y,$Text,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR;
+ $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG;
+ $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB;
+ $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 0;
+ $Align = isset($Format["Align"]) ? $Format["Align"] : TEXT_ALIGN_BOTTOMLEFT;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->FontColorA;
+ $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName;
+ $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
+ $ShowOrigine = isset($Format["ShowOrigine"]) ? $Format["ShowOrigine"] : FALSE;
+ $TOffset = isset($Format["TOffset"]) ? $Format["TOffset"] : 2;
+ $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : FALSE;
+ $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : TRUE;
+ $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 6;
+ $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : FALSE;
+ $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 6;
+ $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 255;
+ $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 255;
+ $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 255;
+ $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50;
+ $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
+ $BoxBorderR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
+ $BoxBorderG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
+ $BoxBorderB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
+ $BoxBorderAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 50;
+ $NoShadow = isset($Format["NoShadow"]) ? $Format["NoShadow"] : FALSE;
+
+ $Shadow = $this->Shadow;
+ if ( $NoShadow ) { $this->Shadow = FALSE; }
+
+ if ( $BoxSurrounding != "" ) { $BoxBorderR = $BoxR - $BoxSurrounding; $BoxBorderG = $BoxG - $BoxSurrounding; $BoxBorderB = $BoxB - $BoxSurrounding; $BoxBorderAlpha = $BoxAlpha; }
+
+ if ( $ShowOrigine )
+ {
+ $MyMarkerSettings = array("R"=>255,"G"=>0,"B"=>0,"BorderR"=>255,"BorderB"=>255,"BorderG"=>255,"Size"=>4);
+ $this->drawRectangleMarker($X,$Y,$MyMarkerSettings);
+ }
+
+ $TxtPos = $this->getTextBox($X,$Y,$FontName,$FontSize,$Angle,$Text);
+
+ if ( $DrawBox && ($Angle == 0 || $Angle == 90 || $Angle == 180 || $Angle == 270))
+ {
+ $T[0]["X"]=0;$T[0]["Y"]=0;$T[1]["X"]=0;$T[1]["Y"]=0;$T[2]["X"]=0;$T[2]["Y"]=0;$T[3]["X"]=0;$T[3]["Y"]=0;
+ if ( $Angle == 0 ) { $T[0]["X"]=-$TOffset;$T[0]["Y"]=$TOffset;$T[1]["X"]=$TOffset;$T[1]["Y"]=$TOffset;$T[2]["X"]=$TOffset;$T[2]["Y"]=-$TOffset;$T[3]["X"]=-$TOffset;$T[3]["Y"]=-$TOffset; }
+
+ $X1 = min($TxtPos[0]["X"],$TxtPos[1]["X"],$TxtPos[2]["X"],$TxtPos[3]["X"]) - $BorderOffset + 3;
+ $Y1 = min($TxtPos[0]["Y"],$TxtPos[1]["Y"],$TxtPos[2]["Y"],$TxtPos[3]["Y"]) - $BorderOffset;
+ $X2 = max($TxtPos[0]["X"],$TxtPos[1]["X"],$TxtPos[2]["X"],$TxtPos[3]["X"]) + $BorderOffset + 3;
+ $Y2 = max($TxtPos[0]["Y"],$TxtPos[1]["Y"],$TxtPos[2]["Y"],$TxtPos[3]["Y"]) + $BorderOffset - 3;
+
+ $X1 = $X1 - $TxtPos[$Align]["X"] + $X + $T[0]["X"];
+ $Y1 = $Y1 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"];
+ $X2 = $X2 - $TxtPos[$Align]["X"] + $X + $T[0]["X"];
+ $Y2 = $Y2 - $TxtPos[$Align]["Y"] + $Y + $T[0]["Y"];
+
+ $Settings = array("R"=>$BoxR,"G"=>$BoxG,"B"=>$BoxB,"Alpha"=>$BoxAlpha,"BorderR"=>$BoxBorderR,"BorderG"=>$BoxBorderG,"BorderB"=>$BoxBorderB,"BorderAlpha"=>$BoxBorderAlpha);
+
+ if ( $BoxRounded )
+ { $this->drawRoundedFilledRectangle($X1,$Y1,$X2,$Y2,$RoundedRadius,$Settings); }
+ else
+ { $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Settings); }
+ }
+
+ $X = $X - $TxtPos[$Align]["X"] + $X;
+ $Y = $Y - $TxtPos[$Align]["Y"] + $Y;
+
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $C_ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$this->Shadowa);
+ imagettftext($this->Picture,$FontSize,$Angle,$X+$this->ShadowX,$Y+$this->ShadowY,$C_ShadowColor,$FontName,$Text);
+ }
+
+ $C_TextColor = $this->AllocateColor($this->Picture,$R,$G,$B,$Alpha);
+ imagettftext($this->Picture,$FontSize,$Angle,$X,$Y,$C_TextColor,$FontName,$Text);
+
+ $this->Shadow = $Shadow;
+
+ return($TxtPos);
+ }
+
+ /* Draw a gradient within a defined area */
+ function drawGradientArea($X1,$Y1,$X2,$Y2,$Direction,$Format="")
+ {
+ $StartR = isset($Format["StartR"]) ? $Format["StartR"] : 90;
+ $StartG = isset($Format["StartG"]) ? $Format["StartG"] : 90;
+ $StartB = isset($Format["StartB"]) ? $Format["StartB"] : 90;
+ $EndR = isset($Format["EndR"]) ? $Format["EndR"] : 0;
+ $EndG = isset($Format["EndG"]) ? $Format["EndG"] : 0;
+ $EndB = isset($Format["EndB"]) ? $Format["EndB"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $Levels = isset($Format["Levels"]) ? $Format["Levels"] : NULL;
+
+ $Shadow = $this->Shadow;
+ $this->Shadow = FALSE;
+
+ if ( $StartR == $EndR && $StartG == $EndG && $StartB == $EndB )
+ {
+ $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,array("R"=>$StartR,"G"=>$StartG,"B"=>$StartB,"Alpha"=>$Alpha));
+ return(0);
+ }
+
+ if ( $Levels != NULL )
+ { $EndR=$StartR+$Levels; $EndG=$StartG+$Levels; $EndB=$StartB+$Levels; }
+
+ if ($X1 > $X2) { list($X1, $X2) = array($X2, $X1); }
+ if ($Y1 > $Y2) { list($Y1, $Y2) = array($Y2, $Y1); }
+
+ if ( $Direction == DIRECTION_VERTICAL ) { $Width = abs($Y2-$Y1); }
+ if ( $Direction == DIRECTION_HORIZONTAL ) { $Width = abs($X2-$X1); }
+
+ $Step = max(abs($EndR-$StartR),abs($EndG-$StartG),abs($EndB-$StartB));
+ $StepSize = $Width/$Step;
+ $RStep = ($EndR-$StartR)/$Step;
+ $GStep = ($EndG-$StartG)/$Step;
+ $BStep = ($EndB-$StartB)/$Step;
+
+ $R=$StartR;$G=$StartG;$B=$StartB;
+ switch($Direction)
+ {
+ case DIRECTION_VERTICAL:
+ $StartY = $Y1; $EndY = floor($Y2)+1; $LastY2 = $StartY;
+ for($i=0;$i<=$Step;$i++)
+ {
+ $Y2 = floor($StartY + ($i * $StepSize));
+
+ if ($Y2 > $EndY) { $Y2 = $EndY; }
+ if (($Y1 != $Y2 && $Y1 < $Y2) || $Y2 == $EndY)
+ {
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha);
+ $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Color);
+ $LastY2 = max($LastY2,$Y2);
+ $Y1 = $Y2+1;
+ }
+ $R = $R + $RStep; $G = $G + $GStep; $B = $B + $BStep;
+ }
+ if ( $LastY2 < $EndY && isset($Color)) { for ($i=$LastY2+1;$i<=$EndY;$i++) { $this->drawLine($X1,$i,$X2,$i,$Color); } }
+ break;
+
+ case DIRECTION_HORIZONTAL:
+ $StartX = $X1; $EndX = $X2;
+ for($i=0;$i<=$Step;$i++)
+ {
+ $X2 = floor($StartX + ($i * $StepSize));
+
+ if ($X2 > $EndX) { $X2 = $EndX; }
+ if (($X1 != $X2 && $X1 < $X2) || $X2 == $EndX)
+ {
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha);
+ $this->drawFilledRectangle($X1,$Y1,$X2,$Y2,$Color);
+ $X1 = $X2+1;
+ }
+ $R = $R + $RStep; $G = $G + $GStep; $B = $B + $BStep;
+ }
+ if ( $X2 < $EndX && isset($Color)) { $this->drawFilledRectangle($X2,$Y1,$EndX,$Y2,$Color); }
+ break;
+ }
+
+ $this->Shadow = $Shadow;
+
+ }
+
+ /* Draw an aliased pixel */
+ function drawAntialiasPixel($X,$Y,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+
+ if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize )
+ return(-1);
+
+ if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
+ if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
+ if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
+
+ if ( !$this->Antialias )
+ {
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$this->Shadowa);
+ imagesetpixel($this->Picture,$X+$this->ShadowX,$Y+$this->ShadowY,$ShadowColor);
+ }
+
+ $PlotColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ imagesetpixel($this->Picture,$X,$Y,$PlotColor);
+
+ return(0);
+ }
+
+ $Plot = "";
+ $Xi = floor($X);
+ $Yi = floor($Y);
+
+ if ( $Xi == $X && $Yi == $Y)
+ {
+ if ( $Alpha == 100 )
+ $this->drawAlphaPixel($X,$Y,100,$R,$G,$B);
+ else
+ $this->drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B);
+ }
+ else
+ {
+ $Alpha1 = (((1 - ($X - floor($X))) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
+ if ( $Alpha1 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi,$Alpha1,$R,$G,$B); }
+
+ $Alpha2 = ((($X - floor($X)) * (1 - ($Y - floor($Y))) * 100) / 100) * $Alpha;
+ if ( $Alpha2 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi,$Alpha2,$R,$G,$B); }
+
+ $Alpha3 = (((1 - ($X - floor($X))) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
+ if ( $Alpha3 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi,$Yi+1,$Alpha3,$R,$G,$B); }
+
+ $Alpha4 = ((($X - floor($X)) * ($Y - floor($Y)) * 100) / 100) * $Alpha;
+ if ( $Alpha4 > $this->AntialiasQuality ) { $this->drawAlphaPixel($Xi+1,$Yi+1,$Alpha4,$R,$G,$B); }
+ }
+ }
+
+ /* Draw a semi-transparent pixel */
+ function drawAlphaPixel($X,$Y,$Alpha,$R,$G,$B)
+ {
+ if ( isset($this->Mask[$X])) { if ( isset($this->Mask[$X][$Y]) ) { return(0); } }
+
+ if ( $X < 0 || $Y < 0 || $X >= $this->XSize || $Y >= $this->YSize )
+ return(-1);
+
+ if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
+ if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
+ if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
+
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $AlphaFactor = floor(($Alpha / 100) * $this->Shadowa);
+ $ShadowColor = $this->allocateColor($this->Picture,$this->ShadowR,$this->ShadowG,$this->ShadowB,$AlphaFactor);
+ imagesetpixel($this->Picture,$X+$this->ShadowX,$Y+$this->ShadowY,$ShadowColor);
+ }
+
+ $C_Aliased = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ imagesetpixel($this->Picture,$X,$Y,$C_Aliased);
+ }
+
+ /* Convert apha to base 10 */
+ function convertAlpha($AlphaValue)
+ { return((127/100)*(100-$AlphaValue)); }
+
+ /* Allocate a color with transparency */
+ function allocateColor($Picture,$R,$G,$B,$Alpha=100)
+ {
+ if ( $R < 0 ) { $R = 0; } if ( $R > 255 ) { $R = 255; }
+ if ( $G < 0 ) { $G = 0; } if ( $G > 255 ) { $G = 255; }
+ if ( $B < 0 ) { $B = 0; } if ( $B > 255 ) { $B = 255; }
+ if ( $Alpha < 0 ) { $Alpha = 0; }
+ if ( $Alpha > 100) { $Alpha = 100; }
+
+ $Alpha = $this->convertAlpha($Alpha);
+ return(imagecolorallocatealpha($Picture,$R,$G,$B,$Alpha));
+ }
+
+ /* Load a PNG file and draw it over the chart */
+ function drawFromPNG($X,$Y,$FileName)
+ { $this->drawFromPicture(1,$FileName,$X,$Y); }
+
+ /* Load a GIF file and draw it over the chart */
+ function drawFromGIF($X,$Y,$FileName)
+ { $this->drawFromPicture(2,$FileName,$X,$Y); }
+
+ /* Load a JPEG file and draw it over the chart */
+ function drawFromJPG($X,$Y,$FileName)
+ { $this->drawFromPicture(3,$FileName,$X,$Y); }
+
+ function getPicInfo($FileName)
+ {
+ $Infos = getimagesize($FileName);
+ $Width = $Infos[0];
+ $Height = $Infos[1];
+ $Type = $Infos["mime"];
+
+ if ( $Type == "image/png") { $Type = 1; }
+ if ( $Type == "image/gif") { $Type = 2; }
+ if ( $Type == "image/jpeg ") { $Type = 3; }
+
+ return(array($Width,$Height,$Type));
+ }
+
+ /* Generic loader function for external pictures */
+ function drawFromPicture($PicType,$FileName,$X,$Y)
+ {
+ if ( file_exists($FileName))
+ {
+ list($Width,$Height) = $this->getPicInfo($FileName);
+
+ if ( $PicType == 1 )
+ { $Raster = imagecreatefrompng($FileName); }
+ elseif ( $PicType == 2 )
+ { $Raster = imagecreatefromgif($FileName); }
+ elseif ( $PicType == 3 )
+ { $Raster = imagecreatefromjpeg($FileName); }
+ else
+ { return(0); }
+
+
+ $RestoreShadow = $this->Shadow;
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $this->Shadow = FALSE;
+ if ( $PicType == 3 )
+ $this->drawFilledRectangle($X+$this->ShadowX,$Y+$this->ShadowY,$X+$Width+$this->ShadowX,$Y+$Height+$this->ShadowY,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa));
+ else
+ {
+ $TranparentID = imagecolortransparent($Raster);
+ for ($Xc=0;$Xc<=$Width-1;$Xc++)
+ {
+ for ($Yc=0;$Yc<=$Height-1;$Yc++)
+ {
+ $RGBa = imagecolorat($Raster,$Xc,$Yc);
+ $Values = imagecolorsforindex($Raster,$RGBa);
+ if ( $Values["alpha"] < 120 )
+ {
+ $AlphaFactor = floor(($this->Shadowa / 100) * ((100 / 127) * (127-$Values["alpha"])));
+ $this->drawAlphaPixel($X+$Xc+$this->ShadowX,$Y+$Yc+$this->ShadowY,$AlphaFactor,$this->ShadowR,$this->ShadowG,$this->ShadowB);
+ }
+ }
+ }
+ }
+ }
+ $this->Shadow = $RestoreShadow;
+
+ imagecopy($this->Picture,$Raster,$X,$Y,0,0,$Width,$Height);
+ imagedestroy($Raster);
+ }
+ }
+
+ /* Draw an arrow */
+ function drawArrow($X1,$Y1,$X2,$Y2,$Format="")
+ {
+ $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0;
+ $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0;
+ $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $Size = isset($Format["Size"]) ? $Format["Size"] : 10;
+ $Ratio = isset($Format["Ratio"]) ? $Format["Ratio"] : .5;
+ $TwoHeads = isset($Format["TwoHeads"]) ? $Format["TwoHeads"] : FALSE;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : FALSE;
+
+ /* Calculate the line angle */
+ $Angle = $this->getAngle($X1,$Y1,$X2,$Y2);
+
+ /* Override Shadow support, this will be managed internally */
+ $RestoreShadow = $this->Shadow;
+ if ( $this->Shadow && $this->ShadowX != 0 && $this->ShadowY != 0 )
+ {
+ $this->Shadow = FALSE;
+ $this->drawArrow($X1+$this->ShadowX,$Y1+$this->ShadowY,$X2+$this->ShadowX,$Y2+$this->ShadowY,array("FillR"=>$this->ShadowR,"FillG"=>$this->ShadowG,"FillB"=>$this->ShadowB,"Alpha"=>$this->Shadowa,"Size"=>$Size,"Ratio"=>$Ratio,"TwoHeads"=>$TwoHeads,"Ticks"=>$Ticks));
+ }
+
+ /* Draw the 1st Head */
+ $TailX = cos(($Angle-180)*PI/180)*$Size+$X2;
+ $TailY = sin(($Angle-180)*PI/180)*$Size+$Y2;
+
+ $Points = "";
+ $Points[] = $X2; $Points[] = $Y2;
+ $Points[] = cos(($Angle-90)*PI/180)*$Size*$Ratio+$TailX; $Points[] = sin(($Angle-90)*PI/180)*$Size*$Ratio+$TailY;
+ $Points[] = cos(($Angle-270)*PI/180)*$Size*$Ratio+$TailX; $Points[] = sin(($Angle-270)*PI/180)*$Size*$Ratio+$TailY;
+ $Points[] = $X2; $Points[] = $Y2;
+
+ /* Visual correction */
+ if ($Angle == 180 || $Angle == 360 ) { $Points[4] = $Points[2]; }
+ if ($Angle == 90 || $Angle == 270 ) { $Points[5] = $Points[3]; }
+
+ $ArrowColor = $this->allocateColor($this->Picture,$FillR,$FillG,$FillB,$Alpha);
+ ImageFilledPolygon($this->Picture,$Points,4,$ArrowColor);
+
+ $this->drawLine($Points[0],$Points[1],$Points[2],$Points[3],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha));
+ $this->drawLine($Points[2],$Points[3],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha));
+ $this->drawLine($Points[0],$Points[1],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha));
+
+ /* Draw the second head */
+ if ( $TwoHeads )
+ {
+ $Angle = $this->getAngle($X2,$Y2,$X1,$Y1);
+
+ $TailX2 = cos(($Angle-180)*PI/180)*$Size+$X1;
+ $TailY2 = sin(($Angle-180)*PI/180)*$Size+$Y1;
+
+ $Points = "";
+ $Points[] = $X1; $Points[] = $Y1;
+ $Points[] = cos(($Angle-90)*PI/180)*$Size*$Ratio+$TailX2; $Points[] = sin(($Angle-90)*PI/180)*$Size*$Ratio+$TailY2;
+ $Points[] = cos(($Angle-270)*PI/180)*$Size*$Ratio+$TailX2; $Points[] = sin(($Angle-270)*PI/180)*$Size*$Ratio+$TailY2;
+ $Points[] = $X1; $Points[] = $Y1;
+
+ /* Visual correction */
+ if ($Angle == 180 || $Angle == 360 ) { $Points[4] = $Points[2]; }
+ if ($Angle == 90 || $Angle == 270 ) { $Points[5] = $Points[3]; }
+
+ $ArrowColor = $this->allocateColor($this->Picture,$FillR,$FillG,$FillB,$Alpha);
+ ImageFilledPolygon($this->Picture,$Points,4,$ArrowColor);
+
+ $this->drawLine($Points[0],$Points[1],$Points[2],$Points[3],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha));
+ $this->drawLine($Points[2],$Points[3],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha));
+ $this->drawLine($Points[0],$Points[1],$Points[4],$Points[5],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha));
+
+ $this->drawLine($TailX,$TailY,$TailX2,$TailY2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ }
+ else
+ $this->drawLine($X1,$Y1,$TailX,$TailY,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+
+ /* Re-enable shadows */
+ $this->Shadow = $RestoreShadow;
+ }
+
+ /* Draw a label with associated arrow */
+ function drawArrowLabel($X1,$Y1,$Text,$Format="")
+ {
+ $FillR = isset($Format["FillR"]) ? $Format["FillR"] : 0;
+ $FillG = isset($Format["FillG"]) ? $Format["FillG"] : 0;
+ $FillB = isset($Format["FillB"]) ? $Format["FillB"] : 0;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $FillR;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $FillG;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $FillB;
+ $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName;
+ $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $Length = isset($Format["Length"]) ? $Format["Length"] : 50;
+ $Angle = isset($Format["Angle"]) ? $Format["Angle"] : 315;
+ $Size = isset($Format["Size"]) ? $Format["Size"] : 10;
+ $Position = isset($Format["Position"]) ? $Format["Position"] : POSITION_TOP;
+ $RoundPos = isset($Format["RoundPos"]) ? $Format["RoundPos"] : FALSE;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL;
+
+ $Angle = $Angle % 360;
+
+ $X2 = sin(($Angle+180)*PI/180)*$Length+$X1;
+ $Y2 = cos(($Angle+180)*PI/180)*$Length+$Y1;
+
+ if ( $RoundPos && $Angle > 0 && $Angle < 180 ) { $Y2 = ceil($Y2); }
+ if ( $RoundPos && $Angle > 180 ) { $Y2 = floor($Y2); }
+
+ $this->drawArrow($X2,$Y2,$X1,$Y1,$Format);
+
+ $Size = imagettfbbox($FontSize,0,$FontName,$Text);
+ $TxtWidth = max(abs($Size[2]-$Size[0]),abs($Size[0]-$Size[6]));
+ $TxtHeight = max(abs($Size[1]-$Size[7]),abs($Size[3]-$Size[1]));
+
+ if ( $Angle > 0 && $Angle < 180 )
+ {
+ $this->drawLine($X2,$Y2,$X2-$TxtWidth,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ if ( $Position == POSITION_TOP )
+ $this->drawText($X2,$Y2-2,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Align"=>TEXT_ALIGN_BOTTOMRIGHT));
+ else
+ $this->drawText($X2,$Y2+4,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Align"=>TEXT_ALIGN_TOPRIGHT));
+ }
+ else
+ {
+ $this->drawLine($X2,$Y2,$X2+$TxtWidth,$Y2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ if ( $Position == POSITION_TOP )
+ $this->drawText($X2,$Y2-2,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha));
+ else
+ $this->drawText($X2,$Y2+4,$Text,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$Alpha,"Align"=>TEXT_ALIGN_TOPLEFT));
+ }
+ }
+
+ /* Draw a progress bar filled with specified % */
+ function drawProgress($X,$Y,$Percent,$Format="")
+ {
+ if ( $Percent > 100 ) { $Percent = 100; }
+ if ( $Percent < 0 ) { $Percent = 0; }
+
+ $Width = isset($Format["Width"]) ? $Format["Width"] : 200;
+ $Height = isset($Format["Height"]) ? $Format["Height"] : 20;
+ $Orientation = isset($Format["Orientation"]) ? $Format["Orientation"] : ORIENTATION_HORIZONTAL;
+ $ShowLabel = isset($Format["ShowLabel"]) ? $Format["ShowLabel"] : FALSE;
+ $LabelPos = isset($Format["LabelPos"]) ? $Format["LabelPos"] : LABEL_POS_INSIDE;
+ $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 10;
+ $R = isset($Format["R"]) ? $Format["R"] : 130;
+ $G = isset($Format["G"]) ? $Format["G"] : 130;
+ $B = isset($Format["B"]) ? $Format["B"] : 130;
+ $RFade = isset($Format["RFade"]) ? $Format["RFade"] : -1;
+ $GFade = isset($Format["GFade"]) ? $Format["GFade"] : -1;
+ $BFade = isset($Format["BFade"]) ? $Format["BFade"] : -1;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
+ $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 0;
+ $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 0;
+ $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 0;
+ $BoxBackR = isset($Format["BoxBackR"]) ? $Format["BoxBackR"] : 255;
+ $BoxBackG = isset($Format["BoxBackG"]) ? $Format["BoxBackG"] : 255;
+ $BoxBackB = isset($Format["BoxBackB"]) ? $Format["BoxBackB"] : 255;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+ $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : NULL;
+ $NoAngle = isset($Format["NoAngle"]) ? $Format["NoAngle"] : FALSE;
+
+ if ( $RFade != -1 && $GFade != -1 && $BFade != -1 )
+ {
+ $RFade = (($RFade-$R)/100)*$Percent+$R;
+ $GFade = (($GFade-$G)/100)*$Percent+$G;
+ $BFade = (($BFade-$B)/100)*$Percent+$B;
+ }
+
+ if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; }
+ if ( $BoxSurrounding != NULL ) { $BoxBorderR = $BoxBackR + $Surrounding; $BoxBorderG = $BoxBackG + $Surrounding; $BoxBorderB = $BoxBackB + $Surrounding; }
+
+ if ( $Orientation == ORIENTATION_VERTICAL )
+ {
+ $InnerHeight = (($Height-2)/100)*$Percent;
+ $this->drawFilledRectangle($X,$Y,$X+$Width,$Y-$Height,array("R"=>$BoxBackR,"G"=>$BoxBackG,"B"=>$BoxBackB,"BorderR"=>$BoxBorderR,"BorderG"=>$BoxBorderG,"BorderB"=>$BoxBorderB,"NoAngle"=>$NoAngle));
+
+ $RestoreShadow = $this->Shadow; $this->Shadow = FALSE;
+ if ( $RFade != -1 && $GFade != -1 && $BFade != -1 )
+ {
+ $GradientOptions = array("StartR"=>$RFade,"StartG"=>$GFade,"StartB"=>$BFade,"EndR"=>$R,"EndG"=>$G,"EndB"=>$B);
+ $this->drawGradientArea($X+1,$Y-1,$X+$Width-1,$Y-$InnerHeight,DIRECTION_VERTICAL,$GradientOptions);
+
+ if ( $Surrounding )
+ $this->drawRectangle($X+1,$Y-1,$X+$Width-1,$Y-$InnerHeight,array("R"=>255,"G"=>255,"B"=>255,"Alpha"=>$Surrounding));
+ }
+ else
+ $this->drawFilledRectangle($X+1,$Y-1,$X+$Width-1,$Y-$InnerHeight,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB));
+
+ $this->Shadow = $RestoreShadow;
+
+ if ( $ShowLabel && $LabelPos == LABEL_POS_BOTTOM ) { $this->drawText($X+($Width/2),$Y+$Margin,$Percent."%",array("Align"=>TEXT_ALIGN_TOPMIDDLE)); }
+ if ( $ShowLabel && $LabelPos == LABEL_POS_TOP ) { $this->drawText($X+($Width/2),$Y-$Height-$Margin,$Percent."%",array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); }
+ if ( $ShowLabel && $LabelPos == LABEL_POS_INSIDE ) { $this->drawText($X+($Width/2),$Y-$InnerHeight-$Margin,$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLELEFT,"Angle"=>90)); }
+ if ( $ShowLabel && $LabelPos == LABEL_POS_CENTER ) { $this->drawText($X+($Width/2),$Y-($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"Angle"=>90)); }
+ }
+ else
+ {
+ if ( $Percent == 100 )
+ $InnerWidth = $Width-1;
+ else
+ $InnerWidth = (($Width-2)/100)*$Percent;
+
+ $this->drawFilledRectangle($X,$Y,$X+$Width,$Y+$Height,array("R"=>$BoxBackR,"G"=>$BoxBackG,"B"=>$BoxBackB,"BorderR"=>$BoxBorderR,"BorderG"=>$BoxBorderG,"BorderB"=>$BoxBorderB,"NoAngle"=>$NoAngle));
+
+ $RestoreShadow = $this->Shadow; $this->Shadow = FALSE;
+ if ( $RFade != -1 && $GFade != -1 && $BFade != -1 )
+ {
+ $GradientOptions = array("StartR"=>$R,"StartG"=>$G,"StartB"=>$B,"EndR"=>$RFade,"EndG"=>$GFade,"EndB"=>$BFade);
+ $this->drawGradientArea($X+1,$Y+1,$X+$InnerWidth,$Y+$Height-1,DIRECTION_HORIZONTAL,$GradientOptions);
+
+ if ( $Surrounding )
+ $this->drawRectangle($X+1,$Y+1,$X+$InnerWidth,$Y+$Height-1,array("R"=>255,"G"=>255,"B"=>255,"Alpha"=>$Surrounding));
+ }
+ else
+ $this->drawFilledRectangle($X+1,$Y+1,$X+$InnerWidth,$Y+$Height-1,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB));
+
+ $this->Shadow = $RestoreShadow;
+
+ if ( $ShowLabel && $LabelPos == LABEL_POS_LEFT ) { $this->drawText($X-$Margin,$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLERIGHT)); }
+ if ( $ShowLabel && $LabelPos == LABEL_POS_RIGHT ) { $this->drawText($X+$Width+$Margin,$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLELEFT)); }
+ if ( $ShowLabel && $LabelPos == LABEL_POS_CENTER ) { $this->drawText($X+($Width/2),$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLEMIDDLE)); }
+ if ( $ShowLabel && $LabelPos == LABEL_POS_INSIDE ) { $this->drawText($X+$InnerWidth+$Margin,$Y+($Height/2),$Percent."%",array("Align"=>TEXT_ALIGN_MIDDLELEFT)); }
+ }
+ }
+
+ /* Get the legend box size */
+ function getLegendSize($Format="")
+ {
+ $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName;
+ $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
+ $BoxSize = isset($Format["BoxSize"]) ? $Format["BoxSize"] : 5;
+ $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5;
+ $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND;
+ $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL;
+ $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5;
+ $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5;
+ $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth;
+ $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight;
+ $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5;
+
+ $Data = $this->DataSet->getData();
+
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] && isset($Serie["Picture"]))
+ {
+ list($PicWidth,$PicHeight) = $this->getPicInfo($Serie["Picture"]);
+ if ( $IconAreaWidth < $PicWidth ) { $IconAreaWidth = $PicWidth; }
+ if ( $IconAreaHeight < $PicHeight ) { $IconAreaHeight = $PicHeight; }
+ }
+ }
+
+ $YStep = max($this->FontSize,$IconAreaHeight) + 5;
+ $XStep = $IconAreaWidth + 5;
+ $XStep = $XSpacing;
+
+ $X=100; $Y=100;
+
+ $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X;
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ if ( $Mode == LEGEND_VERTICAL )
+ {
+ $BoxArray = $this->getTextBox($vX+$IconAreaWidth+4,$vY+$IconAreaHeight/2,$FontName,$FontSize,0,$Serie["Description"]);
+
+ if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; }
+ if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; }
+ if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; }
+
+ $Lines = preg_split("/\n/",$Serie["Description"]);
+ $vY = $vY + max($this->FontSize*count($Lines),$IconAreaHeight) + 5;
+ }
+ elseif ( $Mode == LEGEND_HORIZONTAL )
+ {
+ $Lines = preg_split("/\n/",$Serie["Description"]);
+ $Width = "";
+ foreach($Lines as $Key => $Value)
+ {
+ $BoxArray = $this->getTextBox($vX+$IconAreaWidth+6,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$FontName,$FontSize,0,$Value);
+
+ if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; }
+ if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; }
+ if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; }
+
+ $Width[] = $BoxArray[1]["X"];
+ }
+
+ $vX=max($Width)+$XStep;
+ }
+ }
+ }
+ $vY=$vY-$YStep; $vX=$vX-$XStep;
+
+ $TopOffset = $Y - $Boundaries["T"];
+ if ( $Boundaries["B"]-($vY+$IconAreaHeight) < $TopOffset ) { $Boundaries["B"] = $vY+$IconAreaHeight+$TopOffset; }
+
+ $Width = ($Boundaries["R"]+$Margin) - ($Boundaries["L"]-$Margin);
+ $Height = ($Boundaries["B"]+$Margin) - ($Boundaries["T"]-$Margin);
+
+ return(array("Width"=>$Width,"Height"=>$Height));
+ }
+
+ /* Draw the legend of the active series */
+ function drawLegend($X,$Y,$Format="")
+ {
+ $Family = isset($Format["Family"]) ? $Format["Family"] : LEGEND_FAMILY_BOX;
+ $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName;
+ $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
+ $FontR = isset($Format["FontR"]) ? $Format["FontR"] : $this->FontColorR;
+ $FontG = isset($Format["FontG"]) ? $Format["FontG"] : $this->FontColorG;
+ $FontB = isset($Format["FontB"]) ? $Format["FontB"] : $this->FontColorB;
+ $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 5;
+ $BoxHeight = isset($Format["BoxHeight"]) ? $Format["BoxHeight"] : 5;
+ $IconAreaWidth = isset($Format["IconAreaWidth"]) ? $Format["IconAreaWidth"] : $BoxWidth;
+ $IconAreaHeight = isset($Format["IconAreaHeight"]) ? $Format["IconAreaHeight"] : $BoxHeight;
+ $XSpacing = isset($Format["XSpacing"]) ? $Format["XSpacing"] : 5;
+ $Margin = isset($Format["Margin"]) ? $Format["Margin"] : 5;
+ $R = isset($Format["R"]) ? $Format["R"] : 200;
+ $G = isset($Format["G"]) ? $Format["G"] : 200;
+ $B = isset($Format["B"]) ? $Format["B"] : 200;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 255;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 255;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 255;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+ $Style = isset($Format["Style"]) ? $Format["Style"] : LEGEND_ROUND;
+ $Mode = isset($Format["Mode"]) ? $Format["Mode"] : LEGEND_VERTICAL;
+
+ if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; }
+
+ $Data = $this->DataSet->getData();
+
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] && isset($Serie["Picture"]))
+ {
+ list($PicWidth,$PicHeight) = $this->getPicInfo($Serie["Picture"]);
+ if ( $IconAreaWidth < $PicWidth ) { $IconAreaWidth = $PicWidth; }
+ if ( $IconAreaHeight < $PicHeight ) { $IconAreaHeight = $PicHeight; }
+ }
+ }
+
+ $YStep = max($this->FontSize,$IconAreaHeight) + 5;
+ $XStep = $IconAreaWidth + 5;
+ $XStep = $XSpacing;
+
+ $Boundaries = ""; $Boundaries["L"] = $X; $Boundaries["T"] = $Y; $Boundaries["R"] = 0; $Boundaries["B"] = 0; $vY = $Y; $vX = $X;
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ if ( $Mode == LEGEND_VERTICAL )
+ {
+ $BoxArray = $this->getTextBox($vX+$IconAreaWidth+4,$vY+$IconAreaHeight/2,$FontName,$FontSize,0,$Serie["Description"]);
+
+ if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; }
+ if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; }
+ if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; }
+
+ $Lines = preg_split("/\n/",$Serie["Description"]);
+ $vY = $vY + max($this->FontSize*count($Lines),$IconAreaHeight) + 5;
+ }
+ elseif ( $Mode == LEGEND_HORIZONTAL )
+ {
+ $Lines = preg_split("/\n/",$Serie["Description"]);
+ $Width = "";
+ foreach($Lines as $Key => $Value)
+ {
+ $BoxArray = $this->getTextBox($vX+$IconAreaWidth+6,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$FontName,$FontSize,0,$Value);
+
+ if ( $Boundaries["T"] > $BoxArray[2]["Y"]+$IconAreaHeight/2 ) { $Boundaries["T"] = $BoxArray[2]["Y"]+$IconAreaHeight/2; }
+ if ( $Boundaries["R"] < $BoxArray[1]["X"]+2 ) { $Boundaries["R"] = $BoxArray[1]["X"]+2; }
+ if ( $Boundaries["B"] < $BoxArray[1]["Y"]+2+$IconAreaHeight/2 ) { $Boundaries["B"] = $BoxArray[1]["Y"]+2+$IconAreaHeight/2; }
+
+ $Width[] = $BoxArray[1]["X"];
+ }
+
+ $vX=max($Width)+$XStep;
+ }
+ }
+ }
+ $vY=$vY-$YStep; $vX=$vX-$XStep;
+
+ $TopOffset = $Y - $Boundaries["T"];
+ if ( $Boundaries["B"]-($vY+$IconAreaHeight) < $TopOffset ) { $Boundaries["B"] = $vY+$IconAreaHeight+$TopOffset; }
+
+ if ( $Style == LEGEND_ROUND )
+ $this->drawRoundedFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB));
+ elseif ( $Style == LEGEND_BOX )
+ $this->drawFilledRectangle($Boundaries["L"]-$Margin,$Boundaries["T"]-$Margin,$Boundaries["R"]+$Margin,$Boundaries["B"]+$Margin,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB));
+
+ $RestoreShadow = $this->Shadow; $this->Shadow = FALSE;
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"];
+ $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"];
+
+ if ( isset($Serie["Picture"]) )
+ {
+ $Picture = $Serie["Picture"];
+ list($PicWidth,$PicHeight) = $this->getPicInfo($Picture);
+ $PicX = $X+$IconAreaWidth/2; $PicY = $Y+$IconAreaHeight/2;
+
+ $this->drawFromPNG($PicX-$PicWidth/2,$PicY-$PicHeight/2,$Picture);
+ }
+ else
+ {
+ if ( $Family == LEGEND_FAMILY_BOX )
+ {
+ if ( $BoxWidth != $IconAreaWidth ) { $XOffset = floor(($IconAreaWidth-$BoxWidth)/2); } else { $XOffset = 0; }
+ if ( $BoxHeight != $IconAreaHeight ) { $YOffset = floor(($IconAreaHeight-$BoxHeight)/2); } else { $YOffset = 0; }
+
+ $this->drawFilledRectangle($X+1+$XOffset,$Y+1+$YOffset,$X+$BoxWidth+$XOffset+1,$Y+$BoxHeight+1+$YOffset,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20));
+ $this->drawFilledRectangle($X+$XOffset,$Y+$YOffset,$X+$BoxWidth+$XOffset,$Y+$BoxHeight+$YOffset,array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20));
+ }
+ elseif ( $Family == LEGEND_FAMILY_CIRCLE )
+ {
+ $this->drawFilledCircle($X+1+$IconAreaWidth/2,$Y+1+$IconAreaHeight/2,min($IconAreaHeight/2,$IconAreaWidth/2),array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20));
+ $this->drawFilledCircle($X+$IconAreaWidth/2,$Y+$IconAreaHeight/2,min($IconAreaHeight/2,$IconAreaWidth/2),array("R"=>$R,"G"=>$G,"B"=>$B,"Surrounding"=>20));
+ }
+ elseif ( $Family == LEGEND_FAMILY_LINE )
+ {
+ $this->drawLine($X+1,$Y+1+$IconAreaHeight/2,$X+1+$IconAreaWidth,$Y+1+$IconAreaHeight/2,array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20,"Ticks"=>$Ticks,"Weight"=>$Weight));
+ $this->drawLine($X,$Y+$IconAreaHeight/2,$X+$IconAreaWidth,$Y+$IconAreaHeight/2,array("R"=>$R,"G"=>$G,"B"=>$B,"Ticks"=>$Ticks,"Weight"=>$Weight));
+ }
+ }
+
+ if ( $Mode == LEGEND_VERTICAL )
+ {
+ $Lines = preg_split("/\n/",$Serie["Description"]);
+ foreach($Lines as $Key => $Value)
+ $this->drawText($X+$IconAreaWidth+4,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT,"FontSize"=>$FontSize,"FontName"=>$FontName));
+
+ $Y=$Y+max($this->FontSize*count($Lines),$IconAreaHeight) + 5;
+ }
+ elseif ( $Mode == LEGEND_HORIZONTAL )
+ {
+ $Lines = preg_split("/\n/",$Serie["Description"]);
+ $Width = "";
+ foreach($Lines as $Key => $Value)
+ {
+ $BoxArray = $this->drawText($X+$IconAreaWidth+4,$Y+$IconAreaHeight/2+(($this->FontSize+3)*$Key),$Value,array("R"=>$FontR,"G"=>$FontG,"B"=>$FontB,"Align"=>TEXT_ALIGN_MIDDLELEFT,"FontSize"=>$FontSize,"FontName"=>$FontName));
+ $Width[] = $BoxArray[1]["X"];
+ }
+ $X=max($Width)+2+$XStep;
+ }
+ }
+ }
+
+
+ $this->Shadow = $RestoreShadow;
+ }
+
+ function drawScale($Format="")
+ {
+ $Pos = isset($Format["Pos"]) ? $Format["Pos"] : SCALE_POS_LEFTRIGHT;
+ $Floating = isset($Format["Floating"]) ? $Format["Floating"] : FALSE;
+ $Mode = isset($Format["Mode"]) ? $Format["Mode"] : SCALE_MODE_FLOATING;
+ $RemoveXAxis = isset($Format["RemoveXAxis"]) ? $Format["RemoveXAxis"] : FALSE;
+ $MinDivHeight = isset($Format["MinDivHeight"]) ? $Format["MinDivHeight"] : 20;
+ $Factors = isset($Format["Factors"]) ? $Format["Factors"] : array(1,2,5);
+ $ManualScale = isset($Format["ManualScale"]) ? $Format["ManualScale"] : array("0"=>array("Min"=>-100,"Max"=>100));
+ $XMargin = isset($Format["XMargin"]) ? $Format["XMargin"] : AUTO;
+ $YMargin = isset($Format["YMargin"]) ? $Format["YMargin"] : 0;
+ $ScaleSpacing = isset($Format["ScaleSpacing"]) ? $Format["ScaleSpacing"] : 15;
+ $InnerTickWidth = isset($Format["InnerTickWidth"]) ? $Format["InnerTickWidth"] : 2;
+ $OuterTickWidth = isset($Format["OuterTickWidth"]) ? $Format["OuterTickWidth"] : 2;
+ $DrawXLines = isset($Format["DrawXLines"]) ? $Format["DrawXLines"] : TRUE;
+ $DrawYLines = isset($Format["DrawYLines"]) ? $Format["DrawYLines"] : ALL;
+ $GridTicks = isset($Format["GridTicks"]) ? $Format["GridTicks"] : 4;
+ $GridR = isset($Format["GridR"]) ? $Format["GridR"] : 255;
+ $GridG = isset($Format["GridG"]) ? $Format["GridG"] : 255;
+ $GridB = isset($Format["GridB"]) ? $Format["GridB"] : 255;
+ $GridAlpha = isset($Format["GridAlpha"]) ? $Format["GridAlpha"] : 40;
+ $AxisRo = isset($Format["AxisR"]) ? $Format["AxisR"] : 0;
+ $AxisGo = isset($Format["AxisG"]) ? $Format["AxisG"] : 0;
+ $AxisBo = isset($Format["AxisB"]) ? $Format["AxisB"] : 0;
+ $AxisAlpha = isset($Format["AxisAlpha"]) ? $Format["AxisAlpha"] : 100;
+ $TickRo = isset($Format["TickR"]) ? $Format["TickR"] : 0;
+ $TickGo = isset($Format["TickG"]) ? $Format["TickG"] : 0;
+ $TickBo = isset($Format["TickB"]) ? $Format["TickB"] : 0;
+ $TickAlpha = isset($Format["TickAlpha"]) ? $Format["TickAlpha"] : 100;
+ $DrawSubTicks = isset($Format["DrawSubTicks"]) ? $Format["DrawSubTicks"] : FALSE;
+ $InnerSubTickWidth = isset($Format["InnerSubTickWidth"]) ? $Format["InnerSubTickWidth"] : 0;
+ $OuterSubTickWidth = isset($Format["OuterSubTickWidth"]) ? $Format["OuterSubTickWidth"] : 2;
+ $SubTickR = isset($Format["SubTickR"]) ? $Format["SubTickR"] : 255;
+ $SubTickG = isset($Format["SubTickG"]) ? $Format["SubTickG"] : 0;
+ $SubTickB = isset($Format["SubTickB"]) ? $Format["SubTickB"] : 0;
+ $SubTickAlpha = isset($Format["SubTickAlpha"]) ? $Format["SubTickAlpha"] : 100;
+ $AutoAxisLabels = isset($Format["AutoAxisLabels"]) ? $Format["AutoAxisLabels"] : TRUE;
+ $XReleasePercent = isset($Format["XReleasePercent"]) ? $Format["XReleasePercent"] : 1;
+ $DrawArrows = isset($Format["DrawArrows"]) ? $Format["DrawArrows"] : FALSE;
+ $ArrowSize = isset($Format["ArrowSize"]) ? $Format["ArrowSize"] : 8;
+ $CycleBackground = isset($Format["CycleBackground"]) ? $Format["CycleBackground"] : FALSE;
+ $BackgroundR1 = isset($Format["BackgroundR1"]) ? $Format["BackgroundR1"] : 255;
+ $BackgroundG1 = isset($Format["BackgroundG1"]) ? $Format["BackgroundG1"] : 255;
+ $BackgroundB1 = isset($Format["BackgroundB1"]) ? $Format["BackgroundB1"] : 255;
+ $BackgroundAlpha1 = isset($Format["BackgroundAlpha1"]) ? $Format["BackgroundAlpha1"] : 20;
+ $BackgroundR2 = isset($Format["BackgroundR2"]) ? $Format["BackgroundR2"] : 230;
+ $BackgroundG2 = isset($Format["BackgroundG2"]) ? $Format["BackgroundG2"] : 230;
+ $BackgroundB2 = isset($Format["BackgroundB2"]) ? $Format["BackgroundB2"] : 230;
+ $BackgroundAlpha2 = isset($Format["BackgroundAlpha2"]) ? $Format["BackgroundAlpha2"] : 20;
+ $LabelingMethod = isset($Format["LabelingMethod"]) ? $Format["LabelingMethod"] : LABELING_ALL;
+ $LabelSkip = isset($Format["LabelSkip"]) ? $Format["LabelSkip"] : 0;
+ $LabelRotation = isset($Format["LabelRotation"]) ? $Format["LabelRotation"] : 0;
+ $RemoveSkippedAxis = isset($Format["RemoveSkippedAxis"]) ? $Format["RemoveSkippedAxis"] : FALSE;
+ $SkippedAxisTicks = isset($Format["SkippedAxisTicks"]) ? $Format["SkippedAxisTicks"] : $GridTicks+2;
+ $SkippedAxisR = isset($Format["SkippedAxisR"]) ? $Format["SkippedAxisR"] : $GridR;
+ $SkippedAxisG = isset($Format["SkippedAxisG"]) ? $Format["SkippedAxisG"] : $GridG;
+ $SkippedAxisB = isset($Format["SkippedAxisB"]) ? $Format["SkippedAxisB"] : $GridB;
+ $SkippedAxisAlpha = isset($Format["SkippedAxisAlpha"]) ? $Format["SkippedAxisAlpha"] : $GridAlpha-30;
+ $SkippedTickR = isset($Format["SkippedTickR"]) ? $Format["SkippedTickR"] : $TickRo;
+ $SkippedTickG = isset($Format["SkippedTickG"]) ? $Format["SkippedTickG"] : $TickGo;
+ $SkippedTickB = isset($Format["SkippedTicksB"]) ? $Format["SkippedTickB"] : $TickBo;
+ $SkippedTickAlpha = isset($Format["SkippedTickAlpha"]) ? $Format["SkippedTickAlpha"] : $TickAlpha-80;
+ $SkippedInnerTickWidth = isset($Format["SkippedInnerTickWidth"]) ? $Format["SkippedInnerTickWidth"] : 0;
+ $SkippedOuterTickWidth = isset($Format["SkippedOuterTickWidth"]) ? $Format["SkippedOuterTickWidth"] : 2;
+
+ /* Floating scale require X & Y margins to be set manually */
+ if ( $Floating && ( $XMargin == AUTO || $YMargin == 0 ) ) { $Floating = FALSE; }
+
+ /* Skip a NOTICE event in case of an empty array */
+ if ( $DrawYLines == NONE || $DrawYLines == FALSE ) { $DrawYLines = array("zarma"=>"31"); }
+
+ /* Define the color for the skipped elements */
+ $SkippedAxisColor = array("R"=>$SkippedAxisR,"G"=>$SkippedAxisG,"B"=>$SkippedAxisB,"Alpha"=>$SkippedAxisAlpha,"Ticks"=>$SkippedAxisTicks);
+ $SkippedTickColor = array("R"=>$SkippedTickR,"G"=>$SkippedTickG,"B"=>$SkippedTickB,"Alpha"=>$SkippedTickAlpha);
+
+ $Data = $this->DataSet->getData();
+ if ( isset($Data["Abscissa"]) ) { $Abscissa = $Data["Abscissa"]; } else { $Abscissa = NULL; }
+
+ /* Unset the abscissa axis, needed if we display multiple charts on the same picture */
+ if ( $Abscissa != NULL )
+ {
+ foreach($Data["Axis"] as $AxisID => $Parameters)
+ { if ($Parameters["Identity"] == AXIS_X) { unset($Data["Axis"][$AxisID]); } }
+ }
+
+ /* Build the scale settings */
+ $GotXAxis = FALSE;
+ foreach($Data["Axis"] as $AxisID => $AxisParameter)
+ {
+ if ( $AxisParameter["Identity"] == AXIS_X ) { $GotXAxis = TRUE; }
+
+ if ( $Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_Y)
+ { $Height = $this->GraphAreaY2-$this->GraphAreaY1 - $YMargin*2; }
+ elseif ( $Pos == SCALE_POS_LEFTRIGHT && $AxisParameter["Identity"] == AXIS_X)
+ { $Height = $this->GraphAreaX2-$this->GraphAreaX1; }
+ elseif ( $Pos == SCALE_POS_TOPBOTTOM && $AxisParameter["Identity"] == AXIS_Y)
+ { $Height = $this->GraphAreaX2-$this->GraphAreaX1 - $YMargin*2;; }
+ else
+ { $Height = $this->GraphAreaY2-$this->GraphAreaY1; }
+
+ $AxisMin = ABSOLUTE_MAX; $AxisMax = OUT_OF_SIGHT;
+ if ( $Mode == SCALE_MODE_FLOATING || $Mode == SCALE_MODE_START0 )
+ {
+ foreach($Data["Series"] as $SerieID => $SerieParameter)
+ {
+ if ( $SerieParameter["Axis"] == $AxisID && $Data["Series"][$SerieID]["isDrawable"] && $Data["Abscissa"] != $SerieID)
+ {
+ $AxisMax = max($AxisMax,$Data["Series"][$SerieID]["Max"]);
+ $AxisMin = min($AxisMin,$Data["Series"][$SerieID]["Min"]);
+ }
+ }
+ $AutoMargin = (($AxisMax-$AxisMin)/100)*$XReleasePercent;
+
+ $Data["Axis"][$AxisID]["Min"] = $AxisMin-$AutoMargin; $Data["Axis"][$AxisID]["Max"] = $AxisMax+$AutoMargin;
+ if ( $Mode == SCALE_MODE_START0 ) { $Data["Axis"][$AxisID]["Min"] = 0; }
+ }
+ elseif ( $Mode == SCALE_MODE_MANUAL )
+ {
+ if ( isset($ManualScale[$AxisID]["Min"]) && isset($ManualScale[$AxisID]["Max"]) )
+ {
+ $Data["Axis"][$AxisID]["Min"] = $ManualScale[$AxisID]["Min"];
+ $Data["Axis"][$AxisID]["Max"] = $ManualScale[$AxisID]["Max"];
+ #$Data["Axis"][0]["Max"] = 22;
+ }
+ else
+ { echo "Manual scale boundaries not set."; exit(); }
+ }
+ elseif ( $Mode == SCALE_MODE_ADDALL || $Mode == SCALE_MODE_ADDALL_START0 )
+ {
+ $Series = "";
+ foreach($Data["Series"] as $SerieID => $SerieParameter)
+ { if ( $SerieParameter["Axis"] == $AxisID && $SerieParameter["isDrawable"] && $Data["Abscissa"] != $SerieID ) { $Series[$SerieID] = count($Data["Series"][$SerieID]["Data"]); } }
+
+ for ($ID=0;$ID<=max($Series)-1;$ID++)
+ {
+ $PointMin = 0; $PointMax = 0;
+ foreach($Series as $SerieID => $ValuesCount )
+ {
+ if (isset($Data["Series"][$SerieID]["Data"][$ID]) && $Data["Series"][$SerieID]["Data"][$ID] != NULL )
+ {
+ $Value = $Data["Series"][$SerieID]["Data"][$ID];
+ if ( $Value > 0 ) { $PointMax = $PointMax + $Value; } else { $PointMin = $PointMin + $Value; }
+ }
+ }
+ $AxisMax = max($AxisMax,$PointMax);
+ $AxisMin = min($AxisMin,$PointMin);
+ }
+ $AutoMargin = (($AxisMax-$AxisMin)/100)*$XReleasePercent;
+ $Data["Axis"][$AxisID]["Min"] = $AxisMin-$AutoMargin; $Data["Axis"][$AxisID]["Max"] = $AxisMax+$AutoMargin;
+ }
+ $MaxDivs = floor($Height/$MinDivHeight);
+
+ if ( $Mode == SCALE_MODE_ADDALL_START0 ) { $Data["Axis"][$AxisID]["Min"] = 0; }
+
+ $Scale = $this->computeScale($Data["Axis"][$AxisID]["Min"],$Data["Axis"][$AxisID]["Max"],$MaxDivs,$Factors,$AxisID);
+
+ $Data["Axis"][$AxisID]["Margin"] = $AxisParameter["Identity"] == AXIS_X ? $XMargin : $YMargin;
+ $Data["Axis"][$AxisID]["ScaleMin"] = $Scale["XMin"];
+ $Data["Axis"][$AxisID]["ScaleMax"] = $Scale["XMax"];
+ $Data["Axis"][$AxisID]["Rows"] = $Scale["Rows"];
+ $Data["Axis"][$AxisID]["RowHeight"] = $Scale["RowHeight"];
+
+ if ( isset($Scale["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = $Scale["Format"]; }
+
+ if ( !isset($Data["Axis"][$AxisID]["Display"]) ) { $Data["Axis"][$AxisID]["Display"] = NULL; }
+ if ( !isset($Data["Axis"][$AxisID]["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = NULL; }
+ if ( !isset($Data["Axis"][$AxisID]["Unit"]) ) { $Data["Axis"][$AxisID]["Unit"] = NULL; }
+ }
+
+ /* Still no X axis */
+ if ( $GotXAxis == FALSE )
+ {
+ if ( $Abscissa != NULL )
+ {
+ $Points = count($Data["Series"][$Abscissa]["Data"]);
+ if ( $AutoAxisLabels )
+ $AxisName = isset($Data["Series"][$Abscissa]["Description"]) ? $Data["Series"][$Abscissa]["Description"] : NULL;
+ else
+ $AxisName = NULL;
+ }
+ else
+ {
+ $Points = 0;
+ $AxisName = isset($Data["XAxisName"]) ? $Data["XAxisName"] : NULL;
+ foreach($Data["Series"] as $SerieID => $SerieParameter)
+ { if ( $SerieParameter["isDrawable"] ) { $Points = max($Points,count($SerieParameter["Data"])); } }
+ }
+
+ $AxisID = count($Data["Axis"]);
+ $Data["Axis"][$AxisID]["Identity"] = AXIS_X;
+ if ( $Pos == SCALE_POS_LEFTRIGHT ) { $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_BOTTOM; } else { $Data["Axis"][$AxisID]["Position"] = AXIS_POSITION_LEFT; }
+ if ( isset($Data["AbscissaName"]) ) { $Data["Axis"][$AxisID]["Name"] = $Data["AbscissaName"]; }
+ if ( $XMargin == AUTO )
+ {
+ if ( $Pos == SCALE_POS_LEFTRIGHT )
+ { $Height = $this->GraphAreaX2-$this->GraphAreaX1; }
+ else
+ { $Height = $this->GraphAreaY2-$this->GraphAreaY1; }
+
+ if ( $Points == 1 )
+ $Data["Axis"][$AxisID]["Margin"] = $Height / 2;
+ else
+ $Data["Axis"][$AxisID]["Margin"] = ($Height/$Points) / 2;
+ }
+ else
+ { $Data["Axis"][$AxisID]["Margin"] = $XMargin; }
+ $Data["Axis"][$AxisID]["Rows"] = $Points-1;
+ if ( !isset($Data["Axis"][$AxisID]["Display"]) ) { $Data["Axis"][$AxisID]["Display"] = NULL; }
+ if ( !isset($Data["Axis"][$AxisID]["Format"]) ) { $Data["Axis"][$AxisID]["Format"] = NULL; }
+ if ( !isset($Data["Axis"][$AxisID]["Unit"]) ) { $Data["Axis"][$AxisID]["Unit"] = NULL; }
+ }
+
+ /* Do we need to reverse the abscissa position? */
+ if ( $Pos != SCALE_POS_LEFTRIGHT )
+ {
+ if ( $Data["AbsicssaPosition"] == AXIS_POSITION_BOTTOM )
+ { $Data["AbsicssaPosition"] = AXIS_POSITION_LEFT; }
+ else
+ { $Data["AbsicssaPosition"] = AXIS_POSITION_RIGHT; }
+ }
+ $Data["Axis"][$AxisID]["Position"] = $Data["AbsicssaPosition"];
+
+ $this->DataSet->saveOrientation($Pos);
+ $this->DataSet->saveAxisConfig($Data["Axis"]);
+ $this->DataSet->saveYMargin($YMargin);
+
+ $FontColorRo = $this->FontColorR; $FontColorGo = $this->FontColorG; $FontColorBo = $this->FontColorB;
+
+ $AxisPos["L"] = $this->GraphAreaX1; $AxisPos["R"] = $this->GraphAreaX2; $AxisPos["T"] = $this->GraphAreaY1; $AxisPos["B"] = $this->GraphAreaY2;
+ foreach($Data["Axis"] as $AxisID => $Parameters)
+ {
+ if ( isset($Parameters["Color"]) )
+ {
+ $AxisR = $Parameters["Color"]["R"]; $AxisG = $Parameters["Color"]["G"]; $AxisB = $Parameters["Color"]["B"];
+ $TickR = $Parameters["Color"]["R"]; $TickG = $Parameters["Color"]["G"]; $TickB = $Parameters["Color"]["B"];
+ $this->setFontProperties(array("R"=>$Parameters["Color"]["R"],"G"=>$Parameters["Color"]["G"],"B"=>$Parameters["Color"]["B"]));
+ }
+ else
+ {
+ $AxisR = $AxisRo; $AxisG = $AxisGo; $AxisB = $AxisBo;
+ $TickR = $TickRo; $TickG = $TickGo; $TickB = $TickBo;
+ $this->setFontProperties(array("R"=>$FontColorRo,"G"=>$FontColorGo,"B"=>$FontColorBo));
+ }
+
+ $LastValue = "w00t"; $ID = 1;
+ if ( $Parameters["Identity"] == AXIS_X )
+ {
+ if ( $Pos == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $Parameters["Position"] == AXIS_POSITION_BOTTOM )
+ {
+ if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $YLabelOffset = 2; }
+ if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $YLabelOffset = 5; }
+ if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; $YLabelOffset = 5; }
+ if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $YLabelOffset = 2; }
+
+ if ( !$RemoveXAxis )
+ {
+ if ( $Floating )
+ { $FloatingOffset = $YMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+ else
+ { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["B"],$this->GraphAreaX2,$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+
+ if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["B"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); }
+ }
+
+ $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2;
+
+ if ($Parameters["Rows"] == 0 ) { $Step = $Width; } else { $Step = $Width / ($Parameters["Rows"]); }
+
+ $MaxBottom = $AxisPos["B"];
+ for($i=0;$i<=$Parameters["Rows"];$i++)
+ {
+ $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i;
+ $YPos = $AxisPos["B"];
+
+ if ( $Abscissa != NULL )
+ { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } }
+ else
+ {
+ if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) )
+ $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]);
+ else
+ $Value = $i;
+ }
+
+ $ID++; $Skipped = TRUE;
+ if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis)
+ {
+ $Bounds = $this->drawText($XPos,$YPos+$OuterTickWidth+$YLabelOffset,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign));
+ $TxtBottom = $YPos+$OuterTickWidth+2+($Bounds[0]["Y"]-$Bounds[2]["Y"]);
+ $MaxBottom = max($MaxBottom,$TxtBottom);
+ $LastValue = $Value;
+ $Skipped = FALSE;
+ }
+
+ if ( $RemoveXAxis ) { $Skipped = FALSE; }
+
+ if ( $Skipped )
+ {
+ if ( $DrawXLines && !$RemoveSkippedAxis ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$SkippedAxisColor); }
+ if ( ($SkippedInnerTickWidth !=0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis) { $this->drawLine($XPos,$YPos-$SkippedInnerTickWidth,$XPos,$YPos+$SkippedOuterTickWidth,$SkippedTickColor); }
+ }
+ else
+ {
+ if ( $DrawXLines && ($XPos != $this->GraphAreaX1 && $XPos != $this->GraphAreaX2) ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); }
+ if ( ($InnerTickWidth !=0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos,$YPos-$InnerTickWidth,$XPos,$YPos+$OuterTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); }
+ }
+ }
+
+ if ( isset($Parameters["Name"]) && !$RemoveXAxis)
+ {
+ $YPos = $MaxBottom+2;
+ $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2;
+ $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_TOPMIDDLE));
+ $MaxBottom = $Bounds[0]["Y"];
+
+ $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize;
+ }
+
+ $AxisPos["B"] = $MaxBottom + $ScaleSpacing;
+ }
+ elseif ( $Parameters["Position"] == AXIS_POSITION_TOP )
+ {
+ if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_BOTTOMMIDDLE; $YLabelOffset = 2; }
+ if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $YLabelOffset = 2; }
+ if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_TOPMIDDLE; $YLabelOffset = 5; }
+ if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $YLabelOffset = 5; }
+
+ if ( !$RemoveXAxis )
+ {
+ if ( $Floating )
+ { $FloatingOffset = $YMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+ else
+ { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["T"],$this->GraphAreaX2,$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+
+ if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["T"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); }
+ }
+
+ $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2;
+
+ if ($Parameters["Rows"] == 0 ) { $Step = $Width; } else { $Step = $Width / $Parameters["Rows"]; }
+
+ $MinTop = $AxisPos["T"];
+ for($i=0;$i<=$Parameters["Rows"];$i++)
+ {
+ $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i;
+ $YPos = $AxisPos["T"];
+
+ if ( $Abscissa != NULL )
+ { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } }
+ else
+ {
+ if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) )
+ $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]);
+ else
+ $Value = $i;
+ }
+
+ $ID++; $Skipped = TRUE;
+ if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis)
+ {
+ $Bounds = $this->drawText($XPos,$YPos-$OuterTickWidth-$YLabelOffset,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign));
+ $TxtBox = $YPos-$OuterTickWidth-2-($Bounds[0]["Y"]-$Bounds[2]["Y"]);
+ $MinTop = min($MinTop,$TxtBox);
+ $LastValue = $Value;
+ $Skipped = FALSE;
+ }
+
+ if ( $RemoveXAxis ) { $Skipped = FALSE; }
+
+ if ( $Skipped )
+ {
+ if ( $DrawXLines && !$RemoveSkippedAxis ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$SkippedAxisColor); }
+ if ( ($SkippedInnerTickWidth !=0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis ) { $this->drawLine($XPos,$YPos+$SkippedInnerTickWidth,$XPos,$YPos-$SkippedOuterTickWidth,$SkippedTickColor); }
+ }
+ else
+ {
+ if ( $DrawXLines ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); }
+ if ( ($InnerTickWidth !=0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos,$YPos+$InnerTickWidth,$XPos,$YPos-$OuterTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); }
+ }
+
+ }
+
+ if ( isset($Parameters["Name"]) && !$RemoveXAxis )
+ {
+ $YPos = $MinTop-2;
+ $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2;
+ $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE));
+ $MinTop = $Bounds[2]["Y"];
+
+ $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop;
+ }
+
+ $AxisPos["T"] = $MinTop - $ScaleSpacing;
+ }
+ }
+ elseif ( $Pos == SCALE_POS_TOPBOTTOM )
+ {
+ if ( $Parameters["Position"] == AXIS_POSITION_LEFT )
+ {
+ if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = -2; }
+ if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = -6; }
+ if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = -2; }
+ if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = -5; }
+
+ if ( !$RemoveXAxis )
+ {
+ if ( $Floating )
+ { $FloatingOffset = $YMargin; $this->drawLine($AxisPos["L"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+ else
+ { $FloatingOffset = 0; $this->drawLine($AxisPos["L"],$this->GraphAreaY1,$AxisPos["L"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+
+ if ( $DrawArrows ) { $this->drawArrow($AxisPos["L"],$this->GraphAreaY2-$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY2+($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); }
+ }
+
+ $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2;
+
+ if ($Parameters["Rows"] == 0 ) { $Step = $Height; } else { $Step = $Height / $Parameters["Rows"]; }
+
+ $MinLeft = $AxisPos["L"];
+ for($i=0;$i<=$Parameters["Rows"];$i++)
+ {
+ $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i;
+ $XPos = $AxisPos["L"];
+
+ if ( $Abscissa != NULL )
+ { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } }
+ else
+ {
+ if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) )
+ $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]);
+ else
+ $Value = $i;
+ }
+
+ $ID++; $Skipped = TRUE;
+ if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis)
+ {
+ $Bounds = $this->drawText($XPos-$OuterTickWidth+$XLabelOffset,$YPos,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign));
+ $TxtBox = $XPos-$OuterTickWidth-2-($Bounds[1]["X"]-$Bounds[0]["X"]);
+ $MinLeft = min($MinLeft,$TxtBox);
+ $LastValue = $Value;
+ $Skipped = FALSE;
+ }
+
+ if ( $RemoveXAxis ) { $Skipped = FALSE; }
+
+ if ( $Skipped )
+ {
+ if ( $DrawXLines && !$RemoveSkippedAxis ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,$SkippedAxisColor); }
+ if ( ($SkippedInnerTickWidth !=0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis ) { $this->drawLine($XPos-$SkippedOuterTickWidth,$YPos,$XPos+$SkippedInnerTickWidth,$YPos,$SkippedTickColor); }
+ }
+ else
+ {
+ if ( $DrawXLines && ($YPos != $this->GraphAreaY1 && $YPos != $this->GraphAreaY2) ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); }
+ if ( ($InnerTickWidth !=0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos-$OuterTickWidth,$YPos,$XPos+$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); }
+ }
+
+ }
+ if ( isset($Parameters["Name"]) && !$RemoveXAxis )
+ {
+ $XPos = $MinLeft-2;
+ $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2;
+ $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>90));
+ $MinLeft = $Bounds[0]["X"];
+
+ $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft;
+ }
+
+ $AxisPos["L"] = $MinLeft - $ScaleSpacing;
+ }
+ elseif ( $Parameters["Position"] == AXIS_POSITION_RIGHT )
+ {
+ if ( $LabelRotation == 0 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = 2; }
+ if ( $LabelRotation > 0 && $LabelRotation < 190 ) { $LabelAlign = TEXT_ALIGN_MIDDLELEFT; $XLabelOffset = 6; }
+ if ( $LabelRotation == 180 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = 5; }
+ if ( $LabelRotation > 180 && $LabelRotation < 360 ) { $LabelAlign = TEXT_ALIGN_MIDDLERIGHT; $XLabelOffset = 7; }
+
+ if ( !$RemoveXAxis )
+ {
+ if ( $Floating )
+ { $FloatingOffset = $YMargin; $this->drawLine($AxisPos["R"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+ else
+ { $FloatingOffset = 0; $this->drawLine($AxisPos["R"],$this->GraphAreaY1,$AxisPos["R"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+
+ if ( $DrawArrows ) { $this->drawArrow($AxisPos["R"],$this->GraphAreaY2-$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY2+($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); }
+ }
+
+ $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2;
+
+ if ($Parameters["Rows"] == 0 ) { $Step = $Height; } else { $Step = $Height / $Parameters["Rows"]; }
+
+ $MaxRight = $AxisPos["R"];
+ for($i=0;$i<=$Parameters["Rows"];$i++)
+ {
+ $YPos = $this->GraphAreaY1 + $Parameters["Margin"] + $Step*$i;
+ $XPos = $AxisPos["R"];
+
+ if ( $Abscissa != NULL )
+ { if ( isset($Data["Series"][$Abscissa]["Data"][$i]) ) { $Value = $this->scaleFormat($Data["Series"][$Abscissa]["Data"][$i],$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]); } else { $Value = ""; } }
+ else
+ {
+ if ( isset($Parameters["ScaleMin"]) && isset ($Parameters["RowHeight"]) )
+ $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Data["XAxisDisplay"],$Data["XAxisFormat"],$Data["XAxisUnit"]);
+ else
+ $Value = $i;
+ }
+
+ $ID++; $Skipped = TRUE;
+ if ( $this->isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip) && !$RemoveXAxis)
+ {
+ $Bounds = $this->drawText($XPos+$OuterTickWidth+$XLabelOffset,$YPos,$Value,array("Angle"=>$LabelRotation,"Align"=>$LabelAlign));
+ $TxtBox = $XPos+$OuterTickWidth+2+($Bounds[1]["X"]-$Bounds[0]["X"]);
+ $MaxRight = max($MaxRight,$TxtBox);
+ $LastValue = $Value;
+ $Skipped = FALSE;
+ }
+
+ if ( $RemoveXAxis ) { $Skipped = FALSE; }
+
+ if ( $Skipped )
+ {
+ if ( $DrawXLines && !$RemoveSkippedAxis ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,$SkippedAxisColor); }
+ if ( ($SkippedInnerTickWidth != 0 || $SkippedOuterTickWidth != 0) && !$RemoveXAxis && !$RemoveSkippedAxis ) { $this->drawLine($XPos+$SkippedOuterTickWidth,$YPos,$XPos-$SkippedInnerTickWidth,$YPos,$SkippedTickColor); }
+ }
+ else
+ {
+ if ( $DrawXLines ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); }
+ if ( ($InnerTickWidth != 0 || $OuterTickWidth != 0) && !$RemoveXAxis ) { $this->drawLine($XPos+$OuterTickWidth,$YPos,$XPos-$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha)); }
+ }
+
+ }
+
+ if ( isset($Parameters["Name"]) && !$RemoveXAxis)
+ {
+ $XPos = $MaxRight+4;
+ $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2;
+ $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>270));
+ $MaxRight = $Bounds[1]["X"];
+
+ $this->DataSet->Data["GraphArea"]["X2"] = $MaxRight + $this->FontSize;
+ }
+
+ $AxisPos["R"] = $MaxRight + $ScaleSpacing;
+ }
+ }
+ }
+
+
+
+ if ( $Parameters["Identity"] == AXIS_Y )
+ {
+ if ( $Pos == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $Parameters["Position"] == AXIS_POSITION_LEFT )
+ {
+
+ if ( $Floating )
+ { $FloatingOffset = $XMargin; $this->drawLine($AxisPos["L"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+ else
+ { $FloatingOffset = 0; $this->drawLine($AxisPos["L"],$this->GraphAreaY1,$AxisPos["L"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+
+ if ( $DrawArrows ) { $this->drawArrow($AxisPos["L"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["L"],$this->GraphAreaY1-($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); }
+
+ $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2;
+ $Step = $Height / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MinLeft = $AxisPos["L"];
+ $LastY = NULL;
+ for($i=0;$i<=$Parameters["Rows"];$i++)
+ {
+ $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step*$i;
+ $XPos = $AxisPos["L"];
+ $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]);
+
+ if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); }
+ if ( $LastY != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($this->GraphAreaX1+$FloatingOffset,$LastY,$this->GraphAreaX2-$FloatingOffset,$YPos,$BGColor); }
+
+ if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); }
+
+ if ( $DrawSubTicks && $i != $Parameters["Rows"] )
+ $this->drawLine($XPos-$OuterSubTickWidth,$YPos-$SubTicksSize,$XPos+$InnerSubTickWidth,$YPos-$SubTicksSize,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha));
+
+ $this->drawLine($XPos-$OuterTickWidth,$YPos,$XPos+$InnerTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha));
+ $Bounds = $this->drawText($XPos-$OuterTickWidth-2,$YPos,$Value,array("Align"=>TEXT_ALIGN_MIDDLERIGHT));
+ $TxtLeft = $XPos-$OuterTickWidth-2-($Bounds[1]["X"]-$Bounds[0]["X"]);
+ $MinLeft = min($MinLeft,$TxtLeft);
+
+ $LastY = $YPos;
+ }
+
+ if ( isset($Parameters["Name"]) )
+ {
+ $XPos = $MinLeft-2;
+ $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2;
+ $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>90));
+ $MinLeft = $Bounds[2]["X"];
+
+ $this->DataSet->Data["GraphArea"]["X1"] = $MinLeft;
+ }
+
+ $AxisPos["L"] = $MinLeft - $ScaleSpacing;
+ }
+ elseif ( $Parameters["Position"] == AXIS_POSITION_RIGHT )
+ {
+ if ( $Floating )
+ { $FloatingOffset = $XMargin; $this->drawLine($AxisPos["R"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY2-$Parameters["Margin"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+ else
+ { $FloatingOffset = 0; $this->drawLine($AxisPos["R"],$this->GraphAreaY1,$AxisPos["R"],$this->GraphAreaY2,array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+
+ if ( $DrawArrows ) { $this->drawArrow($AxisPos["R"],$this->GraphAreaY1+$Parameters["Margin"],$AxisPos["R"],$this->GraphAreaY1-($ArrowSize*2),array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); }
+
+ $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Parameters["Margin"]*2;
+ $Step = $Height / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MaxLeft = $AxisPos["R"];
+ $LastY = NULL;
+ for($i=0;$i<=$Parameters["Rows"];$i++)
+ {
+ $YPos = $this->GraphAreaY2 - $Parameters["Margin"] - $Step*$i;
+ $XPos = $AxisPos["R"];
+ $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]);
+
+ if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); }
+ if ( $LastY != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($this->GraphAreaX1+$FloatingOffset,$LastY,$this->GraphAreaX2-$FloatingOffset,$YPos,$BGColor); }
+
+ if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($this->GraphAreaX1+$FloatingOffset,$YPos,$this->GraphAreaX2-$FloatingOffset,$YPos,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); }
+
+ if ( $DrawSubTicks && $i != $Parameters["Rows"] )
+ $this->drawLine($XPos-$OuterSubTickWidth,$YPos-$SubTicksSize,$XPos+$InnerSubTickWidth,$YPos-$SubTicksSize,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha));
+
+ $this->drawLine($XPos-$InnerTickWidth,$YPos,$XPos+$OuterTickWidth,$YPos,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha));
+ $Bounds = $this->drawText($XPos+$OuterTickWidth+2,$YPos,$Value,array("Align"=>TEXT_ALIGN_MIDDLELEFT));
+ $TxtLeft = $XPos+$OuterTickWidth+2+($Bounds[1]["X"]-$Bounds[0]["X"]);
+ $MaxLeft = max($MaxLeft,$TxtLeft);
+
+ $LastY = $YPos;
+ }
+
+ if ( isset($Parameters["Name"]) )
+ {
+ $XPos = $MaxLeft+6;
+ $YPos = $this->GraphAreaY1+($this->GraphAreaY2-$this->GraphAreaY1)/2;
+ $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE,"Angle"=>270));
+ $MaxLeft = $Bounds[2]["X"];
+
+ $this->DataSet->Data["GraphArea"]["X2"] = $MaxLeft + $this->FontSize;
+ }
+ $AxisPos["R"] = $MaxLeft + $ScaleSpacing;
+ }
+ }
+ elseif ( $Pos == SCALE_POS_TOPBOTTOM )
+ {
+ if ( $Parameters["Position"] == AXIS_POSITION_TOP )
+ {
+ if ( $Floating )
+ { $FloatingOffset = $XMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+ else
+ { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["T"],$this->GraphAreaX2,$AxisPos["T"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+
+ if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["T"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["T"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); }
+
+ $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2;
+ $Step = $Width / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MinTop = $AxisPos["T"];
+ $LastX = NULL;
+ for($i=0;$i<=$Parameters["Rows"];$i++)
+ {
+ $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i;
+ $YPos = $AxisPos["T"];
+ $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]);
+
+ if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); }
+ if ( $LastX != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($LastX,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$BGColor); }
+
+ if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); }
+
+ if ( $DrawSubTicks && $i != $Parameters["Rows"] )
+ $this->drawLine($XPos+$SubTicksSize,$YPos-$OuterSubTickWidth,$XPos+$SubTicksSize,$YPos+$InnerSubTickWidth,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha));
+
+ $this->drawLine($XPos,$YPos-$OuterTickWidth,$XPos,$YPos+$InnerTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha));
+ $Bounds = $this->drawText($XPos,$YPos-$OuterTickWidth-2,$Value,array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE));
+ $TxtHeight = $YPos-$OuterTickWidth-2-($Bounds[1]["Y"]-$Bounds[2]["Y"]);
+ $MinTop = min($MinTop,$TxtHeight);
+
+ $LastX = $XPos;
+ }
+
+ if ( isset($Parameters["Name"]) )
+ {
+ $YPos = $MinTop-2;
+ $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2;
+ $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_BOTTOMMIDDLE));
+ $MinTop = $Bounds[2]["Y"];
+
+ $this->DataSet->Data["GraphArea"]["Y1"] = $MinTop;
+ }
+
+ $AxisPos["T"] = $MinTop - $ScaleSpacing;
+ }
+ elseif ( $Parameters["Position"] == AXIS_POSITION_BOTTOM )
+ {
+ if ( $Floating )
+ { $FloatingOffset = $XMargin; $this->drawLine($this->GraphAreaX1+$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+ else
+ { $FloatingOffset = 0; $this->drawLine($this->GraphAreaX1,$AxisPos["B"],$this->GraphAreaX2,$AxisPos["B"],array("R"=>$AxisR,"G"=>$AxisG,"B"=>$AxisB,"Alpha"=>$AxisAlpha)); }
+
+ if ( $DrawArrows ) { $this->drawArrow($this->GraphAreaX2-$Parameters["Margin"],$AxisPos["B"],$this->GraphAreaX2+($ArrowSize*2),$AxisPos["B"],array("FillR"=>$AxisR,"FillG"=>$AxisG,"FillB"=>$AxisB,"Size"=>$ArrowSize)); }
+
+ $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Parameters["Margin"]*2;
+ $Step = $Width / $Parameters["Rows"]; $SubTicksSize = $Step /2; $MaxBottom = $AxisPos["B"];
+ $LastX = NULL;
+ for($i=0;$i<=$Parameters["Rows"];$i++)
+ {
+ $XPos = $this->GraphAreaX1 + $Parameters["Margin"] + $Step*$i;
+ $YPos = $AxisPos["B"];
+ $Value = $this->scaleFormat($Parameters["ScaleMin"] + $Parameters["RowHeight"]*$i,$Parameters["Display"],$Parameters["Format"],$Parameters["Unit"]);
+
+ if ( $i%2 == 1 ) { $BGColor = array("R"=>$BackgroundR1,"G"=>$BackgroundG1,"B"=>$BackgroundB1,"Alpha"=>$BackgroundAlpha1); } else { $BGColor = array("R"=>$BackgroundR2,"G"=>$BackgroundG2,"B"=>$BackgroundB2,"Alpha"=>$BackgroundAlpha2); }
+ if ( $LastX != NULL && $CycleBackground && ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) )) { $this->drawFilledRectangle($LastX,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,$BGColor); }
+
+ if ( $DrawYLines == ALL || in_array($AxisID,$DrawYLines) ) { $this->drawLine($XPos,$this->GraphAreaY1+$FloatingOffset,$XPos,$this->GraphAreaY2-$FloatingOffset,array("R"=>$GridR,"G"=>$GridG,"B"=>$GridB,"Alpha"=>$GridAlpha,"Ticks"=>$GridTicks)); }
+
+ if ( $DrawSubTicks && $i != $Parameters["Rows"] )
+ $this->drawLine($XPos+$SubTicksSize,$YPos-$OuterSubTickWidth,$XPos+$SubTicksSize,$YPos+$InnerSubTickWidth,array("R"=>$SubTickR,"G"=>$SubTickG,"B"=>$SubTickB,"Alpha"=>$SubTickAlpha));
+
+ $this->drawLine($XPos,$YPos-$OuterTickWidth,$XPos,$YPos+$InnerTickWidth,array("R"=>$TickR,"G"=>$TickG,"B"=>$TickB,"Alpha"=>$TickAlpha));
+ $Bounds = $this->drawText($XPos,$YPos+$OuterTickWidth+2,$Value,array("Align"=>TEXT_ALIGN_TOPMIDDLE));
+ $TxtHeight = $YPos+$OuterTickWidth+2+($Bounds[1]["Y"]-$Bounds[2]["Y"]);
+ $MaxBottom = max($MaxBottom,$TxtHeight);
+
+ $LastX = $XPos;
+ }
+
+ if ( isset($Parameters["Name"]) )
+ {
+ $YPos = $MaxBottom+2;
+ $XPos = $this->GraphAreaX1+($this->GraphAreaX2-$this->GraphAreaX1)/2;
+ $Bounds = $this->drawText($XPos,$YPos,$Parameters["Name"],array("Align"=>TEXT_ALIGN_TOPMIDDLE));
+ $MaxBottom = $Bounds[0]["Y"];
+
+ $this->DataSet->Data["GraphArea"]["Y2"] = $MaxBottom + $this->FontSize;
+ }
+
+ $AxisPos["B"] = $MaxBottom + $ScaleSpacing;
+ }
+ }
+ }
+ }
+ }
+
+ function isValidLabel($Value,$LastValue,$LabelingMethod,$ID,$LabelSkip)
+ {
+ if ( $LabelingMethod == LABELING_DIFFERENT && $Value != $LastValue ) { return(TRUE); }
+ if ( $LabelingMethod == LABELING_DIFFERENT && $Value == $LastValue ) { return(FALSE); }
+ if ( $LabelingMethod == LABELING_ALL && $LabelSkip == 0 ) { return(TRUE); }
+ if ( $LabelingMethod == LABELING_ALL && ($ID+$LabelSkip) % ($LabelSkip+1) != 1 ) { return(FALSE); }
+
+ return(TRUE);
+ }
+
+ /* Compute the scale, check for the best visual factors */
+ function computeScale($XMin,$XMax,$MaxDivs,$Factors,$AxisID=0)
+ {
+ /* Compute each factors */
+ $Results = "";
+ foreach ($Factors[$AxisID] as $Key => $Factor)
+ $Results[$Factor] = $this->processScale($XMin,$XMax,$MaxDivs,array($Factor),$AxisID);
+
+ /* Remove scales that are creating to much decimals */
+ $GoodScaleFactors = "";
+ foreach ($Results as $Key => $Result)
+ {
+ $Decimals = preg_split("/\./",$Result["RowHeight"]);
+ if ( (!isset($Decimals[1])) || (strlen($Decimals[1]) < 6) ) { $GoodScaleFactors[] = $Key; }
+ }
+
+ /* Found no correct scale, shame,... returns the 1st one as default */
+ if ( $GoodScaleFactors == "" ) { return($Results[$Factors[0]]); }
+
+ /* Find the factor that cause the maximum number of Rows */
+ $MaxRows = 0; $BestFactor = 0;
+ foreach($GoodScaleFactors as $Key => $Factor)
+ { if ( $Results[$Factor]["Rows"] > $MaxRows ) { $MaxRows = $Results[$Factor]["Rows"]; $BestFactor = $Factor; } }
+
+ /* Return the best visual scale */
+ return($Results[$BestFactor]);
+ }
+
+ /* Compute the best matching scale based on size & factors */
+ function processScale($XMin,$XMax,$MaxDivs,$Factors,$AxisID)
+ {
+ $ScaleHeight = abs(ceil($XMax)-floor($XMin));
+
+ if ( isset($this->DataSet->Data["Axis"][$AxisID]["Format"]) )
+ $Format = $this->DataSet->Data["Axis"][$AxisID]["Format"];
+ else
+ $Format = NULL;
+
+ if ( isset($this->DataSet->Data["Axis"][$AxisID]["Display"]) )
+ $Mode = $this->DataSet->Data["Axis"][$AxisID]["Display"];
+ else
+ $Mode = AXIS_FORMAT_DEFAULT;
+
+ $Scale = "";
+ if ( $XMin != $XMax )
+ {
+ $Found = FALSE; $Rescaled = FALSE; $Scaled10Factor = .0001; $Result = 0;
+ while(!$Found)
+ {
+ foreach($Factors as $Key => $Factor)
+ {
+ if ( !$Found )
+ {
+ if ( !($this->modulo($XMin,$Factor*$Scaled10Factor) == 0) || ($XMin != floor($XMin))) { $XMinRescaled = floor($XMin/($Factor*$Scaled10Factor))*$Factor*$Scaled10Factor; } else { $XMinRescaled = $XMin; }
+ if ( !($this->modulo($XMax,$Factor*$Scaled10Factor) == 0) || ($XMax != floor($XMax))) { $XMaxRescaled = floor($XMax/($Factor*$Scaled10Factor))*$Factor*$Scaled10Factor+($Factor*$Scaled10Factor); } else { $XMaxRescaled = $XMax; }
+ $ScaleHeightRescaled = abs($XMaxRescaled-$XMinRescaled);
+
+ if ( !$Found && floor($ScaleHeightRescaled/($Factor*$Scaled10Factor)) <= $MaxDivs ) { $Found = TRUE; $Rescaled = TRUE; $Result = $Factor * $Scaled10Factor; }
+ }
+ }
+ $Scaled10Factor = $Scaled10Factor * 10;
+ }
+
+ /* ReCall Min / Max / Height */
+ if ( $Rescaled ) { $XMin = $XMinRescaled; $XMax = $XMaxRescaled; $ScaleHeight = $ScaleHeightRescaled; }
+
+ /* Compute rows size */
+ $Rows = floor($ScaleHeight / $Result); if ( $Rows == 0 ) { $Rows = 1; }
+ $RowHeight = $ScaleHeight / $Rows;
+
+ /* Return the results */
+ $Scale["Rows"] = $Rows; $Scale["RowHeight"] = $RowHeight; $Scale["XMin"] = $XMin; $Scale["XMax"] = $XMax;
+
+ /* Compute the needed decimals for the metric view to avoid repetition of the same X Axis labels */
+ if ( $Mode == AXIS_FORMAT_METRIC && $Format == NULL )
+ {
+ $Done = FALSE; $GoodDecimals = 0;
+ for($Decimals=0;$Decimals<=10;$Decimals++)
+ {
+ if ( !$Done )
+ {
+ $LastLabel = "zob"; $ScaleOK = TRUE;
+ for($i=0;$i<=$Rows;$i++)
+ {
+ $Value = $XMin + $i*$RowHeight;
+ $Label = $this->scaleFormat($Value,AXIS_FORMAT_METRIC,$Decimals);
+
+ if ( $LastLabel == $Label ) { $ScaleOK = FALSE; }
+ $LastLabel = $Label;
+ }
+ if ( $ScaleOK ) { $Done = TRUE; $GoodDecimals = $Decimals; }
+ }
+ }
+
+ $Scale["Format"] = $GoodDecimals;
+ }
+ }
+ else
+ {
+ /* If all values are the same we keep a +1/-1 scale */
+ $Rows = 2; $XMin = $XMax-1; $XMax = $XMax+1; $RowHeight = 1;
+
+ /* Return the results */
+ $Scale["Rows"] = $Rows; $Scale["RowHeight"] = $RowHeight; $Scale["XMin"] = $XMin; $Scale["XMax"] = $XMax;
+ }
+
+ return($Scale);
+ }
+
+ /*
+ function modulo($Value1,$Value2)
+ {
+ if ($Value1 > $Value2) { return 0; }
+ if (floor($Value2) == 0) { return(0); }
+ if (floor($Value2) != 0) { return($Value1 % $Value2); }
+
+ $MinValue = min($Value1,$Value2); $Factor = 10;
+ while ( floor($MinValue*$Factor) == 0 )
+ { $Factor = $Factor * 10; }
+
+ return(($Value1*$Factor) % ($Value2*$Factor));
+ }
+ */
+ function modulo( $Value1, $Value2 ) {
+ if ($Value1 > $Value2) { return 0; }
+
+ if ($Value2 == 0) {
+ return 0;
+ } else {
+ $quotient = floor( $Value1 / $Value2 );
+
+ return $Value1 - ( $quotient * $Value2 );
+ }
+ }
+
+ /* Draw an X threshold */
+ function drawXThreshold($Value,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 255;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50;
+ $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6;
+ $Wide = isset($Format["Wide"]) ? $Format["Wide"] : FALSE;
+ $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5;
+ $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : FALSE;
+ $Caption = isset($Format["Caption"]) ? $Format["Caption"] : NULL;
+ $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP;
+ $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 5;
+ $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255;
+ $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255;
+ $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255;
+ $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100;
+ $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE;
+ $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE;
+ $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 3;
+ $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE;
+ $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3;
+ $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
+ $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
+ $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
+ $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 30;
+ $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
+ $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255;
+ $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255;
+ $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255;
+ $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100;
+ $ValueIsLabel = isset($Format["ValueIsLabel"]) ? $Format["ValueIsLabel"] : FALSE;
+
+ $Data = $this->DataSet->getData();
+ $AbscissaMargin = $this->getAbscissaMargin($Data);
+ $XScale = $this->scaleGetXSettings();
+
+ if ( is_array($Value) ) { foreach ($Value as $Key => $ID) { $this->drawXThreshold($ID,$Format); } return(0); }
+
+ if ( $ValueIsLabel )
+ {
+ $Format["ValueIsLabel"] = FALSE;
+ foreach($Data["Series"][$Data["Abscissa"]]["Data"] as $Key => $SerieValue)
+ { if ( $SerieValue == $Value ) { $this->drawXThreshold($Key,$Format); } }
+
+ return(0);
+ }
+
+ $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius,
+ "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding,
+ "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha,
+ "R"=>$CaptionR,"G"=>$CaptionG,"B"=>$CaptionB,"Alpha"=>$CaptionAlpha);
+
+ if ( $Caption == NULL )
+ {
+ if ( isset($Data["Abscissa"]) )
+ {
+ if ( isset($Data["Series"][$Data["Abscissa"]]["Data"][$Value]) )
+ $Caption = $Data["Series"][$Data["Abscissa"]]["Data"][$Value];
+ else
+ $Caption = $Value;
+ }
+ else
+ $Caption = $Value;
+ }
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ $XStep = (($this->GraphAreaX2 - $this->GraphAreaX1) - $XScale[0] *2 ) / $XScale[1];
+ $XPos = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value;
+ $YPos1 = $this->GraphAreaY1 + $Data["YMargin"];
+ $YPos2 = $this->GraphAreaY2 - $Data["YMargin"];
+
+ if ( $XPos >= $this->GraphAreaX1 + $AbscissaMargin && $XPos <= $this->GraphAreaX2 - $AbscissaMargin )
+ {
+ $this->drawLine($XPos,$YPos1,$XPos,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight));
+
+ if ( $Wide )
+ {
+ $this->drawLine($XPos-1,$YPos1,$XPos-1,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks));
+ $this->drawLine($XPos+1,$YPos1,$XPos+1,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks));
+ }
+
+ if ( $WriteCaption )
+ {
+ if ( $CaptionAlign == CAPTION_LEFT_TOP )
+ { $Y = $YPos1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; }
+ else
+ { $Y = $YPos2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; }
+
+ $this->drawText($XPos,$Y,$Caption,$CaptionSettings);
+ }
+
+ return(array("X"=>$XPos));
+ }
+ }
+ elseif( $Data["Orientation"] == SCALE_POS_TOPBOTTOM )
+ {
+ $XStep = (($this->GraphAreaY2 - $this->GraphAreaY1) - $XScale[0] *2 ) / $XScale[1];
+ $XPos = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value;
+ $YPos1 = $this->GraphAreaX1 + $Data["YMargin"];
+ $YPos2 = $this->GraphAreaX2 - $Data["YMargin"];
+
+ if ( $XPos >= $this->GraphAreaY1 + $AbscissaMargin && $XPos <= $this->GraphAreaY2 - $AbscissaMargin )
+ {
+ $this->drawLine($YPos1,$XPos,$YPos2,$XPos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight));
+
+ if ( $Wide )
+ {
+ $this->drawLine($YPos1,$XPos-1,$YPos2,$XPos-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks));
+ $this->drawLine($YPos1,$XPos+1,$YPos2,$XPos+1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks));
+ }
+
+ if ( $WriteCaption )
+ {
+ if ( $CaptionAlign == CAPTION_LEFT_TOP )
+ { $Y = $YPos1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; }
+ else
+ { $Y = $YPos2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; }
+
+ $this->drawText($Y,$XPos,$Caption,$CaptionSettings);
+ }
+
+ return(array("X"=>$XPos));
+ }
+ }
+ }
+
+ /* Draw an X threshold area */
+ function drawXThresholdArea($Value1,$Value2,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 255;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20;
+ $Border = isset($Format["Border"]) ? $Format["Border"] : TRUE;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
+ $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20;
+ $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2;
+ $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : NULL;
+ $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO;
+ $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255;
+ $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255;
+ $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255;
+ $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100;
+ $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : TRUE;
+
+ $RestoreShadow = $this->Shadow;
+ if ( $DisableShadowOnArea && $this->Shadow ) { $this->Shadow = FALSE; }
+
+ if ($BorderAlpha >100) { $BorderAlpha = 100;}
+
+ $Data = $this->DataSet->getData();
+ $XScale = $this->scaleGetXSettings();
+ $AbscissaMargin = $this->getAbscissaMargin($Data);
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ $XStep = (($this->GraphAreaX2 - $this->GraphAreaX1) - $XScale[0] *2 ) / $XScale[1];
+ $XPos1 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value1;
+ $XPos2 = $this->GraphAreaX1 + $XScale[0] + $XStep * $Value2;
+ $YPos1 = $this->GraphAreaY1 + $Data["YMargin"];
+ $YPos2 = $this->GraphAreaY2 - $Data["YMargin"];
+
+ if ( $XPos1 < $this->GraphAreaX1 + $XScale[0] ) { $XPos1 = $this->GraphAreaX1 + $XScale[0]; }
+ if ( $XPos1 > $this->GraphAreaX2 - $XScale[0] ) { $XPos1 = $this->GraphAreaX2 - $XScale[0]; }
+ if ( $XPos2 < $this->GraphAreaX1 + $XScale[0] ) { $XPos2 = $this->GraphAreaX1 + $XScale[0]; }
+ if ( $XPos2 > $this->GraphAreaX2 - $XScale[0] ) { $XPos2 = $this->GraphAreaX2 - $XScale[0]; }
+
+ $this->drawFilledRectangle($XPos1,$YPos1,$XPos2,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+
+ if ( $Border )
+ {
+ $this->drawLine($XPos1,$YPos1,$XPos1,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks));
+ $this->drawLine($XPos2,$YPos1,$XPos2,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks));
+ }
+
+ if ( $AreaName != NULL )
+ {
+ $XPos = ($XPos2-$XPos1)/2 + $XPos1;
+ $YPos = ($YPos2-$YPos1)/2 + $YPos1;
+
+ if ( $NameAngle == ZONE_NAME_ANGLE_AUTO )
+ {
+ $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$AreaName);
+ $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"];
+ if ( abs($XPos2 - $XPos1) > $TxtWidth ) { $NameAngle = 0; } else { $NameAngle = 90; }
+ }
+ $this->Shadow = $RestoreShadow;
+ $this->drawText($XPos,$YPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>$NameAngle,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE));
+ if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; }
+ }
+
+ $this->Shadow = $RestoreShadow;
+ return(array("X1"=>$XPos1,"X2"=>$XPos2));
+ }
+ elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM )
+ {
+ $XStep = (($this->GraphAreaY2 - $this->GraphAreaY1) - $XScale[0] *2 ) / $XScale[1];
+ $XPos1 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value1;
+ $XPos2 = $this->GraphAreaY1 + $XScale[0] + $XStep * $Value2;
+ $YPos1 = $this->GraphAreaX1 + $Data["YMargin"];
+ $YPos2 = $this->GraphAreaX2 - $Data["YMargin"];
+
+ if ( $XPos1 < $this->GraphAreaY1 + $XScale[0] ) { $XPos1 = $this->GraphAreaY1 + $XScale[0]; }
+ if ( $XPos1 > $this->GraphAreaY2 - $XScale[0] ) { $XPos1 = $this->GraphAreaY2 - $XScale[0]; }
+ if ( $XPos2 < $this->GraphAreaY1 + $XScale[0] ) { $XPos2 = $this->GraphAreaY1 + $XScale[0]; }
+ if ( $XPos2 > $this->GraphAreaY2 - $XScale[0] ) { $XPos2 = $this->GraphAreaY2 - $XScale[0]; }
+
+ $this->drawFilledRectangle($YPos1,$XPos1,$YPos2,$XPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+
+ if ( $Border )
+ {
+ $this->drawLine($YPos1,$XPos1,$YPos2,$XPos1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks));
+ $this->drawLine($YPos1,$XPos2,$YPos2,$XPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks));
+ }
+
+ if ( $AreaName != NULL )
+ {
+ $XPos = ($XPos2-$XPos1)/2 + $XPos1;
+ $YPos = ($YPos2-$YPos1)/2 + $YPos1;
+
+ $this->Shadow = $RestoreShadow;
+ $this->drawText($YPos,$XPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>0,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE));
+ if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; }
+ }
+
+ $this->Shadow = $RestoreShadow;
+ return(array("X1"=>$XPos1,"X2"=>$XPos2));
+ }
+ }
+
+ /* Draw an Y threshold with the computed scale */
+ function drawThreshold($Value,$Format="")
+ {
+ $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0;
+ $R = isset($Format["R"]) ? $Format["R"] : 255;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 50;
+ $Weight = isset($Format["Weight"]) ? $Format["Weight"] : NULL;
+ $Ticks = isset($Format["Ticks"]) ? $Format["Ticks"] : 6;
+ $Wide = isset($Format["Wide"]) ? $Format["Wide"] : FALSE;
+ $WideFactor = isset($Format["WideFactor"]) ? $Format["WideFactor"] : 5;
+ $WriteCaption = isset($Format["WriteCaption"]) ? $Format["WriteCaption"] : FALSE;
+ $Caption = isset($Format["Caption"]) ? $Format["Caption"] : NULL;
+ $CaptionAlign = isset($Format["CaptionAlign"]) ? $Format["CaptionAlign"] : CAPTION_LEFT_TOP;
+ $CaptionOffset = isset($Format["CaptionOffset"]) ? $Format["CaptionOffset"] : 10;
+ $CaptionR = isset($Format["CaptionR"]) ? $Format["CaptionR"] : 255;
+ $CaptionG = isset($Format["CaptionG"]) ? $Format["CaptionG"] : 255;
+ $CaptionB = isset($Format["CaptionB"]) ? $Format["CaptionB"] : 255;
+ $CaptionAlpha = isset($Format["CaptionAlpha"]) ? $Format["CaptionAlpha"] : 100;
+ $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE;
+ $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE;
+ $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5;
+ $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE;
+ $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3;
+ $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
+ $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
+ $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
+ $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20;
+ $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
+ $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255;
+ $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255;
+ $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255;
+ $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100;
+ $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : FALSE;
+
+ if ( is_array($Value) ) { foreach ($Value as $Key => $ID) { $this->drawThreshold($ID,$Format); } return(0); }
+
+ $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius,
+ "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding,
+ "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha,
+ "R"=>$CaptionR,"G"=>$CaptionG,"B"=>$CaptionB,"Alpha"=>$CaptionAlpha);
+
+ $Data = $this->DataSet->getData();
+ $AbscissaMargin = $this->getAbscissaMargin($Data);
+
+ if ( $NoMargin ) { $AbscissaMargin = 0; }
+ if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); }
+ if ( $Caption == NULL ) { $Caption = $Value; }
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ $YPos = $this->scaleComputeY($Value,array("AxisID"=>$AxisID));
+ if ( $YPos >= $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"] && $YPos <= $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"] )
+ {
+ $X1 = $this->GraphAreaX1 + $AbscissaMargin;
+ $X2 = $this->GraphAreaX2 - $AbscissaMargin;
+
+ $this->drawLine($X1,$YPos,$X2,$YPos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight));
+
+ if ( $Wide )
+ {
+ $this->drawLine($X1,$YPos-1,$X2,$YPos-1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks));
+ $this->drawLine($X1,$YPos+1,$X2,$YPos+1,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks));
+ }
+
+ if ( $WriteCaption )
+ {
+ if ( $CaptionAlign == CAPTION_LEFT_TOP )
+ { $X = $X1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLELEFT; }
+ else
+ { $X = $X2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_MIDDLERIGHT; }
+
+ $this->drawText($X,$YPos,$Caption,$CaptionSettings);
+ }
+ }
+
+ return(array("Y"=>$YPos));
+ }
+
+ if ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM )
+ {
+ $XPos = $this->scaleComputeY($Value,array("AxisID"=>$AxisID));
+ if ( $XPos >= $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"] && $XPos <= $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"] )
+ {
+ $Y1 = $this->GraphAreaY1 + $AbscissaMargin;
+ $Y2 = $this->GraphAreaY2 - $AbscissaMargin;
+
+ $this->drawLine($XPos,$Y1,$XPos,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight));
+
+ if ( $Wide )
+ {
+ $this->drawLine($XPos-1,$Y1,$XPos-1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks));
+ $this->drawLine($XPos+1,$Y1,$XPos+1,$Y2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/$WideFactor,"Ticks"=>$Ticks));
+ }
+
+ if ( $WriteCaption )
+ {
+ if ( $CaptionAlign == CAPTION_LEFT_TOP )
+ { $Y = $Y1 + $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE; }
+ else
+ { $Y = $Y2 - $CaptionOffset; $CaptionSettings["Align"] = TEXT_ALIGN_BOTTOMMIDDLE; }
+
+ $CaptionSettings["Align"] = TEXT_ALIGN_TOPMIDDLE;
+ $this->drawText($XPos,$Y,$Caption,$CaptionSettings);
+ }
+ }
+
+ return(array("Y"=>$XPos));
+ }
+ }
+
+ /* Draw a threshold with the computed scale */
+ function drawThresholdArea($Value1,$Value2,$Format="")
+ {
+ $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0;
+ $R = isset($Format["R"]) ? $Format["R"] : 255;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 20;
+ $Border = isset($Format["Border"]) ? $Format["Border"] : TRUE;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
+ $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha + 20;
+ $BorderTicks = isset($Format["BorderTicks"]) ? $Format["BorderTicks"] : 2;
+ $AreaName = isset($Format["AreaName"]) ? $Format["AreaName"] : NULL;
+ $NameAngle = isset($Format["NameAngle"]) ? $Format["NameAngle"] : ZONE_NAME_ANGLE_AUTO;
+ $NameR = isset($Format["NameR"]) ? $Format["NameR"] : 255;
+ $NameG = isset($Format["NameG"]) ? $Format["NameG"] : 255;
+ $NameB = isset($Format["NameB"]) ? $Format["NameB"] : 255;
+ $NameAlpha = isset($Format["NameAlpha"]) ? $Format["NameAlpha"] : 100;
+ $DisableShadowOnArea = isset($Format["DisableShadowOnArea"]) ? $Format["DisableShadowOnArea"] : TRUE;
+ $NoMargin = isset($Format["NoMargin"]) ? $Format["NoMargin"] : FALSE;
+
+ if ($Value1 > $Value2) { list($Value1, $Value2) = array($Value2, $Value1); }
+
+ $RestoreShadow = $this->Shadow;
+ if ( $DisableShadowOnArea && $this->Shadow ) { $this->Shadow = FALSE; }
+
+ if ($BorderAlpha >100) { $BorderAlpha = 100;}
+
+ $Data = $this->DataSet->getData();
+ $AbscissaMargin = $this->getAbscissaMargin($Data);
+
+ if ( $NoMargin ) { $AbscissaMargin = 0; }
+ if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); }
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ $XPos1 = $this->GraphAreaX1 + $AbscissaMargin;
+ $XPos2 = $this->GraphAreaX2 - $AbscissaMargin;
+ $YPos1 = $this->scaleComputeY($Value1,array("AxisID"=>$AxisID));
+ $YPos2 = $this->scaleComputeY($Value2,array("AxisID"=>$AxisID));
+
+ if ( $YPos1 < $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"] ) { $YPos1 = $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"]; }
+ if ( $YPos1 > $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"] ) { $YPos1 = $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"]; }
+ if ( $YPos2 < $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"] ) { $YPos2 = $this->GraphAreaY1+$Data["Axis"][$AxisID]["Margin"]; }
+ if ( $YPos2 > $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"] ) { $YPos2 = $this->GraphAreaY2-$Data["Axis"][$AxisID]["Margin"]; }
+
+ $this->drawFilledRectangle($XPos1,$YPos1,$XPos2,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ if ( $Border )
+ {
+ $this->drawLine($XPos1,$YPos1,$XPos2,$YPos1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks));
+ $this->drawLine($XPos1,$YPos2,$XPos2,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks));
+ }
+
+ if ( $AreaName != NULL )
+ {
+ $XPos = ($XPos2-$XPos1)/2 + $XPos1;
+ $YPos = ($YPos2-$YPos1)/2 + $YPos1;
+ $this->Shadow = $RestoreShadow;
+ $this->drawText($XPos,$YPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>0,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE));
+ if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; }
+ }
+
+ $this->Shadow = $RestoreShadow;
+ return(array("Y1"=>$YPos1,"Y2"=>$YPos2));
+ }
+ elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM )
+ {
+ $YPos1 = $this->GraphAreaY1 + $AbscissaMargin;
+ $YPos2 = $this->GraphAreaY2 - $AbscissaMargin;
+ $XPos1 = $this->scaleComputeY($Value1,array("AxisID"=>$AxisID));
+ $XPos2 = $this->scaleComputeY($Value2,array("AxisID"=>$AxisID));
+
+ if ( $XPos1 < $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"] ) { $XPos1 = $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"]; }
+ if ( $XPos1 > $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"] ) { $XPos1 = $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"]; }
+ if ( $XPos2 < $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"] ) { $XPos2 = $this->GraphAreaX1+$Data["Axis"][$AxisID]["Margin"]; }
+ if ( $XPos2 > $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"] ) { $XPos2 = $this->GraphAreaX2-$Data["Axis"][$AxisID]["Margin"]; }
+
+ $this->drawFilledRectangle($XPos1,$YPos1,$XPos2,$YPos2,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ if ( $Border )
+ {
+ $this->drawLine($XPos1,$YPos1,$XPos1,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks));
+ $this->drawLine($XPos2,$YPos1,$XPos2,$YPos2,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Ticks"=>$BorderTicks));
+ }
+
+ if ( $AreaName != NULL )
+ {
+ $XPos = ($YPos2-$YPos1)/2 + $YPos1;
+ $YPos = ($XPos2-$XPos1)/2 + $XPos1;
+
+ if ( $NameAngle == ZONE_NAME_ANGLE_AUTO )
+ {
+ $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$AreaName);
+ $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"];
+ if ( abs($XPos2 - $XPos1) > $TxtWidth ) { $NameAngle = 0; } else { $NameAngle = 90; }
+ }
+ $this->Shadow = $RestoreShadow;
+ $this->drawText($YPos,$XPos,$AreaName,array("R"=>$NameR,"G"=>$NameG,"B"=>$NameB,"Alpha"=>$NameAlpha,"Angle"=>$NameAngle,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE));
+ if ( $DisableShadowOnArea ) { $this->Shadow = FALSE; }
+ }
+
+ $this->Shadow = $RestoreShadow;
+ return(array("Y1"=>$XPos1,"Y2"=>$XPos2));
+ }
+ }
+
+ function scaleGetXSettings()
+ {
+ $Data = $this->DataSet->getData();
+ foreach($Data["Axis"] as $AxisID => $Settings)
+ {
+ if ( $Settings["Identity"] == AXIS_X )
+ {
+ $Rows = $Settings["Rows"];
+
+ return(array($Settings["Margin"],$Rows));
+ }
+ }
+ }
+
+ function scaleComputeY($Values,$Option="",$ReturnOnly0Height=FALSE)
+ {
+ $AxisID = isset($Option["AxisID"]) ? $Option["AxisID"] : 0;
+ $SerieName = isset($Option["SerieName"]) ? $Option["SerieName"] : NULL;
+
+ $Data = $this->DataSet->getData();
+ if ( !isset($Data["Axis"][$AxisID]) ) { return(-1); }
+
+ if ( $SerieName != NULL ) { $AxisID = $Data["Series"][$SerieName]["Axis"]; }
+ if ( !is_array($Values) ) { $tmp = $Values; $Values = ""; $Values[0] = $tmp; }
+
+ $Result = "";
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ $Height = ($this->GraphAreaY2 - $this->GraphAreaY1) - $Data["Axis"][$AxisID]["Margin"]*2;
+ $ScaleHeight = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"];
+ $Step = $Height / $ScaleHeight;
+
+ if ( $ReturnOnly0Height )
+ { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $Step * $Value; } } }
+ else
+ { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $this->GraphAreaY2 - $Data["Axis"][$AxisID]["Margin"] - ($Step * ($Value-$Data["Axis"][$AxisID]["ScaleMin"])); } } }
+ }
+ else
+ {
+ $Width = ($this->GraphAreaX2 - $this->GraphAreaX1) - $Data["Axis"][$AxisID]["Margin"]*2;
+ $ScaleWidth = $Data["Axis"][$AxisID]["ScaleMax"] - $Data["Axis"][$AxisID]["ScaleMin"];
+
+ if ($ScaleWidth > 0) {
+ $Step = $Width / $ScaleWidth;
+ } else {
+ $Step = 1; // or change this to 0 if it doesn't work, I am not sure what this line does.
+ }
+
+ if ( $ReturnOnly0Height )
+ { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $Step * $Value; } } }
+ else
+ { foreach($Values as $Key => $Value) { if ( $Value == VOID ) { $Result[] = VOID; } else { $Result[] = $this->GraphAreaX1 + $Data["Axis"][$AxisID]["Margin"] + ($Step * ($Value-$Data["Axis"][$AxisID]["ScaleMin"])); } } }
+ }
+
+ if ( count($Result) == 1 )
+ return($Result[0]);
+ else
+ return($Result);
+ }
+
+ /* Format the axis values */
+ function scaleFormat($Value,$Mode=NULL,$Format=NULL,$Unit=NULL)
+ {
+ if ( $Value == VOID ) { return(""); }
+
+ if ( $Mode == AXIS_FORMAT_TRAFFIC )
+ {
+ if ( $Value == 0 ) { return("0B"); }
+ $Units = array("B","KB","MB","GB","TB","PB");
+ $Sign = ""; if ( $Value < 0 ) { $Value = abs($Value); $Sign = "-"; }
+
+ $Value = number_format($Value/pow(1024,($Scale=floor(log($Value,1024)))),2,",",".");
+ return($Sign.$Value." ".$Units[$Scale]);
+ }
+
+ if ( $Mode == AXIS_FORMAT_CUSTOM )
+ { if ( function_exists($Format) ) { return(call_user_func($Format,$Value)); } }
+
+ if ( $Mode == AXIS_FORMAT_DATE )
+ { if ( $Format == NULL ) { $Pattern = "d/m/Y"; } else { $Pattern = $Format; } return(gmdate($Pattern,$Value)); }
+
+ if ( $Mode == AXIS_FORMAT_TIME )
+ { if ( $Format == NULL ) { $Pattern = "H:i:s"; } else { $Pattern = $Format; } return(gmdate($Pattern,$Value)); }
+
+ if ( $Mode == AXIS_FORMAT_CURRENCY )
+ { return($Format.number_format($Value,2)); }
+
+ if ( $Mode == AXIS_FORMAT_METRIC )
+ {
+ if (abs($Value) > 1000000000)
+ return(round($Value/1000000000,$Format)."g".$Unit);
+ if (abs($Value) > 1000000)
+ return(round($Value/1000000,$Format)."m".$Unit);
+ elseif (abs($Value) >= 1000)
+ return(round($Value/1000,$Format)."k".$Unit);
+
+ }
+ return($Value.$Unit);
+ }
+
+ /* Write Max value on a chart */
+ function writeBounds($Type=BOUND_BOTH,$Format=NULL)
+ {
+ $MaxLabelTxt = isset($Format["MaxLabelTxt"]) ? $Format["MaxLabelTxt"] : "max=";
+ $MinLabelTxt = isset($Format["MinLabelTxt"]) ? $Format["MinLabelTxt"] : "min=";
+ $Decimals = isset($Format["Decimals"]) ? $Format["Decimals"] : 1;
+ $ExcludedSeries = isset($Format["ExcludedSeries"]) ? $Format["ExcludedSeries"] : "";
+ $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4;
+ $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
+ $MaxDisplayR = isset($Format["MaxDisplayR"]) ? $Format["MaxDisplayR"] : 0;
+ $MaxDisplayG = isset($Format["MaxDisplayG"]) ? $Format["MaxDisplayG"] : 0;
+ $MaxDisplayB = isset($Format["MaxDisplayB"]) ? $Format["MaxDisplayB"] : 0;
+ $MinDisplayR = isset($Format["MinDisplayR"]) ? $Format["MinDisplayR"] : 255;
+ $MinDisplayG = isset($Format["MinDisplayG"]) ? $Format["MinDisplayG"] : 255;
+ $MinDisplayB = isset($Format["MinDisplayB"]) ? $Format["MinDisplayB"] : 255;
+ $MinLabelPos = isset($Format["MinLabelPos"]) ? $Format["MinLabelPos"] : BOUND_LABEL_POS_AUTO;
+ $MaxLabelPos = isset($Format["MaxLabelPos"]) ? $Format["MaxLabelPos"] : BOUND_LABEL_POS_AUTO;
+ $DrawBox = isset($Format["DrawBox"]) ? $Format["DrawBox"] : TRUE;
+ $DrawBoxBorder = isset($Format["DrawBoxBorder"]) ? $Format["DrawBoxBorder"] : FALSE;
+ $BorderOffset = isset($Format["BorderOffset"]) ? $Format["BorderOffset"] : 5;
+ $BoxRounded = isset($Format["BoxRounded"]) ? $Format["BoxRounded"] : TRUE;
+ $RoundedRadius = isset($Format["RoundedRadius"]) ? $Format["RoundedRadius"] : 3;
+ $BoxR = isset($Format["BoxR"]) ? $Format["BoxR"] : 0;
+ $BoxG = isset($Format["BoxG"]) ? $Format["BoxG"] : 0;
+ $BoxB = isset($Format["BoxB"]) ? $Format["BoxB"] : 0;
+ $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 20;
+ $BoxSurrounding = isset($Format["BoxSurrounding"]) ? $Format["BoxSurrounding"] : "";
+ $BoxBorderR = isset($Format["BoxBorderR"]) ? $Format["BoxBorderR"] : 255;
+ $BoxBorderG = isset($Format["BoxBorderG"]) ? $Format["BoxBorderG"] : 255;
+ $BoxBorderB = isset($Format["BoxBorderB"]) ? $Format["BoxBorderB"] : 255;
+ $BoxBorderAlpha = isset($Format["BoxBorderAlpha"]) ? $Format["BoxBorderAlpha"] : 100;
+
+ $CaptionSettings = array("DrawBox"=>$DrawBox,"DrawBoxBorder"=>$DrawBoxBorder,"BorderOffset"=>$BorderOffset,"BoxRounded"=>$BoxRounded,"RoundedRadius"=>$RoundedRadius,
+ "BoxR"=>$BoxR,"BoxG"=>$BoxG,"BoxB"=>$BoxB,"BoxAlpha"=>$BoxAlpha,"BoxSurrounding"=>$BoxSurrounding,
+ "BoxBorderR"=>$BoxBorderR,"BoxBorderG"=>$BoxBorderG,"BoxBorderB"=>$BoxBorderB,"BoxBorderAlpha"=>$BoxBorderAlpha);
+
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+
+ $Data = $this->DataSet->getData();
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] && !isset($ExcludedSeries[$SerieName]))
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"];
+ if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; }
+
+ $MinValue = $this->DataSet->getMin($SerieName);
+ $MaxValue = $this->DataSet->getMax($SerieName);
+
+ $MinPos = VOID; $MaxPos = VOID;
+ foreach($Serie["Data"] as $Key => $Value)
+ {
+ if ( $Value == $MinValue && $MinPos == VOID ) { $MinPos = $Key; }
+ if ( $Value == $MaxValue ) { $MaxPos = $Key; }
+ }
+
+ $AxisID = $Serie["Axis"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs;
+ $X = $this->GraphAreaX1 + $XMargin;
+ $SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0;
+
+ if ( $Type == BOUND_MAX || $Type == BOUND_BOTH )
+ {
+ if ( $MaxLabelPos == BOUND_LABEL_POS_TOP || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0) ) { $YPos = $PosArray[$MaxPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_BOTTOMMIDDLE; }
+ if ( $MaxLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0) ) { $YPos = $PosArray[$MaxPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_TOPMIDDLE; }
+
+ $XPos = $X + $MaxPos*$XStep + $SerieOffset;
+ $Label = $MaxLabelTxt.$this->scaleFormat(round($MaxValue,$Decimals),$Mode,$Format,$Unit);
+
+ $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$Label);
+ $XOffset = 0; $YOffset = 0;
+ if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"])/2); }
+ if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2)/2); }
+ if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"]; }
+ if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2); }
+
+ $CaptionSettings["R"] = $MaxDisplayR; $CaptionSettings["G"] = $MaxDisplayG;
+ $CaptionSettings["B"] = $MaxDisplayB; $CaptionSettings["Align"] = $Align;
+
+ $this->drawText($XPos+$XOffset,$YPos+$YOffset,$Label,$CaptionSettings);
+ }
+
+ if ( $Type == BOUND_MIN || $Type == BOUND_BOTH )
+ {
+ if ( $MinLabelPos == BOUND_LABEL_POS_TOP || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0) ) { $YPos = $PosArray[$MinPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_BOTTOMMIDDLE; }
+ if ( $MinLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0) ) { $YPos = $PosArray[$MinPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_TOPMIDDLE; }
+
+ $XPos = $X + $MinPos*$XStep + $SerieOffset;
+ $Label = $MinLabelTxt.$this->scaleFormat(round($MinValue,$Decimals),$Mode,$Format,$Unit);
+
+ $TxtPos = $this->getTextBox($XPos,$YPos,$this->FontName,$this->FontSize,0,$Label);
+ $XOffset = 0; $YOffset = 0;
+ if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = (($this->GraphAreaX1 - $TxtPos[0]["X"])/2); }
+ if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -(($TxtPos[1]["X"] - $this->GraphAreaX2)/2); }
+ if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = $this->GraphAreaY1 - $TxtPos[2]["Y"]; }
+ if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -($TxtPos[0]["Y"] - $this->GraphAreaY2); }
+
+ $CaptionSettings["R"] = $MinDisplayR; $CaptionSettings["G"] = $MinDisplayG;
+ $CaptionSettings["B"] = $MinDisplayB; $CaptionSettings["Align"] = $Align;
+
+ $this->drawText($XPos+$XOffset,$YPos-$DisplayOffset+$YOffset,$Label,$CaptionSettings);
+ }
+ }
+ else
+ {
+ $XStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs;
+ $X = $this->GraphAreaY1 + $XMargin;
+ $SerieOffset = isset($Serie["XOffset"]) ? $Serie["XOffset"] : 0;
+
+ if ( $Type == BOUND_MAX || $Type == BOUND_BOTH )
+ {
+ if ( $MaxLabelPos == BOUND_LABEL_POS_TOP || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue >= 0) ) { $YPos = $PosArray[$MaxPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLELEFT; }
+ if ( $MaxLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MaxLabelPos == BOUND_LABEL_POS_AUTO && $MaxValue < 0) ) { $YPos = $PosArray[$MaxPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLERIGHT; }
+
+ $XPos = $X + $MaxPos*$XStep + $SerieOffset;
+ $Label = $MaxLabelTxt.$this->scaleFormat($MaxValue,$Mode,$Format,$Unit);
+
+ $TxtPos = $this->getTextBox($YPos,$XPos,$this->FontName,$this->FontSize,0,$Label);
+ $XOffset = 0; $YOffset = 0;
+ if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"]; }
+ if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2); }
+ if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"])/2; }
+ if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2)/2);}
+
+ $CaptionSettings["R"] = $MaxDisplayR; $CaptionSettings["G"] = $MaxDisplayG;
+ $CaptionSettings["B"] = $MaxDisplayB; $CaptionSettings["Align"] = $Align;
+
+ $this->drawText($YPos+$XOffset,$XPos+$YOffset,$Label,$CaptionSettings);
+ }
+
+ if ( $Type == BOUND_MIN || $Type == BOUND_BOTH )
+ {
+ if ( $MinLabelPos == BOUND_LABEL_POS_TOP || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue >= 0) ) { $YPos = $PosArray[$MinPos] + $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLELEFT; }
+ if ( $MinLabelPos == BOUND_LABEL_POS_BOTTOM || ( $MinLabelPos == BOUND_LABEL_POS_AUTO && $MinValue < 0) ) { $YPos = $PosArray[$MinPos] - $DisplayOffset + 2; $Align = TEXT_ALIGN_MIDDLERIGHT; }
+
+ $XPos = $X + $MinPos*$XStep + $SerieOffset;
+ $Label = $MinLabelTxt.$this->scaleFormat($MinValue,$Mode,$Format,$Unit);
+
+ $TxtPos = $this->getTextBox($YPos,$XPos,$this->FontName,$this->FontSize,0,$Label);
+ $XOffset = 0; $YOffset = 0;
+ if ( $TxtPos[0]["X"] < $this->GraphAreaX1 ) { $XOffset = $this->GraphAreaX1 - $TxtPos[0]["X"]; }
+ if ( $TxtPos[1]["X"] > $this->GraphAreaX2 ) { $XOffset = -($TxtPos[1]["X"] - $this->GraphAreaX2); }
+ if ( $TxtPos[2]["Y"] < $this->GraphAreaY1 ) { $YOffset = ($this->GraphAreaY1 - $TxtPos[2]["Y"])/2; }
+ if ( $TxtPos[0]["Y"] > $this->GraphAreaY2 ) { $YOffset = -(($TxtPos[0]["Y"] - $this->GraphAreaY2)/2);}
+
+ $CaptionSettings["R"] = $MinDisplayR; $CaptionSettings["G"] = $MinDisplayG;
+ $CaptionSettings["B"] = $MinDisplayB; $CaptionSettings["Align"] = $Align;
+
+ $this->drawText($YPos+$XOffset,$XPos+$YOffset,$Label,$CaptionSettings);
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw a plot chart */
+ function drawPlotChart($Format=NULL)
+ {
+ $PlotSize = isset($Format["PlotSize"]) ? $Format["PlotSize"] : NULL;
+ $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : FALSE;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 50;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 50;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 50;
+ $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 30;
+ $BorderSize = isset($Format["BorderSize"]) ? $Format["BorderSize"] : 2;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+ $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE;
+ $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 4;
+ $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
+ $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
+ $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
+ $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
+ $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ if ( isset($Serie["Weight"]) ) { $SerieWeight = $Serie["Weight"] + 2; } else { $SerieWeight = 2; }
+ if ( $PlotSize != NULL ) { $SerieWeight = $PlotSize; }
+
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"];
+ if ( $Surrounding != NULL ) { $BorderR = $R + $Surrounding; $BorderG = $G + $Surrounding; $BorderB = $B + $Surrounding; }
+ if ( isset($Serie["Picture"]) )
+ { $Picture = $Serie["Picture"]; list($PicWidth,$PicHeight,$PicType) = $this->getPicInfo($Picture); }
+ else { $Picture = NULL; $PicOffset = 0; }
+
+ if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; }
+
+ $AxisID = $Serie["Axis"];
+ $Shape = $Serie["Shape"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; }
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ if ( $Picture != NULL ) { $PicOffset = $PicHeight / 2; $SerieWeight = 0; }
+ $X = $this->GraphAreaX1 + $XMargin;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ foreach($PosArray as $Key => $Y)
+ {
+ if ( $DisplayValues )
+ $this->drawText($X,$Y-$DisplayOffset-$SerieWeight-$BorderSize-$PicOffset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE));
+
+ if ( $Y != VOID )
+ {
+ if ( $RecordImageMap ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$SerieWeight,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ if ( $Picture != NULL )
+ { $this->drawFromPicture($PicType,$Picture,$X-$PicWidth/2,$Y-$PicHeight/2); }
+ else
+ { $this->drawShape($X,$Y,$Shape,$SerieWeight,$PlotBorder,$BorderSize,$R,$G,$B,$Alpha,$BorderR,$BorderG,$BorderB,$BorderAlpha); }
+ }
+ $X = $X + $XStep;
+ }
+ }
+ else
+ {
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ if ( $Picture != NULL ) { $PicOffset = $PicWidth / 2; $SerieWeight = 0; }
+ $Y = $this->GraphAreaY1 + $XMargin;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ foreach($PosArray as $Key => $X)
+ {
+ if ( $DisplayValues )
+ $this->drawText($X+$DisplayOffset+$SerieWeight+$BorderSize+$PicOffset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE));
+
+ if ( $X != VOID )
+ {
+ if ( $RecordImageMap ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$SerieWeight,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ if ( $Picture != NULL )
+ { $this->drawFromPicture($PicType,$Picture,$X-$PicWidth/2,$Y-$PicHeight/2); }
+ else
+ { $this->drawShape($X,$Y,$Shape,$SerieWeight,$PlotBorder,$BorderSize,$R,$G,$B,$Alpha,$BorderR,$BorderG,$BorderB,$BorderAlpha); }
+ }
+ $Y = $Y + $YStep;
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw a spline chart */
+ function drawSplineChart($Format=NULL)
+ {
+ $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : TRUE;
+ $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4;
+ $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : NULL; // 234
+ $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : NULL; // 55
+ $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : NULL; // 26
+ $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE;
+ $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
+ $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
+ $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
+ $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
+ $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
+ $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE;
+ $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"];
+
+ if ( $BreakR == NULL )
+ $BreakSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks);
+ else
+ $BreakSettings = array("R"=>$BreakR,"G"=>$BreakG,"B"=>$BreakB,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight);
+
+ if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; }
+
+ $AxisID = $Serie["Axis"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; }
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin; $WayPoints = "";
+ $Force = $XStep / 5;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ $LastGoodY = NULL; $LastGoodX = NULL; $LastX = 1; $LastY = 1;
+ foreach($PosArray as $Key => $Y)
+ {
+ if ( $DisplayValues )
+ $this->drawText($X,$Y-$DisplayOffset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE));
+
+ if ( $RecordImageMap && $Y != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ if ( $Y == VOID && $LastY != NULL )
+ { $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); $WayPoints = ""; }
+
+ if ( $Y != VOID && $LastY == NULL && $LastGoodY != NULL && !$BreakVoid )
+ { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); }
+
+ if ( $Y != VOID )
+ $WayPoints[] = array($X,$Y);
+
+ if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; }
+ if ( $Y == VOID ) { $Y = NULL; }
+
+ $LastX = $X; $LastY = $Y;
+ $X = $X + $XStep;
+ }
+ $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight));
+ }
+ else
+ {
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin; $WayPoints = "";
+ $Force = $YStep / 5;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ $LastGoodY = NULL; $LastGoodX = NULL; $LastX = 1; $LastY = 1;
+ foreach($PosArray as $Key => $X)
+ {
+ if ( $DisplayValues )
+ $this->drawText($X+$DisplayOffset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE));
+
+ if ( $RecordImageMap && $X != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ if ( $X == VOID && $LastX != NULL )
+ { $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight)); $WayPoints = ""; }
+
+ if ( $X != VOID && $LastX == NULL && $LastGoodX != NULL && !$BreakVoid )
+ { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); }
+
+ if ( $X != VOID )
+ $WayPoints[] = array($X,$Y);
+
+ if ( $X != VOID ) { $LastGoodX = $X; $LastGoodY = $Y; }
+ if ( $X == VOID ) { $X = NULL; }
+
+ $LastX = $X; $LastY = $Y;
+ $Y = $Y + $YStep;
+ }
+ $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight));
+ }
+ }
+ }
+ }
+
+ /* Draw a filled spline chart */
+ function drawFilledSplineChart($Format=NULL)
+ {
+ $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE;
+ $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
+ $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
+ $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
+ $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
+ $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
+ $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE;
+ $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"];
+ if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; }
+
+ $AxisID = $Serie["Axis"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+ if ( $AroundZero ) { $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); }
+
+ if ( $Threshold != NULL )
+ {
+ foreach($Threshold as $Key => $Params)
+ {
+ $Threshold[$Key]["MinX"] = $this->scaleComputeY($Params["Min"],array("AxisID"=>$Serie["Axis"]));
+ $Threshold[$Key]["MaxX"] = $this->scaleComputeY($Params["Max"],array("AxisID"=>$Serie["Axis"]));
+ }
+ }
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin; $WayPoints = "";
+ $Force = $XStep / 5;
+
+ if ( !$AroundZero ) { $YZero = $this->GraphAreaY2-1; }
+ if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; }
+ if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; }
+
+ $LastX = ""; $LastY = "";
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ foreach($PosArray as $Key => $Y)
+ {
+ if ( $DisplayValues )
+ $this->drawText($X,$Y-$DisplayOffset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE));
+
+ if ( $Y == VOID )
+ {
+ $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE));
+
+ if ( $Area != "" )
+ {
+ foreach ($Area as $key => $Points)
+ {
+ $Corners = ""; $Corners[] = $Area[$key][0]["X"]; $Corners[] = $YZero;
+ foreach($Points as $subKey => $Point)
+ {
+ if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; }
+ $Corners[] = $Point["Y"]+1;
+ }
+ $Corners[] = $Points[$subKey]["X"]-1; $Corners[] = $YZero;
+
+ $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold));
+ }
+ $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ }
+
+ $WayPoints = "";
+ }
+ else
+ $WayPoints[] = array($X,$Y-.5); /* -.5 for AA visual fix */
+
+ $X = $X + $XStep;
+ }
+ $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE));
+
+ if ( $Area != "" )
+ {
+ foreach ($Area as $key => $Points)
+ {
+ $Corners = ""; $Corners[] = $Area[$key][0]["X"]; $Corners[] = $YZero;
+ foreach($Points as $subKey => $Point)
+ {
+ if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; }
+ $Corners[] = $Point["Y"]+1;
+ }
+ $Corners[] = $Points[$subKey]["X"]-1; $Corners[] = $YZero;
+
+ $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold));
+ }
+ $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ }
+ }
+ else
+ {
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin; $WayPoints = "";
+ $Force = $YStep / 5;
+
+ if ( !$AroundZero ) { $YZero = $this->GraphAreaX1+1; }
+ if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; }
+ if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; }
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ foreach($PosArray as $Key => $X)
+ {
+ if ( $DisplayValues )
+ $this->drawText($X+$DisplayOffset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE));
+
+ if ( $X == VOID )
+ {
+ $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE));
+
+ if ( $Area != "" )
+ {
+ foreach ($Area as $key => $Points)
+ {
+ $Corners = ""; $Corners[] = $YZero; $Corners[] = $Area[$key][0]["Y"];
+ foreach($Points as $subKey => $Point)
+ {
+ if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; }
+ $Corners[] = $Point["Y"];
+ }
+ $Corners[] = $YZero; $Corners[] = $Points[$subKey]["Y"]-1;
+
+ $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold));
+ }
+ $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ }
+
+ $WayPoints = "";
+ }
+ else
+ $WayPoints[] = array($X,$Y);
+
+ $Y = $Y + $YStep;
+ }
+ $Area = $this->drawSpline($WayPoints,array("Force"=>$Force,"PathOnly"=>TRUE));
+
+ if ( $Area != "" )
+ {
+ foreach ($Area as $key => $Points)
+ {
+ $Corners = ""; $Corners[] = $YZero; $Corners[] = $Area[$key][0]["Y"];
+ foreach($Points as $subKey => $Point)
+ {
+ if ( $subKey == count($Points)-1) { $Corners[] = $Point["X"]-1; } else { $Corners[] = $Point["X"]; }
+ $Corners[] = $Point["Y"];
+ }
+ $Corners[] = $YZero; $Corners[] = $Points[$subKey]["Y"]-1;
+
+ $this->drawPolygonChart($Corners,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha/2,"NoBorder"=>TRUE,"Threshold"=>$Threshold));
+ }
+ $this->drawSpline($WayPoints,array("Force"=>$Force,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks));
+ }
+
+ }
+ }
+ }
+ }
+
+ /* Draw a line chart */
+ function drawLineChart($Format=NULL)
+ {
+ $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : TRUE;
+ $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4;
+ $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : NULL;
+ $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : NULL;
+ $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : NULL;
+ $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE;
+ $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
+ $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
+ $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
+ $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
+ $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
+ $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE;
+ $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5;
+ $ForceColor = isset($Format["ForceColor"]) ? $Format["ForceColor"] : FALSE;
+ $ForceR = isset($Format["ForceR"]) ? $Format["ForceR"] : 0;
+ $ForceG = isset($Format["ForceG"]) ? $Format["ForceG"] : 0;
+ $ForceB = isset($Format["ForceB"]) ? $Format["ForceB"] : 0;
+ $ForceAlpha = isset($Format["ForceAlpha"]) ? $Format["ForceAlpha"] : 100;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"];
+
+ if ( $ForceColor )
+ { $R = $ForceR; $G = $ForceG; $B = $ForceB; $Alpha = $ForceAlpha; }
+
+ if ( $BreakR == NULL )
+ $BreakSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight);
+ else
+ $BreakSettings = array("R"=>$BreakR,"G"=>$BreakG,"B"=>$BreakB,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight);
+
+ if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; }
+
+ $AxisID = $Serie["Axis"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; }
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ $LastGoodY = NULL; $LastGoodX = NULL;
+ foreach($PosArray as $Key => $Y)
+ {
+ if ( $DisplayValues && $Serie["Data"][$Key] != VOID )
+ {
+ if ( $Serie["Data"][$Key] > 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; }
+ $this->drawText($X,$Y-$Offset-$Weight,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align));
+ }
+
+ if ( $RecordImageMap && $Y != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ if ( $Y != VOID && $LastX != NULL && $LastY != NULL )
+ $this->drawLine($LastX,$LastY,$X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight));
+
+ if ( $Y != VOID && $LastY == NULL && $LastGoodY != NULL && !$BreakVoid )
+ { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); $LastGoodY = NULL; }
+
+ if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; }
+ if ( $Y == VOID ) { $Y = NULL; }
+
+ $LastX = $X; $LastY = $Y;
+ $X = $X + $XStep;
+ }
+ }
+ else
+ {
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ $LastGoodY = NULL; $LastGoodX = NULL;
+ foreach($PosArray as $Key => $X)
+ {
+ if ( $DisplayValues && $Serie["Data"][$Key] != VOID )
+ { $this->drawText($X+$DisplayOffset+$Weight,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_BOTTOMMIDDLE)); }
+
+ if ( $RecordImageMap && $X != VOID ) { $this->addToImageMap("CIRCLE",floor($X).",".floor($Y).",".$ImageMapPlotSize,$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ if ( $X != VOID && $LastX != NULL && $LastY != NULL )
+ $this->drawLine($LastX,$LastY,$X,$Y,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight));
+
+ if ( $X != VOID && $LastX == NULL && $LastGoodY != NULL && !$BreakVoid )
+ { $this->drawLine($LastGoodX,$LastGoodY,$X,$Y,$BreakSettings); $LastGoodY = NULL; }
+
+ if ( $X != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; }
+ if ( $X == VOID ) { $X = NULL; }
+
+ $LastX = $X; $LastY = $Y;
+ $Y = $Y + $YStep;
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw a line chart */
+ function drawZoneChart($SerieA,$SerieB,$Format=NULL)
+ {
+ $AxisID = isset($Format["AxisID"]) ? $Format["AxisID"] : 0;
+ $LineR = isset($Format["LineR"]) ? $Format["LineR"] : 150;
+ $LineG = isset($Format["LineG"]) ? $Format["LineG"] : 150;
+ $LineB = isset($Format["LineB"]) ? $Format["LineB"] : 150;
+ $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 50;
+ $LineTicks = isset($Format["LineTicks"]) ? $Format["LineTicks"] : 1;
+ $AreaR = isset($Format["AreaR"]) ? $Format["AreaR"] : 150;
+ $AreaG = isset($Format["AreaG"]) ? $Format["AreaG"] : 150;
+ $AreaB = isset($Format["AreaB"]) ? $Format["AreaB"] : 150;
+ $AreaAlpha = isset($Format["AreaAlpha"]) ? $Format["AreaAlpha"] : 5;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
+
+ $Data = $this->DataSet->getData();
+ if ( !isset($Data["Series"][$SerieA]["Data"]) || !isset($Data["Series"][$SerieB]["Data"]) ) { return(0); }
+ $SerieAData = $Data["Series"][$SerieA]["Data"];
+ $SerieBData = $Data["Series"][$SerieB]["Data"];
+
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ $PosArrayA = $this->scaleComputeY($SerieAData,array("AxisID"=>$AxisID));
+ $PosArrayB = $this->scaleComputeY($SerieBData,array("AxisID"=>$AxisID));
+ if ( count($PosArrayA) != count($PosArrayB) ) { return(0); }
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL;
+
+ $LastX = NULL; $LastY1 = NULL; $LastY2 = NULL;
+ $BoundsA = ""; $BoundsB = "";
+ foreach($PosArrayA as $Key => $Y1)
+ {
+ $Y2 = $PosArrayB[$Key];
+
+ $BoundsA[] = $X; $BoundsA[] = $Y1;
+ $BoundsB[] = $X; $BoundsB[] = $Y2;
+
+ $LastX = $X;
+ $LastY1 = $Y1; $LastY2 = $Y2;
+
+ $X = $X + $XStep;
+ }
+ $Bounds = array_merge($BoundsA,$this->reversePlots($BoundsB));
+ $this->drawPolygonChart($Bounds,array("R"=>$AreaR,"G"=>$AreaG,"B"=>$AreaB,"Alpha"=>$AreaAlpha));
+
+ for($i=0;$i<=count($BoundsA)-4;$i=$i+2)
+ {
+ $this->drawLine($BoundsA[$i],$BoundsA[$i+1],$BoundsA[$i+2],$BoundsA[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks));
+ $this->drawLine($BoundsB[$i],$BoundsB[$i+1],$BoundsB[$i+2],$BoundsB[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks));
+ }
+ }
+ else
+ {
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL;
+
+ $LastY = NULL; $LastX1 = NULL; $LastX2 = NULL;
+ $BoundsA = ""; $BoundsB = "";
+ foreach($PosArrayA as $Key => $X1)
+ {
+ $X2 = $PosArrayB[$Key];
+
+ $BoundsA[] = $X1; $BoundsA[] = $Y;
+ $BoundsB[] = $X2; $BoundsB[] = $Y;
+
+ $LastY = $Y;
+ $LastX1 = $X1; $LastX2 = $X2;
+
+ $Y = $Y + $YStep;
+ }
+ $Bounds = array_merge($BoundsA,$this->reversePlots($BoundsB));
+ $this->drawPolygonChart($Bounds,array("R"=>$AreaR,"G"=>$AreaG,"B"=>$AreaB,"Alpha"=>$AreaAlpha));
+
+ for($i=0;$i<=count($BoundsA)-4;$i=$i+2)
+ {
+ $this->drawLine($BoundsA[$i],$BoundsA[$i+1],$BoundsA[$i+2],$BoundsA[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks));
+ $this->drawLine($BoundsB[$i],$BoundsB[$i+1],$BoundsB[$i+2],$BoundsB[$i+3],array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha,"Ticks"=>$LineTicks));
+ }
+ }
+ }
+
+ /* Draw a step chart */
+ function drawStepChart($Format=NULL)
+ {
+ $BreakVoid = isset($Format["BreakVoid"]) ? $Format["BreakVoid"] : FALSE;
+ $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : TRUE;
+ $VoidTicks = isset($Format["VoidTicks"]) ? $Format["VoidTicks"] : 4;
+ $BreakR = isset($Format["BreakR"]) ? $Format["BreakR"] : NULL;
+ $BreakG = isset($Format["BreakG"]) ? $Format["BreakG"] : NULL;
+ $BreakB = isset($Format["BreakB"]) ? $Format["BreakB"] : NULL;
+ $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] :FALSE;
+ $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
+ $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
+ $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
+ $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
+ $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
+ $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE;
+ $ImageMapPlotSize = isset($Format["ImageMapPlotSize"]) ? $Format["ImageMapPlotSize"] : 5;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"];
+
+ if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; }
+
+ if ( $BreakR == NULL )
+ $BreakSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight);
+ else
+ $BreakSettings = array("R"=>$BreakR,"G"=>$BreakG,"B"=>$BreakB,"Alpha"=>$Alpha,"Ticks"=>$VoidTicks,"Weight"=>$Weight);
+
+ if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; }
+
+ $AxisID = $Serie["Axis"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight);
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ $LastGoodY = NULL; $LastGoodX = NULL; $Init = FALSE;
+ foreach($PosArray as $Key => $Y)
+ {
+ if ( $DisplayValues && $Serie["Data"][$Key] != VOID )
+ {
+ if ( $Y <= $LastY ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; }
+ $this->drawText($X,$Y-$Offset-$Weight,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align));
+ }
+
+ if ( $Y != VOID && $LastX != NULL && $LastY != NULL )
+ {
+ $this->drawLine($LastX,$LastY,$X,$LastY,$Color);
+ $this->drawLine($X,$LastY,$X,$Y,$Color);
+ if ( $ReCenter && $X+$XStep < $this->GraphAreaX2 - $XMargin )
+ {
+ $this->drawLine($X,$Y,$X+$XStep,$Y,$Color);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X-$ImageMapPlotSize).",".floor($Y-$ImageMapPlotSize).",".floor($X+$XStep+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+ }
+ else
+ { if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($LastY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); } }
+ }
+
+ if ( $Y != VOID && $LastY == NULL && $LastGoodY != NULL && !$BreakVoid )
+ {
+ if ( $ReCenter )
+ {
+ $this->drawLine($LastGoodX+$XStep,$LastGoodY,$X,$LastGoodY,$BreakSettings);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX+$XStep-$ImageMapPlotSize).",".floor($LastGoodY-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($LastGoodY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+ }
+ else
+ {
+ $this->drawLine($LastGoodX,$LastGoodY,$X,$LastGoodY,$BreakSettings);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX-$ImageMapPlotSize).",".floor($LastGoodY-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($LastGoodY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+ }
+
+ $this->drawLine($X,$LastGoodY,$X,$Y,$BreakSettings);
+ $LastGoodY = NULL;
+ }
+ elseif( !$BreakVoid && $LastGoodY == NULL && $Y != VOID )
+ {
+ $this->drawLine($this->GraphAreaX1 + $XMargin,$Y,$X,$Y,$BreakSettings);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($this->GraphAreaX1+$XMargin-$ImageMapPlotSize).",".floor($Y-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+ }
+
+ if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; }
+ if ( $Y == VOID ) { $Y = NULL; }
+
+ if ( !$Init && $ReCenter ) { $X = $X - $XStep/2; $Init = TRUE; }
+ $LastX = $X; $LastY = $Y;
+ if ( $LastX < $this->GraphAreaX1 + $XMargin ) { $LastX = $this->GraphAreaX1 + $XMargin; }
+ $X = $X + $XStep;
+ }
+ if ( $ReCenter )
+ {
+ $this->drawLine($LastX,$LastY,$this->GraphAreaX2 - $XMargin,$LastY,$Color);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($this->GraphAreaX2-$XMargin+$ImageMapPlotSize).",".floor($LastY+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+ }
+ }
+ else
+ {
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ $LastGoodY = NULL; $LastGoodX = NULL; $Init = FALSE;
+ foreach($PosArray as $Key => $X)
+ {
+ if ( $DisplayValues && $Serie["Data"][$Key] != VOID )
+ {
+ if ( $X >= $LastX ) { $Align = TEXT_ALIGN_MIDDLELEFT; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_MIDDLERIGHT; $Offset = -$DisplayOffset; }
+ $this->drawText($X+$Offset+$Weight,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align));
+ }
+
+ if ( $X != VOID && $LastX != NULL && $LastY != NULL )
+ {
+ $this->drawLine($LastX,$LastY,$LastX,$Y,$Color);
+ $this->drawLine($LastX,$Y,$X,$Y,$Color);
+
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($LastX+$XStep+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+ }
+
+ if ( $X != VOID && $LastX == NULL && $LastGoodY != NULL && !$BreakVoid )
+ {
+ $this->drawLine($LastGoodX,$LastGoodY,$LastGoodX,$LastGoodY+$YStep,$Color);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX-$ImageMapPlotSize).",".floor($LastGoodY-$ImageMapPlotSize).",".floor($LastGoodX+$ImageMapPlotSize).",".floor($LastGoodY+$YStep+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ $this->drawLine($LastGoodX,$LastGoodY+$YStep,$LastGoodX,$Y,$BreakSettings);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastGoodX-$ImageMapPlotSize).",".floor($LastGoodY+$YStep-$ImageMapPlotSize).",".floor($LastGoodX+$ImageMapPlotSize).",".floor($YStep+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ $this->drawLine($LastGoodX,$Y,$X,$Y,$BreakSettings);
+ $LastGoodY = NULL;
+ }
+ elseif ( $X != VOID && $LastGoodY == NULL && !$BreakVoid )
+ {
+ $this->drawLine($X,$this->GraphAreaY1 + $XMargin,$X,$Y,$BreakSettings);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X-$ImageMapPlotSize).",".floor($this->GraphAreaY1+$XMargin-$ImageMapPlotSize).",".floor($X+$ImageMapPlotSize).",".floor($Y+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+ }
+
+ if ( $X != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; }
+ if ( $X == VOID ) { $X = NULL; }
+
+ if ( !$Init && $ReCenter ) { $Y = $Y - $YStep/2; $Init = TRUE; }
+ $LastX = $X; $LastY = $Y;
+ if ( $LastY < $this->GraphAreaY1 + $XMargin ) { $LastY = $this->GraphAreaY1 + $XMargin; }
+ $Y = $Y + $YStep;
+ }
+ if ( $ReCenter )
+ {
+ $this->drawLine($LastX,$LastY,$LastX,$this->GraphAreaY2 - $XMargin,$Color);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($LastX-$ImageMapPlotSize).",".floor($LastY-$ImageMapPlotSize).",".floor($LastX+$ImageMapPlotSize).",".floor($this->GraphAreaY2-$XMargin+$ImageMapPlotSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw a step chart */
+ function drawFilledStepChart($Format=NULL)
+ {
+ $ReCenter = isset($Format["ReCenter"]) ? $Format["ReCenter"] : TRUE;
+ $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] :FALSE;
+ $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
+ $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
+ $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : NULL;
+ $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
+ $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
+ $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
+ $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"];
+
+ if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; }
+
+ $AxisID = $Serie["Axis"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B);
+ if ( $ForceTransparency != NULL ) { $Color["Alpha"] = $ForceTransparency; } else { $Color["Alpha"] = $Alpha; }
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+ $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"]));
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; }
+ if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; }
+
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL;
+
+ if ( !$AroundZero ) { $YZero = $this->GraphAreaY2-1; }
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ $LastGoodY = NULL; $LastGoodX = NULL; $Points = ""; $Init = FALSE;
+ foreach($PosArray as $Key => $Y)
+ {
+ if ( $Y == VOID && $LastX != NULL && $LastY != NULL && $Points !="" )
+ {
+ $Points[] = $LastX; $Points[] = $LastY;
+ $Points[] = $X; $Points[] = $LastY;
+ $Points[] = $X; $Points[] = $YZero;
+ $this->drawPolygon($Points,$Color);
+ $Points = "";
+ }
+
+ if ( $Y != VOID && $LastX != NULL && $LastY != NULL )
+ {
+ if ( $Points == "") { $Points[] = $LastX; $Points[] = $YZero; }
+ $Points[] = $LastX; $Points[] = $LastY;
+ $Points[] = $X; $Points[] = $LastY;
+ $Points[] = $X; $Points[] = $Y;
+ }
+
+ if ( $Y != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; }
+ if ( $Y == VOID ) { $Y = NULL; }
+
+ if ( !$Init && $ReCenter ) { $X = $X - $XStep/2; $Init = TRUE; }
+ $LastX = $X; $LastY = $Y;
+ if ( $LastX < $this->GraphAreaX1 + $XMargin ) { $LastX = $this->GraphAreaX1 + $XMargin; }
+ $X = $X + $XStep;
+ }
+
+ if ( $ReCenter )
+ {
+ $Points[] = $LastX+$XStep/2; $Points[] = $LastY;
+ $Points[] = $LastX+$XStep/2; $Points[] = $YZero;
+ }
+ else
+ { $Points[] = $LastX; $Points[] = $YZero; }
+
+ $this->drawPolygon($Points,$Color);
+ }
+ else
+ {
+ if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; }
+ if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; }
+
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ $LastGoodY = NULL; $LastGoodX = NULL; $Points = "";
+ foreach($PosArray as $Key => $X)
+ {
+ if ( $X == VOID && $LastX != NULL && $LastY != NULL && $Points !="" )
+ {
+ $Points[] = $LastX; $Points[] = $LastY;
+ $Points[] = $LastX; $Points[] = $Y;
+ $Points[] = $YZero; $Points[] = $Y;
+ $this->drawPolygon($Points,$Color);
+ $Points = "";
+ }
+
+ if ( $X != VOID && $LastX != NULL && $LastY != NULL )
+ {
+ if ( $Points == "") { $Points[] = $YZero; $Points[] = $LastY; }
+ $Points[] = $LastX; $Points[] = $LastY;
+ $Points[] = $LastX; $Points[] = $Y;
+ $Points[] = $X; $Points[] = $Y;
+ }
+
+ if ( $X != VOID ) { $LastGoodY = $Y; $LastGoodX = $X; }
+ if ( $X == VOID ) { $X = NULL; }
+
+ if ( $LastX == NULL && $ReCenter ) { $Y = $Y - $YStep/2; }
+ $LastX = $X; $LastY = $Y;
+ if ( $LastY < $this->GraphAreaY1 + $XMargin ) { $LastY = $this->GraphAreaY1 + $XMargin; }
+ $Y = $Y + $YStep;
+ }
+
+ if ( $ReCenter )
+ {
+ $Points[] = $LastX; $Points[] = $LastY+$YStep/2;
+ $Points[] = $YZero; $Points[] = $LastY+$YStep/2;
+ }
+ else
+ { $Points[] = $YZero; $Points[] = $LastY; }
+
+ $this->drawPolygon($Points,$Color);
+ }
+ }
+ }
+ }
+
+ /* Draw an area chart */
+ function drawAreaChart($Format=NULL)
+ {
+ $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE;
+ $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
+ $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
+ $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
+ $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
+ $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
+ $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : 25;
+ $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE;
+ $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"];
+ if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; }
+
+ $AxisID = $Serie["Axis"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+ $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"]));
+
+ if ( $Threshold != NULL )
+ {
+ foreach($Threshold as $Key => $Params)
+ {
+ $Threshold[$Key]["MinX"] = $this->scaleComputeY($Params["Min"],array("AxisID"=>$Serie["Axis"]));
+ $Threshold[$Key]["MaxX"] = $this->scaleComputeY($Params["Max"],array("AxisID"=>$Serie["Axis"]));
+ }
+ }
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; }
+
+ $Areas = ""; $AreaID = 0;
+ $Areas[$AreaID][] = $this->GraphAreaX1 + $XMargin;
+ if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; }
+
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin; $LastX = NULL; $LastY = NULL;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ foreach($PosArray as $Key => $Y)
+ {
+ if ( $DisplayValues && $Serie["Data"][$Key] != VOID )
+ {
+ if ( $Serie["Data"][$Key] > 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; }
+ $this->drawText($X,$Y-$Offset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align));
+ }
+
+ if ( $Y == VOID && isset($Areas[$AreaID]) )
+ {
+ if($LastX == NULL)
+ { $Areas[$AreaID][] = $X; }
+ else
+ { $Areas[$AreaID][] = $LastX; }
+
+ if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; }
+ $AreaID++;
+ }
+ elseif ($Y != VOID)
+ {
+ if ( !isset($Areas[$AreaID]) )
+ {
+ $Areas[$AreaID][] = $X;
+ if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; }
+ }
+
+ $Areas[$AreaID][] = $X;
+ $Areas[$AreaID][] = $Y;
+ }
+
+ $LastX = $X;
+ $X = $X + $XStep;
+ }
+ $Areas[$AreaID][] = $LastX;
+ if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaY2-1; }
+
+ /* Handle shadows in the areas */
+ if ( $this->Shadow )
+ {
+ $ShadowArea = "";
+ foreach($Areas as $Key => $Points)
+ {
+ $ShadowArea[$Key] = "";
+ foreach($Points as $Key2 => $Value)
+ {
+ if ( $Key2 % 2 == 0 )
+ { $ShadowArea[$Key][] = $Value + $this->ShadowX; }
+ else
+ { $ShadowArea[$Key][] = $Value + $this->ShadowY; }
+ }
+ }
+
+ foreach($ShadowArea as $Key => $Points)
+ $this->drawPolygonChart($Points,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa));
+ }
+
+ $Alpha = $ForceTransparency != NULL ? $ForceTransparency : $Alpha;
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Threshold"=>$Threshold);
+
+ foreach($Areas as $Key => $Points)
+ $this->drawPolygonChart($Points,$Color);
+ }
+ else
+ {
+ if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; }
+ if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; }
+
+ $Areas = ""; $AreaID = 0;
+ if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; }
+ $Areas[$AreaID][] = $this->GraphAreaY1 + $XMargin;
+
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin; $LastX = NULL; $LastY = NULL;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ foreach($PosArray as $Key => $X)
+ {
+ if ( $DisplayValues && $Serie["Data"][$Key] != VOID )
+ {
+ if ( $Serie["Data"][$Key] > 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; }
+ $this->drawText($X+$Offset,$Y,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("Angle"=>270,"R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align));
+ }
+
+ if ( $X == VOID && isset($Areas[$AreaID]) )
+ {
+ if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; }
+
+ if($LastY == NULL)
+ { $Areas[$AreaID][] = $Y; }
+ else
+ { $Areas[$AreaID][] = $LastY; }
+
+ $AreaID++;
+ }
+ elseif ($X != VOID)
+ {
+ if ( !isset($Areas[$AreaID]) )
+ {
+ if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; }
+ $Areas[$AreaID][] = $Y;
+ }
+
+ $Areas[$AreaID][] = $X;
+ $Areas[$AreaID][] = $Y;
+ }
+
+ $LastX = $X; $LastY = $Y;
+ $Y = $Y + $YStep;
+ }
+ if ( $AroundZero ) { $Areas[$AreaID][] = $YZero; } else { $Areas[$AreaID][] = $this->GraphAreaX1+1; }
+ $Areas[$AreaID][] = $LastY;
+
+ /* Handle shadows in the areas */
+ if ( $this->Shadow )
+ {
+ $ShadowArea = "";
+ foreach($Areas as $Key => $Points)
+ {
+ $ShadowArea[$Key] = "";
+ foreach($Points as $Key2 => $Value)
+ {
+ if ( $Key2 % 2 == 0 )
+ { $ShadowArea[$Key][] = $Value + $this->ShadowX; }
+ else
+ { $ShadowArea[$Key][] = $Value + $this->ShadowY; }
+ }
+ }
+
+ foreach($ShadowArea as $Key => $Points)
+ $this->drawPolygonChart($Points,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa));
+ }
+
+ $Alpha = $ForceTransparency != NULL ? $ForceTransparency : $Alpha;
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Threshold"=>$Threshold);
+
+ foreach($Areas as $Key => $Points)
+ $this->drawPolygonChart($Points,$Color);
+ }
+ }
+ }
+ }
+
+
+ /* Draw a bar chart */
+ function drawBarChart($Format=NULL)
+ {
+ $Floating0Serie = isset($Format["Floating0Serie"]) ? $Format["Floating0Serie"] : NULL;
+ $Floating0Value = isset($Format["Floating0Value"]) ? $Format["Floating0Value"] : NULL;
+ $Draw0Line = isset($Format["Draw0Line"]) ? $Format["Draw0Line"] : FALSE;
+ $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE;
+ $DisplayOrientation = isset($Format["DisplayOrientation"]) ? $Format["DisplayOrientation"] : ORIENTATION_HORIZONTAL;
+ $DisplayOffset = isset($Format["DisplayOffset"]) ? $Format["DisplayOffset"] : 2;
+ $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
+ $DisplayFont = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontName;
+ $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize;
+ $DisplayPos = isset($Format["DisplayPos"]) ? $Format["DisplayPos"] : LABEL_POS_OUTSIDE;
+ $DisplayShadow = isset($Format["DisplayShadow"]) ? $Format["DisplayShadow"] : TRUE;
+ $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
+ $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
+ $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
+ $AroundZero = isset($Format["AroundZero"]) ? $Format["AroundZero"] : TRUE;
+ $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : .5;
+ $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : FALSE;
+ $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
+ $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : FALSE;
+ $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE;
+ $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20;
+ $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255;
+ $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255;
+ $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255;
+ $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0;
+ $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0;
+ $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0;
+ $TxtMargin = isset($Format["TxtMargin"]) ? $Format["TxtMargin"] : 6;
+ $OverrideColors = isset($Format["OverrideColors"]) ? $Format["OverrideColors"] : NULL;
+ $OverrideSurrounding = isset($Format["OverrideSurrounding"]) ? $Format["OverrideSurrounding"] : 30;
+ $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : NULL;
+ $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1;
+ $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1;
+ $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1;
+ $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_REGULAR;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+
+ if ( $OverrideColors != NULL )
+ {
+ $OverrideColors = $this->validatePalette($OverrideColors,$OverrideSurrounding);
+ $this->DataSet->saveExtendedData("Palette",$OverrideColors);
+ }
+
+ $RestoreShadow = $this->Shadow;
+
+ $SeriesCount = $this->countDrawableSeries();
+ $CurrentSerie = 0;
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"];
+ if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = $R; $DisplayG = $G; $DisplayB = $B; }
+ if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; }
+ if ( $InnerSurrounding != NULL ) { $InnerBorderR = $R+$InnerSurrounding; $InnerBorderG = $G+$InnerSurrounding; $InnerBorderB = $B+$InnerSurrounding; }
+ if ( $InnerBorderR == -1 ) { $InnerColor = NULL; } else { $InnerColor = array("R"=>$InnerBorderR,"G"=>$InnerBorderG,"B"=>$InnerBorderB); }
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB);
+
+ $AxisID = $Serie["Axis"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; }
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+
+ if ( $Floating0Value != NULL )
+ { $YZero = $this->scaleComputeY($Floating0Value,array("AxisID"=>$Serie["Axis"])); }
+ else
+ { $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"])); }
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; }
+ if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; }
+
+ if ( $XDivs == 0 ) { $XStep = 0; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin;
+
+ if ( $AroundZero ) { $Y1 = $YZero; } else { $Y1 = $this->GraphAreaY2-1; }
+ if ( $XDivs == 0 ) { $XSize = ($this->GraphAreaX2-$this->GraphAreaX1)/($SeriesCount+$Interleave); } else { $XSize = ($XStep / ($SeriesCount+$Interleave) ); }
+
+ $XOffset = -($XSize*$SeriesCount)/2 + $CurrentSerie * $XSize;
+ if ( $X + $XOffset <= $this->GraphAreaX1 ) { $XOffset = $this->GraphAreaX1 - $X + 1 ; }
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $XOffset + $XSize / 2;
+
+ if ( $Rounded || $BorderR != -1) { $XSpace = 1; } else { $XSpace = 0; }
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+
+ $ID = 0;
+ foreach($PosArray as $Key => $Y2)
+ {
+ if ( $Floating0Serie != NULL )
+ {
+ if ( isset($Data["Series"][$Floating0Serie]["Data"][$Key]) )
+ { $Value = $Data["Series"][$Floating0Serie]["Data"][$Key]; }
+ else
+ { $Value = 0; }
+
+ $YZero = $this->scaleComputeY($Value,array("AxisID"=>$Serie["Axis"]));
+ if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; }
+ if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; }
+
+ if ( $AroundZero ) { $Y1 = $YZero; } else { $Y1 = $this->GraphAreaY2-1; }
+ }
+
+ if ( $OverrideColors != NULL )
+ { if ( isset($OverrideColors[$ID]) ) { $Color = array("R"=>$OverrideColors[$ID]["R"],"G"=>$OverrideColors[$ID]["G"],"B"=>$OverrideColors[$ID]["B"],"Alpha"=>$OverrideColors[$ID]["Alpha"],"BorderR"=>$OverrideColors[$ID]["BorderR"],"BorderG"=>$OverrideColors[$ID]["BorderG"],"BorderB"=>$OverrideColors[$ID]["BorderB"]); } else { $Color = $this->getRandomColor(); } }
+
+ if ( $Y2 != VOID )
+ {
+ $BarHeight = $Y1 - $Y2;
+
+ if ( $Serie["Data"][$Key] == 0 )
+ {
+ $this->drawLine($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y1,$Color);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X+$XOffset+$XSpace).",".floor($Y1-1).",".floor($X+$XOffset+$XSize-$XSpace).",".floor($Y1+1),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+ }
+ else
+ {
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X+$XOffset+$XSpace).",".floor($Y1).",".floor($X+$XOffset+$XSize-$XSpace).",".floor($Y2),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ if ( $Rounded )
+ $this->drawRoundedFilledRectangle($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,$RoundRadius,$Color);
+ else
+ {
+ $this->drawFilledRectangle($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,$Color);
+
+ if ( $InnerColor != NULL ) { $this->drawRectangle($X+$XOffset+$XSpace+1,min($Y1,$Y2)+1,$X+$XOffset+$XSize-$XSpace-1,max($Y1,$Y2)-1,$InnerColor); }
+
+ if ( $Gradient )
+ {
+ $this->Shadow = FALSE;
+
+ if ( $GradientMode == GRADIENT_SIMPLE )
+ {
+ if ( $Serie["Data"][$Key] >= 0 )
+ $GradienColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha);
+ else
+ $GradienColor = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha);
+
+ $this->drawGradientArea($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,DIRECTION_VERTICAL,$GradienColor);
+ }
+ elseif ( $GradientMode == GRADIENT_EFFECT_CAN )
+ {
+ $GradienColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha);
+ $GradienColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha);
+ $XSpan = floor($XSize / 3);
+
+ $this->drawGradientArea($X+$XOffset+$XSpace,$Y1,$X+$XOffset+$XSpan-$XSpace,$Y2,DIRECTION_HORIZONTAL,$GradienColor1);
+ $this->drawGradientArea($X+$XOffset+$XSpan+$XSpace,$Y1,$X+$XOffset+$XSize-$XSpace,$Y2,DIRECTION_HORIZONTAL,$GradienColor2);
+ }
+ $this->Shadow = $RestoreShadow;
+ }
+ }
+
+ if ( $Draw0Line )
+ {
+ $Line0Color = array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20);
+
+ if ( abs($Y1 - $Y2) > 3 ) { $Line0Width = 3; } else { $Line0Width = 1; }
+ if ( $Y1 - $Y2 < 0 ) { $Line0Width = -$Line0Width; }
+
+ $this->drawFilledRectangle($X+$XOffset+$XSpace,floor($Y1),$X+$XOffset+$XSize-$XSpace,floor($Y1)-$Line0Width,$Line0Color);
+ $this->drawLine($X+$XOffset+$XSpace,floor($Y1),$X+$XOffset+$XSize-$XSpace,floor($Y1),$Line0Color);
+ }
+ }
+
+ if ( $DisplayValues && $Serie["Data"][$Key] != VOID )
+ {
+ if ( $DisplayShadow ) { $this->Shadow = TRUE; }
+
+ $Caption = $this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit);
+ $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,90,$Caption);
+ $TxtHeight = $TxtPos[0]["Y"] - $TxtPos[1]["Y"] + $TxtMargin;
+
+ if ( $DisplayPos == LABEL_POS_INSIDE && abs($TxtHeight) < abs($BarHeight) )
+ {
+ $CenterX = (($X+$XOffset+$XSize-$XSpace)-($X+$XOffset+$XSpace))/2 + $X+$XOffset+$XSpace;
+ $CenterY = ($Y2-$Y1)/2 + $Y1;
+
+ $this->drawText($CenterX,$CenterY,$Caption,array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"Angle"=>90));
+ }
+ else
+ {
+ if ( $Serie["Data"][$Key] >= 0 ) { $Align = TEXT_ALIGN_BOTTOMMIDDLE; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_TOPMIDDLE; $Offset = -$DisplayOffset; }
+ $this->drawText($X+$XOffset+$XSize/2,$Y2-$Offset,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align,"FontSize"=>$DisplaySize));
+ }
+
+ $this->Shadow = $RestoreShadow;
+ }
+ }
+
+ $X = $X + $XStep;
+ $ID++;
+ }
+ }
+ else
+ {
+ if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; }
+ if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; }
+
+ if ( $XDivs == 0 ) { $YStep = 0; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+
+ $Y = $this->GraphAreaY1 + $XMargin;
+
+ if ( $AroundZero ) { $X1 = $YZero; } else { $X1 = $this->GraphAreaX1+1; }
+ if ( $XDivs == 0 ) { $YSize = ($this->GraphAreaY2-$this->GraphAreaY1)/($SeriesCount+$Interleave); } else { $YSize = ($YStep / ($SeriesCount+$Interleave) ); }
+
+ $YOffset = -($YSize*$SeriesCount)/2 + $CurrentSerie * $YSize;
+ if ( $Y + $YOffset <= $this->GraphAreaY1 ) { $YOffset = $this->GraphAreaY1 - $Y + 1 ; }
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = $YOffset + $YSize / 2;
+
+ if ( $Rounded || $BorderR != -1 ) { $YSpace = 1; } else { $YSpace = 0; }
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+
+ $ID = 0 ;
+ foreach($PosArray as $Key => $X2)
+ {
+ if ( $Floating0Serie != NULL )
+ {
+ if ( isset($Data["Series"][$Floating0Serie]["Data"][$Key]) )
+ $Value = $Data["Series"][$Floating0Serie]["Data"][$Key];
+ else { $Value = 0; }
+
+ $YZero = $this->scaleComputeY($Value,array("AxisID"=>$Serie["Axis"]));
+ if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; }
+ if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; }
+ if ( $AroundZero ) { $X1 = $YZero; } else { $X1 = $this->GraphAreaX1+1; }
+ }
+
+ if ( $OverrideColors != NULL )
+ { if ( isset($OverrideColors[$ID]) ) { $Color = array("R"=>$OverrideColors[$ID]["R"],"G"=>$OverrideColors[$ID]["G"],"B"=>$OverrideColors[$ID]["B"],"Alpha"=>$OverrideColors[$ID]["Alpha"],"BorderR"=>$OverrideColors[$ID]["BorderR"],"BorderG"=>$OverrideColors[$ID]["BorderG"],"BorderB"=>$OverrideColors[$ID]["BorderB"]); } else { $Color = $this->getRandomColor(); } }
+
+ if ( $X2 != VOID )
+ {
+ $BarWidth = $X2 - $X1;
+
+ if ( $Serie["Data"][$Key] == 0 )
+ {
+ $this->drawLine($X1,$Y+$YOffset+$YSpace,$X1,$Y+$YOffset+$YSize-$YSpace,$Color);
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X1-1).",".floor($Y+$YOffset+$YSpace).",".floor($X1+1).",".floor($Y+$YOffset+$YSize-$YSpace),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+ }
+ else
+ {
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X1).",".floor($Y+$YOffset+$YSpace).",".floor($X2).",".floor($Y+$YOffset+$YSize-$YSpace),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ if ( $Rounded )
+ $this->drawRoundedFilledRectangle($X1+1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSize-$YSpace,$RoundRadius,$Color);
+ else
+ {
+ $this->drawFilledRectangle($X1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSize-$YSpace,$Color);
+
+ if ( $InnerColor != NULL ) { $this->drawRectangle(min($X1,$X2)+1,$Y+$YOffset+$YSpace+1,max($X1,$X2)-1,$Y+$YOffset+$YSize-$YSpace-1,$InnerColor); }
+
+ if ( $Gradient )
+ {
+ $this->Shadow = FALSE;
+
+ if ( $GradientMode == GRADIENT_SIMPLE )
+ {
+ if ( $Serie["Data"][$Key] >= 0 )
+ $GradienColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha);
+ else
+ $GradienColor = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha);
+
+ $this->drawGradientArea($X1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSize-$YSpace,DIRECTION_HORIZONTAL,$GradienColor);
+ }
+ elseif ( $GradientMode == GRADIENT_EFFECT_CAN )
+ {
+ $GradienColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha);
+ $GradienColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha);
+ $YSpan = floor($YSize / 3);
+
+ $this->drawGradientArea($X1,$Y+$YOffset+$YSpace,$X2,$Y+$YOffset+$YSpan-$YSpace,DIRECTION_VERTICAL,$GradienColor1);
+ $this->drawGradientArea($X1,$Y+$YOffset+$YSpan,$X2,$Y+$YOffset+$YSize-$YSpace,DIRECTION_VERTICAL,$GradienColor2);
+ }
+ $this->Shadow = $RestoreShadow;
+ }
+ }
+
+ if ( $Draw0Line )
+ {
+ $Line0Color = array("R"=>0,"G"=>0,"B"=>0,"Alpha"=>20);
+
+ if ( abs($X1 - $X2) > 3 ) { $Line0Width = 3; } else { $Line0Width = 1; }
+ if ( $X2 - $X1 < 0 ) { $Line0Width = -$Line0Width; }
+
+ $this->drawFilledRectangle(floor($X1),$Y+$YOffset+$YSpace,floor($X1)+$Line0Width,$Y+$YOffset+$YSize-$YSpace,$Line0Color);
+ $this->drawLine(floor($X1),$Y+$YOffset+$YSpace,floor($X1),$Y+$YOffset+$YSize-$YSpace,$Line0Color);
+ }
+ }
+
+ if ( $DisplayValues && $Serie["Data"][$Key] != VOID )
+ {
+ if ( $DisplayShadow ) { $this->Shadow = TRUE; }
+
+ $Caption = $this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit);
+ $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,0,$Caption);
+ $TxtWidth = $TxtPos[1]["X"] - $TxtPos[0]["X"] + $TxtMargin;
+
+ if ( $DisplayPos == LABEL_POS_INSIDE && abs($TxtWidth) < abs($BarWidth) )
+ {
+ $CenterX = ($X2-$X1)/2 + $X1;
+ $CenterY = (($Y+$YOffset+$YSize-$YSpace)-($Y+$YOffset+$YSpace))/2 + ($Y+$YOffset+$YSpace);
+
+ $this->drawText($CenterX,$CenterY,$Caption,array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize));
+ }
+ else
+ {
+ if ( $Serie["Data"][$Key] >= 0 ) { $Align = TEXT_ALIGN_MIDDLELEFT; $Offset = $DisplayOffset; } else { $Align = TEXT_ALIGN_MIDDLERIGHT; $Offset = -$DisplayOffset; }
+ $this->drawText($X2+$Offset,$Y+$YOffset+$YSize/2,$Caption,array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>$Align,"FontSize"=>$DisplaySize));
+ }
+
+ $this->Shadow = $RestoreShadow;
+ }
+ }
+ $Y = $Y + $YStep;
+ $ID++;
+ }
+ }
+ $CurrentSerie++;
+ }
+ }
+ }
+
+ /* Draw a bar chart */
+ function drawStackedBarChart($Format=NULL)
+ {
+ $DisplayValues = isset($Format["DisplayValues"]) ? $Format["DisplayValues"] : FALSE;
+ $DisplayOrientation = isset($Format["DisplayOrientation"]) ? $Format["DisplayOrientation"] : ORIENTATION_AUTO;
+ $DisplayRound = isset($Format["DisplayRound"]) ? $Format["DisplayRound"] : 0;
+ $DisplayColor = isset($Format["DisplayColor"]) ? $Format["DisplayColor"] : DISPLAY_MANUAL;
+ $DisplayFont = isset($Format["DisplayFont"]) ? $Format["DisplayFont"] : $this->FontName;
+ $DisplaySize = isset($Format["DisplaySize"]) ? $Format["DisplaySize"] : $this->FontSize;
+ $DisplayR = isset($Format["DisplayR"]) ? $Format["DisplayR"] : 0;
+ $DisplayG = isset($Format["DisplayG"]) ? $Format["DisplayG"] : 0;
+ $DisplayB = isset($Format["DisplayB"]) ? $Format["DisplayB"] : 0;
+ $Interleave = isset($Format["Interleave"]) ? $Format["Interleave"] : .5;
+ $Rounded = isset($Format["Rounded"]) ? $Format["Rounded"] : FALSE;
+ $RoundRadius = isset($Format["RoundRadius"]) ? $Format["RoundRadius"] : 4;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : -1;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : -1;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : -1;
+ $Gradient = isset($Format["Gradient"]) ? $Format["Gradient"] : FALSE;
+ $GradientMode = isset($Format["GradientMode"]) ? $Format["GradientMode"] : GRADIENT_SIMPLE;
+ $GradientAlpha = isset($Format["GradientAlpha"]) ? $Format["GradientAlpha"] : 20;
+ $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255;
+ $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255;
+ $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255;
+ $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 0;
+ $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 0;
+ $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 0;
+ $InnerSurrounding = isset($Format["InnerSurrounding"]) ? $Format["InnerSurrounding"] : NULL;
+ $InnerBorderR = isset($Format["InnerBorderR"]) ? $Format["InnerBorderR"] : -1;
+ $InnerBorderG = isset($Format["InnerBorderG"]) ? $Format["InnerBorderG"] : -1;
+ $InnerBorderB = isset($Format["InnerBorderB"]) ? $Format["InnerBorderB"] : -1;
+ $RecordImageMap = isset($Format["RecordImageMap"]) ? $Format["RecordImageMap"] : FALSE;
+ $FontFactor = isset($Format["FontFactor"]) ? $Format["FontFactor"] : 8;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+
+ $RestoreShadow = $this->Shadow;
+
+ $LastX = ""; $LastY = "";
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"];
+ if ( $DisplayColor == DISPLAY_AUTO ) { $DisplayR = 255; $DisplayG = 255; $DisplayB = 255; }
+ if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; }
+ if ( $InnerSurrounding != NULL ) { $InnerBorderR = $R+$InnerSurrounding; $InnerBorderG = $G+$InnerSurrounding; $InnerBorderB = $B+$InnerSurrounding; }
+ if ( $InnerBorderR == -1 ) { $InnerColor = NULL; } else { $InnerColor = array("R"=>$InnerBorderR,"G"=>$InnerBorderG,"B"=>$InnerBorderB); }
+
+ $AxisID = $Serie["Axis"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ if (isset($Serie["Description"])) { $SerieDescription = $Serie["Description"]; } else { $SerieDescription = $SerieName; }
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]),TRUE);
+ $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"]));
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
+
+ $Color = array("TransCorner"=>TRUE,"R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"BorderR"=>$BorderR,"BorderG"=>$BorderG,"BorderB"=>$BorderB);
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; }
+ if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; }
+
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin;
+
+ $XSize = ($XStep / (1+$Interleave) );
+ $XOffset = -($XSize/2);
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ foreach($PosArray as $Key => $Height)
+ {
+ if ( $Height != VOID && $Serie["Data"][$Key] != 0 )
+ {
+ if ( $Serie["Data"][$Key] > 0 ) { $Pos = "+"; } else { $Pos = "-"; }
+
+ if ( !isset($LastY[$Key] ) ) { $LastY[$Key] = ""; }
+ if ( !isset($LastY[$Key][$Pos] ) ) { $LastY[$Key][$Pos] = $YZero; }
+
+ $Y1 = $LastY[$Key][$Pos];
+ $Y2 = $Y1 - $Height;
+
+ if ( ($Rounded || $BorderR != -1) && ($Pos == "+" && $Y1 != $YZero) ) { $YSpaceUp = 1; } else { $YSpaceUp = 0; }
+ if ( ($Rounded || $BorderR != -1) && ($Pos == "-" && $Y1 != $YZero) ) { $YSpaceDown = 1; } else { $YSpaceDown = 0; }
+
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X+$XOffset).",".floor($Y1-$YSpaceUp+$YSpaceDown).",".floor($X+$XOffset+$XSize).",".floor($Y2),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ if ( $Rounded )
+ $this->drawRoundedFilledRectangle($X+$XOffset,$Y1-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2,$RoundRadius,$Color);
+ else
+ {
+ $this->drawFilledRectangle($X+$XOffset,$Y1-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2,$Color);
+
+ if ( $InnerColor != NULL ) { $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; $this->drawRectangle(min($X+$XOffset+1,$X+$XOffset+$XSize),min($Y1-$YSpaceUp+$YSpaceDown,$Y2)+1,max($X+$XOffset+1,$X+$XOffset+$XSize)-1,max($Y1-$YSpaceUp+$YSpaceDown,$Y2)-1,$InnerColor); $this->Shadow = $RestoreShadow;}
+
+ if ( $Gradient )
+ {
+ $this->Shadow = FALSE;
+
+ if ( $GradientMode == GRADIENT_SIMPLE )
+ {
+ $GradientColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha);
+ $this->drawGradientArea($X+$XOffset,$Y1-1-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2+1,DIRECTION_VERTICAL,$GradientColor);
+ }
+ elseif ( $GradientMode == GRADIENT_EFFECT_CAN )
+ {
+ $GradientColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha);
+ $GradientColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha);
+ $XSpan = floor($XSize / 3);
+
+ $this->drawGradientArea($X+$XOffset-.5,$Y1-.5-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSpan,$Y2+.5,DIRECTION_HORIZONTAL,$GradientColor1);
+ $this->drawGradientArea($X+$XSpan+$XOffset-.5,$Y1-.5-$YSpaceUp+$YSpaceDown,$X+$XOffset+$XSize,$Y2+.5,DIRECTION_HORIZONTAL,$GradientColor2);
+ }
+ $this->Shadow = $RestoreShadow;
+ }
+ }
+
+ if ( $DisplayValues )
+ {
+ $BarHeight = abs($Y2-$Y1)-2;
+ $BarWidth = $XSize+($XOffset/2)-$FontFactor;
+
+ $Caption = $this->scaleFormat(round($Serie["Data"][$Key],$DisplayRound),$Mode,$Format,$Unit);
+ $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,0,$Caption);
+ $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]);
+ $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]);
+
+ $XCenter = ( ($X+$XOffset+$XSize) - ($X+$XOffset) ) / 2 + $X+$XOffset;
+ $YCenter = ( ($Y2) - ($Y1-$YSpaceUp+$YSpaceDown) ) / 2 + $Y1-$YSpaceUp+$YSpaceDown;
+
+ $Done = FALSE;
+ if ( $DisplayOrientation == ORIENTATION_HORIZONTAL || $DisplayOrientation == ORIENTATION_AUTO )
+ {
+ if ( $TxtHeight < $BarHeight && $TxtWidth < $BarWidth )
+ {
+ $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont));
+ $Done = TRUE;
+ }
+ }
+
+ if ( $DisplayOrientation == ORIENTATION_VERTICAL || ( $DisplayOrientation == ORIENTATION_AUTO && !$Done) )
+ {
+ if ( $TxtHeight < $BarWidth && $TxtWidth < $BarHeight )
+ $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Angle"=>90,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont));
+ }
+ }
+
+ $LastY[$Key][$Pos] = $Y2;
+ }
+
+ $X = $X + $XStep;
+ }
+ }
+ else
+ {
+ if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; }
+ if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; }
+
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin;
+
+ $YSize = $YStep / (1+$Interleave);
+ $YOffset = -($YSize/2);
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ foreach($PosArray as $Key => $Width)
+ {
+ if ( $Width != VOID && $Serie["Data"][$Key] != 0 )
+ {
+ if ( $Serie["Data"][$Key] > 0 ) { $Pos = "+"; } else { $Pos = "-"; }
+
+ if ( !isset($LastX[$Key] ) ) { $LastX[$Key] = ""; }
+ if ( !isset($LastX[$Key][$Pos] ) ) { $LastX[$Key][$Pos] = $YZero; }
+
+ $X1 = $LastX[$Key][$Pos];
+ $X2 = $X1 + $Width;
+
+ if ( ($Rounded || $BorderR != -1) && ($Pos == "+" && $X1 != $YZero) ) { $XSpaceLeft = 2; } else { $XSpaceLeft = 0; }
+ if ( ($Rounded || $BorderR != -1) && ($Pos == "-" && $X1 != $YZero) ) { $XSpaceRight = 2; } else { $XSpaceRight = 0; }
+
+ if ( $RecordImageMap ) { $this->addToImageMap("RECT",floor($X1+$XSpaceLeft).",".floor($Y+$YOffset).",".floor($X2-$XSpaceRight).",".floor($Y+$YOffset+$YSize),$this->toHTMLColor($R,$G,$B),$SerieDescription,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit)); }
+
+ if ( $Rounded )
+ $this->drawRoundedFilledRectangle($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSize,$RoundRadius,$Color);
+ else
+ {
+ $this->drawFilledRectangle($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSize,$Color);
+
+ if ( $InnerColor != NULL ) { $RestoreShadow = $this->Shadow; $this->Shadow = FALSE; $this->drawRectangle(min($X1+$XSpaceLeft,$X2-$XSpaceRight)+1,min($Y+$YOffset,$Y+$YOffset+$YSize)+1,max($X1+$XSpaceLeft,$X2-$XSpaceRight)-1,max($Y+$YOffset,$Y+$YOffset+$YSize)-1,$InnerColor); $this->Shadow = $RestoreShadow;}
+
+ if ( $Gradient )
+ {
+ $this->Shadow = FALSE;
+
+ if ( $GradientMode == GRADIENT_SIMPLE )
+ {
+ $GradientColor = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha);
+ $this->drawGradientArea($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSize,DIRECTION_HORIZONTAL,$GradientColor);
+ }
+ elseif ( $GradientMode == GRADIENT_EFFECT_CAN )
+ {
+ $GradientColor1 = array("StartR"=>$GradientEndR,"StartG"=>$GradientEndG,"StartB"=>$GradientEndB,"EndR"=>$GradientStartR,"EndG"=>$GradientStartG,"EndB"=>$GradientStartB,"Alpha"=>$GradientAlpha);
+ $GradientColor2 = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$GradientAlpha);
+ $YSpan = floor($YSize / 3);
+
+ $this->drawGradientArea($X1+$XSpaceLeft,$Y+$YOffset,$X2-$XSpaceRight,$Y+$YOffset+$YSpan,DIRECTION_VERTICAL,$GradientColor1);
+ $this->drawGradientArea($X1+$XSpaceLeft,$Y+$YOffset+$YSpan,$X2-$XSpaceRight,$Y+$YOffset+$YSize,DIRECTION_VERTICAL,$GradientColor2);
+ }
+ $this->Shadow = $RestoreShadow;
+ }
+ }
+
+ if ( $DisplayValues )
+ {
+ $BarWidth = abs($X2-$X1)-$FontFactor;
+ $BarHeight = $YSize+($YOffset/2)-$FontFactor/2;
+ $Caption = $this->scaleFormat(round($Serie["Data"][$Key],$DisplayRound),$Mode,$Format,$Unit);
+ $TxtPos = $this->getTextBox(0,0,$DisplayFont,$DisplaySize,0,$Caption);
+ $TxtHeight = abs($TxtPos[2]["Y"] - $TxtPos[0]["Y"]);
+ $TxtWidth = abs($TxtPos[1]["X"] - $TxtPos[0]["X"]);
+
+ $XCenter = ( $X2 - $X1 ) / 2 + $X1;
+ $YCenter = ( ($Y+$YOffset+$YSize) - ($Y+$YOffset) ) / 2 + $Y+$YOffset;
+
+ $Done = FALSE;
+ if ( $DisplayOrientation == ORIENTATION_HORIZONTAL || $DisplayOrientation == ORIENTATION_AUTO )
+ {
+ if ( $TxtHeight < $BarHeight && $TxtWidth < $BarWidth )
+ {
+ $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont));
+ $Done = TRUE;
+ }
+ }
+
+ if ( $DisplayOrientation == ORIENTATION_VERTICAL || ( $DisplayOrientation == ORIENTATION_AUTO && !$Done) )
+ {
+ if ( $TxtHeight < $BarWidth && $TxtWidth < $BarHeight )
+ $this->drawText($XCenter,$YCenter,$this->scaleFormat($Serie["Data"][$Key],$Mode,$Format,$Unit),array("R"=>$DisplayR,"G"=>$DisplayG,"B"=>$DisplayB,"Angle"=>90,"Align"=>TEXT_ALIGN_MIDDLEMIDDLE,"FontSize"=>$DisplaySize,"FontName"=>$DisplayFont));
+ }
+ }
+
+ $LastX[$Key][$Pos] = $X2;
+ }
+
+ $Y = $Y + $YStep;
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw a stacked area chart */
+ function drawStackedAreaChart($Format=NULL)
+ {
+ $DrawLine = isset($Format["DrawLine"]) ? $Format["DrawLine"] : FALSE;
+ $LineSurrounding = isset($Format["LineSurrounding"]) ? $Format["LineSurrounding"] : NULL;
+ $LineR = isset($Format["LineR"]) ? $Format["LineR"] : VOID;
+ $LineG = isset($Format["LineG"]) ? $Format["LineG"] : VOID;
+ $LineB = isset($Format["LineB"]) ? $Format["LineB"] : VOID;
+ $LineAlpha = isset($Format["LineAlpha"]) ? $Format["LineAlpha"] : 100;
+ $DrawPlot = isset($Format["DrawPlot"]) ? $Format["DrawPlot"] : FALSE;
+ $PlotRadius = isset($Format["PlotRadius"]) ? $Format["PlotRadius"] : 2;
+ $PlotBorder = isset($Format["PlotBorder"]) ? $Format["PlotBorder"] : 1;
+ $PlotBorderSurrounding = isset($Format["PlotBorderSurrounding"]) ? $Format["PlotBorderSurrounding"] : NULL;
+ $PlotBorderR = isset($Format["PlotBorderR"]) ? $Format["PlotBorderR"] : 0;
+ $PlotBorderG = isset($Format["PlotBorderG"]) ? $Format["PlotBorderG"] : 0;
+ $PlotBorderB = isset($Format["PlotBorderB"]) ? $Format["PlotBorderB"] : 0;
+ $PlotBorderAlpha = isset($Format["PlotBorderAlpha"]) ? $Format["PlotBorderAlpha"] : 50;
+ $ForceTransparency = isset($Format["ForceTransparency"]) ? $Format["ForceTransparency"] : NULL;
+
+ $this->LastChartLayout = CHART_LAST_LAYOUT_STACKED;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+
+ $RestoreShadow = $this->Shadow;
+ $this->Shadow = FALSE;
+
+ /* Build the offset data series */
+ $OffsetData = "";
+ $OverallOffset = "";
+ $SerieOrder = "";
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $SerieOrder[] = $SerieName;
+
+ foreach($Serie["Data"] as $Key => $Value)
+ {
+ if ( $Value == VOID ) { $Value = 0; }
+ if ($Value >= 0) { $Sign = "+"; } else { $Sign = "-"; }
+ if ( !isset($OverallOffset[$Key]) || !isset($OverallOffset[$Key][$Sign]) ) { $OverallOffset[$Key][$Sign] = 0; }
+
+ if ( $Sign == "+" )
+ { $Data["Series"][$SerieName]["Data"][$Key] = $Value + $OverallOffset[$Key][$Sign]; }
+ else
+ { $Data["Series"][$SerieName]["Data"][$Key] = $Value - $OverallOffset[$Key][$Sign]; }
+
+ $OverallOffset[$Key][$Sign] = $OverallOffset[$Key][$Sign] + abs($Value);
+ }
+ }
+ }
+ $SerieOrder = array_reverse($SerieOrder);
+
+ $LastX = ""; $LastY = "";
+ foreach($SerieOrder as $Key => $SerieName)
+ {
+ $Serie = $Data["Series"][$SerieName];
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"];
+ if ( $ForceTransparency != NULL ) { $Alpha = $ForceTransparency; }
+
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha);
+
+ if ( $LineSurrounding != NULL )
+ $LineColor = array("R"=>$R+$LineSurrounding,"G"=>$G+$LineSurrounding,"B"=>$B+$LineSurrounding,"Alpha"=>$Alpha);
+ elseif ( $LineR != VOID )
+ $LineColor = array("R"=>$LineR,"G"=>$LineG,"B"=>$LineB,"Alpha"=>$LineAlpha);
+ else
+ $LineColor = $Color;
+
+ if ( $PlotBorderSurrounding != NULL )
+ $PlotBorderColor = array("R"=>$R+$PlotBorderSurrounding,"G"=>$G+$PlotBorderSurrounding,"B"=>$B+$PlotBorderSurrounding,"Alpha"=>$PlotBorderAlpha);
+ else
+ $PlotBorderColor = array("R"=>$PlotBorderR,"G"=>$PlotBorderG,"B"=>$PlotBorderB,"Alpha"=>$PlotBorderAlpha);
+
+ $AxisID = $Serie["Axis"];
+ $Mode = $Data["Axis"][$AxisID]["Display"];
+ $Format = $Data["Axis"][$AxisID]["Format"];
+ $Unit = $Data["Axis"][$AxisID]["Unit"];
+
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]),TRUE);
+ $YZero = $this->scaleComputeY(0,array("AxisID"=>$Serie["Axis"]));
+
+ $this->DataSet->Data["Series"][$SerieName]["XOffset"] = 0;
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $YZero < $this->GraphAreaY1+1 ) { $YZero = $this->GraphAreaY1+1; }
+ if ( $YZero > $this->GraphAreaY2-1 ) { $YZero = $this->GraphAreaY2-1; }
+
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+
+ $Plots = ""; $Plots[] = $X; $Plots[] = $YZero;
+ foreach($PosArray as $Key => $Height)
+ {
+ if ( $Height != VOID ) { $Plots[] = $X; $Plots[] = $YZero-$Height; }
+ $X = $X + $XStep;
+ }
+ $Plots[] = $X-$XStep; $Plots[] = $YZero;
+
+ $this->drawPolygon($Plots,$Color);
+
+ $this->Shadow = $RestoreShadow;
+ if ( $DrawLine ) { for($i=2; $i<=count($Plots)-6; $i=$i+2) { $this->drawLine($Plots[$i],$Plots[$i+1],$Plots[$i+2],$Plots[$i+3],$LineColor); } }
+ if ( $DrawPlot )
+ {
+ for($i=2; $i<=count($Plots)-4; $i=$i+2)
+ {
+ if ( $PlotBorder != 0 )
+ { $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius+$PlotBorder,$PlotBorderColor); }
+
+ $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius,$Color);
+ }
+ }
+ $this->Shadow = FALSE;
+ }
+ elseif ( $Data["Orientation"] == SCALE_POS_TOPBOTTOM )
+ {
+ if ( $YZero < $this->GraphAreaX1+1 ) { $YZero = $this->GraphAreaX1+1; }
+ if ( $YZero > $this->GraphAreaX2-1 ) { $YZero = $this->GraphAreaX2-1; }
+
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+
+ $Plots = ""; $Plots[] = $YZero; $Plots[] = $Y;
+ foreach($PosArray as $Key => $Height)
+ {
+ if ( $Height != VOID ) { $Plots[] = $YZero+$Height; $Plots[] = $Y; }
+ $Y = $Y + $YStep;
+ }
+ $Plots[] = $YZero; $Plots[] = $Y-$YStep;
+
+ $this->drawPolygon($Plots,$Color);
+
+ $this->Shadow = $RestoreShadow;
+ if ( $DrawLine ) { for($i=2; $i<=count($Plots)-6; $i=$i+2) { $this->drawLine($Plots[$i],$Plots[$i+1],$Plots[$i+2],$Plots[$i+3],$LineColor); } }
+ if ( $DrawPlot )
+ {
+ for($i=2; $i<=count($Plots)-4; $i=$i+2)
+ {
+ if ( $PlotBorder != 0 )
+ { $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius+$PlotBorder,$PlotBorderColor); }
+
+ $this->drawFilledCircle($Plots[$i],$Plots[$i+1],$PlotRadius,$Color);
+ }
+ }
+ $this->Shadow = FALSE;
+ }
+ }
+ }
+ $this->Shadow = $RestoreShadow;
+ }
+
+ /* Returns a random color */
+ function getRandomColor($Alpha=100)
+ { return(array("R"=>rand(0,255),"G"=>rand(0,255),"B"=>rand(0,255),"Alpha"=>$Alpha)); }
+
+ /* Validate a palette */
+ function validatePalette($Colors,$Surrounding=NULL)
+ {
+ $Result = "";
+
+ if ( !is_array($Colors) ) { return($this->getRandomColor()); }
+
+ foreach($Colors as $Key => $Values)
+ {
+ if ( isset($Values["R"]) ) { $Result[$Key]["R"] = $Values["R"]; } else { $Result[$Key]["R"] = rand(0,255); }
+ if ( isset($Values["G"]) ) { $Result[$Key]["G"] = $Values["G"]; } else { $Result[$Key]["G"] = rand(0,255); }
+ if ( isset($Values["B"]) ) { $Result[$Key]["B"] = $Values["B"]; } else { $Result[$Key]["B"] = rand(0,255); }
+ if ( isset($Values["Alpha"]) ) { $Result[$Key]["Alpha"] = $Values["Alpha"]; } else { $Result[$Key]["Alpha"] = 100; }
+
+ if ( $Surrounding != NULL )
+ {
+ $Result[$Key]["BorderR"] = $Result[$Key]["R"] + $Surrounding;
+ $Result[$Key]["BorderG"] = $Result[$Key]["G"] + $Surrounding;
+ $Result[$Key]["BorderB"] = $Result[$Key]["B"] + $Surrounding;
+ }
+ else
+ {
+ if ( isset($Values["BorderR"]) ) { $Result[$Key]["BorderR"] = $Values["BorderR"]; } else { $Result[$Key]["BorderR"] = $Result[$Key]["R"]; }
+ if ( isset($Values["BorderG"]) ) { $Result[$Key]["BorderG"] = $Values["BorderG"]; } else { $Result[$Key]["BorderG"] = $Result[$Key]["G"]; }
+ if ( isset($Values["BorderB"]) ) { $Result[$Key]["BorderB"] = $Values["BorderB"]; } else { $Result[$Key]["BorderB"] = $Result[$Key]["B"]; }
+ if ( isset($Values["BorderAlpha"]) ) { $Result[$Key]["BorderAlpha"] = $Values["BorderAlpha"]; } else { $Result[$Key]["BorderAlpha"] = $Result[$Key]["Alpha"]; }
+ }
+ }
+
+ return($Result);
+ }
+
+ /* Draw the derivative chart associated to the data series */
+ function drawDerivative($Format=NULL)
+ {
+ $Offset = isset($Format["Offset"]) ? $Format["Offset"] : 10;
+ $SerieSpacing = isset($Format["SerieSpacing"]) ? $Format["SerieSpacing"] : 3;
+ $DerivativeHeight = isset($Format["DerivativeHeight"]) ? $Format["DerivativeHeight"] : 4;
+ $ShadedSlopeBox = isset($Format["ShadedSlopeBox"]) ? $Format["ShadedSlopeBox"] : FALSE;
+ $DrawBackground = isset($Format["DrawBackground"]) ? $Format["DrawBackground"] : TRUE;
+ $BackgroundR = isset($Format["BackgroundR"]) ? $Format["BackgroundR"] : 255;
+ $BackgroundG = isset($Format["BackgroundG"]) ? $Format["BackgroundG"] : 255;
+ $BackgroundB = isset($Format["BackgroundB"]) ? $Format["BackgroundB"] : 255;
+ $BackgroundAlpha = isset($Format["BackgroundAlpha"]) ? $Format["BackgroundAlpha"] : 20;
+ $DrawBorder = isset($Format["DrawBorder"]) ? $Format["DrawBorder"] : TRUE;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : 0;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : 0;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : 0;
+ $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : 100;
+ $Caption = isset($Format["Caption"]) ? $Format["Caption"] : TRUE;
+ $CaptionHeight = isset($Format["CaptionHeight"]) ? $Format["CaptionHeight"] : 10;
+ $CaptionWidth = isset($Format["CaptionWidth"]) ? $Format["CaptionWidth"] : 20;
+ $CaptionMargin = isset($Format["CaptionMargin"]) ? $Format["CaptionMargin"] : 4;
+ $CaptionLine = isset($Format["CaptionLine"]) ? $Format["CaptionLine"] : FALSE;
+ $CaptionBox = isset($Format["CaptionBox"]) ? $Format["CaptionBox"] : FALSE;
+ $CaptionBorderR = isset($Format["CaptionBorderR"]) ? $Format["CaptionBorderR"] : 0;
+ $CaptionBorderG = isset($Format["CaptionBorderG"]) ? $Format["CaptionBorderG"] : 0;
+ $CaptionBorderB = isset($Format["CaptionBorderB"]) ? $Format["CaptionBorderB"] : 0;
+ $CaptionFillR = isset($Format["CaptionFillR"]) ? $Format["CaptionFillR"] : 255;
+ $CaptionFillG = isset($Format["CaptionFillG"]) ? $Format["CaptionFillG"] : 255;
+ $CaptionFillB = isset($Format["CaptionFillB"]) ? $Format["CaptionFillB"] : 255;
+ $CaptionFillAlpha = isset($Format["CaptionFillAlpha"]) ? $Format["CaptionFillAlpha"] : 80;
+ $PositiveSlopeStartR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 184;
+ $PositiveSlopeStartG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 234;
+ $PositiveSlopeStartB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 88;
+ $PositiveSlopeEndR = isset($Format["PositiveSlopeStartR"]) ? $Format["PositiveSlopeStartR"] : 239;
+ $PositiveSlopeEndG = isset($Format["PositiveSlopeStartG"]) ? $Format["PositiveSlopeStartG"] : 31;
+ $PositiveSlopeEndB = isset($Format["PositiveSlopeStartB"]) ? $Format["PositiveSlopeStartB"] : 36;
+ $NegativeSlopeStartR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 184;
+ $NegativeSlopeStartG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 234;
+ $NegativeSlopeStartB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 88;
+ $NegativeSlopeEndR = isset($Format["NegativeSlopeStartR"]) ? $Format["NegativeSlopeStartR"] : 67;
+ $NegativeSlopeEndG = isset($Format["NegativeSlopeStartG"]) ? $Format["NegativeSlopeStartG"] : 124;
+ $NegativeSlopeEndB = isset($Format["NegativeSlopeStartB"]) ? $Format["NegativeSlopeStartB"] : 227;
+
+ $Data = $this->DataSet->getData();
+
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ $YPos = $this->DataSet->Data["GraphArea"]["Y2"] + $Offset;
+ else
+ $XPos = $this->DataSet->Data["GraphArea"]["X2"] + $Offset;
+
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; $Alpha = $Serie["Color"]["Alpha"]; $Ticks = $Serie["Ticks"]; $Weight = $Serie["Weight"];
+
+ $AxisID = $Serie["Axis"];
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $Caption )
+ {
+ if ( $CaptionLine )
+ {
+ $StartX = floor($this->GraphAreaX1-$CaptionWidth+$XMargin-$CaptionMargin);
+ $EndX = floor($this->GraphAreaX1-$CaptionMargin+$XMargin);
+
+ $CaptionSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight);
+ if ( $CaptionBox ) { $this->drawFilledRectangle($StartX,$YPos,$EndX,$YPos+$CaptionHeight,array("R"=>$CaptionFillR,"G"=>$CaptionFillG,"B"=>$CaptionFillB,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB,"Alpha"=>$CaptionFillAlpha)); }
+ $this->drawLine($StartX+2,$YPos+($CaptionHeight/2),$EndX-2,$YPos+($CaptionHeight/2),$CaptionSettings);
+ }
+ else
+ $this->drawFilledRectangle($this->GraphAreaX1-$CaptionWidth+$XMargin-$CaptionMargin,$YPos,$this->GraphAreaX1-$CaptionMargin+$XMargin,$YPos+$CaptionHeight,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB));
+ }
+
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin;
+
+ $TopY = $YPos + ($CaptionHeight/2) - ($DerivativeHeight/2);
+ $BottomY = $YPos + ($CaptionHeight/2) + ($DerivativeHeight/2);
+
+ $StartX = floor($this->GraphAreaX1+$XMargin);
+ $EndX = floor($this->GraphAreaX2-$XMargin);
+
+ if ( $DrawBackground ) { $this->drawFilledRectangle($StartX-1,$TopY-1,$EndX+1,$BottomY+1,array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha)); }
+ if ( $DrawBorder ) { $this->drawRectangle($StartX-1,$TopY-1,$EndX+1,$BottomY+1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); }
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+
+ $RestoreShadow = $this->Shadow;
+ $this->Shadow = FALSE;
+
+ /* Determine the Max slope index */
+ $LastX = NULL; $LastY = NULL; $MinSlope = 0; $MaxSlope = 1;
+ foreach($PosArray as $Key => $Y)
+ {
+ if ( $Y != VOID && $LastX != NULL )
+ { $Slope = ($LastY - $Y); if ( $Slope > $MaxSlope ) { $MaxSlope = $Slope; } if ( $Slope < $MinSlope ) { $MinSlope = $Slope; } }
+
+ if ( $Y == VOID )
+ { $LastX = NULL; $LastY = NULL; }
+ else
+ { $LastX = $X; $LastY = $Y; }
+ }
+
+ $LastX = NULL; $LastY = NULL; $LastColor = NULL;
+ foreach($PosArray as $Key => $Y)
+ {
+ if ( $Y != VOID && $LastY != NULL )
+ {
+ $Slope = ($LastY - $Y);
+
+ if ( $Slope >= 0 )
+ {
+ $SlopeIndex = (100 / $MaxSlope) * $Slope;
+ $R = (($PositiveSlopeEndR - $PositiveSlopeStartR)/100)*$SlopeIndex+$PositiveSlopeStartR;
+ $G = (($PositiveSlopeEndG - $PositiveSlopeStartG)/100)*$SlopeIndex+$PositiveSlopeStartG;
+ $B = (($PositiveSlopeEndB - $PositiveSlopeStartB)/100)*$SlopeIndex+$PositiveSlopeStartB;
+ }
+ elseif ( $Slope < 0 )
+ {
+ $SlopeIndex = (100 / abs($MinSlope)) * abs($Slope);
+ $R = (($NegativeSlopeEndR - $NegativeSlopeStartR)/100)*$SlopeIndex+$NegativeSlopeStartR;
+ $G = (($NegativeSlopeEndG - $NegativeSlopeStartG)/100)*$SlopeIndex+$NegativeSlopeStartG;
+ $B = (($NegativeSlopeEndB - $NegativeSlopeStartB)/100)*$SlopeIndex+$NegativeSlopeStartB;
+ }
+
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B);
+
+ if ( $ShadedSlopeBox && $LastColor != NULL ) // && $Slope != 0
+ {
+ $GradientSettings = array("StartR"=>$LastColor["R"],"StartG"=>$LastColor["G"],"StartB"=>$LastColor["B"],"EndR"=>$R,"EndG"=>$G,"EndB"=>$B);
+ $this->drawGradientArea($LastX,$TopY,$X,$BottomY,DIRECTION_HORIZONTAL,$GradientSettings);
+ }
+ elseif ( !$ShadedSlopeBox || $LastColor == NULL ) // || $Slope == 0
+ $this->drawFilledRectangle(floor($LastX),$TopY,floor($X),$BottomY,$Color);
+
+ $LastColor = $Color;
+ }
+
+ if ( $Y == VOID )
+ { $LastY = NULL; }
+ else
+ { $LastX = $X; $LastY = $Y; }
+
+ $X = $X + $XStep;
+ }
+
+ $YPos = $YPos + $CaptionHeight + $SerieSpacing;
+ }
+ else
+ {
+ if ( $Caption )
+ {
+ $StartY = floor($this->GraphAreaY1-$CaptionWidth+$XMargin-$CaptionMargin);
+ $EndY = floor($this->GraphAreaY1-$CaptionMargin+$XMargin);
+ if ( $CaptionLine )
+ {
+ $CaptionSettings = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks,"Weight"=>$Weight);
+ if ( $CaptionBox ) { $this->drawFilledRectangle($XPos,$StartY,$XPos+$CaptionHeight,$EndY,array("R"=>$CaptionFillR,"G"=>$CaptionFillG,"B"=>$CaptionFillB,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB,"Alpha"=>$CaptionFillAlpha)); }
+ $this->drawLine($XPos+($CaptionHeight/2),$StartY+2,$XPos+($CaptionHeight/2),$EndY-2,$CaptionSettings);
+ }
+ else
+ $this->drawFilledRectangle($XPos,$StartY,$XPos+$CaptionHeight,$EndY,array("R"=>$R,"G"=>$G,"B"=>$B,"BorderR"=>$CaptionBorderR,"BorderG"=>$CaptionBorderG,"BorderB"=>$CaptionBorderB));
+ }
+
+
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin;
+
+ $TopX = $XPos + ($CaptionHeight/2) - ($DerivativeHeight/2);
+ $BottomX = $XPos + ($CaptionHeight/2) + ($DerivativeHeight/2);
+
+ $StartY = floor($this->GraphAreaY1+$XMargin);
+ $EndY = floor($this->GraphAreaY2-$XMargin);
+
+ if ( $DrawBackground ) { $this->drawFilledRectangle($TopX-1,$StartY-1,$BottomX+1,$EndY+1,array("R"=>$BackgroundR,"G"=>$BackgroundG,"B"=>$BackgroundB,"Alpha"=>$BackgroundAlpha)); }
+ if ( $DrawBorder ) { $this->drawRectangle($TopX-1,$StartY-1,$BottomX+1,$EndY+1,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); }
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+
+ $RestoreShadow = $this->Shadow;
+ $this->Shadow = FALSE;
+
+ /* Determine the Max slope index */
+ $LastX = NULL; $LastY = NULL; $MinSlope = 0; $MaxSlope = 1;
+ foreach($PosArray as $Key => $X)
+ {
+ if ( $X != VOID && $LastX != NULL )
+ { $Slope = ($X - $LastX); if ( $Slope > $MaxSlope ) { $MaxSlope = $Slope; } if ( $Slope < $MinSlope ) { $MinSlope = $Slope; } }
+
+ if ( $X == VOID )
+ { $LastX = NULL; }
+ else
+ { $LastX = $X; }
+ }
+
+ $LastX = NULL; $LastY = NULL; $LastColor = NULL;
+ foreach($PosArray as $Key => $X)
+ {
+ if ( $X != VOID && $LastX != NULL )
+ {
+ $Slope = ($X - $LastX);
+
+ if ( $Slope >= 0 )
+ {
+ $SlopeIndex = (100 / $MaxSlope) * $Slope;
+ $R = (($PositiveSlopeEndR - $PositiveSlopeStartR)/100)*$SlopeIndex+$PositiveSlopeStartR;
+ $G = (($PositiveSlopeEndG - $PositiveSlopeStartG)/100)*$SlopeIndex+$PositiveSlopeStartG;
+ $B = (($PositiveSlopeEndB - $PositiveSlopeStartB)/100)*$SlopeIndex+$PositiveSlopeStartB;
+ }
+ elseif ( $Slope < 0 )
+ {
+ $SlopeIndex = (100 / abs($MinSlope)) * abs($Slope);
+ $R = (($NegativeSlopeEndR - $NegativeSlopeStartR)/100)*$SlopeIndex+$NegativeSlopeStartR;
+ $G = (($NegativeSlopeEndG - $NegativeSlopeStartG)/100)*$SlopeIndex+$NegativeSlopeStartG;
+ $B = (($NegativeSlopeEndB - $NegativeSlopeStartB)/100)*$SlopeIndex+$NegativeSlopeStartB;
+ }
+
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B);
+
+ if ( $ShadedSlopeBox && $LastColor != NULL )
+ {
+ $GradientSettings = array("StartR"=>$LastColor["R"],"StartG"=>$LastColor["G"],"StartB"=>$LastColor["B"],"EndR"=>$R,"EndG"=>$G,"EndB"=>$B);
+
+ $this->drawGradientArea($TopX,$LastY,$BottomX,$Y,DIRECTION_VERTICAL,$GradientSettings);
+ }
+ elseif ( !$ShadedSlopeBox || $LastColor == NULL )
+ $this->drawFilledRectangle($TopX,floor($LastY),$BottomX,floor($Y),$Color);
+
+ $LastColor = $Color;
+ }
+
+ if ( $X == VOID )
+ { $LastX = NULL; }
+ else
+ { $LastX = $X; $LastY = $Y; }
+
+ $Y = $Y + $XStep;
+ }
+
+ $XPos = $XPos + $CaptionHeight + $SerieSpacing;
+ }
+
+ $this->Shadow = $RestoreShadow;
+ }
+ }
+ }
+
+ /* Draw the line of best fit */
+ function drawBestFit($Format="")
+ {
+ $OverrideTicks = isset($Format["Ticks"]) ? $Format["Ticks"] : NULL;
+ $OverrideR = isset($Format["R"]) ? $Format["R"] : VOID;
+ $OverrideG = isset($Format["G"]) ? $Format["G"] : VOID;
+ $OverrideB = isset($Format["B"]) ? $Format["B"] : VOID;
+ $OverrideAlpha = isset($Format["Alpha"]) ? $Format["Alpha"] : VOID;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+
+ foreach($Data["Series"] as $SerieName => $Serie)
+ {
+ if ( $Serie["isDrawable"] == TRUE && $SerieName != $Data["Abscissa"] )
+ {
+ if ( $OverrideR != VOID && $OverrideG != VOID && $OverrideB != VOID ) { $R = $OverrideR; $G = $OverrideG; $B = $OverrideB; } else { $R = $Serie["Color"]["R"]; $G = $Serie["Color"]["G"]; $B = $Serie["Color"]["B"]; }
+ if ( $OverrideTicks == NULL ) { $Ticks = $Serie["Ticks"]; } else { $Ticks = $OverrideTicks; }
+ if ( $OverrideAlpha == VOID ) { $Alpha = $Serie["Color"]["Alpha"]; } else { $Alpha = $OverrideAlpha; }
+
+ $Color = array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha,"Ticks"=>$Ticks);
+
+ $AxisID = $Serie["Axis"];
+ $PosArray = $this->scaleComputeY($Serie["Data"],array("AxisID"=>$Serie["Axis"]));
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ $Sxy = 0; $Sx = 0; $Sy = 0; $Sxx = 0;
+ foreach($PosArray as $Key => $Y)
+ {
+ if ( $Y != VOID )
+ {
+ $Sxy = $Sxy + $X*$Y;
+ $Sx = $Sx + $X;
+ $Sy = $Sy + $Y;
+ $Sxx = $Sxx + $X*$X;
+ }
+
+ $X = $X + $XStep;
+ }
+ $n = count($this->DataSet->stripVOID($PosArray)); //$n = count($PosArray);
+ $M = (($n*$Sxy)-($Sx*$Sy)) / (($n*$Sxx)-($Sx*$Sx));
+ $B = (($Sy)-($M*$Sx))/($n);
+
+ $X1 = $this->GraphAreaX1 + $XMargin;
+ $Y1 = $M * $X1 + $B;
+ $X2 = $this->GraphAreaX2 - $XMargin;
+ $Y2 = $M * $X2 + $B;
+
+ if ( $Y1 < $this->GraphAreaY1 ) { $X1 = $X1 + ($this->GraphAreaY1-$Y1); $Y1 = $this->GraphAreaY1; }
+ if ( $Y1 > $this->GraphAreaY2 ) { $X1 = $X1 + ($Y1-$this->GraphAreaY2); $Y1 = $this->GraphAreaY2; }
+ if ( $Y2 < $this->GraphAreaY1 ) { $X2 = $X2 - ($this->GraphAreaY1-$Y2); $Y2 = $this->GraphAreaY1; }
+ if ( $Y2 > $this->GraphAreaY2 ) { $X2 = $X2 - ($Y2-$this->GraphAreaY2); $Y2 = $this->GraphAreaY2; }
+
+ $this->drawLine($X1,$Y1,$X2,$Y2,$Color);
+ }
+ else
+ {
+ if ( $XDivs == 0 ) { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $YStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin;
+
+ if ( !is_array($PosArray) ) { $Value = $PosArray; $PosArray = ""; $PosArray[0] = $Value; }
+ $Sxy = 0; $Sx = 0; $Sy = 0; $Sxx = 0;
+ foreach($PosArray as $Key => $X)
+ {
+ if ( $X != VOID )
+ {
+ $Sxy = $Sxy + $X*$Y;
+ $Sx = $Sx + $Y;
+ $Sy = $Sy + $X;
+ $Sxx = $Sxx + $Y*$Y;
+ }
+
+ $Y = $Y + $YStep;
+ }
+ $n = count($this->DataSet->stripVOID($PosArray)); //$n = count($PosArray);
+ $M = (($n*$Sxy)-($Sx*$Sy)) / (($n*$Sxx)-($Sx*$Sx));
+ $B = (($Sy)-($M*$Sx))/($n);
+
+ $Y1 = $this->GraphAreaY1 + $XMargin;
+ $X1 = $M * $Y1 + $B;
+ $Y2 = $this->GraphAreaY2 - $XMargin;
+ $X2 = $M * $Y2 + $B;
+
+ if ( $X1 < $this->GraphAreaX1 ) { $Y1 = $Y1 + ($this->GraphAreaX1-$X1); $X1 = $this->GraphAreaX1; }
+ if ( $X1 > $this->GraphAreaX2 ) { $Y1 = $Y1 + ($X1-$this->GraphAreaX2); $X1 = $this->GraphAreaX2; }
+ if ( $X2 < $this->GraphAreaX1 ) { $Y2 = $Y2 - ($this->GraphAreaY1-$X2); $X2 = $this->GraphAreaX1; }
+ if ( $X2 > $this->GraphAreaX2 ) { $Y2 = $Y2 - ($X2-$this->GraphAreaX2); $X2 = $this->GraphAreaX2; }
+
+ $this->drawLine($X1,$Y1,$X2,$Y2,$Color);
+ }
+ }
+ }
+ }
+
+ /* Write labels */
+ function writeLabel($SeriesName,$Indexes,$Format="")
+ {
+ $OverrideTitle = isset($Format["OverrideTitle"]) ? $Format["OverrideTitle"] : NULL;
+ $ForceLabels = isset($Format["ForceLabels"]) ? $Format["ForceLabels"] : NULL;
+ $DrawPoint = isset($Format["DrawPoint"]) ? $Format["DrawPoint"] : LABEL_POINT_BOX;
+ $DrawVerticalLine = isset($Format["DrawVerticalLine"]) ? $Format["DrawVerticalLine"] : FALSE;
+ $VerticalLineR = isset($Format["VerticalLineR"]) ? $Format["VerticalLineR"] : 0;
+ $VerticalLineG = isset($Format["VerticalLineG"]) ? $Format["VerticalLineG"] : 0;
+ $VerticalLineB = isset($Format["VerticalLineB"]) ? $Format["VerticalLineB"] : 0;
+ $VerticalLineAlpha = isset($Format["VerticalLineAlpha"]) ? $Format["VerticalLineAlpha"] : 40;
+ $VerticalLineTicks = isset($Format["VerticalLineTicks"]) ? $Format["VerticalLineTicks"] : 2;
+
+ $Data = $this->DataSet->getData();
+ list($XMargin,$XDivs) = $this->scaleGetXSettings();
+
+ if ( !is_array($Indexes) ) { $Index = $Indexes; $Indexes = ""; $Indexes[] = $Index; }
+ if ( !is_array($SeriesName) ) { $SerieName = $SeriesName; $SeriesName = ""; $SeriesName[] = $SerieName; }
+ if ( $ForceLabels != NULL && !is_array($ForceLabels) ) { $ForceLabel = $ForceLabels; $ForceLabels = ""; $ForceLabels[] = $ForceLabel; }
+
+ foreach ($Indexes as $Key => $Index)
+ {
+ $Series = "";
+
+ if ( $Data["Orientation"] == SCALE_POS_LEFTRIGHT )
+ {
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1)/4; } else { $XStep = ($this->GraphAreaX2-$this->GraphAreaX1-$XMargin*2)/$XDivs; }
+ $X = $this->GraphAreaX1 + $XMargin + $Index * $XStep;
+
+ if ( $DrawVerticalLine ) { $this->drawLine($X,$this->GraphAreaY1+$Data["YMargin"],$X,$this->GraphAreaY2-$Data["YMargin"],array("R"=>$VerticalLineR,"G"=>$VerticalLineG,"B"=>$VerticalLineB,"Alpha"=>$VerticalLineAlpha,"Ticks"=>$VerticalLineTicks)); }
+
+ $MinY = $this->GraphAreaY2;
+ foreach ($SeriesName as $iKey => $SerieName)
+ {
+ if ( isset($Data["Series"][$SerieName]["Data"][$Index]) )
+ {
+ $AxisID = $Data["Series"][$SerieName]["Axis"];
+ $XAxisMode = $Data["XAxisDisplay"];
+ $XAxisFormat = $Data["XAxisFormat"];
+ $XAxisUnit = $Data["XAxisUnit"];
+ $AxisMode = $Data["Axis"][$AxisID]["Display"];
+ $AxisFormat = $Data["Axis"][$AxisID]["Format"];
+ $AxisUnit = $Data["Axis"][$AxisID]["Unit"];
+
+ if ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) )
+ $XLabel = $this->scaleFormat($Data["Series"][$Data["Abscissa"]]["Data"][$Index],$XAxisMode,$XAxisFormat,$XAxisUnit);
+ else
+ $XLabel = "";
+
+ if ( $OverrideTitle != NULL)
+ $Description = $OverrideTitle;
+ elseif ( count($SeriesName) == 1 )
+ {
+ $Description = $Data["Series"][$SerieName]["Description"]." - ".$XLabel;
+ }
+ elseif ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) )
+ $Description = $XLabel;
+
+ $Serie = "";
+ $Serie["R"] = $Data["Series"][$SerieName]["Color"]["R"];
+ $Serie["G"] = $Data["Series"][$SerieName]["Color"]["G"];
+ $Serie["B"] = $Data["Series"][$SerieName]["Color"]["B"];
+ $Serie["Alpha"] = $Data["Series"][$SerieName]["Color"]["Alpha"];
+
+ if ( count($SeriesName) == 1 && isset($Data["Series"][$SerieName]["XOffset"]) )
+ $SerieOffset = $Data["Series"][$SerieName]["XOffset"];
+ else
+ $SerieOffset = 0;
+
+ $Value = $Data["Series"][$SerieName]["Data"][$Index];
+ if ( $Value == VOID ) { $Value = "NaN"; }
+
+ if ( $ForceLabels != NULL )
+ $Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set";
+ else
+ $Caption = $this->scaleFormat($Value,$AxisMode,$AxisFormat,$AxisUnit);
+
+ if ( $this->LastChartLayout == CHART_LAST_LAYOUT_STACKED )
+ {
+ if ( $Value >=0 ) { $LookFor = "+"; } else { $LookFor = "-"; }
+
+ $Value = 0; $Done = FALSE;
+ foreach($Data["Series"] as $Name => $SerieLookup)
+ {
+ if ( $SerieLookup["isDrawable"] == TRUE && $Name != $Data["Abscissa"] && !$Done )
+ {
+ if ( isset($Data["Series"][$Name]["Data"][$Index]) && $Data["Series"][$Name]["Data"][$Index] != VOID )
+ {
+ if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+" ) { $Value = $Value + $Data["Series"][$Name]["Data"][$Index]; }
+ if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-" ) { $Value = $Value - $Data["Series"][$Name]["Data"][$Index]; }
+ if ($Name == $SerieName ) { $Done = TRUE; }
+ }
+ }
+ }
+ }
+
+ $X = floor($this->GraphAreaX1 + $XMargin + $Index * $XStep + $SerieOffset);
+ $Y = floor($this->scaleComputeY($Value,array("AxisID"=>$AxisID)));
+
+ if ($Y < $MinY) { $MinY = $Y; }
+
+ if ( $DrawPoint == LABEL_POINT_CIRCLE )
+ $this->drawFilledCircle($X,$Y,3,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0));
+ elseif ( $DrawPoint == LABEL_POINT_BOX )
+ $this->drawFilledRectangle($X-2,$Y-2,$X+2,$Y+2,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0));
+
+ $Series[] = array("Format"=>$Serie,"Caption"=>$Caption);
+ }
+ }
+ $this->drawLabelBox($X,$MinY-3,$Description,$Series,$Format);
+
+ }
+ else
+ {
+ if ( $XDivs == 0 ) { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1)/4; } else { $XStep = ($this->GraphAreaY2-$this->GraphAreaY1-$XMargin*2)/$XDivs; }
+ $Y = $this->GraphAreaY1 + $XMargin + $Index * $XStep;
+
+ if ( $DrawVerticalLine ) { $this->drawLine($this->GraphAreaX1+$Data["YMargin"],$Y,$this->GraphAreaX2-$Data["YMargin"],$Y,array("R"=>$VerticalLineR,"G"=>$VerticalLineG,"B"=>$VerticalLineB,"Alpha"=>$VerticalLineAlpha,"Ticks"=>$VerticalLineTicks)); }
+
+ $MinX = $this->GraphAreaX2;
+ foreach ($SeriesName as $Key => $SerieName)
+ {
+ if ( isset($Data["Series"][$SerieName]["Data"][$Index]) )
+ {
+ $AxisID = $Data["Series"][$SerieName]["Axis"];
+ $XAxisMode = $Data["XAxisDisplay"];
+ $XAxisFormat = $Data["XAxisFormat"];
+ $XAxisUnit = $Data["XAxisUnit"];
+ $AxisMode = $Data["Axis"][$AxisID]["Display"];
+ $AxisFormat = $Data["Axis"][$AxisID]["Format"];
+ $AxisUnit = $Data["Axis"][$AxisID]["Unit"];
+
+ if ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) )
+ $XLabel = $this->scaleFormat($Data["Series"][$Data["Abscissa"]]["Data"][$Index],$XAxisMode,$XAxisFormat,$XAxisUnit);
+ else
+ $XLabel = "";
+
+ if ( $OverrideTitle != NULL)
+ $Description = $OverrideTitle;
+ elseif ( count($SeriesName) == 1 )
+ {
+ if ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) )
+ $Description = $Data["Series"][$SerieName]["Description"]." - ".$XLabel;
+ }
+ elseif ( isset($Data["Abscissa"]) && isset($Data["Series"][$Data["Abscissa"]]["Data"][$Index]) )
+ $Description = $XLabel;
+
+ $Serie = "";
+ if ( isset($Data["Extended"]["Palette"][$Index] ) )
+ {
+ $Serie["R"] = $Data["Extended"]["Palette"][$Index]["R"];
+ $Serie["G"] = $Data["Extended"]["Palette"][$Index]["G"];
+ $Serie["B"] = $Data["Extended"]["Palette"][$Index]["B"];
+ $Serie["Alpha"] = $Data["Extended"]["Palette"][$Index]["Alpha"];
+ }
+ else
+ {
+ $Serie["R"] = $Data["Series"][$SerieName]["Color"]["R"];
+ $Serie["G"] = $Data["Series"][$SerieName]["Color"]["G"];
+ $Serie["B"] = $Data["Series"][$SerieName]["Color"]["B"];
+ $Serie["Alpha"] = $Data["Series"][$SerieName]["Color"]["Alpha"];
+ }
+
+ if ( count($SeriesName) == 1 && isset($Data["Series"][$SerieName]["XOffset"]) )
+ $SerieOffset = $Data["Series"][$SerieName]["XOffset"];
+ else
+ $SerieOffset = 0;
+
+ $Value = $Data["Series"][$SerieName]["Data"][$Index];
+ if ( $ForceLabels != NULL )
+ $Caption = isset($ForceLabels[$Key]) ? $ForceLabels[$Key] : "Not set";
+ else
+ $Caption = $this->scaleFormat($Value,$AxisMode,$AxisFormat,$AxisUnit);
+ if ( $Value == VOID ) { $Value = "NaN"; }
+
+ if ( $this->LastChartLayout == CHART_LAST_LAYOUT_STACKED )
+ {
+ if ( $Value >=0 ) { $LookFor = "+"; } else { $LookFor = "-"; }
+
+ $Value = 0; $Done = FALSE;
+ foreach($Data["Series"] as $Name => $SerieLookup)
+ {
+ if ( $SerieLookup["isDrawable"] == TRUE && $Name != $Data["Abscissa"] && !$Done )
+ {
+ if ( isset($Data["Series"][$Name]["Data"][$Index]) && $Data["Series"][$Name]["Data"][$Index] != VOID )
+ {
+ if ($Data["Series"][$Name]["Data"][$Index] >= 0 && $LookFor == "+" ) { $Value = $Value + $Data["Series"][$Name]["Data"][$Index]; }
+ if ($Data["Series"][$Name]["Data"][$Index] < 0 && $LookFor == "-" ) { $Value = $Value - $Data["Series"][$Name]["Data"][$Index]; }
+ if ($Name == $SerieName ) { $Done = TRUE; }
+ }
+ }
+ }
+ }
+
+ $X = floor($this->scaleComputeY($Value,array("AxisID"=>$AxisID)));
+ $Y = floor($this->GraphAreaY1 + $XMargin + $Index * $XStep + $SerieOffset);
+
+ if ($X < $MinX) { $MinX = $X; }
+
+ if ( $DrawPoint == LABEL_POINT_CIRCLE )
+ $this->drawFilledCircle($X,$Y,3,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0));
+ elseif ( $DrawPoint == LABEL_POINT_BOX )
+ $this->drawFilledRectangle($X-2,$Y-2,$X+2,$Y+2,array("R"=>255,"G"=>255,"B"=>255,"BorderR"=>0,"BorderG"=>0,"BorderB"=>0));
+
+ $Series[] = array("Format"=>$Serie,"Caption"=>$Caption);
+ }
+ }
+ $this->drawLabelBox($MinX,$Y-3,$Description,$Series,$Format);
+
+ }
+ }
+ }
+
+ /* Draw a label box */
+ function drawLabelBox($X,$Y,$Title,$Captions,$Format="")
+ {
+ $NoTitle = isset($Format["NoTitle"]) ? $Format["NoTitle"] : NULL;
+ $BoxWidth = isset($Format["BoxWidth"]) ? $Format["BoxWidth"] : 50;
+ $DrawSerieColor = isset($Format["DrawSerieColor"]) ? $Format["DrawSerieColor"] : TRUE;
+ $SerieR = isset($Format["SerieR"]) ? $Format["SerieR"] : NULL;
+ $SerieG = isset($Format["SerieG"]) ? $Format["SerieG"] : NULL;
+ $SerieB = isset($Format["SerieB"]) ? $Format["SerieB"] : NULL;
+ $SerieAlpha = isset($Format["SerieAlpha"]) ? $Format["SerieAlpha"] : NULL;
+ $SerieBoxSize = isset($Format["SerieBoxSize"]) ? $Format["SerieBoxSize"] : 6;
+ $SerieBoxSpacing = isset($Format["SerieBoxSpacing"]) ? $Format["SerieBoxSpacing"] : 4;
+ $VerticalMargin = isset($Format["VerticalMargin"]) ? $Format["VerticalMargin"] : 10;
+ $HorizontalMargin = isset($Format["HorizontalMargin"]) ? $Format["HorizontalMargin"] : 8;
+ $R = isset($Format["R"]) ? $Format["R"] : $this->FontColorR;
+ $G = isset($Format["G"]) ? $Format["G"] : $this->FontColorG;
+ $B = isset($Format["B"]) ? $Format["B"] : $this->FontColorB;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : $this->FontColorA;
+ $FontName = isset($Format["FontName"]) ? $Format["FontName"] : $this->FontName;
+ $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : $this->FontSize;
+ $TitleMode = isset($Format["TitleMode"]) ? $Format["TitleMode"] : LABEL_TITLE_NOBACKGROUND;
+ $TitleR = isset($Format["TitleR"]) ? $Format["TitleR"] : $R;
+ $TitleG = isset($Format["TitleG"]) ? $Format["TitleG"] : $G;
+ $TitleB = isset($Format["TitleB"]) ? $Format["TitleB"] : $B;
+ $TitleAlpha = isset($Format["TitleAlpha"]) ? $Format["TitleAlpha"] : 100;
+ $TitleBackgroundR = isset($Format["TitleBackgroundR"]) ? $Format["TitleBackgroundR"] : 0;
+ $TitleBackgroundG = isset($Format["TitleBackgroundG"]) ? $Format["TitleBackgroundG"] : 0;
+ $TitleBackgroundB = isset($Format["TitleBackgroundB"]) ? $Format["TitleBackgroundB"] : 0;
+ $TitleBackgroundAlpha = isset($Format["TitleBackgroundAlpha"]) ? $Format["TitleBackgroundAlpha"] : 100;
+ $GradientStartR = isset($Format["GradientStartR"]) ? $Format["GradientStartR"] : 255;
+ $GradientStartG = isset($Format["GradientStartG"]) ? $Format["GradientStartG"] : 255;
+ $GradientStartB = isset($Format["GradientStartB"]) ? $Format["GradientStartB"] : 255;
+ $GradientEndR = isset($Format["GradientEndR"]) ? $Format["GradientEndR"] : 220;
+ $GradientEndG = isset($Format["GradientEndG"]) ? $Format["GradientEndG"] : 220;
+ $GradientEndB = isset($Format["GradientEndB"]) ? $Format["GradientEndB"] : 220;
+ $BoxAlpha = isset($Format["BoxAlpha"]) ? $Format["BoxAlpha"] : 100;
+
+ if ( !$DrawSerieColor ) { $SerieBoxSize = 0; $SerieBoxSpacing = 0; }
+
+ $TxtPos = $this->getTextBox($X,$Y,$FontName,$FontSize,0,$Title);
+ $TitleWidth = ($TxtPos[1]["X"] - $TxtPos[0]["X"])+$VerticalMargin*2;
+ $TitleHeight = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]);
+
+ if ( $NoTitle ) { $TitleWidth = 0; $TitleHeight = 0; }
+
+ $CaptionWidth = 0; $CaptionHeight = -$HorizontalMargin;
+ foreach($Captions as $Key =>$Caption)
+ {
+ $TxtPos = $this->getTextBox($X,$Y,$FontName,$FontSize,0,$Caption["Caption"]);
+ $CaptionWidth = max($CaptionWidth,($TxtPos[1]["X"] - $TxtPos[0]["X"])+$VerticalMargin*2);
+ $CaptionHeight = $CaptionHeight + max(($TxtPos[0]["Y"] - $TxtPos[2]["Y"]),($SerieBoxSize+2)) + $HorizontalMargin;
+ }
+
+ if ( $CaptionHeight <= 5 ) { $CaptionHeight = $CaptionHeight + $HorizontalMargin/2; }
+
+ if ( $DrawSerieColor ) { $CaptionWidth = $CaptionWidth + $SerieBoxSize + $SerieBoxSpacing; }
+
+ $BoxWidth = max($BoxWidth,$TitleWidth,$CaptionWidth);
+
+ $XMin = $X - 5 - floor(($BoxWidth-10) / 2);
+ $XMax = $X + 5 + floor(($BoxWidth-10) / 2);
+
+ $RestoreShadow = $this->Shadow;
+ if ( $this->Shadow == TRUE )
+ {
+ $this->Shadow = FALSE;
+
+ $Poly = "";
+ $Poly[] = $X+$this->ShadowX; $Poly[] = $Y+$this->ShadowX;
+ $Poly[] = $X+5+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX;
+ $Poly[] = $XMax+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX;
+
+ if ( $NoTitle )
+ {
+ $Poly[] = $XMax+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2+$this->ShadowX;
+ $Poly[] = $XMin+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2+$this->ShadowX;
+ }
+ else
+ {
+ $Poly[] = $XMax+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3+$this->ShadowX;
+ $Poly[] = $XMin+$this->ShadowX; $Poly[] = $Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3+$this->ShadowX;
+ }
+
+ $Poly[] = $XMin+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX;
+ $Poly[] = $X-5+$this->ShadowX; $Poly[] = $Y-5+$this->ShadowX;
+ $this->drawPolygon($Poly,array("R"=>$this->ShadowR,"G"=>$this->ShadowG,"B"=>$this->ShadowB,"Alpha"=>$this->Shadowa));
+ }
+
+ /* Draw the background */
+ $GradientSettings = array("StartR"=>$GradientStartR,"StartG"=>$GradientStartG,"StartB"=>$GradientStartB,"EndR"=>$GradientEndR,"EndG"=>$GradientEndG,"EndB"=>$GradientEndB,"Alpha"=>$BoxAlpha);
+ if ( $NoTitle )
+ $this->drawGradientArea($XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax,$Y-6,DIRECTION_VERTICAL,$GradientSettings);
+ else
+ $this->drawGradientArea($XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-6,DIRECTION_VERTICAL,$GradientSettings);
+ $Poly = ""; $Poly[] = $X; $Poly[] = $Y; $Poly[] = $X-5; $Poly[] = $Y-5; $Poly[] = $X+5; $Poly[] = $Y-5;
+ $this->drawPolygon($Poly,array("R"=>$GradientEndR,"G"=>$GradientEndG,"B"=>$GradientEndB,"Alpha"=>$BoxAlpha,"NoBorder"=>TRUE));
+
+ /* Outer border */
+ $OuterBorderColor = $this->allocateColor($this->Picture,100,100,100,$BoxAlpha);
+ imageline($this->Picture,$XMin,$Y-5,$X-5,$Y-5,$OuterBorderColor);
+ imageline($this->Picture,$X,$Y,$X-5,$Y-5,$OuterBorderColor);
+ imageline($this->Picture,$X,$Y,$X+5,$Y-5,$OuterBorderColor);
+ imageline($this->Picture,$X+5,$Y-5,$XMax,$Y-5,$OuterBorderColor);
+ if ( $NoTitle )
+ {
+ imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMin,$Y-5,$OuterBorderColor);
+ imageline($this->Picture,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax,$Y-5,$OuterBorderColor);
+ imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$OuterBorderColor);
+ }
+ else
+ {
+ imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMin,$Y-5,$OuterBorderColor);
+ imageline($this->Picture,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-5,$OuterBorderColor);
+ imageline($this->Picture,$XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$OuterBorderColor);
+ }
+
+ /* Inner border */
+ $InnerBorderColor = $this->allocateColor($this->Picture,255,255,255,$BoxAlpha);
+ imageline($this->Picture,$XMin+1,$Y-6,$X-5,$Y-6,$InnerBorderColor);
+ imageline($this->Picture,$X,$Y-1,$X-5,$Y-6,$InnerBorderColor);
+ imageline($this->Picture,$X,$Y-1,$X+5,$Y-6,$InnerBorderColor);
+ imageline($this->Picture,$X+5,$Y-6,$XMax-1,$Y-6,$InnerBorderColor);
+ if ( $NoTitle )
+ {
+ imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMin+1,$Y-6,$InnerBorderColor);
+ imageline($this->Picture,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax-1,$Y-6,$InnerBorderColor);
+ imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*2,$InnerBorderColor);
+ }
+ else
+ {
+ imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMin+1,$Y-6,$InnerBorderColor);
+ imageline($this->Picture,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax-1,$Y-6,$InnerBorderColor);
+ imageline($this->Picture,$XMin+1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax-1,$Y-4-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$InnerBorderColor);
+ }
+
+ /* Draw the separator line */
+ if ( $TitleMode == LABEL_TITLE_NOBACKGROUND && !$NoTitle )
+ {
+ $YPos = $Y-7-$CaptionHeight-$HorizontalMargin-$HorizontalMargin/2;
+ $XMargin = $VerticalMargin / 2;
+ $this->drawLine($XMin+$XMargin,$YPos+1,$XMax-$XMargin,$YPos+1,array("R"=>$GradientEndR,"G"=>$GradientEndG,"B"=>$GradientEndB,"Alpha"=>$BoxAlpha));
+ $this->drawLine($XMin+$XMargin,$YPos,$XMax-$XMargin,$YPos,array("R"=>$GradientStartR,"G"=>$GradientStartG,"B"=>$GradientStartB,"Alpha"=>$BoxAlpha));
+ }
+ elseif ( $TitleMode == LABEL_TITLE_BACKGROUND )
+ {
+ $this->drawFilledRectangle($XMin,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin*3,$XMax,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin+$HorizontalMargin/2,array("R"=>$TitleBackgroundR,"G"=>$TitleBackgroundG,"B"=>$TitleBackgroundB,"Alpha"=>$BoxAlpha));
+ imageline($this->Picture,$XMin+1,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin+$HorizontalMargin/2+1,$XMax-1,$Y-5-$TitleHeight-$CaptionHeight-$HorizontalMargin+$HorizontalMargin/2+1,$InnerBorderColor);
+ }
+
+ /* Write the description */
+ if ( !$NoTitle )
+ $this->drawText($XMin+$VerticalMargin,$Y-7-$CaptionHeight-$HorizontalMargin*2,$Title,array("Align"=>TEXT_ALIGN_BOTTOMLEFT,"R"=>$TitleR,"G"=>$TitleG,"B"=>$TitleB));
+
+ /* Write the value */
+ $YPos = $Y-5-$HorizontalMargin; $XPos = $XMin+$VerticalMargin+$SerieBoxSize+$SerieBoxSpacing;
+ foreach($Captions as $Key => $Caption)
+ {
+ $CaptionTxt = $Caption["Caption"];
+ $TxtPos = $this->getTextBox($XPos,$YPos,$FontName,$FontSize,0,$CaptionTxt);
+ $CaptionHeight = ($TxtPos[0]["Y"] - $TxtPos[2]["Y"]);
+
+ /* Write the serie color if needed */
+ if ( $DrawSerieColor )
+ {
+ $BoxSettings = array("R"=>$Caption["Format"]["R"],"G"=>$Caption["Format"]["G"],"B"=>$Caption["Format"]["B"],"Alpha"=>$Caption["Format"]["Alpha"],"BorderR"=>0,"BorderG"=>0,"BorderB"=>0);
+ $this->drawFilledRectangle($XMin+$VerticalMargin,$YPos-$SerieBoxSize,$XMin+$VerticalMargin+$SerieBoxSize,$YPos,$BoxSettings);
+ }
+
+ $this->drawText($XPos,$YPos,$CaptionTxt,array("Align"=>TEXT_ALIGN_BOTTOMLEFT));
+
+ $YPos = $YPos - $CaptionHeight - $HorizontalMargin;
+ }
+
+ $this->Shadow = $RestoreShadow;
+ }
+
+ /* Draw a basic shape */
+ function drawShape($X,$Y,$Shape,$PlotSize,$PlotBorder,$BorderSize,$R,$G,$B,$Alpha,$BorderR,$BorderG,$BorderB,$BorderAlpha)
+ {
+ if ( $Shape == SERIE_SHAPE_FILLEDCIRCLE )
+ {
+ if ( $PlotBorder ) { $this->drawFilledCircle($X,$Y,$PlotSize+$BorderSize,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); }
+ $this->drawFilledCircle($X,$Y,$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ }
+ elseif ( $Shape == SERIE_SHAPE_FILLEDSQUARE )
+ {
+ if ( $PlotBorder ) { $this->drawFilledRectangle($X-$PlotSize-$BorderSize,$Y-$PlotSize-$BorderSize,$X+$PlotSize+$BorderSize,$Y+$PlotSize+$BorderSize,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha)); }
+ $this->drawFilledRectangle($X-$PlotSize,$Y-$PlotSize,$X+$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ }
+ elseif ( $Shape == SERIE_SHAPE_FILLEDTRIANGLE )
+ {
+ if ( $PlotBorder )
+ {
+ $Pos = ""; $Pos[]=$X; $Pos[]=$Y-$PlotSize-$BorderSize; $Pos[]=$X-$PlotSize-$BorderSize; $Pos[]=$Y+$PlotSize+$BorderSize; $Pos[]=$X+$PlotSize+$BorderSize; $Pos[]=$Y+$PlotSize+$BorderSize;
+ $this->drawPolygon($Pos,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha));
+ }
+
+ $Pos = ""; $Pos[]=$X; $Pos[]=$Y-$PlotSize; $Pos[]=$X-$PlotSize; $Pos[]=$Y+$PlotSize; $Pos[]=$X+$PlotSize; $Pos[]=$Y+$PlotSize;
+ $this->drawPolygon($Pos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ }
+ elseif ( $Shape == SERIE_SHAPE_TRIANGLE )
+ {
+ $this->drawLine($X,$Y-$PlotSize,$X-$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ $this->drawLine($X-$PlotSize,$Y+$PlotSize,$X+$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ $this->drawLine($X+$PlotSize,$Y+$PlotSize,$X,$Y-$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ }
+ elseif ( $Shape == SERIE_SHAPE_SQUARE )
+ $this->drawRectangle($X-$PlotSize,$Y-$PlotSize,$X+$PlotSize,$Y+$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ elseif ( $Shape == SERIE_SHAPE_CIRCLE )
+ $this->drawCircle($X,$Y,$PlotSize,$PlotSize,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ elseif ( $Shape == SERIE_SHAPE_DIAMOND )
+ {
+ $Pos = ""; $Pos[]=$X-$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y-$PlotSize; $Pos[]=$X+$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y+$PlotSize;
+ $this->drawPolygon($Pos,array("NoFill"=>TRUE,"BorderR"=>$R,"BorderG"=>$G,"BorderB"=>$B,"BorderAlpha"=>$Alpha));
+ }
+ elseif ( $Shape == SERIE_SHAPE_FILLEDDIAMOND )
+ {
+ if ( $PlotBorder )
+ {
+ $Pos = ""; $Pos[]=$X-$PlotSize-$BorderSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y-$PlotSize-$BorderSize; $Pos[]=$X+$PlotSize+$BorderSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y+$PlotSize+$BorderSize;
+ $this->drawPolygon($Pos,array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha));
+ }
+
+ $Pos = ""; $Pos[]=$X-$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y-$PlotSize; $Pos[]=$X+$PlotSize; $Pos[]=$Y; $Pos[]=$X; $Pos[]=$Y+$PlotSize;
+ $this->drawPolygon($Pos,array("R"=>$R,"G"=>$G,"B"=>$B,"Alpha"=>$Alpha));
+ }
+ }
+
+ function drawPolygonChart($Points,$Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $NoFill = isset($Format["NoFill"]) ? $Format["NoFill"] : FALSE;
+ $NoBorder = isset($Format["NoBorder"]) ? $Format["NoBorder"] : FALSE;
+ $BorderR = isset($Format["BorderR"]) ? $Format["BorderR"] : $R;
+ $BorderG = isset($Format["BorderG"]) ? $Format["BorderG"] : $G;
+ $BorderB = isset($Format["BorderB"]) ? $Format["BorderB"] : $B;
+ $BorderAlpha = isset($Format["BorderAlpha"]) ? $Format["BorderAlpha"] : $Alpha / 2;
+ $Surrounding = isset($Format["Surrounding"]) ? $Format["Surrounding"] : NULL;
+ $Threshold = isset($Format["Threshold"]) ? $Format["Threshold"] : NULL;
+
+ if ( $Surrounding != NULL ) { $BorderR = $R+$Surrounding; $BorderG = $G+$Surrounding; $BorderB = $B+$Surrounding; }
+
+ $RestoreShadow = $this->Shadow;
+ $this->Shadow = FALSE;
+
+ $AllIntegers = TRUE;
+ for($i=0;$i<=count($Points)-2;$i=$i+2)
+ { if ( $this->getFirstDecimal($Points[$i+1]) != 0 ) { $AllIntegers = FALSE; } }
+
+ /* Convert polygon to segments */
+ $Segments = "";
+ for($i=2;$i<=count($Points)-2;$i=$i+2)
+ { $Segments[] = array("X1"=>$Points[$i-2],"Y1"=>$Points[$i-1],"X2"=>$Points[$i],"Y2"=>$Points[$i+1]); }
+ $Segments[] = array("X1"=>$Points[$i-2],"Y1"=>$Points[$i-1],"X2"=>$Points[0],"Y2"=>$Points[1]);
+
+ /* Simplify straight lines */
+ $Result = ""; $inHorizon = FALSE; $LastX = VOID;
+ foreach($Segments as $Key => $Pos)
+ {
+ if ( $Pos["Y1"] != $Pos["Y2"] )
+ {
+ if ( $inHorizon ) { $inHorizon = FALSE; $Result[] = array("X1"=>$LastX,"Y1"=>$Pos["Y1"],"X2"=>$Pos["X1"],"Y2"=>$Pos["Y1"]); }
+
+ $Result[] = array("X1"=>$Pos["X1"],"Y1"=>$Pos["Y1"],"X2"=>$Pos["X2"],"Y2"=>$Pos["Y2"]);
+ }
+ else { if ( !$inHorizon ) { $inHorizon = TRUE; $LastX = $Pos["X1"];} }
+ }
+ $Segments = $Result;
+
+ /* Do we have something to draw */
+ if ( $Segments == "" ) { return(0); }
+
+ /* For segments debugging purpose */
+ //foreach($Segments as $Key => $Pos)
+ // echo $Pos["X1"].",".$Pos["Y1"].",".$Pos["X2"].",".$Pos["Y2"]."\r\n";
+
+ /* Find out the min & max Y boundaries */
+ $MinY = OUT_OF_SIGHT; $MaxY = OUT_OF_SIGHT;
+ foreach($Segments as $Key => $Coords)
+ {
+ if ( $MinY == OUT_OF_SIGHT || $MinY > min($Coords["Y1"],$Coords["Y2"]) ) { $MinY = min($Coords["Y1"],$Coords["Y2"]); }
+ if ( $MaxY == OUT_OF_SIGHT || $MaxY < max($Coords["Y1"],$Coords["Y2"]) ) { $MaxY = max($Coords["Y1"],$Coords["Y2"]); }
+ }
+
+ if ( $AllIntegers ) { $YStep = 1; } else { $YStep = .5; }
+
+ $MinY = floor($MinY); $MaxY = floor($MaxY);
+
+ /* Scan each Y lines */
+ $DefaultColor = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ $DebugLine = 0; $DebugColor = $this->allocateColor($this->Picture,255,0,0,100);
+
+ $MinY = floor($MinY); $MaxY = floor($MaxY); $YStep = 1;
+
+ if ( !$NoFill )
+ {
+ //if ( $DebugLine ) { $MinY = $DebugLine; $MaxY = $DebugLine; }
+ for($Y=$MinY;$Y<=$MaxY;$Y=$Y+$YStep)
+ {
+ $Intersections = ""; $LastSlope = NULL; $RestoreLast = "-";
+ foreach($Segments as $Key => $Coords)
+ {
+ $X1 = $Coords["X1"]; $X2 = $Coords["X2"]; $Y1 = $Coords["Y1"]; $Y2 = $Coords["Y2"];
+
+ if ( min($Y1,$Y2) <= $Y && max($Y1,$Y2) >= $Y )
+ {
+ if ( $Y1 == $Y2 )
+ { $X = $X1; }
+ else
+ { $X = $X1 + ( ($Y-$Y1)*$X2 - ($Y-$Y1)*$X1 ) / ($Y2-$Y1); }
+
+ $X = floor($X);
+
+ if ( $X2 == $X1 )
+ { $Slope = "!"; }
+ else
+ {
+ $SlopeC = ($Y2 - $Y1) / ($X2 - $X1);
+ if( $SlopeC == 0 )
+ { $Slope = "="; }
+ elseif( $SlopeC > 0 )
+ { $Slope = "+"; }
+ elseif ( $SlopeC < 0 )
+ { $Slope = "-"; }
+ }
+
+ if ( !is_array($Intersections) )
+ { $Intersections[] = $X; }
+ elseif( !in_array($X,$Intersections) )
+ { $Intersections[] = $X; }
+ elseif( in_array($X,$Intersections) )
+ {
+ if ($Y == $DebugLine) { echo $Slope."/".$LastSlope."(".$X.") "; }
+
+ if ( $Slope == "=" && $LastSlope == "-" ) { $Intersections[] = $X; }
+ if ( $Slope != $LastSlope && $LastSlope != "!" && $LastSlope != "=" ) { $Intersections[] = $X; }
+ if ( $Slope != $LastSlope && $LastSlope == "!" && $Slope == "+" ) { $Intersections[] = $X; }
+ }
+
+ if ( is_array($Intersections) && in_array($X,$Intersections) && $LastSlope == "=" && ($Slope == "-" )) { $Intersections[] = $X; }
+
+ $LastSlope = $Slope;
+ }
+ }
+ if ( $RestoreLast != "-" ) { $Intersections[] = $RestoreLast; echo "@".$Y."\r\n"; }
+
+ if ( is_array($Intersections) )
+ {
+ sort($Intersections);
+
+ if ($Y == $DebugLine) { print_r($Intersections); }
+
+ /* Remove NULL plots */
+ $Result = "";
+ for($i=0;$i<=count($Intersections)-1;$i=$i+2)
+ {
+ if ( isset($Intersections[$i+1]) )
+ { if ( $Intersections[$i] != $Intersections[$i+1] ) { $Result[] = $Intersections[$i]; $Result[] = $Intersections[$i+1]; } }
+ }
+
+ if ( is_array($Result) )
+ {
+ $Intersections = $Result;
+
+ $LastX = OUT_OF_SIGHT;
+ foreach($Intersections as $Key => $X)
+ {
+ if ( $LastX == OUT_OF_SIGHT )
+ $LastX = $X;
+ elseif ( $LastX != OUT_OF_SIGHT )
+ {
+ if ( $this->getFirstDecimal($LastX) > 1 ) { $LastX++; }
+
+ $Color = $DefaultColor;
+ if ( $Threshold != NULL )
+ {
+ foreach($Threshold as $Key => $Parameters)
+ {
+ if ( $Y <= $Parameters["MinX"] && $Y >= $Parameters["MaxX"])
+ {
+ if ( isset($Parameters["R"]) ) { $R = $Parameters["R"]; } else { $R = 0; }
+ if ( isset($Parameters["G"]) ) { $G = $Parameters["G"]; } else { $G = 0; }
+ if ( isset($Parameters["B"]) ) { $B = $Parameters["B"]; } else { $B = 0; }
+ if ( isset($Parameters["Alpha"]) ) { $Alpha = $Parameters["Alpha"]; } else { $Alpha = 100; }
+ $Color = $this->allocateColor($this->Picture,$R,$G,$B,$Alpha);
+ }
+ }
+ }
+
+ imageline($this->Picture,$LastX,$Y,$X,$Y,$Color);
+
+ if ( $Y == $DebugLine) { imageline($this->Picture,$LastX,$Y,$X,$Y,$DebugColor); }
+
+ $LastX = OUT_OF_SIGHT;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Draw the polygon border, if required */
+ if ( !$NoBorder)
+ {
+ foreach($Segments as $Key => $Coords)
+ $this->drawLine($Coords["X1"],$Coords["Y1"],$Coords["X2"],$Coords["Y2"],array("R"=>$BorderR,"G"=>$BorderG,"B"=>$BorderB,"Alpha"=>$BorderAlpha,"Threshold"=>$Threshold));
+ }
+
+ $this->Shadow = $RestoreShadow;
+ }
+
+ /* Return the abscissa margin */
+ function getAbscissaMargin($Data)
+ {
+ foreach($Data["Axis"] as $AxisID => $Values) { if ( $Values["Identity"] == AXIS_X ) { return($Values["Margin"]); } }
+ return(0);
+ }
+
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/graph/pImage.php b/system/library/games/graph/pImage.php
new file mode 100644
index 0000000..99b6cae
--- /dev/null
+++ b/system/library/games/graph/pImage.php
@@ -0,0 +1,461 @@
+TransparentBackground = $TransparentBackground;
+
+ if ( $DataSet != NULL ) { $this->DataSet = $DataSet; }
+
+ $this->XSize = $XSize;
+ $this->YSize = $YSize;
+ $this->Picture = imagecreatetruecolor($XSize,$YSize);
+
+ if ( $this->TransparentBackground )
+ {
+ imagealphablending($this->Picture,FALSE);
+ imagefilledrectangle($this->Picture, 0,0,$XSize, $YSize, imagecolorallocatealpha($this->Picture, 255, 255, 255, 127));
+ imagealphablending($this->Picture,TRUE);
+ imagesavealpha($this->Picture,true);
+ }
+ else
+ {
+ $C_White = $this->AllocateColor($this->Picture,255,255,255);
+ imagefilledrectangle($this->Picture,0,0,$XSize,$YSize,$C_White);
+ }
+ }
+
+ /* Enable / Disable and set shadow properties */
+ function setShadow($Enabled=TRUE,$Format="")
+ {
+ $X = isset($Format["X"]) ? $Format["X"] : 2;
+ $Y = isset($Format["Y"]) ? $Format["Y"] : 2;
+ $R = isset($Format["R"]) ? $Format["R"] : 0;
+ $G = isset($Format["G"]) ? $Format["G"] : 0;
+ $B = isset($Format["B"]) ? $Format["B"] : 0;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 10;
+
+ $this->Shadow = $Enabled;
+ $this->ShadowX = $X;
+ $this->ShadowY = $Y;
+ $this->ShadowR = $R;
+ $this->ShadowG = $G;
+ $this->ShadowB = $B;
+ $this->Shadowa = $Alpha;
+ }
+
+ /* Set the graph area position */
+ function setGraphArea($X1,$Y1,$X2,$Y2)
+ {
+ if ( $X2 < $X1 || $X1 == $X2 || $Y2 < $Y1 || $Y1 == $Y2 ) { return(-1); }
+
+ $this->GraphAreaX1 = $X1; $this->DataSet->Data["GraphArea"]["X1"] = $X1;
+ $this->GraphAreaY1 = $Y1; $this->DataSet->Data["GraphArea"]["Y1"] = $Y1;
+ $this->GraphAreaX2 = $X2; $this->DataSet->Data["GraphArea"]["X2"] = $X2;
+ $this->GraphAreaY2 = $Y2; $this->DataSet->Data["GraphArea"]["Y2"] = $Y2;
+ }
+
+ /* Return the width of the picture */
+ function getWidth()
+ { return($this->XSize); }
+
+ /* Return the heigth of the picture */
+ function getHeight()
+ { return($this->YSize); }
+
+ /* Render the picture to a file */
+ function render($FileName)
+ {
+ if ( $this->TransparentBackground ) { imagealphablending($this->Picture,false); imagesavealpha($this->Picture,true); }
+ imagepng($this->Picture,$FileName);
+ }
+
+ /* Render the picture to a web browser stream */
+ function stroke($BrowserExpire=FALSE)
+ {
+ if ( $this->TransparentBackground ) { imagealphablending($this->Picture,false); imagesavealpha($this->Picture,true); }
+
+ if ( $BrowserExpire )
+ {
+ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+ header("Cache-Control: no-cache");
+ header("Pragma: no-cache");
+ }
+
+ header('Content-type: image/png');
+ imagepng($this->Picture);
+ }
+
+ /* Automatic output method based on the calling interface */
+ function autoOutput($FileName="output.png")
+ {
+ if (php_sapi_name() == "cli")
+ $this->Render($FileName);
+ else
+ $this->Stroke();
+ }
+
+ /* Return the length between two points */
+ function getLength($X1,$Y1,$X2,$Y2)
+ { return(sqrt(pow(max($X1,$X2)-min($X1,$X2),2)+pow(max($Y1,$Y2)-min($Y1,$Y2),2))); }
+
+ /* Return the orientation of a line */
+ function getAngle($X1,$Y1,$X2,$Y2)
+ {
+ $Opposite = $Y2 - $Y1; $Adjacent = $X2 - $X1;$Angle = rad2deg(atan2($Opposite,$Adjacent));
+ if ($Angle > 0) { return($Angle); } else { return(360-abs($Angle)); }
+ }
+
+ /* Return the surrounding box of text area */
+ function getTextBox_deprecated($X,$Y,$FontName,$FontSize,$Angle,$Text)
+ {
+ $Size = imagettfbbox($FontSize,$Angle,$FontName,$Text);
+ $Width = $this->getLength($Size[0],$Size[1],$Size[2],$Size[3])+1;
+ $Height = $this->getLength($Size[2],$Size[3],$Size[4],$Size[5])+1;
+
+ $RealPos[0]["X"] = $X; $RealPos[0]["Y"] = $Y;
+ $RealPos[1]["X"] = cos((360-$Angle)*PI/180)*$Width + $RealPos[0]["X"]; $RealPos[1]["Y"] = sin((360-$Angle)*PI/180)*$Width + $RealPos[0]["Y"];
+ $RealPos[2]["X"] = cos((270-$Angle)*PI/180)*$Height + $RealPos[1]["X"]; $RealPos[2]["Y"] = sin((270-$Angle)*PI/180)*$Height + $RealPos[1]["Y"];
+ $RealPos[3]["X"] = cos((180-$Angle)*PI/180)*$Width + $RealPos[2]["X"]; $RealPos[3]["Y"] = sin((180-$Angle)*PI/180)*$Width + $RealPos[2]["Y"];
+
+ $RealPos[TEXT_ALIGN_BOTTOMLEFT]["X"] = $RealPos[0]["X"]; $RealPos[TEXT_ALIGN_BOTTOMLEFT]["Y"] = $RealPos[0]["Y"];
+ $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["X"] = $RealPos[1]["X"]; $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["Y"] = $RealPos[1]["Y"];
+
+ return($RealPos);
+ }
+
+ /* Return the surrounding box of text area */
+ function getTextBox($X,$Y,$FontName,$FontSize,$Angle,$Text)
+ {
+ $coords = imagettfbbox($FontSize, 0, $FontName, $Text);
+
+ $a = deg2rad($Angle); $ca = cos($a); $sa = sin($a); $RealPos = array();
+ for($i = 0; $i < 7; $i += 2)
+ {
+ $RealPos[$i/2]["X"] = $X + round($coords[$i] * $ca + $coords[$i+1] * $sa);
+ $RealPos[$i/2]["Y"] = $Y + round($coords[$i+1] * $ca - $coords[$i] * $sa);
+ }
+
+ $RealPos[TEXT_ALIGN_BOTTOMLEFT]["X"] = $RealPos[0]["X"]; $RealPos[TEXT_ALIGN_BOTTOMLEFT]["Y"] = $RealPos[0]["Y"];
+ $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["X"] = $RealPos[1]["X"]; $RealPos[TEXT_ALIGN_BOTTOMRIGHT]["Y"] = $RealPos[1]["Y"];
+ $RealPos[TEXT_ALIGN_TOPLEFT]["X"] = $RealPos[3]["X"]; $RealPos[TEXT_ALIGN_TOPLEFT]["Y"] = $RealPos[3]["Y"];
+ $RealPos[TEXT_ALIGN_TOPRIGHT]["X"] = $RealPos[2]["X"]; $RealPos[TEXT_ALIGN_TOPRIGHT]["Y"] = $RealPos[2]["Y"];
+ $RealPos[TEXT_ALIGN_BOTTOMMIDDLE]["X"] = ($RealPos[1]["X"]-$RealPos[0]["X"])/2+$RealPos[0]["X"]; $RealPos[TEXT_ALIGN_BOTTOMMIDDLE]["Y"] = ($RealPos[0]["Y"]-$RealPos[1]["Y"])/2+$RealPos[1]["Y"];
+ $RealPos[TEXT_ALIGN_TOPMIDDLE]["X"] = ($RealPos[2]["X"]-$RealPos[3]["X"])/2+$RealPos[3]["X"]; $RealPos[TEXT_ALIGN_TOPMIDDLE]["Y"] = ($RealPos[3]["Y"]-$RealPos[2]["Y"])/2+$RealPos[2]["Y"];
+ $RealPos[TEXT_ALIGN_MIDDLELEFT]["X"] = ($RealPos[0]["X"]-$RealPos[3]["X"])/2+$RealPos[3]["X"]; $RealPos[TEXT_ALIGN_MIDDLELEFT]["Y"] = ($RealPos[0]["Y"]-$RealPos[3]["Y"])/2+$RealPos[3]["Y"];
+ $RealPos[TEXT_ALIGN_MIDDLERIGHT]["X"] = ($RealPos[1]["X"]-$RealPos[2]["X"])/2+$RealPos[2]["X"]; $RealPos[TEXT_ALIGN_MIDDLERIGHT]["Y"] = ($RealPos[1]["Y"]-$RealPos[2]["Y"])/2+$RealPos[2]["Y"];
+ $RealPos[TEXT_ALIGN_MIDDLEMIDDLE]["X"] = ($RealPos[1]["X"]-$RealPos[3]["X"])/2+$RealPos[3]["X"]; $RealPos[TEXT_ALIGN_MIDDLEMIDDLE]["Y"] = ($RealPos[0]["Y"]-$RealPos[2]["Y"])/2+$RealPos[2]["Y"];
+
+ return($RealPos);
+ }
+
+ /* Set current font properties */
+ function setFontProperties($Format="")
+ {
+ $R = isset($Format["R"]) ? $Format["R"] : -1;
+ $G = isset($Format["G"]) ? $Format["G"] : -1;
+ $B = isset($Format["B"]) ? $Format["B"] : -1;
+ $Alpha = isset($Format["Alpha"]) ? $Format["Alpha"] : 100;
+ $FontName = isset($Format["FontName"]) ? $Format["FontName"] : NULL;
+ $FontSize = isset($Format["FontSize"]) ? $Format["FontSize"] : NULL;
+
+ if ( $R != -1) { $this->FontColorR = $R; }
+ if ( $G != -1) { $this->FontColorG = $G; }
+ if ( $B != -1) { $this->FontColorB = $B; }
+ if ( $Alpha != NULL) { $this->FontColorA = $Alpha; }
+
+ if ( $FontName != NULL )
+ $this->FontName = $FontName;
+
+ if ( $FontSize != NULL )
+ $this->FontSize = $FontSize;
+ }
+
+ /* Returns the 1st decimal values (used to correct AA bugs) */
+ function getFirstDecimal($Value)
+ {
+ $Values = preg_split("/\./",$Value);
+ if ( isset($Values[1]) ) { return(substr($Values[1],0,1)); } else { return(0); }
+ }
+
+ /* Attach a dataset to your pChart Object */
+ function setDataSet(&$DataSet)
+ { $this->DataSet = $DataSet; }
+
+ /* Print attached dataset contents to STDOUT */
+ function printDataSet()
+ { print_r($this->DataSet); }
+
+ /* Initialise the image map methods */
+ function initialiseImageMap($Name="pChart",$StorageMode=IMAGE_MAP_STORAGE_SESSION,$UniqueID="imageMap",$StorageFolder="tmp")
+ {
+ $this->ImageMapIndex = $Name;
+ $this->ImageMapStorageMode = $StorageMode;
+
+ if ($StorageMode == IMAGE_MAP_STORAGE_SESSION)
+ {
+ if(!isset($_SESSION)) { session_start(); }
+ $_SESSION[$this->ImageMapIndex] = NULL;
+ }
+ elseif($StorageMode == IMAGE_MAP_STORAGE_FILE)
+ {
+ $this->ImageMapFileName = $UniqueID;
+ $this->ImageMapStorageFolder = $StorageFolder;
+
+ if (file_exists($StorageFolder."/".$UniqueID.".map")) { unlink($StorageFolder."/".$UniqueID.".map"); }
+ }
+ }
+
+ /* Add a zone to the image map */
+ function addToImageMap($Type,$Plots,$Color=NULL,$Title=NULL,$Message=NULL,$HTMLEncode=FALSE)
+ {
+ if ( $this->ImageMapStorageMode == NULL ) { $this->initialiseImageMap(); }
+
+ /* Encode the characters in the imagemap in HTML standards */
+ $Title = str_replace("€","\u20AC",$Title);
+ $Title = htmlentities($Title,ENT_QUOTES,"ISO-8859-15");
+ if ( $HTMLEncode )
+ {
+ $Message = htmlentities($Message,ENT_QUOTES,"ISO-8859-15");
+ $Message = str_replace("<","<",$Message);
+ $Message = str_replace(">",">",$Message);
+ }
+
+ if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION )
+ {
+ if(!isset($_SESSION)) { $this->initialiseImageMap(); }
+ $_SESSION[$this->ImageMapIndex][] = array($Type,$Plots,$Color,$Title,$Message);
+ }
+ elseif($this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE)
+ {
+ $Handle = fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", 'a');
+ fwrite($Handle, $Type.IMAGE_MAP_DELIMITER.$Plots.IMAGE_MAP_DELIMITER.$Color.IMAGE_MAP_DELIMITER.$Title.IMAGE_MAP_DELIMITER.$Message."\r\n");
+ fclose($Handle);
+ }
+ }
+
+ /* Remove VOID values from an imagemap custom values array */
+ function removeVOIDFromArray($SerieName, $Values)
+ {
+ if ( !isset($this->DataSet->Data["Series"][$SerieName]) ) { return(-1); }
+
+ $Result = "";
+ foreach($this->DataSet->Data["Series"][$SerieName]["Data"] as $Key => $Value)
+ { if ( $Value != VOID && isset($Values[$Key]) ) { $Result[] = $Values[$Key]; } }
+ return($Result);
+ }
+
+ /* Replace the title of one image map serie */
+ function replaceImageMapTitle($OldTitle, $NewTitle)
+ {
+ if ( $this->ImageMapStorageMode == NULL ) { return(-1); }
+
+ if ( is_array($NewTitle) ) { $NewTitle = $this->removeVOIDFromArray($OldTitle, $NewTitle); }
+
+ if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION )
+ {
+ if(!isset($_SESSION)) { return(-1); }
+ if ( is_array($NewTitle) )
+ { $ID = 0; foreach($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { if ( $Settings[3] == $OldTitle && isset($NewTitle[$ID])) { $_SESSION[$this->ImageMapIndex][$Key][3] = $NewTitle[$ID]; $ID++; } } }
+ else
+ { foreach($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { if ( $Settings[3] == $OldTitle ) { $_SESSION[$this->ImageMapIndex][$Key][3] = $NewTitle; } } }
+ }
+ elseif( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE )
+ {
+ $TempArray = "";
+ $Handle = @fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", "r");
+ if ($Handle)
+ {
+ while (($Buffer = fgets($Handle, 4096)) !== false)
+ {
+ $Fields = preg_split("/".IMAGE_MAP_DELIMITER."/",str_replace(array(chr(10),chr(13)),"",$Buffer));
+ $TempArray[] = array($Fields[0],$Fields[1],$Fields[2],$Fields[3],$Fields[4]);
+ }
+ fclose($Handle);
+
+ if ( is_array($NewTitle) )
+ { $ID = 0; foreach($TempArray as $Key => $Settings) { if ( $Settings[3] == $OldTitle && isset($NewTitle[$ID]) ) { $TempArray[$Key][3] = $NewTitle[$ID]; $ID++; } } }
+ else
+ { foreach($TempArray as $Key => $Settings) { if ( $Settings[3] == $OldTitle ) { $TempArray[$Key][3] = $NewTitle; } } }
+
+ $Handle = fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", 'w');
+ foreach($TempArray as $Key => $Settings)
+ { fwrite($Handle, $Settings[0].IMAGE_MAP_DELIMITER.$Settings[1].IMAGE_MAP_DELIMITER.$Settings[2].IMAGE_MAP_DELIMITER.$Settings[3].IMAGE_MAP_DELIMITER.$Settings[4]."\r\n"); }
+ fclose($Handle);
+ }
+ }
+ }
+
+ /* Replace the values of the image map contents */
+ function replaceImageMapValues($Title, $Values)
+ {
+ if ( $this->ImageMapStorageMode == NULL ) { return(-1); }
+
+ $Values = $this->removeVOIDFromArray($Title, $Values);
+ $ID = 0;
+ if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION )
+ {
+ if(!isset($_SESSION)) { return(-1); }
+ foreach($_SESSION[$this->ImageMapIndex] as $Key => $Settings) { if ( $Settings[3] == $Title ) { if ( isset($Values[$ID]) ) { $_SESSION[$this->ImageMapIndex][$Key][4] = $Values[$ID]; } $ID++; } }
+ }
+ elseif( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE )
+ {
+ $TempArray = "";
+ $Handle = @fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", "r");
+ if ($Handle)
+ {
+ while (($Buffer = fgets($Handle, 4096)) !== false)
+ {
+ $Fields = preg_split("/".IMAGE_MAP_DELIMITER."/",str_replace(array(chr(10),chr(13)),"",$Buffer));
+ $TempArray[] = array($Fields[0],$Fields[1],$Fields[2],$Fields[3],$Fields[4]);
+ }
+ fclose($Handle);
+
+ foreach($TempArray as $Key => $Settings) { if ( $Settings[3] == $Title ) { if ( isset($Values[$ID]) ) { $TempArray[$Key][4] = $Values[$ID]; } $ID++; } }
+
+ $Handle = fopen($this->ImageMapStorageFolder."/".$this->ImageMapFileName.".map", 'w');
+ foreach($TempArray as $Key => $Settings)
+ { fwrite($Handle, $Settings[0].IMAGE_MAP_DELIMITER.$Settings[1].IMAGE_MAP_DELIMITER.$Settings[2].IMAGE_MAP_DELIMITER.$Settings[3].IMAGE_MAP_DELIMITER.$Settings[4]."\r\n"); }
+ fclose($Handle);
+ }
+ }
+ }
+
+ /* Dump the image map */
+ function dumpImageMap($Name="pChart",$StorageMode=IMAGE_MAP_STORAGE_SESSION,$UniqueID="imageMap",$StorageFolder="tmp")
+ {
+ $this->ImageMapIndex = $Name;
+ $this->ImageMapStorageMode = $StorageMode;
+
+ if ( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_SESSION )
+ {
+ if(!isset($_SESSION)) { session_start(); }
+ if ( $_SESSION[$Name] != NULL )
+ {
+ foreach($_SESSION[$Name] as $Key => $Params)
+ { echo $Params[0].IMAGE_MAP_DELIMITER.$Params[1].IMAGE_MAP_DELIMITER.$Params[2].IMAGE_MAP_DELIMITER.$Params[3].IMAGE_MAP_DELIMITER.$Params[4]."\r\n"; }
+ }
+ }
+ elseif( $this->ImageMapStorageMode == IMAGE_MAP_STORAGE_FILE )
+ {
+ if (file_exists($StorageFolder."/".$UniqueID.".map"))
+ {
+ $Handle = @fopen($StorageFolder."/".$UniqueID.".map", "r");
+ if ($Handle) { while (($Buffer = fgets($Handle, 4096)) !== false) { echo $Buffer; } }
+ fclose($Handle);
+
+ if ( $this->ImageMapAutoDelete ) { unlink($StorageFolder."/".$UniqueID.".map"); }
+ }
+ }
+
+ /* When the image map is returned to the client, the script ends */
+ exit();
+ }
+
+ /* Return the HTML converted color from the RGB composite values */
+ function toHTMLColor($R,$G,$B)
+ {
+ $R=intval($R); $G=intval($G); $B=intval($B);
+ $R=dechex($R<0?0:($R>255?255:$R)); $G=dechex($G<0?0:($G>255?255:$G));$B=dechex($B<0?0:($B>255?255:$B));
+ $Color="#".(strlen($R) < 2?'0':'').$R; $Color.=(strlen($G) < 2?'0':'').$G; $Color.= (strlen($B) < 2?'0':'').$B;
+ return($Color);
+ }
+
+ /* Reverse an array of points */
+ function reversePlots($Plots)
+ {
+ $Result = "";
+ for($i=count($Plots)-2;$i>=0;$i=$i-2) { $Result[] = $Plots[$i]; $Result[] = $Plots[$i+1]; }
+ return($Result);
+ }
+
+ /* Mirror Effect */
+ function drawAreaMirror($X,$Y,$Width,$Height,$Format="")
+ {
+ $StartAlpha = isset($Format["StartAlpha"]) ? $Format["StartAlpha"] : 80;
+ $EndAlpha = isset($Format["EndAlpha"]) ? $Format["EndAlpha"] : 0;
+
+ $AlphaStep = ($StartAlpha-$EndAlpha)/$Height;
+
+ $Picture = imagecreatetruecolor($this->XSize,$this->YSize);
+ imagecopy($Picture,$this->Picture,0,0,0,0,$this->XSize,$this->YSize);
+
+ for($i=1;$i<=$Height;$i++)
+ {
+ if ( $Y+($i-1) < $this->YSize && $Y-$i > 0 ) { imagecopymerge($Picture,$this->Picture,$X,$Y+($i-1),$X,$Y-$i,$Width,1,$StartAlpha-$AlphaStep*$i); }
+ }
+
+ imagecopy($this->Picture,$Picture,0,0,0,0,$this->XSize,$this->YSize);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/mc/action.php b/system/library/games/mc/action.php
new file mode 100644
index 0000000..003c3ac
--- /dev/null
+++ b/system/library/games/mc/action.php
@@ -0,0 +1,110 @@
+query('SELECT `uid`, `unit`, `tarif`, `game`, `address`, `slots_start`, `name`, `ram`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe;');
+
+ // Временный файл
+ $temp = sys::temp(action::config($ip, $port, $server['slots_start'], $ssh->get('cat '.$tarif['install'].'/'.$server['uid'].'/server.properties')));
+
+ // Обновление файла server.cfg
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/server.properties', 0644);
+
+ unlink($temp);
+
+ // Параметры запуска
+ $bash = 'java -Xmx'.$server['ram'].'M -Xms'.$server['ram'].'M -jar start.jar nogui';
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd '.$tarif['install'].$server['uid'].';' // переход в директорию игрового сервера
+ .'sudo -u server'.$server['uid'].' mkdir -p oldstart;' // Создание папки логов
+ .'cat console.log >> oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm console.log; rm oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 server.properties start.sh;' // Обновление владельца файлов
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c "./start.sh > console.log"'); // Запуск игровго сервера
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="'.$type.'", `online`="0", `players`="", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function config($ip, $port, $slots, $config)
+ {
+ $aLine = explode("\n", $config);
+
+ $search = array(
+ "#^server-ip=(.*?)$#is",
+ "#^server-port=(.*?)$#is",
+ "#^rcon\.port=(.*?)$#is",
+ "#^query\.port=(.*?)$#is",
+ "#^max-players=(.*?)$#is",
+ "#^enable-query=(.*?)$#is",
+ "#^debug=(.*?)$#is"
+ );
+
+ $config = '';
+
+ foreach($aLine as $line)
+ {
+ if(str_replace(array(' ', "\t"), '', $line) != '')
+ $edit = trim(preg_replace($search, array('','','','','','',''), $line));
+
+ if($edit != '')
+ $config .= $edit.PHP_EOL;
+
+ $edit = '';
+ }
+
+ $config .= 'server-ip='.$ip.PHP_EOL
+ .'server-port='.$port.PHP_EOL
+ .'rcon.port='.($port+10000).PHP_EOL
+ .'query.port='.($port).PHP_EOL
+ .'max-players='.$slots.PHP_EOL
+ .'enable-query=true'.PHP_EOL
+ .'debug=false';
+
+ return $config;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/mc/scan.php b/system/library/games/mc/scan.php
new file mode 100644
index 0000000..ed18502
--- /dev/null
+++ b/system/library/games/mc/scan.php
@@ -0,0 +1,113 @@
+query('SELECT `address`, `game`, `name`, `map`, `online`, `players`, `status`, `time`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ include(LIB.'games/query/McQuery.php');
+
+ $sq = new McQuery($ip, $port);
+
+ if($players_get)
+ $nmch = 'server_scan_mon_pl_'.$id;
+ else
+ $nmch = 'server_scan_mon_'.$id;
+
+ if(is_array($mcache->get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $out['time'] = 'Арендован до: '.date('d.m.Y - H:i', $server['time']);
+
+ if($server['status'] == 'overdue')
+ $out['time_end'] = 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400);
+ else
+ $out['time_end'] = 'Осталось: '.sys::date('min', $server['time']);
+
+ $info = $sq->getInfo($players_get);
+
+ if(!is_array($info))
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status'], $server['game']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ $players = scan::players($info['players_list']);
+
+ $info['map'] = htmlspecialchars(mb_convert_encoding($info['map'], 'UTF-8', 'WINDOWS-1251'));
+ $out['name'] = htmlspecialchars(mb_convert_encoding($info['hostname'], 'UTF-8', 'WINDOWS-1251'));
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = sys::int($info['online']);
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working', $server['game']);
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', $player['name']);
+ $html->set('ping', $player['ping']);
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function players($aPlayrs)
+ {
+ $i = 1;
+ $aData = array();
+
+ foreach($aPlayrs as $n => $player)
+ {
+ $aData[$i]['i'] = $i;
+ $aData[$i]['name'] = $player['nickname'] == '' ? 'Подключается' : htmlspecialchars($player['nickname']);
+
+ $i+=1;
+ }
+
+ return $aData;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/mc/service.php b/system/library/games/mc/service.php
new file mode 100644
index 0000000..38745a8
--- /dev/null
+++ b/system/library/games/mc/service.php
@@ -0,0 +1,369 @@
+query('SELECT `address`, `test` FROM `units` WHERE `id`="'.$aData['unit'].'" AND `mc`="1" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Локация не найдена.'));
+
+ $unit = $sql->get();
+
+ // Проверка тарифа
+ $sql->query('SELECT `id` FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" AND `unit`="'.$aData['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Тариф не найден.'));
+
+ $sql->query('SELECT '
+ .'`slots_min`,'
+ .'`slots_max`,'
+ .'`port_min`,'
+ .'`port_max`,'
+ .'`hostname`,'
+ .'`packs`,'
+ .'`ram`,'
+ .'`param_fix`,'
+ .'`time`,'
+ .'`test`,'
+ .'`tests`,'
+ .'`discount`,'
+ .'`map`,'
+ .'`ftp`,'
+ .'`plugins`,'
+ .'`console`,'
+ .'`stats`,'
+ .'`copy`,'
+ .'`web`,'
+ .'`plugins_install`,'
+ .'`hdd`,'
+ .'`autostop`,'
+ .'`core_fix`,'
+ .'`ip`,'
+ .'`price`'
+ .' FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ $tarif = $sql->get();
+
+ // Проверка сборки
+ if(!array_key_exists($aData['pack'], sys::b64djs($tarif['packs'], true)))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ // Проверка RAM
+ if(!in_array($aData['ram'], explode(':', $tarif['ram'])))
+ sys::outjs(array('e' => 'Переданные данные ram неверны.'));
+
+ $test = 0;
+
+ // Проверка периода на тест
+ if($aData['test'])
+ {
+ if(!$tarif['test'] || !$unit['test'])
+ sys::outjs(array('e' => 'Тестовый период недоступен.'));
+
+
+ // Проверка на повторный запрос
+ $sql->query('SELECT `id`, `game` FROM `tests` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $test_info = $sql->get();
+
+ if(!$cfg['tests']['game'] || $test_info['game'] == 'mc')
+ sys::outjs(array('e' => 'Тестовый период предоставляется один раз.'));
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" AND `test`="1" LIMIT 1');
+ if($sql->num() AND !$cfg['tests']['sametime'])
+ sys::outjs(array('e' => 'Чтобы получить тестовый период другой игры, дождитесь окончания текущего.'));
+ }
+
+ // Проверка наличия мест на локации
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$unit['test']);
+ if($sql->num() == $unit['test'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода нет.'));
+
+ // Проверка наличия мест для выбранного тарифа
+ $sql->query('SELECT `id` FROM `servers` WHERE `tarif`="'.$aData['tarif'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$tarif['tests']);
+ if($sql->num() == $tarif['tests'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода выбранного тарифа нет.'));
+
+ $test = 1;
+ }else
+ // Проверка периода
+ if(!$cfg['settlement_period'] AND !in_array($aData['time'], explode(':', $tarif['time'])))
+ sys::outjs(array('e' => 'Переданные данные периода неверны.'));
+
+ // Проверка слот
+ if($aData['slots'] < $tarif['slots_min'] || $aData['slots'] > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданные данные слот неверны.'));
+
+ // Определение цены
+ $aPrice = explode(':', $tarif['price']);
+ $price = $aPrice[array_search($aData['ram'], explode(':', $tarif['ram']))];
+
+ // Определение суммы
+ if($cfg['settlement_period'])
+ {
+ // Цена аренды за расчетный период
+ $sum = games::define_sum($tarif['discount'], $price, $aData['slots'], $start_point);
+
+ $aData['time'] = games::define_period('buy', params::$aDayMonth);
+ }else
+ $sum = games::define_sum($tarif['discount'], $price, $aData['slots'], $aData['time']);
+
+ // Проверка промо-кода
+ $promo = games::define_promo(
+ $aData['promo'],
+ array(
+ 'tarif' => $aData['tarif'],
+ 'ram' => $aData['ram'],
+ 'slots' => $aData['slots'],
+ 'time' => $aData['time'],
+ 'user' => $user['id']
+ ),
+ $tarif['discount'],
+ $sum
+ );
+
+ $days = $aData['time']; // Кол-во дней аренды
+
+ // Использование промо-кода
+ if(is_array($promo))
+ {
+ if(array_key_exists('sum', $promo))
+ $sum = $promo['sum'];
+ else
+ $days += $promo['days']; // Кол-во дней аренды с учетом подарочных (промо-код)
+ }
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']));
+
+ // Выделенный адрес игрового сервера
+ if(!empty($tarif['ip']))
+ {
+ $aIp = explode(':', $tarif['ip']);
+
+ $ip = false;
+ $port = params::$aDefPort['mc'];
+
+ // Проверка наличия свободного адреса
+ foreach($aIp as $adr)
+ {
+ $adr = trim($adr);
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `address` LIKE "'.$adr.':%" LIMIT 1');
+ if(!$sql->num())
+ {
+ $ip = $adr;
+
+ break;
+ }
+ }
+ }else{
+ $ip = sys::first(explode(':', $unit['address']));
+ $port = false;
+
+ // Проверка наличия свободного порта
+ for($tarif['port_min']; $tarif['port_min'] <= $tarif['port_max']; $tarif['port_min']+=1)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND (`address`="'.$ip.':'.$tarif['port_min'].'" OR `port`="'.$tarif['port_min'].'") LIMIT 1');
+ if(!$sql->num())
+ {
+ $port = $tarif['port_min'];
+
+ break;
+ }
+ }
+ }
+
+ if(!$ip || !$port)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+
+ if($test)
+ $aData['time'] = games::time($start_point, $tarif['test']);
+ else
+ $aData['time'] = games::time($start_point, $days);
+
+ $fix_one = 0;
+ $core = 0;
+
+ if($tarif['core_fix'] != '')
+ {
+ $aCore = explode(',', $tarif['core_fix']);
+
+ foreach($aCore as $cpu)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `tarif`="'.$aData['tarif'].'" AND `core_fix`="'.$cpu.'" AND `core_fix_one`="1" LIMIT 1');
+
+ if($sql->num())
+ continue;
+
+ $fix_one = 1;
+ $core = $cpu;
+
+ break;
+ }
+
+ if(!$core)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+ }
+
+ $ram = $tarif['param_fix'] ? $aData['ram'] : $aData['ram']*$aData['slots'];
+
+ // Массив данных
+ $aSDATA = array(
+ 'unit' => $aData['unit'], // идентификатор локации
+ 'tarif' => $aData['tarif'], // идентификатор тарифа
+ 'ram' => $ram, // значение ram
+ 'param_fix' => $tarif['param_fix'], // фиксированные параметры
+ 'pack' => $aData['pack'], // Выбранная сборка для установки
+ 'time' => $aData['time'], // Время аренды
+ 'days' => $days, // Число дней
+ 'sum' => $sum, // Сумма списания
+ 'test' => $test, // тестовый период
+ 'address' => $ip.':'.$port, // адрес игрового сервера
+ 'port' => $port, // порт игрового сервера
+ 'slots' => $aData['slots'], // Кол-во слот
+ 'map' => $tarif['map'], // Фиксированное значение слот
+ 'autostop' => $tarif['autostop'], // Выключение при 0 онлайне
+ 'ftp' => $tarif['ftp'], // Использование ftp
+ 'plugins' => $tarif['plugins'], // Использование плагинов
+ 'console' => $tarif['console'], // Использование консоли
+ 'stats' => $tarif['stats'], // Использование графиков (ведение статистики)
+ 'copy' => $tarif['copy'], // Использование резервных копий
+ 'web' => $tarif['web'], // Использование доп услуг
+ 'plugins_install' => $tarif['plugins_install'], // Список установленных плагинов
+ 'hdd' => $tarif['hdd'], // Дисковое пространство
+ 'core_fix' => $core, // Выделенный поток
+ 'core_fix_one' => $fix_one, // Выделенный поток
+ 'promo' => $promo // Использование промо-кода
+ );
+
+ return $aSDATA;
+ }
+
+ public static function install($aSDATA = array())
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ // Массив данных локации (адрес,пароль)
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$aSDATA['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Массив данных тарифа (путь сборки,путь установки)
+ $sql->query('SELECT `path`, `install`, `hostname` FROM `tarifs` WHERE `id`="'.$aSDATA['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Получение идентификаторов игрового сервера
+ $sql->query('INSERT INTO `servers` set uid="1"');
+ $id = $sql->id();
+ $uid = $id+1000;
+
+ // Директория сборки
+ $path = $tarif['path'].$aSDATA['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$uid;
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -s /bin/false -d '.$install.' -g servers -u '.$uid.' server'.$uid.';' // Создание пользователя сервера на локации
+ .'chown server'.$uid.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u server'.$uid.' screen -dmS i_'.$uid.' sh -c "cp -r '.$path.'/. .;' // Копирование файлов сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame['mc'].'"');
+
+ // Запись данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `uid`="'.$uid.'",
+ `unit`="'.$aSDATA['unit'].'",
+ `tarif`="'.$aSDATA['tarif'].'",
+ `user`="'.$user['id'].'",
+ `address`="'.$aSDATA['address'].'",
+ `port`="'.$aSDATA['port'].'",
+ `game`="mc",
+ `slots`="'.$aSDATA['slots'].'",
+ `slots_start`="'.$aSDATA['slots'].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$aSDATA['pack'].'",
+ `plugins_use`="'.$aSDATA['plugins'].'",
+ `ftp_use`="'.$aSDATA['ftp'].'",
+ `console_use`="'.$aSDATA['console'].'",
+ `stats_use`="'.$aSDATA['stats'].'",
+ `copy_use`="'.$aSDATA['copy'].'",
+ `web_use`="'.$aSDATA['web'].'",
+ `ram`="'.$aSDATA['ram'].'",
+ `ram_fix`="'.$aSDATA['param_fix'].'",
+ `map_start`="'.$aSDATA['map'].'",
+ `vac`="1",
+ `hdd`="'.$aSDATA['hdd'].'",
+ `time`="'.$aSDATA['time'].'",
+ `date`="'.$start_point.'",
+ `test`="'.$aSDATA['test'].'",
+ `core_fix`="'.$aSDATA['core_fix'].'",
+ `core_fix_one`="'.$aSDATA['core_fix_one'].'",
+ `autostop`="'.$aSDATA['autostop'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($aSDATA['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64djs($aSDATA['plugins_install']);
+
+ if(isset($aPlugins[$aSDATA['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$aSDATA['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$aSDATA['sum']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Запись получения тестового периода
+ if($aSDATA['test'])
+ {
+ $sql->query('INSERT INTO `tests` set `server`="'.$id.'", `unit`="'.$aSDATA['unit'].'", `game`="mc", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_test'), array('id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="0"');
+ }else{
+ // Реф. система
+ games::part($user['id'], $aSDATA['sum']);
+
+ // Запись логов
+ if(!is_array($aSDATA['promo']))
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ else{
+ $sql->query('UPDATE `servers` set `benefit`="'.$aSDATA['time'].'" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('INSERT INTO `promo_use` set `promo`="'.$aSDATA['promo']['id'].'", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_promo'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'promo' => $aSDATA['promo']['cod'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ }
+ }
+
+ return $id;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/mc/tarif.php b/system/library/games/mc/tarif.php
new file mode 100644
index 0000000..923aa1f
--- /dev/null
+++ b/system/library/games/mc/tarif.php
@@ -0,0 +1,231 @@
+get('extend', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['ram'].' RAM');
+ $html->set('tarif', $tarif_name);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function extend_sp($server, $tarif, $sid)
+ {
+ global $cfg, $sql, $html, $start_point;
+
+ tarifs::extend_address($server['game'], $sid);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aRAM = explode(':', $tarif['ram']);
+
+ $ram = $server['ram_fix'] ? $server['ram'] : $server['ram']/$server['slots'];
+
+ $sum = $tarif['slots'] ? $aPrice[array_search($ram, $aRAM)] : $aPrice[array_search($ram, $aRAM)]*$server['slots'];
+
+ $html->get('extend_sp', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('date', $server['time'] > $start_point ? 'Сервер продлен до: '.date('d.m.Y', $server['time']) : 'Текущая дата: '.date('d.m.Y', $start_point));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['ram'].' RAM');
+ $html->set('tarif', $tarif['name']);
+ $html->set('sum', $sum);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function plan($server, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ $sql->query('SELECT `ram`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $tarif = $sql->get();
+
+ $options = 'Выберете тарифный план ';
+
+ $aPrice = explode(':', $tarif['price']);
+ $aRAM = explode(':', $tarif['ram']);
+
+ $ram = $server['ram_fix'] ? $server['ram'] : $server['ram']/$server['slots'];
+
+ // Если есть выбор
+ if(count($aRAM) > 1)
+ {
+ // Удалить при наличии ram сервера из выбора
+ if(in_array($ram, $aRAM))
+ unset($aRAM[array_search($ram, $aRAM)]);
+
+ foreach($aRAM as $index => $ram)
+ $options .= ''
+ .$ram.' RAM '
+ .'('.$aPrice[$index].' '.$cfg['currency'].'/слот | '
+ .($aPrice[$index]*$server['slots']).' '.$cfg['currency'].'/месяц)'
+ .' ';
+ }else
+ return NULL;
+
+ $html->get('plan', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('info', $server['ram'].' RAM');
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit($server, $unit_name, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ if(!$cfg['change_unit'][$server['game']])
+ return NULL;
+
+ $tarifs = $sql->query('SELECT `unit`, `ram` FROM `tarifs` WHERE `game`="'.$server['game'].'" AND `name`="'.$tarif_name.'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num($tarifs))
+ return NULL;
+
+ $units = 0;
+
+ $options = 'Выберете новую локацию ';
+
+ $ram = $server['ram_fix'] ? $server['ram'] : $server['ram']/$server['slots'];
+
+ while($tarif = $sql->get($tarifs))
+ {
+ if(!in_array($ram, explode(':', $tarif['ram'])))
+ continue;
+
+ $sql->query('SELECT `id`, `name` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $unit = $sql->get();
+
+ $options .= ''.$unit['name'].' ';
+
+ $units+=1;
+ }
+
+ if(!$units)
+ return NULL;
+
+ $html->get('unit', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', $server['ram'].' RAM');
+ $html->set('unit', $unit_name);
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit_new($tarif, $unit, $server, $mcache)
+ {
+ global $ssh, $sql, $user, $start_point;
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Директория сборки
+ $path = $tarif['path'].$tarif['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Пользователь сервера
+ $uS = 'server'.$server['uid'];
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -d '.$install.' -g servers -u '.$server['uid'].' '.$uS.';' // Создание пользователя сервера на локации
+ .'chown '.$uS.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u '.$uS.' screen -dmS i_'.$server['uid'].' cp -r '.$path.'/. .'); // Копирование файлов сборки для сервера
+
+ $address = explode(':', $server['address']);
+
+ $fix_one = $tarif['core_fix'] ? 1 : 0;
+
+ // Обновление данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `unit`="'.$tarif['unit'].'",
+ `tarif`="'.$tarif['id'].'",
+ `address`="'.$server['address'].'",
+ `port`="'.$address[1].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$tarif['pack'].'",
+ `map_start`="'.$tarif['map'].'",
+ `vac`="1",
+ `hdd`="'.$tarif['hdd'].'",
+ `time`="'.$tarif['time'].'",
+ `autostop`="'.$tarif['autostop'].'",
+ `core_fix`="'.$tarif['core_fix'].'",
+ `core_fix_one`="'.$fix_one.'",
+ `reinstall`="'.$start_point.'" WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($tarif['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64js($tarif['plugins_install']);
+
+ if(isset($aPlugins[$tarif['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$tarif['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$server['id'].'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/mta/action.php b/system/library/games/mta/action.php
new file mode 100644
index 0000000..6e2cc55
--- /dev/null
+++ b/system/library/games/mta/action.php
@@ -0,0 +1,157 @@
+query('SELECT `uid`, `unit`, `tarif`, `game`, `address`, `slots_start`, `name`, `time_start`, `core_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe;');
+
+ // Временный файл
+ $temp = sys::temp(action::config($ip, $port, $server['slots_start'], $ssh->get('cat '.$tarif['install'].'/'.$server['uid'].'/mods/deathmatch/mtaserver.conf')));
+
+ // Обновление файла server.cfg
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/mods/deathmatch/mtaserver.conf', 0644);
+
+ unlink($temp);
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] AND !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Параметры запуска
+ $bash = './mta-server';
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd '.$tarif['install'].$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'sudo -u server'.$server['uid'].' mkdir -p mods/deathmatch/oldstart;' // Создание папки логов
+ .'cat mods/deathmatch/logs/server.log >> mods/deathmatch/oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm mods/deathmatch/logs/server.log; rm mods/deathmatch/logs/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 mods/deathmatch/mtaserver.conf start.sh;' // Обновление владельца файлов
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c "./start.sh"'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function config($ip, $port, $slots, $config)
+ {
+ $aRepl = array(
+ '', ' ',
+ '', ' ',
+ '', ' ',
+ '', ' ',
+ '', ' ',
+ '', ' ',
+ '', ' '
+ );
+
+ $config = str_ireplace($aRepl, $aRepl, $config);
+ $config = str_ireplace(array('', ' ', "\r", "\t"),'', $config);
+ $config = preg_replace("#\<\!--(.+?)--\>#is", '', $config);
+
+ $aLine = explode("\n", $config);
+
+ $search = array(
+ "#\(.+?)\<\/serverip\>#is",
+ "#\(.+?)\<\/maxplayers\>#is",
+ "#\(.+?)\<\/httpserver\>#is",
+ "#\(.+?)\<\/serverport\>#is",
+ "#\(.+?)\<\/httpport\>#is",
+ "#\(.+?)\<\/logfile\>#is",
+ "#\(.+?)\<\/ase\>#is"
+ );
+
+ $config = ''.PHP_EOL;
+
+ foreach($aLine as $line)
+ {
+ if(str_replace(array(' ', "\t"), '', $line) != '')
+ $edit = trim(preg_replace($search, array('','','','','',''), $line));
+
+ if($edit != '')
+ $config .= ' '.$edit.PHP_EOL;
+
+ $edit = '';
+ }
+
+ $config .= ' '.$ip.' '.PHP_EOL
+ .' '.$slots.' '.PHP_EOL
+ .' 1 '.PHP_EOL
+ .' '.$port.' '.PHP_EOL
+ .' '.($port+10000).' '.PHP_EOL
+ .' 1 '.PHP_EOL
+ .' logs/server.log ';
+
+ return $config."\n".' ';
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/mta/scan.php b/system/library/games/mta/scan.php
new file mode 100644
index 0000000..7fe2d08
--- /dev/null
+++ b/system/library/games/mta/scan.php
@@ -0,0 +1,114 @@
+query('SELECT `address`, `game`, `name`, `map`, `online`, `players`, `status`, `time`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ include(LIB.'games/query/MtaQuery.php');
+
+ $sq = new MtaQuery($ip, $port);
+
+ if($players_get)
+ $nmch = 'server_scan_mon_pl_'.$id;
+ else
+ $nmch = 'server_scan_mon_'.$id;
+
+ if(is_array($mcache->get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $out['time'] = 'Арендован до: '.date('d.m.Y - H:i', $server['time']);
+
+ if($server['status'] == 'overdue')
+ $out['time_end'] = 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400);
+ else
+ $out['time_end'] = 'Осталось: '.sys::date('min', $server['time']);
+
+ $info = $sq->getInfo($players_get);
+
+ if(!is_array($info))
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status'], $server['game']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ $players = scan::players($info['players_list']);
+
+ $info['map'] = htmlspecialchars(mb_convert_encoding($info['map'], 'UTF-8', 'WINDOWS-1251'));
+ $out['name'] = htmlspecialchars(mb_convert_encoding($info['hostname'], 'UTF-8', 'WINDOWS-1251'));
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = sys::int($info['players']);
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working', $server['game']);
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', $player['name']);
+ $html->set('ping', $player['ping']);
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function players($aPlayrs)
+ {
+ $i = 1;
+ $aData = array();
+
+ foreach($aPlayrs as $n => $player)
+ {
+ $aData[$i]['i'] = $i;
+ $aData[$i]['name'] = $player['nickname'] == '' ? 'Подключается' : htmlspecialchars($player['nickname']);
+ $aData[$i]['ping'] = sys::int($player['ping']);
+
+ $i+=1;
+ }
+
+ return $aData;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/mta/service.php b/system/library/games/mta/service.php
new file mode 100644
index 0000000..74aa423
--- /dev/null
+++ b/system/library/games/mta/service.php
@@ -0,0 +1,354 @@
+query('SELECT `address`, `test` FROM `units` WHERE `id`="'.$aData['unit'].'" AND `mta`="1" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Локация не найдена.'));
+
+ $unit = $sql->get();
+
+ // Проверка тарифа
+ $sql->query('SELECT `id` FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" AND `unit`="'.$aData['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Тариф не найден.'));
+
+ $sql->query('SELECT '
+ .'`slots_min`,'
+ .'`slots_max`,'
+ .'`port_min`,'
+ .'`port_max`,'
+ .'`hostname`,'
+ .'`packs`,'
+ .'`time`,'
+ .'`test`,'
+ .'`tests`,'
+ .'`discount`,'
+ .'`ftp`,'
+ .'`plugins`,'
+ .'`console`,'
+ .'`stats`,'
+ .'`copy`,'
+ .'`web`,'
+ .'`plugins_install`,'
+ .'`hdd`,'
+ .'`autostop`,'
+ .'`core_fix`,'
+ .'`ip`,'
+ .'`price`'
+ .' FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ $tarif = $sql->get();
+
+ // Проверка сборки
+ if(!array_key_exists($aData['pack'], sys::b64djs($tarif['packs'], true)))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ $test = 0;
+
+ // Проверка периода на тест
+ if($aData['test'])
+ {
+ if(!$tarif['test'] || !$unit['test'])
+ sys::outjs(array('e' => 'Тестовый период недоступен.'));
+
+
+ // Проверка на повторный запрос
+ $sql->query('SELECT `id`, `game` FROM `tests` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $test_info = $sql->get();
+
+ if(!$cfg['tests']['game'] || $test_info['game'] == 'mta')
+ sys::outjs(array('e' => 'Тестовый период предоставляется один раз.'));
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" AND `test`="1" LIMIT 1');
+ if($sql->num() AND !$cfg['tests']['sametime'])
+ sys::outjs(array('e' => 'Чтобы получить тестовый период другой игры, дождитесь окончания текущего.'));
+ }
+
+ // Проверка наличия мест на локации
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$unit['test']);
+ if($sql->num() == $unit['test'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода нет.'));
+
+ // Проверка наличия мест для выбранного тарифа
+ $sql->query('SELECT `id` FROM `servers` WHERE `tarif`="'.$aData['tarif'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$tarif['tests']);
+ if($sql->num() == $tarif['tests'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода выбранного тарифа нет.'));
+
+ $test = 1;
+ }else
+ // Проверка периода
+ if(!$cfg['settlement_period'] AND !in_array($aData['time'], explode(':', $tarif['time'])))
+ sys::outjs(array('e' => 'Переданные данные периода неверны.'));
+
+ // Проверка слот
+ if($aData['slots'] < $tarif['slots_min'] || $aData['slots'] > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданные данные слот неверны.'));
+
+ // Определение суммы
+ if($cfg['settlement_period'])
+ {
+ // Цена аренды за расчетный период
+ $sum = games::define_sum($tarif['discount'], $tarif['price'], $aData['slots'], $start_point);
+
+ $aData['time'] = games::define_period('buy', params::$aDayMonth);
+ }else
+ $sum = games::define_sum($tarif['discount'], $tarif['price'], $aData['slots'], $aData['time']);
+
+ // Проверка промо-кода
+ $promo = games::define_promo(
+ $aData['promo'],
+ array(
+ 'tarif' => $aData['tarif'],
+ 'slots' => $aData['slots'],
+ 'time' => $aData['time'],
+ 'user' => $user['id']
+ ),
+ $tarif['discount'],
+ $sum
+ );
+
+ $days = $aData['time']; // Кол-во дней аренды
+
+ // Использование промо-кода
+ if(is_array($promo))
+ {
+ if(array_key_exists('sum', $promo))
+ $sum = $promo['sum'];
+ else
+ $days += $promo['days']; // Кол-во дней аренды с учетом подарочных (промо-код)
+ }
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']));
+
+ // Выделенный адрес игрового сервера
+ if(!empty($tarif['ip']))
+ {
+ $aIp = explode(':', $tarif['ip']);
+
+ $ip = false;
+ $port = params::$aDefPort['mta'];
+
+ // Проверка наличия свободного адреса
+ foreach($aIp as $adr)
+ {
+ $adr = trim($adr);
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `address` LIKE "'.$adr.':%" LIMIT 1');
+ if(!$sql->num())
+ {
+ $ip = $adr;
+
+ break;
+ }
+ }
+ }else{
+ $ip = sys::first(explode(':', $unit['address']));
+ $port = false;
+
+ // Проверка наличия свободного порта
+ for($tarif['port_min']; $tarif['port_min'] <= $tarif['port_max']; $tarif['port_min']+=1)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND (`address`="'.$ip.':'.$tarif['port_min'].'" OR `port`="'.$tarif['port_min'].'") LIMIT 1');
+ if(!$sql->num())
+ {
+ $port = $tarif['port_min'];
+
+ break;
+ }
+ }
+ }
+
+ if(!$ip || !$port)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+
+ if($test)
+ $aData['time'] = games::time($start_point, $tarif['test']);
+ else
+ $aData['time'] = games::time($start_point, $days);
+
+ $fix_one = 0;
+ $core = 0;
+
+ if($tarif['core_fix'] != '')
+ {
+ $aCore = explode(',', $tarif['core_fix']);
+
+ foreach($aCore as $cpu)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `tarif`="'.$aData['tarif'].'" AND `core_fix`="'.$cpu.'" AND `core_fix_one`="1" LIMIT 1');
+
+ if($sql->num())
+ continue;
+
+ $fix_one = 1;
+ $core = $cpu;
+
+ break;
+ }
+
+ if(!$core)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+ }
+
+ $ram = $tarif['param_fix'] ? $aData['ram'] : $cfg['ram']['mta']*$aSDATA['slots'];
+
+ // Массив данных
+ $aSDATA = array(
+ 'unit' => $aData['unit'], // идентификатор локации
+ 'tarif' => $aData['tarif'], // идентификатор тарифа
+ 'ram' => $ram, // значение ram
+ 'param_fix' => $tarif['param_fix'], // фиксированные параметры
+ 'pack' => $aData['pack'], // Выбранная сборка для установки
+ 'time' => $aData['time'], // Время аренды
+ 'days' => $days, // Число дней
+ 'sum' => $sum, // Сумма списания
+ 'test' => $test, // тестовый период
+ 'address' => $ip.':'.$port, // адрес игрового сервера
+ 'port' => $port, // порт игрового сервера
+ 'slots' => $aData['slots'], // Кол-во слот
+ 'autostop' => $tarif['autostop'], // Выключение при 0 онлайне
+ 'ftp' => $tarif['ftp'], // Использование ftp
+ 'plugins' => $tarif['plugins'], // Использование плагинов
+ 'console' => $tarif['console'], // Использование консоли
+ 'stats' => $tarif['stats'], // Использование графиков (ведение статистики)
+ 'copy' => $tarif['copy'], // Использование резервных копий
+ 'web' => $tarif['web'], // Использование доп услуг
+ 'plugins_install' => $tarif['plugins_install'], // Список установленных плагинов
+ 'hdd' => $tarif['hdd'], // Дисковое пространство
+ 'core_fix' => $core, // Выделенный поток
+ 'core_fix_one' => $fix_one, // Выделенный поток
+ 'promo' => $promo // Использование промо-кода
+ );
+
+ return $aSDATA;
+ }
+
+ public static function install($aSDATA = array())
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ // Массив данных локации (адрес,пароль)
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$aSDATA['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Массив данных тарифа (путь сборки,путь установки)
+ $sql->query('SELECT `path`, `install`, `hostname` FROM `tarifs` WHERE `id`="'.$aSDATA['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Получение идентификаторов игрового сервера
+ $sql->query('INSERT INTO `servers` set uid="1"');
+ $id = $sql->id();
+ $uid = $id+1000;
+
+ // Директория сборки
+ $path = $tarif['path'].$aSDATA['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$uid;
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -s /bin/false -d '.$install.' -g servers -u '.$uid.' server'.$uid.';' // Создание пользователя сервера на локации
+ .'chown server'.$uid.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u server'.$uid.' screen -dmS i_'.$uid.' sh -c "cp -r '.$path.'/. .;' // Копирование файлов сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 777 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['mta']].'"');
+
+ // Запись данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `uid`="'.$uid.'",
+ `unit`="'.$aSDATA['unit'].'",
+ `tarif`="'.$aSDATA['tarif'].'",
+ `user`="'.$user['id'].'",
+ `address`="'.$aSDATA['address'].'",
+ `port`="'.$aSDATA['port'].'",
+ `game`="mta",
+ `slots`="'.$aSDATA['slots'].'",
+ `slots_start`="'.$aSDATA['slots'].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$aSDATA['pack'].'",
+ `plugins_use`="'.$aSDATA['plugins'].'",
+ `ftp_use`="'.$aSDATA['ftp'].'",
+ `console_use`="'.$aSDATA['console'].'",
+ `stats_use`="'.$aSDATA['stats'].'",
+ `copy_use`="'.$aSDATA['copy'].'",
+ `web_use`="'.$aSDATA['web'].'",
+ `vac`="1",
+ `hdd`="'.$aSDATA['hdd'].'",
+ `time`="'.$aSDATA['time'].'",
+ `date`="'.$start_point.'",
+ `test`="'.$aSDATA['test'].'",
+ `ram`="'.$aSDATA['ram'].'",
+ `core_fix`="'.$aSDATA['core_fix'].'",
+ `core_fix_one`="'.$aSDATA['core_fix_one'].'",
+ `autostop`="'.$aSDATA['autostop'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($aSDATA['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64djs($aSDATA['plugins_install']);
+
+ if(isset($aPlugins[$aSDATA['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$aSDATA['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$aSDATA['sum']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Запись получения тестового периода
+ if($aSDATA['test'])
+ {
+ $sql->query('INSERT INTO `tests` set `server`="'.$id.'", `unit`="'.$aSDATA['unit'].'", `game`="mta", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_test'), array('id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="0"');
+ }else{
+ // Реф. система
+ games::part($user['id'], $aSDATA['sum']);
+
+ // Запись логов
+ if(!is_array($aSDATA['promo']))
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ else{
+ $sql->query('UPDATE `servers` set `benefit`="'.$aSDATA['time'].'" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('INSERT INTO `promo_use` set `promo`="'.$aSDATA['promo']['id'].'", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_promo'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'promo' => $aSDATA['promo']['cod'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ }
+ }
+
+ return $id;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/mta/tarif.php b/system/library/games/mta/tarif.php
new file mode 100644
index 0000000..a65da45
--- /dev/null
+++ b/system/library/games/mta/tarif.php
@@ -0,0 +1,179 @@
+get('extend', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', '');
+ $html->set('tarif', $tarif_name);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function extend_sp($server, $tarif, $sid)
+ {
+ global $cfg, $sql, $html, $start_point;
+
+ tarifs::extend_address($server['game'], $sid);
+
+ $sum = $tarif['slots'] ? $tarif['price'] : $tarif['price']*$server['slots'];
+
+ $html->get('extend_sp', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('date', $server['time'] > $start_point ? 'Сервер продлен до: '.date('d.m.Y', $server['time']) : 'Текущая дата: '.date('d.m.Y', $start_point));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', '');
+ $html->set('tarif', $tarif['name']);
+ $html->set('sum', $sum);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function plan()
+ {
+ return NULL;
+ }
+
+ public static function unit($server, $unit_name, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ if(!$cfg['change_unit'][$server['game']])
+ return NULL;
+
+ $tarifs = $sql->query('SELECT `unit` FROM `tarifs` WHERE `game`="'.$server['game'].'" AND `name`="'.$tarif_name.'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num($tarifs))
+ return NULL;
+
+ $units = 0;
+
+ $options = 'Выберете новую локацию ';
+
+ while($tarif = $sql->get($tarifs))
+ {
+ $sql->query('SELECT `id`, `name` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $unit = $sql->get();
+
+ $options .= ''.$unit['name'].' ';
+
+ $units+=1;
+ }
+
+ if(!$units)
+ return NULL;
+
+ $html->get('unit', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', '');
+ $html->set('unit', $unit_name);
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit_new($tarif, $unit, $server, $mcache)
+ {
+ global $ssh, $sql, $user, $start_point;
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Директория сборки
+ $path = $tarif['path'].$tarif['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Пользователь сервера
+ $uS = 'server'.$server['uid'];
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -d '.$install.' -g servers -u '.$server['uid'].' '.$uS.';' // Создание пользователя сервера на локации
+ .'chown '.$uS.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u '.$uS.' screen -dmS i_'.$server['uid'].' cp -r '.$path.'/. .'); // Копирование файлов сборки для сервера
+
+ $address = explode(':', $server['address']);
+
+ $fix_one = $tarif['core_fix'] ? 1 : 0;
+
+ // Обновление данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `unit`="'.$tarif['unit'].'",
+ `tarif`="'.$tarif['id'].'",
+ `address`="'.$server['address'].'",
+ `port`="'.$address[1].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$tarif['pack'].'",
+ `map_start`="'.$tarif['map'].'",
+ `hdd`="'.$tarif['hdd'].'",
+ `time`="'.$tarif['time'].'",
+ `autostop`="'.$tarif['autostop'].'",
+ `core_fix`="'.$tarif['core_fix'].'",
+ `core_fix_one`="'.$fix_one.'",
+ `reinstall`="'.$start_point.'" WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($tarif['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64js($tarif['plugins_install']);
+
+ if(isset($aPlugins[$tarif['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$tarif['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$server['id'].'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/plugins.php b/system/library/games/plugins.php
new file mode 100644
index 0000000..cbfe1e5
--- /dev/null
+++ b/system/library/games/plugins.php
@@ -0,0 +1,181 @@
+arr['images']))
+ unset($html->arr['images']);
+
+ $aImg = explode("\n", $images);
+
+ foreach($aImg as $img)
+ {
+ $html->get('plugin_images', 'sections/servers/games/plugins');
+
+ $html->set('id', $plugin);
+ $html->set('img', $img);
+
+ $html->pack('images');
+ }
+
+ return isset($html->arr['images']) ? $html->arr['images'] : '';
+ }
+
+ public static function status($status)
+ {
+ global $html;
+
+ if(!$status)
+ {
+ $html->unit('unstable');
+ $html->unit('stable', 1);
+ $html->unit('testing');
+ }elseif($status == 2){
+ $html->unit('unstable');
+ $html->unit('stable');
+ $html->unit('testing', 1);
+ }else{
+ $html->unit('unstable', 1);
+ $html->unit('stable');
+ $html->unit('testing');
+ }
+
+ return NULL;
+ }
+
+ public static function required($id, $required, $choice, $mcache)
+ {
+ global $sql;
+
+ if($required == '')
+ return NULL;
+
+ $aRequi = explode(':', $required);
+
+ foreach($aRequi as $pl)
+ {
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$pl.'" LIMIT 1');
+ if(!$sql->num())
+ {
+ $sql->query('SELECT `name` FROM `plugins` WHERE `id`="'.$pl.'" LIMIT 1');
+ $plRequi = $sql->get();
+
+ if($choice != '')
+ {
+ $aChoice = explode(' ', $choice);
+
+ foreach($aChoice as $plugins)
+ {
+ $aPlugins = explode(':', $plugins);
+
+ if(in_array($pl, $aPlugins))
+ {
+ $options = '';
+ foreach($aPlugins as $plugin)
+ {
+ $sql->query('SELECT `name`, `upd` FROM `plugins` WHERE `id`="'.$plugin.'" LIMIT 1');
+ $data = $sql->get();
+
+ if($data['upd'])
+ {
+ $sql->query('SELECT `name` FROM `plugins_update` WHERE `plugin`="'.$plugin.'" ORDER BY `id` DESC LIMIT 1');
+ $data = $sql->get();
+ }
+
+ $options .= ''.strip_tags($data['name']).' ';
+ }
+
+ if($options != '')
+ sys::outjs(array('e' => 'Для данного плагина требуется установка одного из родителя', 'required' => true, 'pid' => $pl, 'select' => $options), $mcache);
+ }
+ }
+ }
+
+ sys::outjs(array('e' => 'Для данного плагина требуется установка родителя', 'required' => true, 'pid' => $pl, 'pname' => htmlspecialchars_decode($plRequi['name'])), $mcache);
+ }
+ }
+
+ return NULL;
+ }
+
+ public static function incompatible($id, $incompatible, $mcache)
+ {
+ global $sql;
+
+ if($incompatible == '')
+ return NULL;
+
+ $aIncomp = explode(':', $incompatible);
+
+ foreach($aIncomp as $pl)
+ {
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$pl.'" LIMIT 1');
+ if($sql->num())
+ {
+ $sql->query('SELECT `name` FROM `plugins` WHERE `id`="'.$pl.'" LIMIT 1');
+ $plIncomp = $sql->get();
+
+ sys::outjs(array('e' => 'Данный плагин несовместим с уже установленным плагином', 'pid' => $pl, 'pname' => htmlspecialchars_decode($plIncomp['name'])), $mcache);
+ }
+ }
+
+ return NULL;
+ }
+
+ public static function clear($clear, $uid, $dir)
+ {
+ global $ssh;
+
+ // Если регулярное выражение
+ if(isset($clear['regex']) AND $clear['regex'])
+ {
+ $file = preg_replace($clear['text'], '', $ssh->get('sudo -u server'.$uid.' cat '.$dir.$clear['file']));
+
+ // Временный файл
+ $temp = sys::temp($file);
+
+ $ssh->setfile($temp, $dir.$clear['file'], 0644);
+
+ unlink($temp);
+
+ $query = 'chown server'.$uid.':servers '.$dir.$clear['file'].';';
+
+ }else
+ // Удаление текста из файла
+ $query = 'sudo -u server'.$uid.' sed -i '."'s/".str_replace('/', '\/', htmlspecialchars_decode($clear['text']))."//g'".' '.$dir.$clear['file'].';';
+
+ $ssh->set($query.'sudo -u server'.$uid.' sed -i '."'/./!d'".' '.$dir.$clear['file']);
+
+ return NULL;
+ }
+
+ public static function write($write, $uid, $dir)
+ {
+ global $ssh;
+
+ // Костыль (добавить пустую строку на всякий случай)
+ $query = 'sudo -u server'.$uid.' echo "" >> '.$dir.$write['file'].';';
+
+ // Исключить дублирование, путем удаления добавляемого текста
+ $query .= 'sudo -u server'.$uid.' sed -i '."'s/".str_replace('/', '\/', htmlspecialchars_decode($write['text']))."//g'".' '.$dir.$write['file'].';';
+
+ // Добавление текста в начало файла
+ if($write['top'])
+ $query .= 'sudo -u server'.$uid.' touch '.$dir.$write['file'].'; sudo -u server'.$uid.' sed -i '."'1i ".str_replace(array('/', "'", '\"'), array('\/', "\'", '"'), htmlspecialchars_decode($write['text']))."'".' '.$dir.$write['file'].';';
+ else
+ // Добавление текста в конец файла
+ $query .= 'sudo -u server'.$uid.' touch '.$dir.$write['file'].'; sudo -u server'.$uid.' echo "'.str_replace('"', '\"', htmlspecialchars_decode($write['text'])).'" >> '.$dir.$write['file'].';';
+
+ $ssh->set($query.'sudo -u server'.$uid.' sed -i '."'/./!d'".' '.$dir.$clear['file']);
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/query/BaseSocket.php b/system/library/games/query/BaseSocket.php
new file mode 100644
index 0000000..0e7cb0e
--- /dev/null
+++ b/system/library/games/query/BaseSocket.php
@@ -0,0 +1,98 @@
+Close();
+ }
+
+ abstract public function Close();
+ abstract public function Open($Address, $Port, $Timeout, $Engine);
+ abstract public function Write($Header, $String = '');
+ abstract public function Read($Length = 1400);
+
+ protected function ReadInternal($Buffer, $Length, $SherlockFunction)
+ {
+ if($Buffer->Remaining() === 0)
+ return false;
+
+ if($Buffer->Remaining() === 0)
+ return false;
+
+ $Header = $Buffer->GetLong();
+
+ if($Header === -2)
+ {
+ $Packets = [];
+ $IsCompressed = false;
+ $ReadMore = false;
+
+ do
+ {
+ $RequestID = $Buffer->GetLong();
+
+ switch($this->Engine)
+ {
+ case SourceQuery::GOLDSOURCE:
+ {
+ $PacketCountAndNumber = $Buffer->GetByte();
+ $PacketCount = $PacketCountAndNumber & 0xF;
+ $PacketNumber = $PacketCountAndNumber >> 4;
+
+ break;
+ }
+
+ case SourceQuery::SOURCE:
+ {
+ $IsCompressed = ($RequestID & 0x80000000) !== 0;
+ $PacketCount = $Buffer->GetByte();
+ $PacketNumber = $Buffer->GetByte() + 1;
+
+ if($IsCompressed)
+ {
+ $Buffer->GetLong();
+
+ $PacketChecksum = $Buffer->GetUnsignedLong();
+ }else
+ $Buffer->GetShort();
+
+ break;
+ }
+ }
+
+ $Packets[$PacketNumber] = $Buffer->Get();
+
+ $ReadMore = $PacketCount > sizeof($Packets);
+ }
+
+ while($ReadMore && $SherlockFunction($Buffer, $Length));
+
+ $Data = Implode($Packets);
+
+ if($IsCompressed)
+ {
+ if(!Function_Exists('bzdecompress'))
+ return false;
+
+ $Data = bzdecompress($Data);
+
+ if(CRC32($Data) !== $PacketChecksum)
+ return false;
+ }
+
+ $Buffer->Set(SubStr($Data, 4));
+ }else
+ return false;
+
+ return $Buffer;
+ }
+ }
\ No newline at end of file
diff --git a/system/library/games/query/Buffer.php b/system/library/games/query/Buffer.php
new file mode 100644
index 0000000..ae2e9d5
--- /dev/null
+++ b/system/library/games/query/Buffer.php
@@ -0,0 +1,102 @@
+Buffer = $Buffer;
+ $this->Length = StrLen($Buffer);
+ $this->Position = 0;
+ }
+
+ public function Remaining()
+ {
+ return $this->Length - $this->Position;
+ }
+
+ public function Get($Length = -1)
+ {
+ if($Length === 0)
+ return '';
+
+ $Remaining = $this->Remaining();
+
+ if($Length === -1)
+ $Length = $Remaining;
+
+ else if($Length > $Remaining)
+ return '';
+
+ $Data = SubStr($this->Buffer, $this->Position, $Length);
+
+ $this->Position += $Length;
+
+ return $Data;
+ }
+
+ public function GetByte()
+ {
+ return Ord($this->Get(1));
+ }
+
+ public function GetShort()
+ {
+ if($this->Remaining() < 2)
+ return false;
+
+ $Data = UnPack('v', $this->Get(2));
+
+ return $Data[ 1 ];
+ }
+
+ public function GetLong()
+ {
+ if($this->Remaining() < 4)
+ return false;
+
+ $Data = UnPack('l', $this->Get(4));
+
+ return $Data[ 1 ];
+ }
+
+ public function GetFloat()
+ {
+ if($this->Remaining() < 4)
+ return false;
+
+ $Data = UnPack('f', $this->Get(4));
+
+ return $Data[ 1 ];
+ }
+
+ public function GetUnsignedLong()
+ {
+ if($this->Remaining() < 4)
+ return false;
+
+ $Data = UnPack('V', $this->Get(4));
+
+ return $Data[ 1 ];
+ }
+
+ public function GetString()
+ {
+ $ZeroBytePosition = StrPos($this->Buffer, "\0", $this->Position);
+
+ if($ZeroBytePosition === false)
+ return '';
+
+ $String = $this->Get($ZeroBytePosition - $this->Position);
+
+ $this->Position++;
+
+ return $String;
+ }
+ }
\ No newline at end of file
diff --git a/system/library/games/query/GoldSourceRcon.php b/system/library/games/query/GoldSourceRcon.php
new file mode 100644
index 0000000..3e2f466
--- /dev/null
+++ b/system/library/games/query/GoldSourceRcon.php
@@ -0,0 +1,105 @@
+Socket = $Socket;
+ }
+
+ public function Close()
+ {
+ $this->RconChallenge = 0;
+ $this->RconRequestId = 0;
+ $this->RconPassword = 0;
+ }
+
+ public function Open()
+ {
+ //
+ }
+
+ public function Write($Header, $String = '')
+ {
+ $Command = Pack('cccca*', 0xFF, 0xFF, 0xFF, 0xFF, $String);
+ $Length = StrLen($Command);
+
+ return $Length === FWrite($this->Socket->Socket, $Command, $Length);
+ }
+
+ public function Read($Length = 1400)
+ {
+ $Buffer = $this->Socket->Read();
+
+ $StringBuffer = '';
+ $ReadMore = false;
+
+ do
+ {
+ $ReadMore = $Buffer->Remaining() > 0;
+
+ if($ReadMore)
+ {
+ if($Buffer->GetByte() !== SourceQuery::S2A_RCON)
+ sys::outjs(array('e' => 'неправильный rcon запрос.'));
+
+ $Packet = $Buffer->Get();
+ $StringBuffer .= $Packet;
+
+ $ReadMore = StrLen($Packet) > 1000;
+
+ if($ReadMore)
+ $Buffer = $this->Socket->Read();
+ }
+ }
+
+ while($ReadMore);
+
+ $Trimmed = trim($StringBuffer);
+
+ if($Trimmed === 'Bad rcon_password.')
+ sys::outjs(array('e' => 'rcon_password из server.cfg не подходит.'));
+
+ else if($Trimmed === 'You have been banned from this server.')
+ sys::outjs(array('e' => 'Игровой сервер заблокировал доступ.'));
+
+ $Buffer->Set($Trimmed);
+
+ return $Buffer;
+ }
+
+ public function Command($Command)
+ {
+ if(!$this->RconChallenge)
+ return false;
+
+ $this->Write(0, 'rcon ' . $this->RconChallenge . ' "' . $this->RconPassword . '" ' . $Command . "\0");
+ $Buffer = $this->Read();
+
+ if($Buffer)
+ return $Buffer->Get();
+
+ return $Buffer;
+ }
+
+ public function Authorize($Password)
+ {
+ $this->RconPassword = $Password;
+
+ $this->Write(0, 'challenge rcon');
+ $Buffer = $this->Socket->Read();
+
+ if($Buffer->Get(14) !== 'challenge rcon')
+ sys::outjs(array('e' => 'Не удалось выполнить rcon запрос.'));
+
+ $this->RconChallenge = Trim($Buffer->Get());
+ }
+ }
\ No newline at end of file
diff --git a/system/library/games/query/McQuery.php b/system/library/games/query/McQuery.php
new file mode 100644
index 0000000..2615d2c
--- /dev/null
+++ b/system/library/games/query/McQuery.php
@@ -0,0 +1,96 @@
+stack = fsockopen('udp://'.$ip, $port, $errorNum, $errorString, 2);
+
+ socket_set_timeout($this->stack, 2);
+ }
+
+ public function getInfo($pl = false)
+ {
+ $Data = $this->WriteData(0x00, $this->GetChallenge().Pack('c*', 0x00, 0x00, 0x00, 0x00));
+
+ $server_info = array();
+
+ $Data = substr($Data, 11);
+ $Data = explode("\x00\x00\x01player_\x00\x00", $Data);
+ $aData = explode("\x00", $Data[0]);
+
+ $last = '';
+
+ $keys = Array(
+ 'hostname' => 'hostname',
+ 'version' => 'version',
+ 'plugins' => 'plugins',
+ 'map' => 'map',
+ 'numplayers' => 'online'
+ );
+
+ foreach($aData as $index => $val)
+ {
+ if(~$index & 1)
+ {
+ if(!array_key_exists($val, $keys))
+ {
+ $last = false;
+ continue;
+ }
+
+ $last = $keys[$val];
+ $server_info[$last] = '';
+
+ }elseif($last != false)
+ $server_info[$last] = $val;
+ }
+
+ if(!count($server_info))
+ return NULL;
+
+ if(!$pl)
+ return $server_info;
+
+ $server_info['players_list'] = explode("\x00", substr($Data[1], 0, -2));
+
+ if(!isset($server_info['players_list'][1]))
+ $server_info['players_list'] = array();
+
+ return $server_info;
+ }
+
+ private function GetChallenge()
+ {
+ $Data = $this->WriteData(0x09);
+
+ if($Data === false)
+ return NULL;
+
+ return Pack('N', $Data);
+ }
+
+ private function WriteData($Command, $Append = '')
+ {
+ $Command = Pack('c*', 0xFE, 0xFD, $Command, 0x01, 0x02, 0x03, 0x04).$Append;
+ $Length = StrLen($Command);
+
+ if($Length !== fwrite($this->stack, $Command, $Length))
+ return NULL;
+
+ $Data = fread($this->stack, 2048);
+
+ if($Data === false)
+ return NULL;
+
+ if(StrLen($Data) < 5 || $Data[0] != $Command[2])
+ return false;
+
+ return SubStr($Data, 5);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/query/MtaQuery.php b/system/library/games/query/MtaQuery.php
new file mode 100644
index 0000000..e86cdb0
--- /dev/null
+++ b/system/library/games/query/MtaQuery.php
@@ -0,0 +1,93 @@
+stack = fsockopen('udp://'.$ip, $port+123, $errorNum, $errorString, 2);
+
+ socket_set_timeout($this->stack, 2);
+ }
+
+ public function getInfo($pl = false)
+ {
+ fwrite($this->stack, 's');
+
+ $buffer = fread($this->stack, 4096);
+
+ if(!$buffer)
+ return NULL;
+
+ $buffer = substr($buffer, 4);
+
+ $server_info = array();
+
+ $server_info['gamename'] = $this->cut_pascal($buffer, 1, -1);
+ $server_info['hostport'] = $this->cut_pascal($buffer, 1, -1);
+ $server_info['hostname'] = $this->cut_pascal($buffer, 1, -1);
+ $server_info['gamemode'] = $this->cut_pascal($buffer, 1, -1);
+ $server_info['map'] = $this->cut_pascal($buffer, 1, -1);
+ $server_info['version'] = $this->cut_pascal($buffer, 1, -1);
+ $server_info['password'] = $this->cut_pascal($buffer, 1, -1);
+ $server_info['players'] = $this->cut_pascal($buffer, 1, -1);
+ $server_info['playersmax'] = $this->cut_pascal($buffer, 1, -1);
+
+ while($buffer && $buffer[0] != "\x01")
+ {
+ $item_key = strtolower($this->cut_pascal($buffer, 1, -1));
+ $item_value = $this->cut_pascal($buffer, 1, -1);
+
+ $server_info[$item_key] = $item_value;
+ }
+
+ if(!$pl)
+ return $server_info;
+
+ $buffer = substr($buffer, 1);
+
+ $i = 1;
+
+ while($buffer)
+ {
+ $bit_flags = $this->cut_byte($buffer, 1);
+
+ $field_list = array('name', '', '', '', 'ping', '');
+
+ foreach($field_list as $item_key)
+ {
+ $item_value = $this->cut_pascal($buffer, 1, -1);
+
+ if(!$item_key)
+ continue;
+
+ $server_info['players_list'][$i][$item_key] = $item_value;
+ }
+
+ $i+=1;
+ }
+
+ return $server_info;
+ }
+
+ private function cut_pascal(&$buffer, $start_byte = 1, $length_adjust = 0, $end_byte = 0)
+ {
+ $length = ord(substr($buffer, 0, $start_byte)) + $length_adjust;
+ $string = substr($buffer, $start_byte, $length);
+ $buffer = substr($buffer, $start_byte + $length + $end_byte);
+
+ return $string;
+ }
+
+ private function cut_byte(&$buffer, $length)
+ {
+ $string = substr($buffer, 0, $length);
+ $buffer = substr($buffer, $length);
+
+ return $string;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/query/SampQuery.php b/system/library/games/query/SampQuery.php
new file mode 100644
index 0000000..5b6a2ba
--- /dev/null
+++ b/system/library/games/query/SampQuery.php
@@ -0,0 +1,122 @@
+stack = fsockopen('udp://'.$ip, $port, $errorNum, $errorString, 2);
+
+ socket_set_timeout($this->stack, 2);
+ }
+
+ public function getInfo()
+ {
+ @fwrite($this->stack, $this->assemblePacket('i'));
+
+ fread($this->stack, 11);
+
+ $serverInfo = array();
+
+ $serverInfo['password'] = sys::int(ord(fread($this->stack, 1)));
+
+ $serverInfo['players'] = sys::int($this->toInt(fread($this->stack, 2)));
+
+ $serverInfo['maxplayers'] = sys::int($this->toInt(fread($this->stack, 2)));
+
+ $strLen = ord(fread($this->stack, 4));
+
+ if(!$strLen)
+ return -1;
+
+ $serverInfo['hostname'] = fread($this->stack, $strLen);
+
+ $strLen = ord(fread($this->stack, 4));
+ $serverInfo['gamemode'] = fread($this->stack, $strLen);
+
+ $strLen = ord(fread($this->stack, 4));
+ $serverInfo['map'] = fread($this->stack, $strLen);
+
+ return $serverInfo;
+ }
+
+ public function getDetailedPlayers()
+ {
+ @fwrite($this->stack, $this->assemblePacket('d'));
+ fread($this->stack, 11);
+
+ $playerCount = ord(fread($this->stack, 2));
+ $players = array();
+
+ for($i = 0; $i < $playerCount; ++$i)
+ {
+ $player['playerid'] = ord(fread($this->stack, 1));
+
+ $strLen = ord(fread($this->stack, 1));
+ $player['nickname'] = fread($this->stack, $strLen);
+
+ $player['ping'] = $this->toInt(fread($this->stack, 4));
+
+ $players[$i] = $player;
+
+ unset($player);
+ }
+
+ return $players;
+ }
+
+ private function toInt($string)
+ {
+ if($string === '')
+ return null;
+
+ $int = 0;
+ $int += (ord($string[0]));
+
+ if(isset($string[1]))
+ $int += (ord($string[1]) << 8);
+
+ if(isset($string[2]))
+ $int += (ord($string[2]) << 16);
+
+ if(isset($string[3]))
+ $int += (ord($string[3]) << 24);
+
+ if($int >= 4294967294)
+ $int -= 4294967296;
+
+ return $int;
+ }
+
+ private function assemblePacket($type)
+ {
+ $packet = 'SAMP';
+ $packet .= chr(strtok($this->server, '.'));
+ $packet .= chr(strtok('.'));
+ $packet .= chr(strtok('.'));
+ $packet .= chr(strtok('.'));
+ $packet .= chr($this->port & 0xFF);
+ $packet .= chr($this->port >> 8 & 0xFF);
+ $packet .= $type;
+
+ return $packet;
+ }
+
+ public function connect()
+ {
+ $connected = false;
+ fwrite($this->stack, $this->assemblePacket('p0101'));
+
+ if(fread($this->stack, 10))
+ {
+ if(fread($this->stack, 5) == 'p0101')
+ $connected = true;
+ }
+
+ return $connected;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/query/Socket.php b/system/library/games/query/Socket.php
new file mode 100644
index 0000000..ffe1444
--- /dev/null
+++ b/system/library/games/query/Socket.php
@@ -0,0 +1,62 @@
+Socket)
+ {
+ FClose($this->Socket);
+
+ $this->Socket = null;
+ }
+ }
+
+ public function Open($Address, $Port, $Timeout, $Engine)
+ {
+ $this->Timeout = $Timeout;
+ $this->Engine = $Engine;
+ $this->Port = $Port;
+ $this->Address = $Address;
+
+ $this->Socket = @FSockOpen('udp://' . $Address, $Port, $ErrNo, $ErrStr, $Timeout);
+
+ if($ErrNo || $this->Socket === false)
+ return false;
+
+ Stream_Set_Timeout($this->Socket, $Timeout);
+ Stream_Set_Blocking($this->Socket, true);
+ }
+
+ public function Write($Header, $String = '')
+ {
+ $Command = Pack('ccccca*', 0xFF, 0xFF, 0xFF, 0xFF, $Header, $String);
+ $Length = StrLen($Command);
+
+ return $Length === FWrite($this->Socket, $Command, $Length);
+ }
+
+ public function Read($Length = 1400)
+ {
+ $Buffer = new Buffer();
+ $Buffer->Set(FRead($this->Socket, $Length));
+
+ $this->ReadInternal($Buffer, $Length, [$this, 'Sherlock']);
+
+ return $Buffer;
+ }
+
+ public function Sherlock($Buffer, $Length)
+ {
+ $Data = FRead($this->Socket, $Length);
+
+ if(StrLen($Data) < 4)
+ return false;
+
+ $Buffer->Set($Data);
+
+ return $Buffer->GetLong() === -2;
+ }
+ }
\ No newline at end of file
diff --git a/system/library/games/query/SourceQuery.php b/system/library/games/query/SourceQuery.php
new file mode 100644
index 0000000..69f5685
--- /dev/null
+++ b/system/library/games/query/SourceQuery.php
@@ -0,0 +1,347 @@
+Socket = $Socket ?: new Socket();
+ }
+
+ public function __destruct()
+ {
+ $this->Disconnect();
+ }
+
+ public function Connect($Address, $Port, $Timeout = 3, $Engine = self::SOURCE)
+ {
+ $this->Disconnect();
+
+ if(!is_int($Timeout) || $Timeout < 0)
+ return false;
+
+ $this->Socket->Open($Address, (int)$Port, $Timeout, (int)$Engine);
+
+ $this->Connected = true;
+ }
+
+ public function SetUseOldGetChallengeMethod($Value)
+ {
+ $Previous = $this->UseOldGetChallengeMethod;
+
+ $this->UseOldGetChallengeMethod = $Value === true;
+
+ return $Previous;
+ }
+
+ public function Disconnect()
+ {
+ $this->Connected = false;
+ $this->Challenge = 0;
+
+ $this->Socket->Close();
+
+ if($this->Rcon)
+ {
+ $this->Rcon->Close();
+
+ $this->Rcon = null;
+ }
+ }
+
+ public function Ping()
+ {
+ if(!$this->Connected)
+ return false;
+
+ $this->Socket->Write(self::A2S_PING);
+ $Buffer = $this->Socket->Read();
+
+ return $Buffer->GetByte() === self::S2A_PING;
+ }
+
+ public function GetInfo()
+ {
+ if(!$this->Connected)
+ return false;
+
+ $this->Socket->Write(self::A2S_INFO, "Source Engine Query\0");
+ $Buffer = $this->Socket->Read();
+
+ $Type = $Buffer->GetByte();
+
+ if($Type === self::S2A_INFO_OLD && $this->Socket->Engine === self::GOLDSOURCE)
+ {
+ $Server['Address'] = $Buffer->GetString();
+ $Server['HostName'] = $Buffer->GetString();
+ $Server['Map'] = $Buffer->GetString();
+ $Server['ModDir'] = $Buffer->GetString();
+ $Server['ModDesc'] = $Buffer->GetString();
+ $Server['Players'] = $Buffer->GetByte();
+ $Server['MaxPlayers'] = $Buffer->GetByte();
+ $Server['Protocol'] = $Buffer->GetByte();
+ $Server['Dedicated'] = Chr($Buffer->GetByte());
+ $Server['Os'] = Chr($Buffer->GetByte());
+ $Server['Password'] = $Buffer->GetByte() === 1;
+ $Server['IsMod'] = $Buffer->GetByte() === 1;
+
+ if($Server['IsMod'])
+ {
+ $Mod['Url'] = $Buffer->GetString();
+ $Mod['Download'] = $Buffer->GetString();
+ $Buffer->Get(1);
+ $Mod['Version'] = $Buffer->GetLong();
+ $Mod['Size'] = $Buffer->GetLong();
+ $Mod['ServerSide'] = $Buffer->GetByte() === 1;
+ $Mod['CustomDLL'] = $Buffer->GetByte() === 1;
+ }
+
+ $Server['Secure'] = $Buffer->GetByte() === 1;
+ $Server['Bots'] = $Buffer->GetByte();
+
+ if(isset($Mod))
+ $Server['Mod'] = $Mod;
+
+ return $Server;
+ }
+
+ if($Type !== self::S2A_INFO)
+ return false;
+
+ if($Type !== self::S2A_INFO)
+ return false;
+
+ $Server['Protocol'] = $Buffer->GetByte();
+ $Server['HostName'] = $Buffer->GetString();
+ $Server['Map'] = $Buffer->GetString();
+ $Server['ModDir'] = $Buffer->GetString();
+ $Server['ModDesc'] = $Buffer->GetString();
+ $Server['AppID'] = $Buffer->GetShort();
+ $Server['Players'] = $Buffer->GetByte();
+ $Server['MaxPlayers'] = $Buffer->GetByte();
+ $Server['Bots'] = $Buffer->GetByte();
+ $Server['Dedicated'] = Chr($Buffer->GetByte());
+ $Server['Os'] = Chr($Buffer->GetByte());
+ $Server['Password'] = $Buffer->GetByte() === 1;
+ $Server['Secure'] = $Buffer->GetByte() === 1;
+
+ if($Server['AppID'] === 2400)
+ {
+ $Server['GameMode'] = $Buffer->GetByte();
+ $Server['WitnessCount'] = $Buffer->GetByte();
+ $Server['WitnessTime'] = $Buffer->GetByte();
+ }
+
+ $Server['Version'] = $Buffer->GetString();
+
+ if($Buffer->Remaining() > 0)
+ {
+ $Server['ExtraDataFlags'] = $Flags = $Buffer->GetByte();
+
+ if($Flags & 0x80)
+ $Server['GamePort'] = $Buffer->GetShort();
+
+ if($Flags & 0x10)
+ {
+ $SteamIDLower = $Buffer->GetUnsignedLong();
+ $SteamIDInstance = $Buffer->GetUnsignedLong();
+ $SteamID = 0;
+
+ if(PHP_INT_SIZE === 4)
+ {
+ if(extension_loaded('gmp'))
+ {
+ $SteamIDLower = gmp_abs($SteamIDLower);
+ $SteamIDInstance = gmp_abs($SteamIDInstance);
+ $SteamID = gmp_strval(gmp_or($SteamIDLower, gmp_mul($SteamIDInstance, gmp_pow(2, 32))));
+ }else
+ return false;
+ }else
+ $SteamID = $SteamIDLower | ($SteamIDInstance << 32);
+
+ $Server['SteamID'] = $SteamID;
+
+ unset($SteamIDLower, $SteamIDInstance, $SteamID);
+ }
+
+ if($Flags & 0x40)
+ {
+ $Server['SpecPort'] = $Buffer->GetShort();
+ $Server['SpecName'] = $Buffer->GetString();
+ }
+
+ if($Flags & 0x20)
+ $Server['GameTags'] = $Buffer->GetString();
+
+ if($Flags & 0x01)
+ $Server['GameID'] = $Buffer->GetUnsignedLong() | ($Buffer->GetUnsignedLong() << 32);
+
+ if($Buffer->Remaining() > 0)
+ return false;
+ }
+
+ return $Server;
+ }
+
+ public function GetPlayers()
+ {
+ if(!$this->Connected)
+ return false;
+
+ $this->GetChallenge(self::A2S_PLAYER, self::S2A_PLAYER);
+
+ $this->Socket->Write(self::A2S_PLAYER, $this->Challenge);
+ $Buffer = $this->Socket->Read(14000);
+ $Type = $Buffer->GetByte();
+
+ if($Type !== self::S2A_PLAYER)
+ return false;
+
+ $Players = [];
+ $Count = $Buffer->GetByte();
+
+ while($Count-- > 0 && $Buffer->Remaining() > 0)
+ {
+ $Player['Id'] = $Buffer->GetByte();
+ $Player['Name'] = $Buffer->GetString();
+ $Player['Frags'] = $Buffer->GetLong();
+ $Player['Time'] = (int)$Buffer->GetFloat();
+ $Player['TimeF'] = GMDate(($Player['Time'] > 3600 ? "H:i:s" : "i:s"), $Player['Time']);
+
+ $Players[] = $Player;
+ }
+
+ return $Players;
+ }
+
+ public function GetRules()
+ {
+ if(!$this->Connected)
+ return false;
+
+ $this->GetChallenge(self::A2S_RULES, self::S2A_RULES);
+ $this->Socket->Write(self::A2S_RULES, $this->Challenge);
+ $Buffer = $this->Socket->Read();
+ $Type = $Buffer->GetByte();
+
+ if($Type !== self::S2A_RULES)
+ return false;
+
+ $Rules = [];
+ $Count = $Buffer->GetShort();
+
+ while($Count-- > 0 && $Buffer->Remaining() > 0)
+ {
+ $Rule = $Buffer->GetString();
+ $Value = $Buffer->GetString();
+
+ if(!empty($Rule))
+ $Rules[$Rule] = $Value;
+ }
+
+ return $Rules;
+ }
+
+ private function GetChallenge($Header, $ExpectedResult)
+ {
+ if($this->Challenge)
+ return;
+
+ if($this->UseOldGetChallengeMethod)
+ $Header = self::A2S_SERVERQUERY_GETCHALLENGE;
+
+ $this->Socket->Write($Header, "\xFF\xFF\xFF\xFF");
+ $Buffer = $this->Socket->Read();
+ $Type = $Buffer->GetByte();
+
+ switch($Type)
+ {
+ case self::S2A_CHALLENGE:
+ {
+ $this->Challenge = $Buffer->Get(4);
+
+ return;
+ }
+ case $ExpectedResult:
+ {
+ return;
+ }
+ case 0:
+ {
+ return;
+ }
+ default:
+ {
+ return;
+ }
+ }
+ }
+
+ public function SetRconPassword($Password)
+ {
+ if(!$this->Connected)
+ {
+ return false;
+ }
+
+ switch($this->Socket->Engine)
+ {
+ case SourceQuery::GOLDSOURCE:
+ {
+ $this->Rcon = new GoldSourceRcon($this->Socket);
+
+ break;
+ }
+ case SourceQuery::SOURCE:
+ {
+ $this->Rcon = new SourceRcon($this->Socket);
+
+ break;
+ }
+ }
+
+ $this->Rcon->Open();
+ $this->Rcon->Authorize($Password);
+ }
+
+ public function Rcon($Command)
+ {
+ if(!$this->Connected)
+ {
+ return false;
+ }
+
+ if($this->Rcon === null)
+ {
+ return false;
+ }
+
+ return $this->Rcon->Command($Command);
+ }
+ }
\ No newline at end of file
diff --git a/system/library/games/query/SourceRcon.php b/system/library/games/query/SourceRcon.php
new file mode 100644
index 0000000..76ac507
--- /dev/null
+++ b/system/library/games/query/SourceRcon.php
@@ -0,0 +1,148 @@
+Socket = $Socket;
+ }
+
+ public function Close()
+ {
+ if($this->RconSocket)
+ {
+ FClose($this->RconSocket);
+
+ $this->RconSocket = null;
+ }
+
+ $this->RconRequestId = 0;
+ }
+
+ public function Open()
+ {
+ if(!$this->RconSocket)
+ {
+ $this->RconSocket = @FSockOpen($this->Socket->Address, $this->Socket->Port, $ErrNo, $ErrStr, $this->Socket->Timeout);
+
+ if($ErrNo || !$this->RconSocket)
+ return false;
+
+ Stream_Set_Timeout($this->RconSocket, $this->Socket->Timeout);
+ Stream_Set_Blocking($this->RconSocket, true);
+ }
+ }
+
+ public function Write($Header, $String = '')
+ {
+ $Command = Pack('VV', ++$this->RconRequestId, $Header) . $String . "\x00\x00";
+ $Command = Pack('V', StrLen($Command)) . $Command;
+ $Length = StrLen($Command);
+
+ return $Length === FWrite($this->RconSocket, $Command, $Length);
+ }
+
+ public function Read()
+ {
+ $Buffer = new Buffer();
+ $Buffer->Set(FRead($this->RconSocket, 4));
+
+ if($Buffer->Remaining() < 4)
+ return false;
+
+ $PacketSize = $Buffer->GetLong();
+
+ $Buffer->Set(FRead($this->RconSocket, $PacketSize));
+
+ $Data = $Buffer->Get();
+
+ $Remaining = $PacketSize - StrLen($Data);
+
+ while($Remaining > 0)
+ {
+ $Data2 = FRead($this->RconSocket, $Remaining);
+
+ $PacketSize = StrLen($Data2);
+
+ if($PacketSize === 0)
+ return false;
+
+ $Data .= $Data2;
+ $Remaining -= $PacketSize;
+ }
+
+ $Buffer->Set($Data);
+
+ return $Buffer;
+ }
+
+ public function Command($Command)
+ {
+ $this->Write(SourceQuery::SERVERDATA_EXECCOMMAND, $Command);
+ $Buffer = $this->Read();
+
+ $Buffer->GetLong();
+
+ $Type = $Buffer->GetLong();
+
+ if($Type === SourceQuery::SERVERDATA_AUTH_RESPONSE)
+ return false;
+
+ if($Type !== SourceQuery::SERVERDATA_RESPONSE_VALUE)
+ return false;
+
+ $Data = $Buffer->Get();
+
+ if(StrLen($Data) >= 4000)
+ {
+ do
+ {
+ $this->Write(SourceQuery::SERVERDATA_RESPONSE_VALUE);
+
+ $Buffer = $this->Read();
+
+ $Buffer->GetLong();
+
+ if($Buffer->GetLong() !== SourceQuery::SERVERDATA_RESPONSE_VALUE)
+ break;
+
+ $Data2 = $Buffer->Get();
+
+ if($Data2 === "\x00\x01\x00\x00\x00\x00")
+ break;
+
+ $Data .= $Data2;
+ }
+
+ while(true);
+ }
+
+ return rtrim($Data, "\0");
+ }
+
+ public function Authorize($Password)
+ {
+ $this->Write(SourceQuery::SERVERDATA_AUTH, $Password);
+ $Buffer = $this->Read();
+
+ $RequestID = $Buffer->GetLong();
+ $Type = $Buffer->GetLong();
+
+ if($Type === SourceQuery::SERVERDATA_RESPONSE_VALUE)
+ {
+ $Buffer = $this->Read();
+
+ $RequestID = $Buffer->GetLong();
+ $Type = $Buffer->GetLong();
+ }
+
+ if($RequestID === -1 || $Type !== SourceQuery::SERVERDATA_AUTH_RESPONSE)
+ return false;
+ }
+ }
\ No newline at end of file
diff --git a/system/library/games/samp/action.php b/system/library/games/samp/action.php
new file mode 100644
index 0000000..6d96f64
--- /dev/null
+++ b/system/library/games/samp/action.php
@@ -0,0 +1,129 @@
+query('SELECT `uid`, `unit`, `tarif`, `game`, `address`, `slots_start`, `name`, `map_start`, `time_start`, `core_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соедниения пу с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return array('e' => sys::text('error', 'ssh'));
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe;');
+
+ // Временный файл
+ $temp = sys::temp(action::config($ip, $port, $server['slots_start'], $ssh->get('cat '.$tarif['install'].'/'.$server['uid'].'/server.cfg')));
+
+ // Обновление файла server.cfg
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/server.cfg', 0644);
+
+ unlink($temp);
+
+ $taskset = '';
+
+ // Если включена система автораспределения и не установлен фиксированный поток
+ if($cfg['cpu_route'] AND !$server['core_fix'])
+ {
+ $proc_stat = array();
+
+ $proc_stat[0] = $ssh->get('cat /proc/stat');
+ }
+
+ // Если система автораспределения продолжить парсинг загрузки процессора
+ if(isset($proc_stat))
+ {
+ $proc_stat[1] = $ssh->get('cat /proc/stat');
+
+ // Ядро/поток, на котором будет запущен игровой сервер (поток выбран с рассчетом наименьшей загруженности в момент запуска игрового сервера)
+ $core = sys::cpu_idle($proc_stat, $server['unit'], false); // число от 1 до n (где n число ядер/потоков в процессоре (без нулевого)
+
+ if(!is_numeric($core))
+ return array('e' => sys::text('error', 'cpu'));
+
+ $taskset = 'taskset -c '.$core;
+ }
+
+ if($server['core_fix'])
+ {
+ $core = $server['core_fix']-1;
+ $taskset = 'taskset -c '.$core;
+ }
+
+ // Параметры запуска
+ $bash = './samp03svr';
+
+ // Временный файл
+ $temp = sys::temp($bash);
+
+ // Обновление файла start.sh
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/start.sh', 0500);
+
+ // Строка запуска
+ $ssh->set('cd '.$tarif['install'].$server['uid'].';' // переход в директорию игрового сервера
+ .'rm *.pid;' // Удаление *.pid файлов
+ .'sudo -u server'.$server['uid'].' mkdir -p oldstart;' // Создание папки логов
+ .'cat server_log.txt >> oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log; rm server_log.txt; rm oldstart/01.01.1970_03:00:00.log;' // Перемещение лога предыдущего запуска
+ .'chown server'.$server['uid'].':1000 server.cfg start.sh;' // Обновление владельца файлов
+ .'sudo -u server'.$server['uid'].' screen -dmS s_'.$server['uid'].' '.$taskset.' sh -c "./start.sh"'); // Запуск игровго сервера
+
+ $core = !isset($core) ? 0 : $core+1;
+
+ // Обновление информации в базе
+ $sql->query('UPDATE `servers` set `status`="'.$type.'", `online`="0", `players`="", `core_use`="'.$core.'", `time_start`="'.$start_point.'", `stop`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ unlink($temp);
+
+ // Сброс кеша
+ actions::clmcache($id);
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => $type, 'online' => 0));
+
+ return array('s' => 'ok');
+ }
+
+ public static function config($ip, $port, $slots, $config)
+ {
+ $aLine = explode("\n", $config);
+
+ $eConfig = '';
+
+ foreach($aLine as $line)
+ {
+ $param = explode(' ', trim($line));
+
+ if(in_array(trim($param[0]), array('bind', 'port', 'maxplayers', 'query')))
+ continue;
+
+ $eConfig .= $line.PHP_EOL;
+ }
+
+ $eConfig .= 'bind '.$ip.PHP_EOL
+ .'port '.$port.PHP_EOL
+ .'maxplayers '.$slots.PHP_EOL
+ .'query 1';
+
+ return $eConfig;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/samp/scan.php b/system/library/games/samp/scan.php
new file mode 100644
index 0000000..36cac68
--- /dev/null
+++ b/system/library/games/samp/scan.php
@@ -0,0 +1,114 @@
+query('SELECT `address`, `game`, `name`, `map`, `online`, `players`, `status`, `time`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ include(LIB.'games/query/SampQuery.php');
+
+ $sq = new SampQuery($ip, $port);
+
+ if($players_get)
+ $nmch = 'server_scan_mon_pl_'.$id;
+ else
+ $nmch = 'server_scan_mon_'.$id;
+
+ if(is_array($mcache->get($nmch)))
+ return $mcache->get($nmch);
+
+ $out = array();
+
+ $out['time'] = 'Арендован до: '.date('d.m.Y - H:i', $server['time']);
+
+ if($server['status'] == 'overdue')
+ $out['time_end'] = 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400);
+ else
+ $out['time_end'] = 'Осталось: '.sys::date('min', $server['time']);
+
+ if(!$sq->connect())
+ {
+ $out['name'] = $server['name'];
+ $out['status'] = sys::status($server['status'], $server['game'], $server['map']);
+ $out['online'] = $server['online'];
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, $server['status'], $server['game']);
+
+ if($players_get)
+ $out['players'] = base64_decode($server['players']);
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ $info = $sq->getInfo();
+
+ if($players_get)
+ $players = scan::players($sq->getDetailedPlayers());
+
+ $info['map'] = htmlspecialchars(mb_convert_encoding($info['map'], 'UTF-8', 'WINDOWS-1251'));
+ $out['name'] = htmlspecialchars(mb_convert_encoding($info['hostname'], 'UTF-8', 'WINDOWS-1251'));
+ $out['status'] = sys::status('working', $server['game'], $info['map']);
+ $out['online'] = sys::int($info['players']);
+ $out['image'] = ' ';
+ $out['buttons'] = sys::buttons($id, 'working', $server['game']);
+ $out['players'] = '';
+
+ if($players_get)
+ {
+ foreach($players as $index => $player)
+ {
+ $html->get($server['game'], 'sections/servers/players');
+
+ $html->set('i', $player['i']);
+ $html->set('name', htmlspecialchars($player['name']));
+
+ $html->pack('list');
+ }
+
+ $out['players'] = isset($html->arr['list']) ? $html->arr['list'] : '';
+ }
+
+ $sql->query('UPDATE `servers` set '
+ .'`name`="'.$out['name'].'", '
+ .'`online`="'.$out['online'].'", '
+ .'`map`="'.$info['map'].'", '
+ .'`status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if($players_get)
+ $sql->query('UPDATE `servers` set `players`="'.base64_encode($out['players']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $out, false, $cfg['mcache_server_mon']);
+
+ return $out;
+ }
+
+ public static function players($aPlayrs)
+ {
+ $i = 1;
+ $aData = array();
+
+ foreach($aPlayrs as $n => $player)
+ {
+ $aData[$i]['i'] = $i;
+ $aData[$i]['name'] = $player['nickname'] == '' ? 'Подключается' : htmlspecialchars($player['nickname']);
+ $aData[$i]['ping'] = sys::int($player['ping']);
+
+ $i+=1;
+ }
+
+ return $aData;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/samp/service.php b/system/library/games/samp/service.php
new file mode 100644
index 0000000..0d45d60
--- /dev/null
+++ b/system/library/games/samp/service.php
@@ -0,0 +1,355 @@
+query('SELECT `address`, `test` FROM `units` WHERE `id`="'.$aData['unit'].'" AND `samp`="1" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Локация не найдена.'));
+
+ $unit = $sql->get();
+
+ // Проверка тарифа
+ $sql->query('SELECT `id` FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" AND `unit`="'.$aData['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Тариф не найден.'));
+
+ $sql->query('SELECT '
+ .'`slots_min`,'
+ .'`slots_max`,'
+ .'`port_min`,'
+ .'`port_max`,'
+ .'`hostname`,'
+ .'`packs`,'
+ .'`time`,'
+ .'`test`,'
+ .'`tests`,'
+ .'`discount`,'
+ .'`ftp`,'
+ .'`plugins`,'
+ .'`console`,'
+ .'`stats`,'
+ .'`copy`,'
+ .'`web`,'
+ .'`plugins_install`,'
+ .'`hdd`,'
+ .'`autostop`,'
+ .'`core_fix`,'
+ .'`ip`,'
+ .'`price`'
+ .' FROM `tarifs` WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ $tarif = $sql->get();
+
+ // Проверка сборки
+ if(!array_key_exists($aData['pack'], sys::b64djs($tarif['packs'], true)))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ $test = 0;
+
+ // Проверка периода на тест
+ if($aData['test'])
+ {
+ if(!$tarif['test'] || !$unit['test'])
+ sys::outjs(array('e' => 'Тестовый период недоступен.'));
+
+
+ // Проверка на повторный запрос
+ $sql->query('SELECT `id`, `game` FROM `tests` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $test_info = $sql->get();
+
+ if(!$cfg['tests']['game'] || $test_info['game'] == 'samp')
+ sys::outjs(array('e' => 'Тестовый период предоставляется один раз.'));
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" AND `test`="1" LIMIT 1');
+ if($sql->num() AND !$cfg['tests']['sametime'])
+ sys::outjs(array('e' => 'Чтобы получить тестовый период другой игры, дождитесь окончания текущего.'));
+ }
+
+ // Проверка наличия мест на локации
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$unit['test']);
+ if($sql->num() == $unit['test'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода нет.'));
+
+ // Проверка наличия мест для выбранного тарифа
+ $sql->query('SELECT `id` FROM `servers` WHERE `tarif`="'.$aData['tarif'].'" AND `test`="1" AND `time`>"'.$start_point.'" LIMIT '.$tarif['tests']);
+ if($sql->num() == $tarif['tests'])
+ sys::outjs(array('e' => 'Свободного места для тестового периода выбранного тарифа нет.'));
+
+ $test = 1;
+ }else
+ // Проверка периода
+ if(!$cfg['settlement_period'] AND !in_array($aData['time'], explode(':', $tarif['time'])))
+ sys::outjs(array('e' => 'Переданные данные периода неверны.'));
+
+ // Проверка слот
+ if($aData['slots'] < $tarif['slots_min'] || $aData['slots'] > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданные данные слот неверны.'));
+
+ // Определение суммы
+ if($cfg['settlement_period'])
+ {
+ // Цена аренды за расчетный период
+ $sum = games::define_sum($tarif['discount'], $tarif['price'], $aData['slots'], $start_point);
+
+ $aData['time'] = games::define_period('buy', params::$aDayMonth);
+ }else
+ $sum = games::define_sum($tarif['discount'], $tarif['price'], $aData['slots'], $aData['time']);
+
+ // Проверка промо-кода
+ $promo = games::define_promo(
+ $aData['promo'],
+ array(
+ 'tarif' => $aData['tarif'],
+ 'slots' => $aData['slots'],
+ 'time' => $aData['time'],
+ 'user' => $user['id']
+ ),
+ $tarif['discount'],
+ $sum
+ );
+
+ $days = $aData['time']; // Кол-во дней аренды
+
+ // Использование промо-кода
+ if(is_array($promo))
+ {
+ if(array_key_exists('sum', $promo))
+ $sum = $promo['sum'];
+ else
+ $days += $promo['days']; // Кол-во дней аренды с учетом подарочных (промо-код)
+ }
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']));
+
+ // Выделенный адрес игрового сервера
+ if(!empty($tarif['ip']))
+ {
+ $aIp = explode(':', $tarif['ip']);
+
+ $ip = false;
+ $port = params::$aDefPort['samp'];
+
+ // Проверка наличия свободного адреса
+ foreach($aIp as $adr)
+ {
+ $adr = trim($adr);
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `address` LIKE "'.$adr.':%" LIMIT 1');
+ if(!$sql->num())
+ {
+ $ip = $adr;
+
+ break;
+ }
+ }
+ }else{
+ $ip = sys::first(explode(':', $unit['address']));
+ $port = false;
+
+ // Проверка наличия свободного порта
+ for($tarif['port_min']; $tarif['port_min'] <= $tarif['port_max']; $tarif['port_min']+=1)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND (`address`="'.$ip.':'.$tarif['port_min'].'" OR `port`="'.$tarif['port_min'].'") LIMIT 1');
+ if(!$sql->num())
+ {
+ $port = $tarif['port_min'];
+
+ break;
+ }
+ }
+ }
+
+ if(!$ip || !$port)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+
+ if($test)
+ $aData['time'] = games::time($start_point, $tarif['test']);
+ else
+ $aData['time'] = games::time($start_point, $days);
+
+ $fix_one = 0;
+ $core = 0;
+
+ if($tarif['core_fix'] != '')
+ {
+ $aCore = explode(',', $tarif['core_fix']);
+
+ foreach($aCore as $cpu)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$aData['unit'].'" AND `tarif`="'.$aData['tarif'].'" AND `core_fix`="'.$cpu.'" AND `core_fix_one`="1" LIMIT 1');
+
+ if($sql->num())
+ continue;
+
+ $fix_one = 1;
+ $core = $cpu;
+
+ break;
+ }
+
+ if(!$core)
+ {
+ $sql->query('UPDATE `tarifs` set `show`="0" WHERE `id`="'.$aData['tarif'].'" LIMIT 1');
+
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+ }
+ }
+
+ $ram = $tarif['param_fix'] ? $aData['ram'] : $cfg['ram']['samp']*$aSDATA['slots'];
+
+ // Массив данных
+ $aSDATA = array(
+ 'unit' => $aData['unit'], // идентификатор локации
+ 'tarif' => $aData['tarif'], // идентификатор тарифа
+ 'ram' => $ram, // значение ram
+ 'param_fix' => $tarif['param_fix'], // фиксированные параметры
+ 'pack' => $aData['pack'], // Выбранная сборка для установки
+ 'time' => $aData['time'], // Время аренды
+ 'days' => $days, // Число дней
+ 'sum' => $sum, // Сумма списания
+ 'test' => $test, // тестовый период
+ 'address' => $ip.':'.$port, // адрес игрового сервера
+ 'port' => $port, // порт игрового сервера
+ 'slots' => $aData['slots'], // Кол-во слот
+ 'autostop' => $tarif['autostop'], // Выключение при 0 онлайне
+ 'ftp' => $tarif['ftp'], // Использование ftp
+ 'plugins' => $tarif['plugins'], // Использование плагинов
+ 'console' => $tarif['console'], // Использование консоли
+ 'stats' => $tarif['stats'], // Использование графиков (ведение статистики)
+ 'copy' => $tarif['copy'], // Использование резервных копий
+ 'web' => $tarif['web'], // Использование доп услуг
+ 'plugins_install' => $tarif['plugins_install'], // Список установленных плагинов
+ 'hdd' => $tarif['hdd'], // Дисковое пространство
+ 'core_fix' => $core, // Выделенный поток
+ 'core_fix_one' => $fix_one, // Выделенный поток
+ 'promo' => $promo // Использование промо-кода
+ );
+
+ return $aSDATA;
+ }
+
+ public static function install($aSDATA = array())
+ {
+ global $cfg, $sql, $user, $start_point;
+
+ include(LIB.'ssh.php');
+
+ // Массив данных локации (адрес,пароль)
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$aSDATA['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Массив данных тарифа (путь сборки,путь установки)
+ $sql->query('SELECT `path`, `install`, `hostname` FROM `tarifs` WHERE `id`="'.$aSDATA['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Получение идентификаторов игрового сервера
+ $sql->query('INSERT INTO `servers` set uid="1"');
+ $id = $sql->id();
+ $uid = $id+1000;
+
+ // Директория сборки
+ $path = $tarif['path'].$aSDATA['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$uid;
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -s /bin/false -d '.$install.' -g servers -u '.$uid.' server'.$uid.';' // Создание пользователя сервера на локации
+ .'chown server'.$uid.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u server'.$uid.' screen -dmS i_'.$uid.' sh -c "cp -r '.$path.'/. .;' // Копирование файлов сборки для сервера
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 777 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['samp']].'"');
+
+ // Запись данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `uid`="'.$uid.'",
+ `unit`="'.$aSDATA['unit'].'",
+ `tarif`="'.$aSDATA['tarif'].'",
+ `user`="'.$user['id'].'",
+ `address`="'.$aSDATA['address'].'",
+ `port`="'.$aSDATA['port'].'",
+ `game`="samp",
+ `slots`="'.$aSDATA['slots'].'",
+ `slots_start`="'.$aSDATA['slots'].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$aSDATA['pack'].'",
+ `plugins_use`="'.$aSDATA['plugins'].'",
+ `ftp_use`="'.$aSDATA['ftp'].'",
+ `console_use`="'.$aSDATA['console'].'",
+ `stats_use`="'.$aSDATA['stats'].'",
+ `copy_use`="'.$aSDATA['copy'].'",
+ `web_use`="'.$aSDATA['web'].'",
+ `vac`="1",
+ `hdd`="'.$aSDATA['hdd'].'",
+ `time`="'.$aSDATA['time'].'",
+ `date`="'.$start_point.'",
+ `test`="'.$aSDATA['test'].'",
+ `ram`="'.$aSDATA['ram'].'",
+ `map_start`="'.sys::passwd(8).'",
+ `core_fix`="'.$aSDATA['core_fix'].'",
+ `core_fix_one`="'.$aSDATA['core_fix_one'].'",
+ `autostop`="'.$aSDATA['autostop'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($aSDATA['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64djs($aSDATA['plugins_install']);
+
+ if(isset($aPlugins[$aSDATA['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$aSDATA['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$aSDATA['sum']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Запись получения тестового периода
+ if($aSDATA['test'])
+ {
+ $sql->query('INSERT INTO `tests` set `server`="'.$id.'", `unit`="'.$aSDATA['unit'].'", `game`="samp", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_test'), array('id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="0"');
+ }else{
+ // Реф. система
+ games::part($user['id'], $aSDATA['sum']);
+
+ // Запись логов
+ if(!is_array($aSDATA['promo']))
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ else{
+ $sql->query('UPDATE `servers` set `benefit`="'.$aSDATA['time'].'" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('INSERT INTO `promo_use` set `promo`="'.$aSDATA['promo']['id'].'", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_server_promo'), array('days' => games::parse_day($aSDATA['days'], true), 'money' => $aSDATA['sum'], 'promo' => $aSDATA['promo']['cod'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$aSDATA['sum'].'"');
+ }
+ }
+
+ return $id;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/samp/tarif.php b/system/library/games/samp/tarif.php
new file mode 100644
index 0000000..a65da45
--- /dev/null
+++ b/system/library/games/samp/tarif.php
@@ -0,0 +1,179 @@
+get('extend', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', '');
+ $html->set('tarif', $tarif_name);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function extend_sp($server, $tarif, $sid)
+ {
+ global $cfg, $sql, $html, $start_point;
+
+ tarifs::extend_address($server['game'], $sid);
+
+ $sum = $tarif['slots'] ? $tarif['price'] : $tarif['price']*$server['slots'];
+
+ $html->get('extend_sp', 'sections/servers/games/tarif');
+
+ if(isset($html->arr['extend_address']))
+ {
+ $html->unit('extend_address', 1);
+ $html->set('extend_address', $html->arr['extend_address']);
+ }else
+ $html->unit('extend_address');
+
+ $html->set('id', $sid);
+ $html->set('time', sys::date('min', $server['time']));
+ $html->set('date', $server['time'] > $start_point ? 'Сервер продлен до: '.date('d.m.Y', $server['time']) : 'Текущая дата: '.date('d.m.Y', $start_point));
+ $html->set('options', 'Выберете период продления '.$options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', '');
+ $html->set('tarif', $tarif['name']);
+ $html->set('sum', $sum);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function plan()
+ {
+ return NULL;
+ }
+
+ public static function unit($server, $unit_name, $tarif_name, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ if(!$cfg['change_unit'][$server['game']])
+ return NULL;
+
+ $tarifs = $sql->query('SELECT `unit` FROM `tarifs` WHERE `game`="'.$server['game'].'" AND `name`="'.$tarif_name.'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num($tarifs))
+ return NULL;
+
+ $units = 0;
+
+ $options = 'Выберете новую локацию ';
+
+ while($tarif = $sql->get($tarifs))
+ {
+ $sql->query('SELECT `id`, `name` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $unit = $sql->get();
+
+ $options .= ''.$unit['name'].' ';
+
+ $units+=1;
+ }
+
+ if(!$units)
+ return NULL;
+
+ $html->get('unit', 'sections/servers/games/tarif');
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('slots', $server['slots']);
+ $html->set('info', '');
+ $html->set('unit', $unit_name);
+ $html->set('tarif', $tarif_name);
+
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function unit_new($tarif, $unit, $server, $mcache)
+ {
+ global $ssh, $sql, $user, $start_point;
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ // Директория сборки
+ $path = $tarif['path'].$tarif['pack'];
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ // Пользователь сервера
+ $uS = 'server'.$server['uid'];
+
+ $ssh->set('mkdir '.$install.';' // Создание директории
+ .'useradd -d '.$install.' -g servers -u '.$server['uid'].' '.$uS.';' // Создание пользователя сервера на локации
+ .'chown '.$uS.':1000 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u '.$uS.' screen -dmS i_'.$server['uid'].' cp -r '.$path.'/. .'); // Копирование файлов сборки для сервера
+
+ $address = explode(':', $server['address']);
+
+ $fix_one = $tarif['core_fix'] ? 1 : 0;
+
+ // Обновление данных нового сервера
+ $sql->query('UPDATE `servers` set
+ `unit`="'.$tarif['unit'].'",
+ `tarif`="'.$tarif['id'].'",
+ `address`="'.$server['address'].'",
+ `port`="'.$address[1].'",
+ `status`="install",
+ `name`="'.$tarif['hostname'].'",
+ `pack`="'.$tarif['pack'].'",
+ `map_start`="'.$tarif['map'].'",
+ `hdd`="'.$tarif['hdd'].'",
+ `time`="'.$tarif['time'].'",
+ `autostop`="'.$tarif['autostop'].'",
+ `core_fix`="'.$tarif['core_fix'].'",
+ `core_fix_one`="'.$fix_one.'",
+ `reinstall`="'.$start_point.'" WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ // Запись установленных плагинов
+ if($tarif['plugins'])
+ {
+ // Массив идентификаторов плагинов
+ $aPlugins = sys::b64js($tarif['plugins_install']);
+
+ if(isset($aPlugins[$tarif['pack']]))
+ {
+ $plugins = explode(',', $aPlugins[$tarif['pack']]);
+
+ foreach($plugins as $plugin)
+ if($plugin)
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$server['id'].'", `plugin`="'.$plugin.'", `time`="'.$start_point.'"');
+ }
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/scans.php b/system/library/games/scans.php
new file mode 100644
index 0000000..5a94ea6
--- /dev/null
+++ b/system/library/games/scans.php
@@ -0,0 +1,237 @@
+ 'hlds_',
+ 'cssold' => 'srcds_i686',
+ 'css' => 'srcds_',
+ 'csgo' => 'srcds_',
+ 'samp' => 'samp',
+ 'crmp' => 'samp',
+ 'mta' => 'mta',
+ 'mc' => 'java'
+ );
+
+ public static function resources($id)
+ {
+ global $cfg, $sql, $mcache;
+
+ $nmch = 'server_resources_'.$id;
+
+ if(is_array($mcache->get($nmch)))
+ return $mcache->get($nmch);
+
+ $sql->query('SELECT `uid`, `unit`, `tarif`, `game`, `slots`, `slots_start`, `status`, `online`, `ram`, `hdd`, `hdd_use` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $server = $sql->get();
+
+ $resources = array(
+ 'usr' => 0,
+ 'cpu' => 0,
+ 'ram' => 0,
+ 'hdd' => $server['hdd_use']
+ );
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `ram` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return $resources;
+
+ if(!in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ return $resources;
+
+ $resources['usr'] = ceil(100/$server['slots_start']*$server['online']);
+ $resources['usr'] = $resources['usr'] > 100 ? 100 : $resources['usr'];
+
+ $cr = explode('|', $ssh->get('top -u '.$server['uid'].' -b -n 1 | grep '.(scans::$process[$server['game']]).' | sort | tail -1 | awk \'{print $9"|"$10}\''));
+
+ if(isset($cr[0]))
+ $resources['cpu'] = str_replace(',', '.', $cr[0]);
+
+ $resources['cpu'] = $resources['cpu'] > 100 ? 100 : round($resources['cpu']);
+
+ if(isset($cr[1]))
+ $resources['ram'] = str_replace(',', '.', $cr[1]);
+
+ // ram на сервер
+ $ram = $server['ram'] ? $server['ram'] : $server['slots']*$cfg['ram'][$server['game']];
+
+ $resources['ram'] = $unit['ram']/100*$resources['ram']/($ram/100);
+
+ $resources['ram'] = $resources['ram'] > 100 ? 100 : round($resources['ram']);
+
+ $resources['hdd'] = ceil(sys::int($ssh->get('cd '.$tarif['install'].$server['uid'].' && du -ms'))/($server['hdd']/100));
+ $resources['hdd'] = $resources['hdd'] > 100 ? 100 : $resources['hdd'];
+
+ $sql->query('UPDATE `servers` set `ram_use`="'.$resources['ram'].'", `cpu_use`="'.$resources['cpu'].'", `hdd_use`="'.$resources['hdd'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->set($nmch, $resources, false, $cfg['mcache_server_resources']);
+
+ return $resources;
+ }
+
+ public static function status($id)
+ {
+ global $start_point, $cfg, $sql, $mcache;
+
+ $nmch = 'server_status_'.$id;
+
+ if($mcache->get($nmch))
+ return 'mcache -> system_block_operation';
+
+ $mcache->set($nmch, true, false, $cfg['mcache_server_status']);
+
+ $sql->query('SELECT `uid`, `unit`, `game`, `address`, `status`, `name`, `online`, `players`, `time`, `overdue`, `ftp`, `block` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ // Если аренда не закончилась, а сервер просрочен
+ if($server['time'] > $start_point && $server['status'] == 'overdue')
+ {
+ $sql->query('UPDATE `servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> extend -> off';
+ }
+
+ // Если аренда закончилась и сервер просрочен длительное время
+ if($server['time'] < $start_point && $server['status'] == 'overdue' && ($server['overdue']+$cfg['server_delete']*86400) < $start_point)
+ {
+ $sql->query('UPDATE `servers` set `user`="-1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ return 'server -> overdue -> delete';
+ }
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ return 'unit error connect';
+
+ // Если аренда закончилась, а сервер не просрочен
+ if($server['time'] < $start_point && !in_array($server['status'], array('overdue', 'blocked')))
+ {
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@'.$server['address'].' | awk '."'{print $2}'".' | grep -v PID | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ if($server['ftp'])
+ $ssh->set("mysql -P ".$unit['sql_port']." -u".$unit['sql_login']." -p".$unit['sql_passwd']." --database ".$unit['sql_ftp']." -e \"DELETE FROM ftp WHERE user='".$server['uid']."'\"");
+
+ $sql->query('UPDATE `servers` set `status`="overdue", `online`="0", `players`="", `ftp`="0", `overdue`="'.$start_point.'", `mail`="1" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'overdue', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'overdue', 'online' => 0));
+
+ return 'server -> overdue -> stoping';
+ }
+
+ switch($server['status'])
+ {
+ case 'working': case 'change': case 'start': case 'restart':
+ if(!sys::int($ssh->get('ps aux | grep s_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `servers` set `status`="off", `online`="0", `players`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> working -> off';
+ }
+
+ break;
+
+ case 'off':
+ if(sys::int($ssh->get('ps aux | grep s_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `servers` set `status`="working" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'working', 'online' => $server['online'], 'players' => $server['players']));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'working', 'online' => $server['online']));
+
+ return 'server -> off -> working';
+ }
+
+ break;
+
+ case 'reinstall':
+ if(!sys::int($ssh->get('ps aux | grep r_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> reinstall -> end';
+ }
+
+ break;
+
+ case 'update':
+ if(!sys::int($ssh->get('ps aux | grep u_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> update -> end';
+ }
+
+ break;
+
+ case 'install':
+ if(!sys::int($ssh->get('ps aux | grep i_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> install -> end';
+ }
+
+ break;
+
+ case 'recovery':
+ if(!sys::int($ssh->get('ps aux | grep rec_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ {
+ $sql->query('UPDATE `servers` set `status`="off" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+
+ return 'server -> recovery -> end';
+ }
+
+ break;
+
+ case 'blocked':
+ if($server['block'] > $start_point)
+ break;
+
+ $sql->query('UPDATE `servers` set `status`="off", `block`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::reset_mcache('server_scan_mon_pl_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0, 'players' => ''));
+ sys::reset_mcache('server_scan_mon_'.$id, $id, array('name' => $server['name'], 'game' => $server['game'], 'status' => 'off', 'online' => 0));
+ }
+
+ return 'server -> no change -> end scan';
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/services.php b/system/library/games/services.php
new file mode 100644
index 0000000..4ab56b6
--- /dev/null
+++ b/system/library/games/services.php
@@ -0,0 +1,58 @@
+query('SELECT `id` FROM `units` WHERE `'.$game.'`="1" AND `show`="1" ORDER BY `sort` ASC');
+ while($unit = $sql->get($units))
+ {
+ $sql->query('SELECT `id` FROM `tarifs` WHERE `unit`="'.$unit['id'].'" AND `game`="'.$game.'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ continue;
+ else{
+ $sel = $unit['id'];
+ break;
+ }
+ }
+
+ return 'SELECT `id`, `test` FROM `units` WHERE `id`="'.$sel.'" LIMIT 1';
+ }
+
+ public static function units($game)
+ {
+ global $sql;
+
+ $list = '';
+
+ $units = $sql->query('SELECT `id`, `name` FROM `units` WHERE `'.$game.'`="1" AND `show`="1" ORDER BY `sort` ASC');
+ while($unit = $sql->get($units))
+ {
+ $sql->query('SELECT `id` FROM `tarifs` WHERE `unit`="'.$unit['id'].'" AND `game`="'.$game.'" AND `show`="1" LIMIT 1');
+ if($sql->num())
+ $list .= '#'.$unit['id'].' '.$unit['name'].' ';
+ }
+
+ return $list;
+ }
+
+ public static function tarifs($game, $unit)
+ {
+ global $sql;
+
+ $list = '';
+
+ $sql->query('SELECT `id`, `name` FROM `tarifs` WHERE `game`="'.$game.'" AND `unit`="'.$unit.'" AND `show`="1" ORDER BY `sort` ASC');
+ while($tarif = $sql->get())
+ $list .= ''.$tarif['name'].' ';
+
+ return $list;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/games/tarifs.php b/system/library/games/tarifs.php
new file mode 100644
index 0000000..bda7f67
--- /dev/null
+++ b/system/library/games/tarifs.php
@@ -0,0 +1,269 @@
+query('SELECT `aid`, `time` FROM `address_buy` WHERE `server`="'.$sid.'" LIMIT 1');
+ if(!$sql->num())
+ return NULL;
+
+ $ip_buy = $sql->get();
+
+ $sql->query('SELECT `ip`, `price` FROM `address` WHERE `id`="'.$ip_buy['aid'].'" LIMIT 1');
+
+ $ip_buy = array_merge($ip_buy, $sql->get());
+
+ $html->get('extend_address', 'sections/servers/games/tarif');
+ $html->set('address', $ip_buy['ip'].':'.params::$aDefPort[$game]);
+ $html->set('iptime', sys::date('max', $ip_buy['time']));
+ $html->set('ipprice', $ip_buy['price']);
+ $html->set('cur', $cfg['currency']);
+ $html->pack('extend_address');
+
+ return NULL;
+ }
+
+ public static function address($server, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ $sql->query('SELECT `address` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $sUnit = $sql->get();
+
+ if(sys::first(explode(':', $sUnit['address'])) != sys::first(explode(':', $server['address'])))
+ {
+ if($cfg['buy_address'][$server['game']])
+ tarif::address_extend($server['address'], $sid);
+
+ return NULL;
+ }
+
+ $options = 'Выберете выделенный адрес ';
+
+ $sql->query('SELECT `id`, `ip`, `price` FROM `address` WHERE `unit`="'.$server['unit'].'" AND `buy`="0"');
+ if(!$sql->num())
+ return NULL;
+
+ while($ip = $sql->get())
+ $options .= ''.$ip['ip'].':'.params::$aDefPort[$server['game']].' ';
+
+ $html->get('address', 'sections/servers/games/tarif');
+ if($cfg['buy_address'][$server['game']]){
+ $html->unit('!mounth');
+ $html->unit('mounth', 1);
+ }else{
+ $html->unit('!mounth', 1);
+ $html->unit('mounth');
+ }
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('address', $server['address']);
+ $html->set('cur', $cfg['currency']);
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function address_extend($address, $sid)
+ {
+ global $cfg, $sql, $html;
+
+ $sql->query('SELECT `aid`, `time` FROM `address_buy` WHERE `server`="'.$sid.'" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $add = $sql->get();
+
+ $sql->query('SELECT `price` FROM `address` WHERE `id`="'.$add['aid'].'" LIMIT 1');
+
+ if(!$sql->num())
+ return NULL;
+
+ $add = array_merge($add, $sql->get());
+
+ $html->get('address_extend', 'sections/servers/games/tarif');
+ $html->set('address', $address);
+ $html->set('time', sys::date('max', $add['time']));
+ $html->set('price', $add['price']);
+ $html->set('cur', $cfg['currency']);
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function address_add_sum($address, $server)
+ {
+ global $sql;
+
+ if(!$address)
+ return 0;
+
+ $ip = sys::first(explode(':', $server['address']));
+
+ $sql->query('SELECT `address` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if($address AND sys::first(explode(':', $unit['address'])) != $ip)
+ {
+ $sql->query('SELECT `price` FROM `address` WHERE `ip`="'.$ip.'" LIMIT 1');
+
+ if($sql->num())
+ {
+ $add = $sql->get();
+
+ return $add['price'];
+ }
+ }
+
+ return 0;
+ }
+
+ public static function slots($server, $aSlots, $sid)
+ {
+ global $cfg, $html, $start_point;
+
+ $options = 'Выберете количество слот ';
+
+ // С уменьшением (min ==> max) || закончился срок аренды
+ if(($cfg['change_slots'][$server['game']]['days'] AND $cfg['change_slots'][$server['game']]['down']) || $server['time'] < $start_point)
+ {
+ for($i = $aSlots['min']; $i <= $aSlots['max']; $i+=1)
+ $options .= ''.$i.' шт. ';
+
+ $html->get('slots', 'sections/servers/games/tarif');
+ }else{
+ if($server['slots'] == $aSlots['max'])
+ return NULL;
+
+ $max = $aSlots['max']-$server['slots'];
+
+ if($max < 1)
+ return NULL;
+
+ for($i = 1; $i <= $max; $i+=1)
+ $options .= ''.$i.' шт. ';
+
+ $html->get('slots_buy', 'sections/servers/games/tarif');
+ }
+
+ $html->set('id', $sid);
+ $html->set('options', $options);
+ $html->set('slots', $server['slots']);
+ $html->set('cur', $cfg['currency']);
+ $html->pack('main');
+
+ return NULL;
+ }
+
+ public static function price($plan)
+ {
+ $aPrice = explode(':', $plan);
+
+ $check = $aPrice[0];
+
+ unset($aPrice[0]);
+
+ if(!count($aPrice))
+ return false;
+
+ foreach($aPrice as $price)
+ if($check != $price)
+ return true;
+
+ return false;
+ }
+
+ public static function unit_old($tarif, $unit, $server, $mcache)
+ {
+ global $ssh, $sql, $user, $start_point;
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $mcache);
+
+ // Убить процессы
+ $ssh->set('kill -9 `ps aux | grep s_'.$server['uid'].' | grep -v grep | awk '."'{print $2}'".' | xargs;'
+ .'lsof -i@:'.$server['address'].' | awk '."'{print $2}'".' | xargs`; sudo -u server'.$server['uid'].' screen -wipe');
+
+ // Директория игрового сервера
+ $install = $tarif['install'].$server['uid'];
+
+ $copys = 'screen -dmS r_copy_'.$server['uid'].' sh -c "';
+
+ $scopy = $sql->query('SELECT `id`, `name` FROM `copy` WHERE `server`="'.$server['id'].'"');
+ while($copy = $sql->get($scopy))
+ {
+ $copys .= 'rm /copy/'.$copy['name'].'.tar;';
+
+ $sql->query('DELETE FROM `copy` WHERE `id`="'.$copy['id'].'" LIMIT 1');
+ }
+
+ $copys .= '";';
+
+ $ssh->set($copys // Удаление резервных копий
+ .'screen -dmS r_'.$server['uid'].' rm -r '.$install.';' // Удаление директории сервера
+ .'userdel server'.$server['uid']); // Удаление пользователя сервера c локации
+
+ // Удаление ftp доступа
+ $qSql = 'DELETE FROM users WHERE username=\''.$server['uid'].'\';'
+ .'DELETE FROM quotalimits WHERE name=\''.$server['uid'].'\';'
+ .'DELETE FROM quotatallies WHERE name=\''.$server['uid'].'\'';
+
+ $ssh->set('screen -dmS ftp'.$server['uid'].' mysql -P '.$unit['sql_port'].' -u'.$unit['sql_login'].' -p'.$unit['sql_passwd'].' --database '.$unit['sql_ftp'].' -e "'.$qSql.'"');
+
+ $sql->query('UPDATE `servers` SET `ftp`="0" WHERE `id`="'.$server['id'].'" LIMIT 1');
+
+ // Очистка правил FireWall
+ games::iptables($server['id'], 'remove', NULL, NULL, NULL, false, $ssh);
+
+ // Удаление заданий из crontab
+ $sql->query('SELECT `address`, `passwd` FROM `panel` LIMIT 1');
+ $panel = $sql->get();
+
+ if(!$ssh->auth($panel['passwd'], $panel['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $mcache);
+
+ $crons = $sql->query('SELECT `id`, `cron` FROM `crontab` WHERE `server`="'.$server['id'].'"');
+ while($cron = $sql->get($crons))
+ {
+ $ssh->set('echo "" >> /etc/crontab && cat /etc/crontab');
+ $crontab = str_replace($cron['cron'], '', $ssh->get());
+
+ // Временный файл
+ $temp = sys::temp($crontab);
+
+ $ssh->setfile($temp, '/etc/crontab', 0644);
+
+ $ssh->set("sed -i '/^$/d' /etc/crontab");
+ $ssh->set('crontab -u root /etc/crontab');
+
+ unlink($temp);
+
+ $sql->query('DELETE FROM `crontab` WHERE `id`="'.$cron['id'].'" LIMIT 1');
+ }
+
+ // Удаление установок игрового сервера
+ $sql->query('DELETE FROM `admins_'.$server['game'].'` WHERE `server`="'.$server['id'].'" LIMIT 1');
+ $sql->query('DELETE FROM `plugins_install` WHERE `server`="'.$server['id'].'" LIMIT 1');
+
+ // Обновление данных выделенного адреса
+ $sql->query('SELECT `id`, `aid` FROM `address_buy` WHERE `server`="'.$server['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $add = $sql->get();
+
+ $sql->query('UPDATE `address` set `buy`="0" WHERE `id`="'.$add['aid'].'" LIMIT 1');
+ $sql->query('DELETE FROM `address_buy` WHERE `id`="'.$add['id'].'" LIMIT 1');
+ }
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/geo.php b/system/library/geo.php
new file mode 100644
index 0000000..443e85a
--- /dev/null
+++ b/system/library/geo.php
@@ -0,0 +1,393 @@
+fh = fopen($db_file, 'rb');
+
+ $header = fread($this->fh, 40);
+ if(substr($header, 0, 3) != 'SxG')
+ die("Can't open {$db_file}\n");
+
+ $info = unpack('Cver/Ntime/Ctype/Ccharset/Cb_idx_len/nm_idx_len/nrange/Ndb_items/Cid_len/nmax_region/nmax_city/Nregion_size/Ncity_size/nmax_country/Ncountry_size/npack_size', substr($header, 3));
+
+ if($info['b_idx_len'] * $info['m_idx_len'] * $info['range'] * $info['db_items'] * $info['time'] * $info['id_len'] == 0)
+ die("Wrong file format {$db_file}\n");
+
+ $this->range = $info['range'];
+ $this->b_idx_len = $info['b_idx_len'];
+ $this->m_idx_len = $info['m_idx_len'];
+ $this->db_items = $info['db_items'];
+ $this->id_len = $info['id_len'];
+ $this->block_len = 3 + $this->id_len;
+ $this->max_region = $info['max_region'];
+ $this->max_city = $info['max_city'];
+ $this->max_country = $info['max_country'];
+ $this->country_size = $info['country_size'];
+ $this->batch_mode = $type & SXGEO_BATCH;
+ $this->memory_mode = $type & SXGEO_MEMORY;
+ $this->pack = $info['pack_size'] ? explode("\0", fread($this->fh, $info['pack_size'])) : '';
+ $this->b_idx_str = fread($this->fh, $info['b_idx_len'] * 4);
+ $this->m_idx_str = fread($this->fh, $info['m_idx_len'] * 4);
+ $this->db_begin = ftell($this->fh);
+
+ if($this->batch_mode)
+ {
+ $this->b_idx_arr = array_values(unpack("N*", $this->b_idx_str));
+
+ unset($this->b_idx_str);
+
+ $this->m_idx_arr = str_split($this->m_idx_str, 4);
+
+ unset($this->m_idx_str);
+ }
+
+ if($this->memory_mode)
+ {
+ $this->db = fread($this->fh, $this->db_items * $this->block_len);
+ $this->regions_db = $info['region_size'] > 0 ? fread($this->fh, $info['region_size']) : '';
+ $this->cities_db = $info['city_size'] > 0 ? fread($this->fh, $info['city_size']) : '';
+ }
+
+ $this->info = $info;
+
+ $this->info['regions_begin'] = $this->db_begin + $this->db_items * $this->block_len;
+ $this->info['cities_begin'] = $this->info['regions_begin'] + $info['region_size'];
+ }
+
+ protected function search_idx($ipn, $min, $max)
+ {
+ if($this->batch_mode)
+ {
+ while($max - $min > 8)
+ {
+ $offset = ($min + $max) >> 1;
+
+ if($ipn > $this->m_idx_arr[$offset])
+ $min = $offset;
+ else
+ $max = $offset;
+ }
+
+ while ($ipn > $this->m_idx_arr[$min] && $min++ < $max){};
+ }else{
+ while($max - $min > 8)
+ {
+ $offset = ($min + $max) >> 1;
+
+ if($ipn > substr($this->m_idx_str, $offset*4, 4))
+ $min = $offset;
+ else
+ $max = $offset;
+ }
+
+ while($ipn > substr($this->m_idx_str, $min*4, 4) && $min++ < $max){};
+ }
+
+ return $min;
+ }
+
+ protected function search_db($str, $ipn, $min, $max)
+ {
+ if($max - $min > 1)
+ {
+ $ipn = substr($ipn, 1);
+
+ while($max - $min > 8)
+ {
+ $offset = ($min + $max) >> 1;
+ if($ipn > substr($str, $offset * $this->block_len, 3))
+ $min = $offset;
+ else
+ $max = $offset;
+ }
+
+ while($ipn >= substr($str, $min * $this->block_len, 3) && ++$min < $max){};
+ }else
+ $min++;
+
+ return hexdec(bin2hex(substr($str, $min * $this->block_len - $this->id_len, $this->id_len)));
+ }
+
+ public function get_num($ip)
+ {
+ $ip1n = (int)$ip;
+
+ if($ip1n == 0 || $ip1n == 10 || $ip1n == 127 || $ip1n >= $this->b_idx_len || false === ($ipn = ip2long($ip)))
+ return false;
+
+ $ipn = pack('N', $ipn);
+ $this->ip1c = chr($ip1n);
+
+ if($this->batch_mode)
+ $blocks = array('min' => $this->b_idx_arr[$ip1n-1], 'max' => $this->b_idx_arr[$ip1n]);
+ else
+ $blocks = unpack("Nmin/Nmax", substr($this->b_idx_str, ($ip1n - 1) * 4, 8));
+
+ if($blocks['max'] - $blocks['min'] > $this->range)
+ {
+ $part = $this->search_idx($ipn, floor($blocks['min'] / $this->range), floor($blocks['max'] / $this->range)-1);
+
+ $min = $part > 0 ? $part * $this->range : 0;
+ $max = $part > $this->m_idx_len ? $this->db_items : ($part+1) * $this->range;
+
+ if($min < $blocks['min'])
+ $min = $blocks['min'];
+
+ if($max > $blocks['max'])
+ $max = $blocks['max'];
+ }else{
+ $min = $blocks['min'];
+ $max = $blocks['max'];
+ }
+
+ $len = $max - $min;
+
+ if($this->memory_mode)
+ return $this->search_db($this->db, $ipn, $min, $max);
+
+ else{
+ fseek($this->fh, $this->db_begin + $min * $this->block_len);
+
+ return $this->search_db(fread($this->fh, $len * $this->block_len), $ipn, 0, $len);
+ }
+ }
+
+ protected function readData($seek, $max, $type)
+ {
+ $raw = '';
+ if($seek && $max)
+ {
+ if ($this->memory_mode)
+ $raw = substr($type == 1 ? $this->regions_db : $this->cities_db, $seek, $max);
+ else{
+ fseek($this->fh, $this->info[$type == 1 ? 'regions_begin' : 'cities_begin'] + $seek);
+
+ $raw = fread($this->fh, $max);
+ }
+ }
+
+ return $this->unpack($this->pack[$type], $raw);
+ }
+
+ protected function parseCity($seek, $full = false)
+ {
+ if(!$this->pack)
+ return false;
+
+ $only_country = false;
+
+ if($seek < $this->country_size)
+ {
+ $country = $this->readData($seek, $this->max_country, 0);
+ $city = $this->unpack($this->pack[2]);
+ $city['lat'] = $country['lat'];
+ $city['lon'] = $country['lon'];
+ $only_country = true;
+ }else{
+ $city = $this->readData($seek, $this->max_city, 2);
+ $country = array('id' => $city['country_id'], 'iso' => $this->id2iso[$city['country_id']]);
+ unset($city['country_id']);
+ }
+
+ if($full){
+ $region = $this->readData($city['region_seek'], $this->max_region, 1);
+
+ if(!$only_country)
+ $country = $this->readData($region['country_seek'], $this->max_country, 0);
+
+ unset($city['region_seek']);
+ unset($region['country_seek']);
+
+ return array('city' => $city, 'region' => $region, 'country' => $country);
+ }else{
+ unset($city['region_seek']);
+
+ return array('city' => $city, 'country' => array('id' => $country['id'], 'iso' => $country['iso']));
+ }
+ }
+
+ protected function unpack($pack, $item = '')
+ {
+ $unpacked = array();
+ $empty = empty($item);
+ $pack = explode('/', $pack);
+ $pos = 0;
+
+ foreach($pack AS $p)
+ {
+ list($type, $name) = explode(':', $p);
+
+ $type0 = $type{0};
+
+ if($empty)
+ {
+ $unpacked[$name] = $type0 == 'b' || $type0 == 'c' ? '' : 0;
+
+ continue;
+ }
+
+ switch($type0)
+ {
+ case 't':
+ case 'T': $l = 1; break;
+ case 's':
+ case 'n':
+ case 'S': $l = 2; break;
+ case 'm':
+ case 'M': $l = 3; break;
+ case 'd': $l = 8; break;
+ case 'c': $l = (int)substr($type, 1); break;
+ case 'b': $l = strpos($item, "\0", $pos)-$pos; break;
+ default: $l = 4;
+ }
+
+ $val = substr($item, $pos, $l);
+
+ switch($type0)
+ {
+ case 't': $v = unpack('c', $val); break;
+ case 'T': $v = unpack('C', $val); break;
+ case 's': $v = unpack('s', $val); break;
+ case 'S': $v = unpack('S', $val); break;
+ case 'm': $v = unpack('l', $val . (ord($val{2}) >> 7 ? "\xff" : "\0")); break;
+ case 'M': $v = unpack('L', $val . "\0"); break;
+ case 'i': $v = unpack('l', $val); break;
+ case 'I': $v = unpack('L', $val); break;
+ case 'f': $v = unpack('f', $val); break;
+ case 'd': $v = unpack('d', $val); break;
+
+ case 'n': $v = current(unpack('s', $val)) / pow(10, $type{1}); break;
+ case 'N': $v = current(unpack('l', $val)) / pow(10, $type{1}); break;
+
+ case 'c': $v = rtrim($val, ' '); break;
+ case 'b': $v = $val; $l++; break;
+ }
+
+ $pos += $l;
+ $unpacked[$name] = is_array($v) ? current($v) : $v;
+ }
+
+ return $unpacked;
+ }
+
+ public function get($ip)
+ {
+ return $this->max_city ? $this->getCity($ip) : $this->getCountry($ip);
+ }
+
+ public function getCountry($ip)
+ {
+ if($this->max_city)
+ {
+ $tmp = $this->parseCity($this->get_num($ip));
+
+ return $tmp['country']['iso'];
+ }else
+ return $this->id2iso[$this->get_num($ip)];
+ }
+
+ public function getCountryId($ip)
+ {
+ if($this->max_city)
+ {
+ $tmp = $this->parseCity($this->get_num($ip));
+
+ return $tmp['country']['id'];
+ }else
+ return $this->get_num($ip);
+ }
+
+ public function getCity($ip)
+ {
+ $seek = $this->get_num($ip);
+
+ return $seek ? $this->parseCity($seek) : false;
+ }
+
+ public function getCityFull($ip)
+ {
+ $seek = $this->get_num($ip);
+
+ return $seek ? $this->parseCity($seek, 1) : false;
+ }
+
+ public function about()
+ {
+ $charset = array('utf-8', 'latin1', 'cp1251');
+ $types = array('n/a', 'SxGeo Country', 'SxGeo City RU', 'SxGeo City EN', 'SxGeo City', 'SxGeo City Max RU', 'SxGeo City Max EN', 'SxGeo City Max');
+
+ return array(
+ 'Created' => date('Y.m.d', $this->info['time']),
+ 'Timestamp' => $this->info['time'],
+ 'Charset' => $charset[$this->info['charset']],
+ 'Type' => $types[$this->info['type']],
+ 'Byte Index' => $this->b_idx_len,
+ 'Main Index' => $this->m_idx_len,
+ 'Blocks In Index Item' => $this->range,
+ 'IP Blocks' => $this->db_items,
+ 'Block Size' => $this->block_len,
+
+ 'City' => array(
+ 'Max Length' => $this->max_city,
+ 'Total Size' => $this->info['city_size'],
+ ),
+
+ 'Region' => array(
+ 'Max Length' => $this->max_region,
+ 'Total Size' => $this->info['region_size'],
+ ),
+
+ 'Country' => array(
+ 'Max Length' => $this->max_country,
+ 'Total Size' => $this->info['country_size'],
+ ),
+ );
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/help.php b/system/library/help.php
new file mode 100644
index 0000000..d94f78d
--- /dev/null
+++ b/system/library/help.php
@@ -0,0 +1,100 @@
+ $str)
+ {
+ $check = str_replace(' ', '', $str);
+
+ if(isset($aStr[$line+1]) AND ($check == '' AND str_replace(' ', '', $aStr[$line+1]) == ''))
+ continue;
+ else{
+ $etext .= rtrim(str_replace("\t", ' ', $str))."\n";
+
+ continue;
+ }
+
+ if($check != '')
+ $etext .= rtrim(str_replace("\t", ' ', $str))."\n";
+ }
+
+ $str_search = array(
+ "#\\\n#is",
+ "#\[spoiler\](.+?)\[\/spoiler\]#is",
+ "#\[sp\](.+?)\[\/sp\]#is",
+ "#\[b\](.+?)\[\/b\]#is",
+ "#\[u\](.+?)\[\/u\]#is",
+ "#\[code\](.+?)\[\/code\]#is",
+ "#\[quote\](.+?)\[\/quote\]#is",
+ "#\[url=(.+?)\](.+?)\[\/url\]#is",
+ "#(^|[\n ])([\w]+?://[\w\#$%&~/.\-;:=,?@\[\]+]*)#is"
+ );
+
+ $str_replace = array(
+ " ",
+ "",
+ "",
+ "\\1 ",
+ "\\1 ",
+ "",
+ "\\1
",
+ "\\2 ",
+ " \\2 "
+ );
+
+ return preg_replace($str_search, $str_replace, $etext);
+ }
+
+ public static function ago($time, $brackets = false)
+ {
+ global $start_point;
+
+ $diff = $start_point-$time;
+
+ if($diff < 0)
+ return '';
+
+ if(!$diff)
+ $diff = 1;
+
+ $seconds = array('секунду', 'секунды', 'секунд');
+ $minutes = array('минуту', 'минуты', 'минут');
+ $hours = array('час', 'часа', 'часов');
+ $days = array('день', 'дня', 'дней');
+ $weeks = array('неделю', 'недели', 'недель');
+ $months = array('месяц', 'месяца', 'месяцев');
+ $years = array('год', 'года', 'лет');
+
+ $phrase = array($seconds, $minutes, $hours, $days, $weeks, $months, $years);
+ $length = array(1, 60, 3600, 86400, 604800, 2630880, 31570560);
+
+ for($i = 6; ($i >= 0) AND (($no = $diff/$length[$i]) <= 1); $i-=1);
+
+ if($i < 0)
+ $i = 0;
+
+ $_time = $start_point-($diff % $length[$i]);
+ $no = ceil($no);
+
+ if($brackets)
+ return '('.$no.' '.help::parse_ago($no, $phrase[$i]).' назад)';
+
+ return $no.' '.help::parse_ago($no, $phrase[$i]).' назад';
+ }
+
+ private static function parse_ago($number, $titles)
+ {
+ $cases = array(2, 0, 1, 1, 1, 2);
+
+ return $titles[($number % 100 > 4 AND $number % 100 < 20 ) ? 2 : $cases[min($number % 10, 5)]];
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/html.php b/system/library/html.php
new file mode 100644
index 0000000..de631fc
--- /dev/null
+++ b/system/library/html.php
@@ -0,0 +1,173 @@
+data['['.$name.']'] = $var;
+
+ if($unset)
+ unset($this->arr[$name]);
+
+ return NULL;
+ }
+
+ public function unit($name, $var = false, $mirror = false)
+ {
+ $block = str_replace($name, "'\\|".$name."\\|(.*?)\\|_".$name."\\|'si", $name);
+
+ $var = $var ? '\\1' : '';
+
+ $this->unitblock[$block] = $var;
+
+ if($mirror)
+ {
+ $block = str_replace($name, "'\\|!".$name."\\|(.*?)\\|_!".$name."\\|'si", $name);
+
+ $var = !$var ? '\\1' : '';
+
+ $this->unitblock[$block] = $var;
+ }
+
+ return NULL;
+ }
+
+ public function nav($name, $link = false)
+ {
+ $this->get('nav');
+ if($link)
+ {
+ $this->set('link', $link);
+ $this->unit('link', 1, 1);
+ }else
+ $this->unit('link', 0, 1);
+ $this->set('name', $name);
+ $this->pack('nav');
+
+ return NULL;
+ }
+
+ public function get($name, $path = '')
+ {
+ global $device, $cfg;
+
+ $path_root = $device == '!mobile' ? '' : 'megp/';
+
+ $path = $path_root.$path;
+
+ if($path != '')
+ $name = str_replace('//', '/', $path.'/'.$name);
+
+ if(!file_exists($this->dir.'/'.$name.'.html'))
+ {
+ $route = explode('/', $name);
+ $namefile = end($route);
+ $dir = $this->dir.str_replace($namefile, '', $name);
+
+ die('Error: html file '.$namefile.'.html not found in: '.$dir.' ');
+ }
+
+ $this->template = file_get_contents($this->dir.'/'.$name.'.html');
+ $this->select_template = $this->template;
+
+ return NULL;
+ }
+
+ private function delete()
+ {
+ unset($this->data);
+ unset($this->unitblock);
+
+ $this->select_template = $this->template;
+
+ return NULL;
+ }
+
+ public function pack($compile)
+ {
+ if(isset($this->unitblock))
+ {
+ $find_preg = array();
+ $replace_preg = array();
+
+ foreach($this->unitblock as $key_find => $key_replace)
+ {
+ $find_preg[] = $key_find;
+ $replace_preg[] = $key_replace;
+ }
+
+ $this->select_template = preg_replace($find_preg, $replace_preg, $this->select_template);
+ }
+
+ $find = array();
+ $replace = array();
+
+ if(isset($this->data))
+ {
+ foreach($this->data as $key_find => $key_replace)
+ {
+ $find[] = $key_find;
+ $replace[] = $key_replace;
+ }
+ }
+
+ $this->select_template = str_replace($find, $replace, $this->select_template);
+
+ if(isset($this->arr[$compile]))
+ $this->arr[$compile] .= $this->select_template;
+ else
+ $this->arr[$compile] = $this->select_template;
+
+ $this->delete();
+
+ return NULL;
+ }
+
+ public function upd($old = array(), $new = array(), $name)
+ {
+ $this->arr[$name] = str_replace($old, $new, $this->arr[$name]);
+
+ return NULL;
+ }
+
+ public function unitall($arr = array(), $name, $var = false, $mirror = false)
+ {
+ $block = str_replace($name, "'\\|".$name."\\|(.*?)\\|_".$name."\\|'si", $name);
+
+ $var = $var ? '\\1' : '';
+
+ $this->unitblock[$block] = $var;
+
+ if($mirror)
+ {
+ $block = str_replace($name, "'\\|!".$name."\\|(.*?)\\|_!".$name."\\|'si", $name);
+
+ $var = !$var ? '\\1' : '';
+
+ $this->unitblock[$block] = $var;
+ }
+
+ if(count($this->unitblock))
+ {
+ foreach($this->unitblock as $key_find => $key_replace)
+ {
+ $find_preg[] = $key_find;
+ $replace_preg[] = $key_replace;
+ }
+
+ $this->arr[$arr] = preg_replace($find_preg, $replace_preg, $this->arr[$arr]);
+ }
+
+ return NULL;
+ }
+ }
+
+ $html = new html;
+?>
\ No newline at end of file
diff --git a/system/library/megp.php b/system/library/megp.php
new file mode 100644
index 0000000..697954c
--- /dev/null
+++ b/system/library/megp.php
@@ -0,0 +1,744 @@
+ array('matches' => array(
+ 'application/x-obml2d',
+ 'application/vnd.rim.html',
+ 'text/vnd.wap.wml',
+ 'application/vnd.wap.xhtml+xml'
+ )),
+ 'HTTP_X_WAP_PROFILE' => null,
+ 'HTTP_X_WAP_CLIENTID' => null,
+ 'HTTP_WAP_CONNECTION' => null,
+ 'HTTP_PROFILE' => null,
+ 'HTTP_X_OPERAMINI_PHONE_UA' => null,
+ 'HTTP_X_NOKIA_GATEWAY_ID' => null,
+ 'HTTP_X_ORANGE_ID' => null,
+ 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
+ 'HTTP_X_HUAWEI_USERID' => null,
+ 'HTTP_UA_OS' => null,
+ 'HTTP_X_MOBILE_GATEWAY' => null,
+ 'HTTP_X_ATT_DEVICEID' => null,
+ 'HTTP_UA_CPU' => array('matches' => array('ARM')),
+ );
+
+ protected static $phoneDevices = array(
+ 'iPhone' => '\biPhone\b|\biPod\b',
+ 'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+',
+ 'HTC' => 'HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\bEVO\b|T-Mobile G1|Z520m',
+ 'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 6',
+ 'Dell' => 'Dell.*Streak|Dell.*Aero|Dell.*Venue|DELL.*Venue Pro|Dell Flash|Dell Smoke|Dell Mini 3iX|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b',
+ 'Motorola' => 'Motorola|DROIDX|DROID BIONIC|\bDroid\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\bMoto E\b',
+ 'Samsung' => '\bSamsung\b|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K|SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558|GT-I9205|SM-G9350|SM-J120F|SM-G920F|SM-G920V|SM-G930F|SM-N910C',
+ 'LG' => '\bLG\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323)',
+ 'Sony' => 'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533',
+ 'Asus' => 'Asus.*Galaxy|PadFone.*Mobile',
+ 'NokiaLumia' => 'Lumia [0-9]{3,4}',
+ 'Micromax' => 'Micromax.*\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\b',
+ 'Palm' => 'PalmSource|Palm',
+ 'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature',
+ 'Pantech' => 'PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790',
+ 'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250',
+ 'Wiko' => 'KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY|BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM',
+ 'iMobile' => 'i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)',
+ 'SimValley' => '\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\b',
+ 'Wolfgang' => 'AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q',
+ 'Alcatel' => 'Alcatel',
+ 'Nintendo' => 'Nintendo 3DS',
+ 'Amoi' => 'Amoi',
+ 'INQ' => 'INQ',
+ 'GenericPhone' => 'Tapatalk|PDA;|SAGEM|\bmmp\b|pocket|\bpsp\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\bwap\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser',
+ );
+
+ protected static $tabletDevices = array(
+ 'iPad' => 'iPad|iPad.*Mobile',
+ 'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)',
+ 'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587', // SCH-P709|SCH-P729|SM-T2558|GT-I9205 - Samsung Mega - treat them like a regular phone.
+ 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\b|Android.*Silk/[0-9.]+ like Chrome/[0-9.]+ (?!Mobile)',
+ 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)',
+ 'HPTablet' => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10',
+ 'AsusTablet' => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b|\bK00C\b|\bK00E\b|\bK00L\b|TX201LA|ME176C|ME102A|\bM80TA\b|ME372CL|ME560CG|ME372CG|ME302KL| K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\bME70C\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z',
+ 'BlackBerryTablet' => 'PlayBook|RIM Tablet',
+ 'HTCtablet' => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410',
+ 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
+ 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
+ 'AcerTablet' => 'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\b|W3-810|\bA3-A10\b|\bA3-A11\b|\bA3-A20\b|\bA3-A30',
+ 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
+ 'LGTablet' => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b',
+ 'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b',
+ 'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002',
+ 'LenovoTablet' => 'Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)',
+ 'DellTablet' => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7',
+ 'YarvikTablet' => 'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b',
+ 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
+ 'ArnovaTablet' => '97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2',
+ 'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004',
+ 'IRUTablet' => 'M702pro',
+ 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
+ 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
+ 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
+ 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b',
+ 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
+ 'NokiaLumiaTablet' => 'Lumia 2520',
+ 'SonyTablet' => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP612|SOT31',
+ 'PhilipsTablet' => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b',
+ 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
+ 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
+ 'MIDTablet' => 'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10',
+ 'MSITablet' => 'MSI \b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\b',
+ 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
+ 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
+ 'FlyTablet' => 'IQ310|Fly Vision',
+ 'bqTablet' => 'Android.*(bq)?.*(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris [E|M]10)|Maxwell.*Lite|Maxwell.*Plus',
+ 'HuaweiTablet' => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim',
+ 'NecTablet' => '\bN-06D|\bN-08D',
+ 'PantechTablet' => 'Pantech.*P4100',
+ 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
+ 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
+ 'ZyncTablet' => 'z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900',
+ 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
+ 'NabiTablet' => 'Android.*\bNabi',
+ 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
+ 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
+ 'TexetTablet' => 'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE',
+ 'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
+ 'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab',
+ 'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b',
+ 'AdvanTablet' => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ',
+ 'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1',
+ 'GalapadTablet' => 'Android.*\bG1\b',
+ 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
+ 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
+ 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
+ 'PROSCANTablet' => '\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\b',
+ 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
+ 'ChangJiaTablet' => 'TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503',
+ 'GUTablet' => 'TX-A1301|TX-M9002|Q702|kf026', // A12R|D75A|D77|D79|R83|A95|A106C|R15|A75|A76|D71|D72|R71|R73|R77|D82|R85|D92|A97|D92|R91|A10F|A77F|W71F|A78F|W78F|W81F|A97F|W91F|W97F|R16G|C72|C73E|K72|K73|R96G
+ 'PointOfViewTablet' => 'TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10',
+ 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)',
+ 'HCLTablet' => 'HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync',
+ 'DPSTablet' => 'DPS Dream 9|DPS Dual 7',
+ 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
+ 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
+ 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
+ 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
+ 'GoCleverTablet' => 'GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042',
+ 'ModecomTablet' => 'FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003',
+ 'VoninoTablet' => '\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b',
+ 'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1',
+ 'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab',
+ 'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497',
+ 'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2',
+ 'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711',
+ 'iMobileTablet' => 'i-mobile i-note',
+ 'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine',
+ 'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b',
+ 'AMPETablet' => 'Android.* A78 ',
+ 'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)',
+ 'TecnoTablet' => 'TECNO P9',
+ 'JXDTablet' => 'Android.* \b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b',
+ 'iJoyTablet' => 'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)',
+ 'FX2Tablet' => 'FX2 PAD7|FX2 PAD10',
+ 'XoroTablet' => 'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151',
+ 'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a',
+ 'OdysTablet' => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10',
+ 'CaptivaTablet' => 'CAPTIVA PAD',
+ 'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S',
+ 'TeclastTablet' => 'T98 4G|\bP80\b|\bX90HD\b|X98 Air|X98 Air 3G|\bX89\b|P80 3G|\bX80h\b|P98 Air|\bX89HD\b|P98 3G|\bP90HD\b|P89 3G|X98 3G|\bP70h\b|P79HD 3G|G18d 3G|\bP79HD\b|\bP89s\b|\bA88\b|\bP10HD\b|\bP19HD\b|G18 3G|\bP78HD\b|\bA78\b|\bP75\b|G17s 3G|G17h 3G|\bP85t\b|\bP90\b|\bP11\b|\bP98t\b|\bP98HD\b|\bG18d\b|\bP85s\b|\bP11HD\b|\bP88s\b|\bA80HD\b|\bA80se\b|\bA10h\b|\bP89\b|\bP78s\b|\bG18\b|\bP85\b|\bA70h\b|\bA70\b|\bG17\b|\bP18\b|\bA80s\b|\bA11s\b|\bP88HD\b|\bA80h\b|\bP76s\b|\bP76h\b|\bP98\b|\bA10HD\b|\bP78\b|\bP88\b|\bA11\b|\bA10t\b|\bP76a\b|\bP76t\b|\bP76e\b|\bP85HD\b|\bP85a\b|\bP86\b|\bP75HD\b|\bP76v\b|\bA12\b|\bP75a\b|\bA15\b|\bP76Ti\b|\bP81HD\b|\bA10\b|\bT760VE\b|\bT720HD\b|\bP76\b|\bP73\b|\bP71\b|\bP72\b|\bT720SE\b|\bC520Ti\b|\bT760\b|\bT720VE\b|T720-3GE|T720-WiFi',
+ 'OndaTablet' => '\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\b[\s]+',
+ 'JaytechTablet' => 'TPC-PA762',
+ 'BlaupunktTablet' => 'Endeavour 800NG|Endeavour 1010',
+ 'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b',
+ 'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b',
+ 'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY|\bE-TAB\b',
+ 'AocTablet' => 'MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712',
+ 'MpmanTablet' => 'MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\bMPG7\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010',
+ 'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b',
+ 'WolderTablet' => 'miTab \b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\b',
+ 'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b',
+ 'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One',
+ 'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI',
+ 'LeaderTablet' => 'TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100',
+ 'UbislateTablet' => 'UbiSlate[\s]?7C',
+ 'PocketBookTablet' => 'Pocketbook',
+ 'KocasoTablet' => '\b(TB-1207)\b',
+ 'HisenseTablet' => '\b(F5281|E2371)\b',
+ 'Hudl' => 'Hudl HT7S3|Hudl 2',
+ 'TelstraTablet' => 'T-Hub2',
+ 'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\bM6pro\b|CT1020W|arc 10HD|\bTP750\b'
+ );
+
+ protected static $operatingSystems = array(
+ 'AndroidOS' => 'Android',
+ 'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os',
+ 'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino',
+ 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b',
+ 'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Window Mobile|Windows Phone [0-9.]+|WCE;',
+ 'WindowsPhoneOS' => 'Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;',
+ 'iOS' => '\biPhone.*Mobile|\biPod|\biPad',
+ 'MeeGoOS' => 'MeeGo',
+ 'MaemoOS' => 'Maemo',
+ 'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b',
+ 'webOS' => 'webOS|hpwOS',
+ 'badaOS' => '\bBada\b',
+ 'BREWOS' => 'BREW'
+ );
+
+ protected static $browsers = array(
+ 'Chrome' => '\bCrMo\b|CriOS|Android.*Chrome/[.0-9]* (Mobile)?',
+ 'Dolfin' => '\bDolfin\b',
+ 'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+|Coast/[0-9.]+',
+ 'Skyfire' => 'Skyfire',
+ 'Edge' => 'Mobile Safari/[.0-9]* Edge',
+ 'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+
+ 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS',
+ 'Bolt' => 'bolt',
+ 'TeaShark' => 'teashark',
+ 'Blazer' => 'Blazer',
+ 'Safari' => 'Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari',
+ 'UCBrowser' => 'UC.*Browser|UCWEB',
+ 'baiduboxapp' => 'baiduboxapp',
+ 'baidubrowser' => 'baidubrowser',
+ 'DiigoBrowser' => 'DiigoBrowser',
+ 'Puffin' => 'Puffin',
+ 'Mercury' => '\bMercury\b',
+ 'ObigoBrowser' => 'Obigo',
+ 'NetFront' => 'NF-Browser',
+ 'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger',
+ 'PaleMoon' => 'Android.*PaleMoon|Mobile.*PaleMoon'
+ );
+
+ protected static $utilities = array(
+ 'Bot' => 'Googlebot|facebookexternalhit|AdsBot-Google|Google Keyword Suggestion|Facebot|YandexBot|YandexMobileBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|Exabot|MJ12bot|YandexImages|TurnitinBot|Pingdom',
+ 'MobileBot' => 'Googlebot-Mobile|AdsBot-Google-Mobile|YahooSeeker/M1A1-R2D2',
+ 'DesktopMode' => 'WPDesktop',
+ 'TV' => 'SonyDTV|HbbTV', // experimental
+ 'WebKit' => '(webkit)[ /]([\w.]+)',
+ 'Console' => '\b(Nintendo|Nintendo WiiU|Nintendo 3DS|PLAYSTATION|Xbox)\b',
+ 'Watch' => 'SM-V700'
+ );
+
+ protected static $uaHttpHeaders = array(
+ 'HTTP_USER_AGENT',
+ 'HTTP_X_OPERAMINI_PHONE_UA',
+ 'HTTP_X_DEVICE_USER_AGENT',
+ 'HTTP_X_ORIGINAL_USER_AGENT',
+ 'HTTP_X_SKYFIRE_PHONE',
+ 'HTTP_X_BOLT_PHONE_UA',
+ 'HTTP_DEVICE_STOCK_UA',
+ 'HTTP_X_UCBROWSER_DEVICE_UA'
+ );
+
+ protected static $properties = array(
+ 'Mobile' => 'Mobile/[VER]',
+ 'Build' => 'Build/[VER]',
+ 'Version' => 'Version/[VER]',
+ 'VendorID' => 'VendorID/[VER]',
+ 'iPad' => 'iPad.*CPU[a-z ]+[VER]',
+ 'iPhone' => 'iPhone.*CPU[a-z ]+[VER]',
+ 'iPod' => 'iPod.*CPU[a-z ]+[VER]',
+ 'Kindle' => 'Kindle/[VER]',
+ 'Chrome' => array('Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'),
+ 'Coast' => array('Coast/[VER]'),
+ 'Dolfin' => 'Dolfin/[VER]',
+ 'Firefox' => array('Firefox/[VER]', 'FxiOS/[VER]'),
+ 'Fennec' => 'Fennec/[VER]',
+ 'Edge' => 'Edge/[VER]',
+ 'IE' => array('IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];', 'Trident/[0-9.]+;.*rv:[VER]'),
+ 'NetFront' => 'NetFront/[VER]',
+ 'NokiaBrowser' => 'NokiaBrowser/[VER]',
+ 'Opera' => array('OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]'),
+ 'Opera Mini' => 'Opera Mini/[VER]',
+ 'Opera Mobi' => 'Version/[VER]',
+ 'UC Browser' => 'UC Browser[VER]',
+ 'MQQBrowser' => 'MQQBrowser/[VER]',
+ 'MicroMessenger' => 'MicroMessenger/[VER]',
+ 'baiduboxapp' => 'baiduboxapp/[VER]',
+ 'baidubrowser' => 'baidubrowser/[VER]',
+ 'SamsungBrowser' => 'SamsungBrowser/[VER]',
+ 'Iron' => 'Iron/[VER]',
+ 'Safari' => array('Version/[VER]', 'Safari/[VER]'),
+ 'Skyfire' => 'Skyfire/[VER]',
+ 'Tizen' => 'Tizen/[VER]',
+ 'Webkit' => 'webkit[ /][VER]',
+ 'PaleMoon' => 'PaleMoon/[VER]',
+ 'Gecko' => 'Gecko/[VER]',
+ 'Trident' => 'Trident/[VER]',
+ 'Presto' => 'Presto/[VER]',
+ 'Goanna' => 'Goanna/[VER]',
+ 'iOS' => ' \bi?OS\b [VER][ ;]{1}',
+ 'Android' => 'Android [VER]',
+ 'BlackBerry' => array('BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'),
+ 'BREW' => 'BREW [VER]',
+ 'Java' => 'Java/[VER]',
+ 'Windows Phone OS' => array('Windows Phone OS [VER]', 'Windows Phone [VER]'),
+ 'Windows Phone' => 'Windows Phone [VER]',
+ 'Windows CE' => 'Windows CE/[VER]',
+ 'Windows NT' => 'Windows NT [VER]',
+ 'Symbian' => array('SymbianOS/[VER]', 'Symbian/[VER]'),
+ 'webOS' => array('webOS/[VER]', 'hpwOS/[VER];')
+ );
+
+ public function __construct(array $headers = null, $userAgent = null)
+ {
+ $this->setHttpHeaders($headers);
+ $this->setUserAgent($userAgent);
+ }
+
+ public static function getScriptVersion()
+ {
+ return self::VERSION;
+ }
+
+ public function setHttpHeaders($httpHeaders = null)
+ {
+ if(!is_array($httpHeaders) || !count($httpHeaders))
+ $httpHeaders = $_SERVER;
+
+ $this->httpHeaders = array();
+
+ foreach($httpHeaders as $key => $value)
+ {
+ if(substr($key, 0, 5) === 'HTTP_')
+ $this->httpHeaders[$key] = $value;
+ }
+
+ $this->setCfHeaders($httpHeaders);
+ }
+
+ public function getHttpHeaders()
+ {
+ return $this->httpHeaders;
+ }
+
+ public function getHttpHeader($header)
+ {
+ if(strpos($header, '_') === false)
+ {
+ $header = str_replace('-', '_', $header);
+ $header = strtoupper($header);
+ }
+
+ $altHeader = 'HTTP_' . $header;
+
+ if(isset($this->httpHeaders[$header]))
+ return $this->httpHeaders[$header];
+ elseif(isset($this->httpHeaders[$altHeader]))
+ return $this->httpHeaders[$altHeader];
+
+ return null;
+ }
+
+ public function getMobileHeaders()
+ {
+ return self::$mobileHeaders;
+ }
+
+ public function getUaHttpHeaders()
+ {
+ return self::$uaHttpHeaders;
+ }
+
+ public function setCfHeaders($cfHeaders = null)
+ {
+ if(!is_array($cfHeaders) || !count($cfHeaders))
+ $cfHeaders = $_SERVER;
+
+ $this->cloudfrontHeaders = array();
+ $response = false;
+
+ foreach($cfHeaders as $key => $value)
+ {
+ if(substr(strtolower($key), 0, 16) === 'http_cloudfront_')
+ {
+ $this->cloudfrontHeaders[strtoupper($key)] = $value;
+ $response = true;
+ }
+ }
+
+ return $response;
+ }
+
+ public function getCfHeaders()
+ {
+ return $this->cloudfrontHeaders;
+ }
+
+ public function setUserAgent($userAgent = null)
+ {
+ $this->cache = array();
+
+ if(false === empty($userAgent))
+ return $this->userAgent = $userAgent;
+ else{
+ $this->userAgent = null;
+
+ foreach($this->getUaHttpHeaders() as $altHeader)
+ if(false === empty($this->httpHeaders[$altHeader]))
+ $this->userAgent .= $this->httpHeaders[$altHeader].' ';
+
+ if(!empty($this->userAgent))
+ return $this->userAgent = trim($this->userAgent);
+ }
+
+ if(count($this->getCfHeaders()) > 0)
+ return $this->userAgent = 'Amazon CloudFront';
+
+ return $this->userAgent = null;
+ }
+
+ public function getUserAgent()
+ {
+ return $this->userAgent;
+ }
+
+ public function setDetectionType($type = null)
+ {
+ if($type === null)
+ $type = self::DETECTION_TYPE_MOBILE;
+
+ if($type !== self::DETECTION_TYPE_MOBILE && $type !== self::DETECTION_TYPE_EXTENDED)
+ return;
+
+ $this->detectionType = $type;
+ }
+
+ public function getMatchingRegex()
+ {
+ return $this->matchingRegex;
+ }
+
+ public function getMatchesArray()
+ {
+ return $this->matchesArray;
+ }
+
+ public static function getPhoneDevices()
+ {
+ return self::$phoneDevices;
+ }
+
+ public static function getTabletDevices()
+ {
+ return self::$tabletDevices;
+ }
+
+ public static function getUserAgents()
+ {
+ return self::getBrowsers();
+ }
+
+ public static function getBrowsers()
+ {
+ return self::$browsers;
+ }
+
+ public static function getUtilities()
+ {
+ return self::$utilities;
+ }
+
+ public static function getMobileDetectionRules()
+ {
+ static $rules;
+
+ if(!$rules)
+ {
+ $rules = array_merge(
+ self::$phoneDevices,
+ self::$tabletDevices,
+ self::$operatingSystems,
+ self::$browsers
+ );
+ }
+
+ return $rules;
+ }
+
+ public function getMobileDetectionRulesExtended()
+ {
+ static $rules;
+
+ if(!$rules)
+ {
+ $rules = array_merge(
+ self::$phoneDevices,
+ self::$tabletDevices,
+ self::$operatingSystems,
+ self::$browsers,
+ self::$utilities
+ );
+ }
+
+ return $rules;
+ }
+
+ public function getRules()
+ {
+ if($this->detectionType == self::DETECTION_TYPE_EXTENDED)
+ return self::getMobileDetectionRulesExtended();
+
+ return self::getMobileDetectionRules();
+ }
+
+ public static function getOperatingSystems()
+ {
+ return self::$operatingSystems;
+ }
+
+ public function checkHttpHeadersForMobile()
+ {
+ foreach($this->getMobileHeaders() as $mobileHeader => $matchType)
+ {
+ if(isset($this->httpHeaders[$mobileHeader]))
+ {
+ if(is_array($matchType['matches']))
+ {
+ foreach ($matchType['matches'] as $_match)
+ if(strpos($this->httpHeaders[$mobileHeader], $_match) !== false)
+ return true;
+
+ return false;
+ }else
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function __call($name, $arguments)
+ {
+ if(substr($name, 0, 2) !== 'is')
+ throw new BadMethodCallException('No such method exists: '.$name);
+
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
+ $key = substr($name, 2);
+
+ return $this->matchUAAgainstKey($key);
+ }
+
+ protected function matchDetectionRulesAgainstUA($userAgent = null)
+ {
+ foreach($this->getRules() as $_regex)
+ {
+ if(empty($_regex))
+ continue;
+
+ if($this->match($_regex, $userAgent))
+ return true;
+ }
+
+ return false;
+ }
+
+ protected function matchUAAgainstKey($key)
+ {
+ $key = strtolower($key);
+
+ if(false === isset($this->cache[$key]))
+ {
+ $_rules = array_change_key_case($this->getRules());
+
+ if(false === empty($_rules[$key]))
+ $this->cache[$key] = $this->match($_rules[$key]);
+
+ if(false === isset($this->cache[$key]))
+ $this->cache[$key] = false;
+ }
+
+ return $this->cache[$key];
+ }
+
+ public function isMobile($userAgent = null, $httpHeaders = null)
+ {
+ if($httpHeaders)
+ $this->setHttpHeaders($httpHeaders);
+
+ if($userAgent)
+ $this->setUserAgent($userAgent);
+
+ if($this->getUserAgent() === 'Amazon CloudFront')
+ {
+ $cfHeaders = $this->getCfHeaders();
+
+ if(array_key_exists('HTTP_CLOUDFRONT_IS_MOBILE_VIEWER', $cfHeaders) && $cfHeaders['HTTP_CLOUDFRONT_IS_MOBILE_VIEWER'] === 'true')
+ return true;
+ }
+
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
+
+ if($this->checkHttpHeadersForMobile())
+ return true;
+
+ return $this->matchDetectionRulesAgainstUA();
+ }
+
+ public function isTablet($userAgent = null, $httpHeaders = null)
+ {
+ if($this->getUserAgent() === 'Amazon CloudFront')
+ {
+ $cfHeaders = $this->getCfHeaders();
+
+ if(array_key_exists('HTTP_CLOUDFRONT_IS_TABLET_VIEWER', $cfHeaders) && $cfHeaders['HTTP_CLOUDFRONT_IS_TABLET_VIEWER'] === 'true')
+ return true;
+ }
+
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
+
+ foreach(self::$tabletDevices as $_regex)
+ if($this->match($_regex, $userAgent))
+ return true;
+
+ return false;
+ }
+
+ public function is($key, $userAgent = null, $httpHeaders = null)
+ {
+ if($httpHeaders)
+ $this->setHttpHeaders($httpHeaders);
+
+ if($userAgent)
+ $this->setUserAgent($userAgent);
+
+ $this->setDetectionType(self::DETECTION_TYPE_EXTENDED);
+
+ return $this->matchUAAgainstKey($key);
+ }
+
+ public function match($regex, $userAgent = null)
+ {
+ $match = (bool) preg_match(sprintf('#%s#is', $regex), (false === empty($userAgent) ? $userAgent : $this->userAgent), $matches);
+
+ if($match)
+ {
+ $this->matchingRegex = $regex;
+ $this->matchesArray = $matches;
+ }
+
+ return $match;
+ }
+
+ public static function getProperties()
+ {
+ return self::$properties;
+ }
+
+ public function prepareVersionNo($ver)
+ {
+ $ver = str_replace(array('_', ' ', '/'), '.', $ver);
+ $arrVer = explode('.', $ver, 2);
+
+ if(isset($arrVer[1]))
+ $arrVer[1] = @str_replace('.', '', $arrVer[1]);
+
+ return (float) implode('.', $arrVer);
+ }
+
+ public function version($propertyName, $type = self::VERSION_TYPE_STRING)
+ {
+ if(empty($propertyName))
+ return false;
+
+ if($type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT)
+ $type = self::VERSION_TYPE_STRING;
+
+ $properties = self::getProperties();
+
+ if(true === isset($properties[$propertyName]))
+ {
+ $properties[$propertyName] = (array) $properties[$propertyName];
+
+ foreach($properties[$propertyName] as $propertyMatchString)
+ {
+ $propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);
+ preg_match(sprintf('#%s#is', $propertyPattern), $this->userAgent, $match);
+
+ if(false === empty($match[1]))
+ {
+ $version = ($type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1]);
+
+ return $version;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public function mobileGrade()
+ {
+ $isMobile = $this->isMobile();
+ if($this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT) >= 4.3 ||
+ $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT) >= 4.3 ||
+ $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT) >= 4.3 ||
+ ( $this->version('Android', self::VERSION_TYPE_FLOAT)>2.1 && $this->is('Webkit') ) ||
+ $this->version('Windows Phone OS', self::VERSION_TYPE_FLOAT) >= 7.5 ||
+ $this->is('BlackBerry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) >= 6.0 ||
+ $this->match('Playbook.*Tablet') ||
+ ( $this->version('webOS', self::VERSION_TYPE_FLOAT) >= 1.4 && $this->match('Palm|Pre|Pixi') ) ||
+ $this->match('hp.*TouchPad') ||
+ ( $this->is('Firefox') && $this->version('Firefox', self::VERSION_TYPE_FLOAT) >= 18 ) ||
+ ( $this->is('Chrome') && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 4.0 ) ||
+ ( $this->is('Skyfire') && $this->version('Skyfire', self::VERSION_TYPE_FLOAT) >= 4.1 && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 ) ||
+ ( $this->is('Opera') && $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT) >= 11.5 && $this->is('AndroidOS') ) ||
+ $this->is('MeeGoOS') ||
+ $this->is('Tizen') ||
+ $this->is('Dolfin') && $this->version('Bada', self::VERSION_TYPE_FLOAT) >= 2.0 ||
+ ( ($this->is('UC Browser') || $this->is('Dolfin')) && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 ) ||
+ ( $this->match('Kindle Fire') ||
+ $this->is('Kindle') && $this->version('Kindle', self::VERSION_TYPE_FLOAT) >= 3.0 ) ||
+ $this->is('AndroidOS') && $this->is('NookTablet') ||
+ $this->version('Chrome', self::VERSION_TYPE_FLOAT) >= 16 && !$isMobile ||
+ $this->version('Safari', self::VERSION_TYPE_FLOAT) >= 5.0 && !$isMobile ||
+ $this->version('Firefox', self::VERSION_TYPE_FLOAT) >= 10.0 && !$isMobile ||
+ $this->version('IE', self::VERSION_TYPE_FLOAT) >= 7.0 && !$isMobile ||
+ $this->version('Opera', self::VERSION_TYPE_FLOAT) >= 10 && !$isMobile)
+ return self::MOBILE_GRADE_A;
+
+ if($this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT)<4.3 ||
+ $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT)<4.3 ||
+ $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT)<4.3 ||
+ $this->is('Blackberry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) >= 5 && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<6 ||
+ ($this->version('Opera Mini', self::VERSION_TYPE_FLOAT) >= 5.0 && $this->version('Opera Mini', self::VERSION_TYPE_FLOAT) <= 7.0 &&
+ ($this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 || $this->is('iOS')) ) ||
+ $this->match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') ||
+ $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT) >= 11 && $this->is('SymbianOS'))
+ return self::MOBILE_GRADE_B;
+
+ if($this->version('BlackBerry', self::VERSION_TYPE_FLOAT) <= 5.0 ||
+ $this->match('MSIEMobile|Windows CE.*Mobile') || $this->version('Windows Mobile', self::VERSION_TYPE_FLOAT) <= 5.2 ||
+ $this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT) <= 3.2 ||
+ $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT) <= 3.2 ||
+ $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT) <= 3.2 ||
+ $this->version('IE', self::VERSION_TYPE_FLOAT) <= 7.0 && !$isMobile)
+ return self::MOBILE_GRADE_C;
+
+ return self::MOBILE_GRADE_C;
+ }
+ }
+
+ $megp = new megp;
+?>
\ No newline at end of file
diff --git a/system/library/smtp.php b/system/library/smtp.php
new file mode 100644
index 0000000..d2a3361
--- /dev/null
+++ b/system/library/smtp.php
@@ -0,0 +1,134 @@
+smtp_username = $smtp_username;
+ $this->smtp_password = $smtp_password;
+ $this->smtp_host = $smtp_host;
+ $this->smtp_from = $smtp_from;
+ $this->smtp_port = $smtp_port;
+ $this->smtp_charset = $smtp_charset;
+ }
+
+ function send($mailTo, $subject, $message, $headers)
+ {
+ global $cfg;
+
+ $contentMail = 'Date: '.date('D, d M Y H:i:s')." UT\r\n";
+ $contentMail .= 'Subject: =?'.$this->smtp_charset.'?B?'.base64_encode($subject)."=?=\r\n";
+ $contentMail .= $headers."\r\n";
+ $contentMail .= $message."\r\n";
+
+ try
+ {
+ if(!$socket = @fsockopen($this->smtp_host, $this->smtp_port, $errorNumber, $errorDescription, 30))
+ throw new Exception($errorNumber.'.'.$errorDescription);
+
+ if(!$this->_parseServer($socket, '220'))
+ throw new Exception('Connection error');
+
+ $server_name = $cfg['url'];
+
+ fputs($socket, 'HELO '.$server_name."\r\n");
+
+ if(!$this->_parseServer($socket, '250'))
+ {
+ fclose($socket);
+ throw new Exception('Error of command sending: HELO');
+ }
+
+ fputs($socket, 'AUTH LOGIN'."\r\n");
+
+ if(!$this->_parseServer($socket, '334'))
+ {
+ fclose($socket);
+ throw new Exception('Autorization error');
+ }
+
+ fputs($socket, base64_encode($this->smtp_username)."\r\n");
+
+ if(!$this->_parseServer($socket, '334'))
+ {
+ fclose($socket);
+ throw new Exception('Autorization error');
+ }
+
+ fputs($socket, base64_encode($this->smtp_password)."\r\n");
+
+ if(!$this->_parseServer($socket, '235'))
+ {
+ fclose($socket);
+ throw new Exception('Autorization error');
+ }
+
+ fputs($socket, 'MAIL FROM: <'.$this->smtp_username.">\r\n");
+
+ if(!$this->_parseServer($socket, '250'))
+ {
+ fclose($socket);
+ throw new Exception('Error of command sending: MAIL FROM');
+ }
+
+ $mailTo = ltrim($mailTo, '<');
+ $mailTo = rtrim($mailTo, '>');
+
+ fputs($socket, 'RCPT TO: <'.$mailTo.">\r\n");
+
+ if(!$this->_parseServer($socket, '250'))
+ {
+ fclose($socket);
+ throw new Exception('Error of command sending: RCPT TO');
+ }
+
+ fputs($socket, 'DATA'."\r\n");
+
+ if(!$this->_parseServer($socket, "354"))
+ {
+ fclose($socket);
+ throw new Exception('Error of command sending: DATA');
+ }
+
+ fputs($socket, $contentMail."\r\n.\r\n");
+
+ if(!$this->_parseServer($socket, '250'))
+ {
+ fclose($socket);
+ throw new Exception('E-mail didn\'t sent');
+ }
+
+ fputs($socket, 'QUIT'."\r\n");
+ fclose($socket);
+ }
+
+ catch(Exception $e)
+ {
+ return $e->getMessage();
+ }
+
+ return true;
+ }
+
+ private function _parseServer($socket, $response)
+ {
+ while(@substr($responseServer, 3, 1) != ' ')
+ if(!($responseServer = fgets($socket, 256)))
+ return false;
+
+ if(!(substr($responseServer, 0, 3) == $response))
+ return false;
+
+ return true;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/sql.php b/system/library/sql.php
new file mode 100644
index 0000000..477a78b
--- /dev/null
+++ b/system/library/sql.php
@@ -0,0 +1,87 @@
+sql_id = @new mysqli($c, $u, $p, $n))
+ {
+ if(!ERROR_DATABASE)
+ return NULL;
+
+ $this->out_error(mysqli_connect_error());
+ }
+
+ mysqli_query($this->sql_id, "/*!40101 SET NAMES 'utf8' */");
+
+ $this->sql_connect = true;
+
+ return NULL;
+ }
+
+ public function query($query)
+ {
+ if(!$this->sql_connect)
+ $this->connect_mysql(CONNECT_DATABASE, USER_DATABASE, PASSWORD_DATABASE, NAME_DATABASE);
+
+ if(!($this->query_id = mysqli_query($this->sql_id, $query)) and (mysqli_error($this->sql_id) and ERROR_DATABASE))
+ $this->out_error(mysqli_error($this->sql_id), $query);
+
+ return $this->query_id;
+ }
+
+ public function get($query_id = false)
+ {
+ if(!$query_id)
+ $query_id = $this->query_id;
+
+ $get = mysqli_fetch_assoc($query_id);
+
+ return $get;
+ }
+
+ public function num($query_id = false)
+ {
+ if(!$query_id)
+ $query_id = $this->query_id;
+
+ return mysqli_num_rows($query_id);
+ }
+
+ public function id()
+ {
+ return mysqli_insert_id($this->sql_id);
+ }
+
+ public function esc()
+ {
+ mysqli_close($this->query_id);
+ mysqli_stmt_close($this->sql_id);
+ }
+
+ private function out_error($error, $query = '')
+ {
+ global $go;
+
+ if($go)
+ sys::outjs(array('e' => 'Query: '.$query.' Error: '.$error));
+
+ if($query != '')
+ echo 'Query: '.$query.' ';
+
+ echo 'Error: '.$error;
+
+ exit();
+ }
+ }
+
+ $sql = new mysql;
+?>
\ No newline at end of file
diff --git a/system/library/ssh.php b/system/library/ssh.php
new file mode 100644
index 0000000..4385ea1
--- /dev/null
+++ b/system/library/ssh.php
@@ -0,0 +1,99 @@
+connect($address) AND $this->auth_pwd('root', $passwd))
+ return true;
+
+ return false;
+ }
+
+ public function connect($address)
+ {
+ list($host, $port) = explode(':', $address);
+
+ if($port == '')
+ $port = 22;
+
+ ini_set('default_socket_timeout', '3');
+
+ if($this->conn = ssh2_connect($host, $port))
+ {
+ ini_set('default_socket_timeout', '180');
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public function setfile($localFile, $remoteFile, $permision)
+ {
+ if(@ssh2_scp_send($this->conn, $localFile, $remoteFile, $permision))
+ return true;
+
+ return false;
+ }
+
+ public function getfile($remoteFile, $localFile)
+ {
+ if(@ssh2_scp_recv($this->conn, $remoteFile, $localFile))
+ return true;
+
+ return false;
+ }
+
+ public function set($cmd)
+ {
+ $this->stream = ssh2_exec($this->conn, $cmd);
+
+ stream_set_blocking($this->stream, true);
+ }
+
+ public function auth_pwd($u, $p)
+ {
+ if(@ssh2_auth_password($this->conn, $u, $p))
+ return true;
+
+ return false;
+ }
+
+ public function get($cmd = false)
+ {
+ if($cmd)
+ {
+ $this->stream = ssh2_exec($this->conn, $cmd);
+
+ stream_set_blocking($this->stream, true);
+ }
+
+ $line = '';
+
+ while($get = fgets($this->stream))
+ $line.= $get;
+
+ return $line;
+ }
+
+ public function esc()
+ {
+ if(function_exists('ssh2_disconnect'))
+ ssh2_disconnect($this->conn);
+ else{
+ @fclose($this->conn);
+ unset($this->conn);
+ }
+
+ return NULL;
+ }
+ }
+
+ $ssh = new ssh;
+?>
\ No newline at end of file
diff --git a/system/library/system.php b/system/library/system.php
new file mode 100644
index 0000000..1395217
--- /dev/null
+++ b/system/library/system.php
@@ -0,0 +1,1463 @@
+query('UPDATE `users` set `time`="'.$start_point.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ return NULL;
+ }
+
+ public static function users($users, $user, $authkey, $del = false)
+ {
+ global $mcache;
+
+ if($del)
+ unset($users[md5($user['login'].$user['authkey'].$user['passwd'])]);
+ else
+ $users[md5($user['login'].$user['authkey'].$user['passwd'])] = $user;
+
+ $mcache->set('users_auth', $users, false, 1000);
+
+ return NULL;
+ }
+
+ public static function nav($server, $sid, $active)
+ {
+ global $cfg, $html, $sql, $mcache, $start_point;
+
+ $notice_sid = $mcache->get('notice_'.$sid);
+
+ $notice = is_array($notice_sid) ? $notice_sid : $mcache->get('notice_'.$server['unit']);
+
+ if(!is_array($notice))
+ {
+ $sql->query('SELECT `server`, `text`, `color` FROM `notice` WHERE `server`="'.$sid.'" AND `time`>"'.$start_point.'" ORDER BY `id` DESC LIMIT 1');
+
+ if(!$sql->num())
+ $sql->query('SELECT `unit`, `text`, `color` FROM `notice` WHERE `unit`="'.$server['unit'].'" AND `time`>"'.$start_point.'" ORDER BY `id` DESC LIMIT 1');
+
+ if($sql->num())
+ {
+ $notice = $sql->get();
+
+ $nmc = $notice['server'] ? 'notice_'.$sid : 'notice_'.$server['unit'];
+
+ $mcache->set('notice_'.$nmc, $notice, false, 10);
+ }else
+ $mcache->set('notice_'.$server['unit'], NULL, false, 10);
+ }
+
+ $aUnit = array('index', 'console', 'settings', 'plugins', 'maps', 'owners', 'filetp', 'tarif', 'copy', 'graph', 'web', 'boost');
+
+ $html->get('gmenu', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $sid);
+ $html->set('home', $cfg['http']);
+
+ if(is_array($notice))
+ {
+ global $device;
+
+ if($device == '!mobile')
+ $html->set('notice', ''.$notice['text'].'
');
+ else
+ $html->set('notice', '');
+ }else
+ $html->set('notice', '');
+
+ if($server['console_use']) $html->unit('console_use', 1); else $html->unit('console_use');
+ if($server['plugins_use']) $html->unit('plugins_use', 1); else $html->unit('plugins_use');
+ if($server['ftp_use']) $html->unit('ftp_use', 1); else $html->unit('ftp_use');
+ if($server['stats_use']) $html->unit('graph_use', 1); else $html->unit('graph_use');
+ if($server['web_use']) $html->unit('web_use', 1); else $html->unit('web_use');
+ if($server['copy_use']) $html->unit('copy_use', 1); else $html->unit('copy_use');
+
+ foreach($aUnit as $unit)
+ if($unit == $active) $html->unit($unit, 1); else $html->unit($unit);
+
+ $html->pack('main');
+
+ $html->get('vmenu', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $sid);
+ $html->set('home', $cfg['http']);
+
+ if($server['console_use']) $html->unit('console_use', 1); else $html->unit('console_use');
+ if($server['plugins_use']) $html->unit('plugins_use', 1); else $html->unit('plugins_use');
+ if($server['ftp_use']) $html->unit('ftp_use', 1); else $html->unit('ftp_use');
+ if($server['stats_use']) $html->unit('graph_use', 1); else $html->unit('graph_use');
+ if($server['web_use']) $html->unit('web_use', 1); else $html->unit('web_use');
+ if($server['copy_use']) $html->unit('copy_use', 1); else $html->unit('copy_use');
+
+ foreach($aUnit as $unit)
+ if($unit == $active) $html->unit($unit, 1); else $html->unit($unit);
+
+ $html->pack('vmenu');
+
+ return NULL;
+ }
+
+ public static function route($server, $inc, $go, $all = false)
+ {
+ global $device, $start_point;
+
+ $dir = $device == '!mobile' ? '' : 'megp/';
+ $use = true;
+
+ if(in_array($inc, array('plugins', 'ftp', 'console', 'graph', 'copy', 'web')))
+ {
+ $server['graph_use'] = $server['stats_use'];
+
+ if(!$server[$inc.'_use'])
+ $use = false;
+ }
+
+ if(!$use || $server['time'] < $start_point || in_array($server['status'], array('install', 'reinstall', 'update', 'recovery', 'blocked')))
+ {
+ if($go)
+ sys::out('Раздел недоступен');
+
+ if(!$use)
+ return SEC.$dir.'servers/'.$server['game'].'/index.php';
+
+ return SEC.$dir.'servers/noaccess.php';
+ }
+
+ if($all)
+ return SEC.'servers/games/'.$inc.'.php';
+
+ if(!file_exists(SEC.$dir.'servers/'.$server['game'].'/'.$inc.'.php'))
+ return SEC.$dir.'servers/'.$server['game'].'/index.php';
+
+ return SEC.$dir.'servers/'.$server['game'].'/'.$inc.'.php';
+ }
+
+ public static function int($data, $width = false)
+ {
+ if($width)
+ return preg_replace("([^0-9]{0, ".$width."})", '', $data);
+
+ return preg_replace("([^0-9])", '', $data);
+ }
+
+ public static function b64js($data)
+ {
+ return base64_encode(json_encode($data));
+ }
+
+ public static function b64djs($data)
+ {
+ return json_decode(base64_decode($data), true);
+ }
+
+ public static function hb64($data)
+ {
+ return base64_encode(htmlspecialchars($data));
+ }
+
+ public static function hb64d($data)
+ {
+ return htmlspecialchars_decode(base64_decode($data));
+ }
+
+ public static function outjs($val, $cache = false)
+ {
+ global $mcache;
+
+ if($cache)
+ $mcache->delete($cache);
+
+ die(json_encode($val));
+ }
+
+ public static function out($val = '', $cache = false)
+ {
+ global $mcache;
+
+ if($cache)
+ $mcache->delete($cache);
+
+ die(''.$val.'');
+ }
+
+ public static function outhtml($text, $time = 3, $url = false, $cache = false)
+ {
+ global $device, $mcache, $html, $cfg;
+
+ if($cache)
+ $mcache->delete($cache);
+
+ $tpl = $device == '!mobile' ? '' : '/megp';
+
+ $html->get('out');
+
+ $html->set('title', $cfg['name']);
+ $html->set('home', $cfg['http']);
+ $html->set('css', $cfg['http'].'template'.$tpl.'/css/');
+ $html->set('js', $cfg['http'].'template'.$tpl.'/js/');
+ $html->set('img', $cfg['http'].'template'.$tpl.'/images/');
+ $html->set('text', $text);
+
+ $html->pack('out');
+
+ if(!$url)
+ $url = $cfg['http'];
+
+ header('Refresh: '.$time.'; URL='.$url);
+
+ die($html->arr['out']);
+ }
+
+ public static function valid($val, $type, $preg = '')
+ {
+ switch($type)
+ {
+ case 'promo':
+ if(!preg_match("/^[A-Za-z0-9]{2,20}$/", $val))
+ return true;
+
+ return false;
+
+ case 'en':
+ if(!preg_match("/^[A-Za-z0-9]$/", $val))
+ return true;
+
+ return false;
+
+ case 'ru':
+ if(!preg_match("/^[А-Яа-я]$/u", $val))
+ return true;
+
+ return false;
+
+ case 'wm':
+ if(!preg_match('/^R[0-9]{12,12}$|^Z[0-9]{12,12}$|^U[0-9]{12,12}$/m', $val))
+ return true;
+
+ return false;
+
+ case 'ip':
+ if(!preg_match("/^(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}$/", $val))
+ return true;
+
+ return false;
+
+ case 'steamid':
+ if(!preg_match("/^STEAM_[0-9]:[0-9]:[0-9]{6,12}$|^HLTV$|^STEAM_ID_LAN$|^STEAM_ID_PENDING$|^VALVE_ID_LAN$|^VALVE_ID_PENDING$|^STEAM_666:88:666$/", $val))
+ return true;
+
+ return false;
+
+ case 'steamid3':
+ if(!preg_match("/^\[U:[01]:[0-9]{3,12}\]$/i", $val))
+ return true;
+
+ return false;
+
+ case 'num':
+ if(!preg_match('/[^0-9]/', $val))
+ return true;
+
+ return false;
+
+ case 'md5':
+ if(!preg_match("/^[a-z0-9]{32,32}$/", $val))
+ return true;
+
+ return false;
+
+ case 'other':
+ if(!preg_match($preg, $val))
+ return true;
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public static function mail($name, $text, $mail)
+ {
+ global $cfg;
+
+ require_once(LIB.'smtp.php');
+
+ $tpl = file_get_contents(DATA.'mail.ini', "r");
+
+ $text = str_replace(
+ array('[name]', '[text]', '[http]', '[img]', '[css]'),
+ array($cfg['name'], $text, $cfg['http'], $cfg['http'].'template/images/', $cfg['http'].'template/css/'),
+ $tpl
+ );
+
+ $smtp = new smtp($cfg['smtp_login'], $cfg['smtp_passwd'], $cfg['smtp_url'], $cfg['smtp_mail'], 465);
+
+ $headers = "MIME-Version: 1.0\r\n";
+ $headers .= "Content-type: text/html; charset=utf-8\r\n";
+ $headers .= "From: ".$cfg['smtp_name']." <".$cfg['smtp_mail'].">\r\n";
+
+ if($smtp->send($mail, $name, $text, $headers))
+ return true;
+
+ return false;
+ }
+
+ public static function mail_domain($mail)
+ {
+ $domain = explode('@', $mail);
+
+ $domain = end($domain);
+
+ if(in_array($domain, array('list.ru', 'bk.ru', 'inbox.ru')))
+ $domain = 'mail.ru';
+
+ switch($domain)
+ {
+ case 'mail.ru':
+ return $domain;
+
+ case 'yandex.ru':
+ return 'mail.yandex.ru';
+
+ case 'google.com':
+ return 'mail.google.com';
+
+ default:
+ return '';
+ }
+ }
+
+ public static function domain($domain)
+ {
+ $domain = explode('.', $domain);
+
+ unset($domain[0]);
+
+ return implode('.', $domain);
+ }
+
+ public static function updtext($text, $data)
+ {
+ foreach($data as $name => $val)
+ $text = str_replace('['.$name.']', $val, $text);
+
+ return $text;
+ }
+
+ public static function login($mail, $lchar)
+ {
+ if(!$lchar)
+ return str_replace(array('.', '_', '+', '-'), '', sys::first(explode('@', $mail)));
+
+ $list = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuWwXxYyZz0123456789';
+ $a = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuWwXxYyZz';
+ $selections = strlen($list)-1;
+ $start = strlen($a)-1;
+ $b = rand(0, $start);
+ $start = $a[$b];
+ $login = array();
+
+ $i = 0;
+
+ for($i; $i <= 10; $i+=1)
+ {
+ $n = rand(0, $selections);
+ $login[] = $list[$n];
+ }
+
+ return $start.implode('', $login);
+ }
+
+ public static function passwd($length = 8)
+ {
+ $list = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuWwXxYyZz0123456789';
+ $a = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuWwXxYyZz';
+ $selections = strlen($list)-1;
+ $start = strlen($a)-1;
+ $b = rand(0, $start);
+ $start = $a[$b];
+ $passwd = array();
+
+ $i = 0;
+
+ for($i; $i <= $length-2; $i+=1)
+ {
+ $n = rand(0, $selections);
+ $passwd[] = $list[$n];
+ }
+
+ return $start.implode('', $passwd);
+ }
+
+ public static function passwdkey($passwd)
+ {
+ return md5($passwd);
+ }
+
+ public static function cookie($name, $value, $expires)
+ {
+ $expires = time() + ($expires * 86400);
+ setcookie($name, $value, $expires, "/", $_SERVER['HTTP_HOST'], null, true);
+ }
+
+ public static function auth()
+ {
+ global $auth, $go, $text, $cfg;
+
+ if($auth)
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('output', 'auth')));
+
+ global $device;
+
+ $link = $device == '!mobile' ? 'user/section/lk' : '';
+
+ exit(header('Refresh: 0; URL='.$cfg['http'].$link));
+ }
+
+ return NULL;
+ }
+
+ public static function noauth()
+ {
+ global $auth, $go, $text, $cfg;
+
+ if(!$auth)
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('output', 'noauth')));
+
+ global $device;
+
+ $link = $device == '!mobile' ? 'user/section/auth' : 'auth';
+
+ exit(header('Refresh: 0; URL='.$cfg['http'].$link));
+ }
+
+ return NULL;
+ }
+
+ public static function browser($agent)
+ {
+ if(strpos($agent, 'Firefox') !== false)
+ return 'Mozilla Firefox';
+
+ if(strpos($agent, 'Opera') !== false)
+ return 'Opera';
+
+ if(strpos($agent, 'Chrome') !== false)
+ return 'Google Chrome';
+
+ if(strpos($agent, 'MSIE') !== false)
+ return 'Internet Explorer';
+
+ if(strpos($agent, 'Safari') !== false)
+ return 'Safari';
+
+ return 'Неизвестный';
+ }
+
+ public static function date($lenght, $date)
+ {
+ global $start_point;
+
+ $check_time = $date-$start_point;
+
+ if($check_time < 1)
+ return 'время истекло.';
+
+ $days = floor($check_time/86400);
+ $hours = floor(($check_time%86400)/3600);
+ $minutes = floor(($check_time%3600)/60);
+ $seconds = $check_time%60;
+
+ $adata = array(
+ 'min' => array(
+ 'days' => array('день', 'дня', 'дней'),
+ 'hours' => array('ч.', 'ч.', 'ч.'),
+ 'minutes' => array('мин.', 'мин.', 'мин.'),
+ 'seconds' => array('сек.', 'сек.', 'сек.')
+ ),
+ 'max' => array(
+ 'days' => array('день', 'дня', 'дней'),
+ 'hours' => array('час', 'часа', 'часов'),
+ 'minutes' => array('минуту','минуты','минут'),
+ 'seconds' => array('секунду','секунды','секунд')
+ )
+ );
+
+ $text = '';
+
+ if($days > 0)
+ $text .= sys::date_decl($days, $adata[$lenght]['days']);
+
+ if($days < 1 AND $hours > 0)
+ $text .= ' '.sys::date_decl($hours, $adata[$lenght]['hours']);
+
+ if($days < 1 AND $minutes > 0)
+ $text .= ' '.sys::date_decl($minutes, $adata[$lenght]['minutes']);
+
+ if($days < 1 AND $seconds > 0)
+ $text .= ' '.sys::date_decl($seconds, $adata[$lenght]['seconds']);
+
+ return $text;
+ }
+
+ public static function date_decl($digit, $expr, $onlyword = false)
+ {
+ if(!is_array($expr))
+ $expr = array_filter(explode(' ', $expr));
+
+ if(empty($expr[2]))
+ $expr[2] = $expr[1];
+
+ $i = sys::int($digit)%100;
+
+ if($onlyword)
+ $digit = '';
+
+ if($i > 4 AND $i < 21)
+ $res = $digit.' '.$expr[2];
+ else
+ $i%=10;
+
+ if($i == 1)
+ $res = $digit.' '.$expr[0];
+ elseif($i > 1 AND $i < 5)
+ $res = $digit.' '.$expr[1];
+ else
+ $res = $digit.' '.$expr[2];
+
+ return trim($res);
+ }
+
+ public static function today($time, $cp = false)
+ {
+ global $start_point;
+
+ $today = date('d.m.Y', $start_point);
+ $day = date('d.m.Y', $time);
+
+ if($day == $today)
+ {
+ if($cp)
+ return 'Сегодня '.date('H:i', $time);
+
+ return 'Сегодня '.date('- H:i', $time);
+ }
+
+ $yesterday_first = sys::int(sys::first(explode('.', $today)))-1;
+ $yesterday_full = date('m.Y', $time);
+
+ if($day == $yesterday_first.'.'.$yesterday_full AND !$yesterday_first)
+ {
+ if($cp)
+ return 'Вчера '.date('H:i', $time);
+
+ return 'Вчера '.date('- H:i', $time);
+ }
+
+ if($cp)
+ return date('d.m.Y H:i', $time);
+
+ return date('d.m.Y - H:i', $time);
+ }
+
+ public static function day($time)
+ {
+ $days = array('день', 'дня', 'дней');
+
+ $time = $time % 100;
+
+ if($n > 10 AND $n < 20)
+ return $days[2];
+
+ $time = $time % 10;
+
+ if($time > 1 AND $time < 5)
+ return $days[1];
+
+ if($time == 1)
+ return $days[0];
+
+ return $days[2];
+ }
+
+ public static function bbc($text)
+ {
+ global $cfg;
+
+ $lines = explode("\n", $text);
+
+ $str_search = array(
+ "#\\\n#is",
+ "#\[spoiler\](.+?)\[\/spoiler\]#is",
+ "#\[sp\](.+?)\[\/sp\]#is",
+ "#\[b\](.+?)\[\/b\]#is",
+ "#\[u\](.+?)\[\/u\]#is",
+ "#\[code\](.+?)\[\/code\]#is",
+ "#\[quote\](.+?)\[\/quote\]#is",
+ "#\[url=(.+?)\](.+?)\[\/url\]#is",
+ "#\[img=(.+?)\] \[\/img\]#is",
+ "#(^|[\n ])([\w]+?://[\w\#$%&~/.\-;:=,?@\[\]+]*)#is"
+ );
+
+ $str_replace = array(
+ " ",
+ "",
+ "",
+ "\\1 ",
+ "\\1 ",
+ "",
+ "\\1
",
+ "\\2 ",
+ " ",
+ " \\2 "
+ );
+
+ $uptext = '';
+
+ foreach($lines as $line)
+ $uptext .= preg_replace($str_search, $str_replace, $line)." ";
+
+ return $uptext;
+ }
+
+ public static function first($array = array())
+ {
+ return $array[0];
+ }
+
+ public static function back($url)
+ {
+ exit(header('Refresh: 0; URL='.$url));
+ }
+
+ public static function strlen($str)
+ {
+ return iconv_strlen($str, 'UTF-8');
+ }
+
+ public static function text($section, $name)
+ {
+ global $cfg, $user;
+
+ $group = isset($user['group']) ? $user['group'] : 'user';
+
+ if($section != 'error' || !$cfg['text_group'])
+ $group = 'all';
+
+ include(DATA.'text/'.$section.'.php');
+
+ return isset($text[$name][$group]) ? $text[$name][$group] : $text[$name];
+ }
+
+ public static function key($param = 'defegp')
+ {
+ return md5(sha1(rand(1, 15).$param.rand(16, 30).rand(200, 1000).rand(1, 100)));
+ }
+
+ public static function captcha($type, $ip)
+ {
+ global $mcache;
+
+ $cod = '';
+ $width = 100;
+ $height = 45;
+ $font_size = 16;
+ $symbols = 3;
+ $symbols_fon = 20;
+ $font = LIB.'captcha/text.ttf';
+
+ $chars = array('a','b','c','d','e','f','g','h','j','k','m','n','p','q','r','s','t','u','v','w','x','y','z','2','3','4','5','6','7','9');
+ $colors = array('20','50','80','100');
+
+ $src = imagecreatetruecolor($width, $height);
+ $fon = imagecolorallocate($src, 255, 255, 255);
+
+ imagefill($src, 0, 0, $fon);
+
+ $i = 0;
+ for($i; $i < $symbols_fon; $i+=1)
+ {
+ $color = imagecolorallocatealpha($src, rand(0,255), rand(0,255), rand(0,255), 100);
+ $char = $chars[rand(0, sizeof($chars)-1)];
+ $size = rand($font_size-2, $font_size+2);
+
+ imagettftext($src, $size, rand(0,45), rand($width*0.1,$width-$width*0.1), rand($height*0.2,$height), $color, $font, $char);
+ }
+
+ $i = 0;
+ for($i; $i < $symbols; $i+=1)
+ {
+ $color = imagecolorallocatealpha($src, $colors[rand(0,sizeof($colors)-1)], $colors[rand(0,sizeof($colors)-1)], $colors[rand(0,sizeof($colors)-1)], rand(20,40));
+ $char = $chars[rand(0, sizeof($chars)-1)];
+ $size = rand($font_size*2.1-2, $font_size*2.1+2);
+
+ $x = ($i+1)*$font_size + rand(6,8);
+ $y = (($height*2)/3) + rand(3,7);
+
+ $cod .= $char;
+
+ imagettftext($src, $size, rand(0,15), $x, $y, $color, $font, $char);
+ }
+
+ $mcache->set($type.'_captcha_'.$ip, $cod, false, 120);
+
+ header("Content-type: image/gif");
+ imagegif($src);
+ imagedestroy($src);
+ exit;
+ }
+
+ public static function captcha_check($type, $ip, $cod = '')
+ {
+ global $cfg, $mcache;
+
+ // Если повтор ввода капчи выключен и в кеше есть подтвержденный сеанс
+ if(!$cfg['recaptcha'] AND $mcache->get($type.'_captcha_valid_'.$ip))
+ return false;
+
+ if($mcache->get($type.'_captcha_'.$ip) != strtolower($cod))
+ {
+ $mcache->set($type.'_captcha_valid_'.$ip, true, false, 60);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function ismail($data)
+ {
+ $aData = explode('@', $data);
+
+ if(count($aData) > 1)
+ return true;
+
+ return false;
+ }
+
+ public static function smscode()
+ {
+ return rand(1,9).rand(100,500).rand(10,99);
+ }
+
+ public static function code($length = 8)
+ {
+ $list = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuWwXxYyZz0123456789';
+ $a = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuWwXxYyZz';
+ $selections = strlen($list)-1;
+ $start = strlen($a)-1;
+ $b = rand(0, $start);
+ $start = $a[$b];
+ $code = array();
+
+ $i = 0;
+
+ for($i; $i <= $length-2; $i+=1)
+ {
+ $n = rand(0, $selections);
+ $code[] = $list[$n];
+ }
+
+ return $start.implode('', $code);
+ }
+
+ public static function sms($text, $phone)
+ {
+ global $cfg;
+
+ $out = file_get_contents($cfg['sms_gateway'].'&'.$cfg['sms_to'].'='.$phone.'&'.$cfg['sms_text'].'='.urlencode($text));
+
+ $aOut = explode("\n", $out);
+
+ if(trim($aOut[0]) == $cfg['sms_ok'])
+ return true;
+
+ return false;
+ }
+
+ public static function find($text, $find)
+ {
+ $words = explode(' ', $find);
+
+ foreach($words as $word)
+ if(strlen($word) >= 2)
+ $text = preg_replace('#'.quotemeta($word).'#iu', '$0 ', $text);
+
+ return $text;
+ }
+
+ public static function str_first_replace($search, $replace, $text)
+ {
+ $pos = strpos($text, $search);
+
+ return $pos!==false ? substr_replace($text, $replace, $pos, strlen($search)) : $text;
+ }
+
+ public static function cmd($command)
+ {
+ $text = preg_replace('/\\$/', '$ы', trim($command));
+
+ mb_internal_encoding('UTF-8');
+
+ if(mb_substr($text, -1) == 'ы')
+ $text = quotemeta(substr($text, 0, -2));
+
+ return $text;
+ }
+
+ public static function map($map)
+ {
+ $name = quotemeta(trim($map));
+
+ if(substr($name, -1) == '$')
+ $name = substr($name, 0, -2).'$';
+
+ return str_replace(array('\.', '\*'), array('.', '*'), $name);
+ }
+
+ public static function temp($text)
+ {
+ $temp = TEMP.md5(time().rand(5, 100).rand(10, 20).rand(1, 20).rand(40, 80));
+
+ $file = fopen($temp, "w");
+
+ fputs($file, $text);
+
+ fclose($file);
+
+ return $temp;
+ }
+
+ public static function size($val)
+ {
+ $aSize = array(' Байт', ' Кб', ' Мб', ' Гб', ' Тб', ' Пб');
+
+ return $val ? round($val/pow(1024, ($i = floor(log($val, 1024)))), 2) . $aSize[$i] : '0 Байт';
+ }
+
+ public static function unidate($date)
+ {
+ $aDate = explode('-', $date);
+
+ $aFirst = explode(' ', $aDate[2]);
+
+ return $aFirst[1].' - '.$aFirst[0].'.'.$aDate[1].'.'.$aDate[0];
+ }
+
+ public static function page($page, $nums, $num)
+ {
+ $ceil = ceil($nums/$num);
+
+ if($page > $ceil)
+ $page = $ceil;
+
+ $next = $page*$num;
+
+ if($next <= $nums)
+ $next = $next-$num;
+
+ if($next > $nums)
+ $next = $next-$num;
+
+ if($next < 1)
+ $next = 0;
+
+ $num_go = $next;
+ if($page == '')
+ $page = 1;
+
+ $aPage = array(
+ 'page' => $page,
+ 'num' => $num_go,
+ 'ceil' => $ceil
+ );
+
+ return $aPage;
+ }
+
+ public static function page_list($countnum, $actnum)
+ {
+ if($countnum == 0 || $countnum == 1)
+ return array();
+
+ if($countnum > 10)
+ {
+ if($actnum <= 4 || $actnum + 3 >= $countnum)
+ {
+ for($i = 0; $i <= 4; $i++)
+ $numlist[$i] = $i + 1;
+
+ $numlist[5] = '...';
+ for($j = 6, $k = 4; $j <= 10; $j+=1, $k-=1)
+ $numlist[$j] = $countnum - $k;
+ }else{
+ $numlist[0] = 1;
+ $numlist[1] = 2;
+ $numlist[2] = '...';
+ $numlist[3] = $actnum - 2;
+ $numlist[4] = $actnum - 1;
+ $numlist[5] = $actnum;
+ $numlist[6] = $actnum + 1;
+ $numlist[7] = $actnum + 2;
+ $numlist[8] = '...';
+ $numlist[9] = $countnum - 1;
+ $numlist[10] = $countnum;
+ }
+ }else
+ for($n = 0; $n < $countnum; $n+=1)
+ $numlist[$n] = $n + 1;
+
+ return $numlist;
+ }
+
+ public static function page_gen($ceil, $page, $actnum, $section)
+ {
+ global $cfg, $html;
+
+ $aNum = sys::page_list($ceil, $actnum);
+
+ $pages = '';
+
+ $html->get('pages');
+
+ if($ceil)
+ {
+ if($page != 1)
+ {
+ $next = $page-1;
+ $pages .= 'Предыдущая ';
+ }
+
+ foreach($aNum as $v)
+ {
+ if($v != $page && $v != '...')
+ $pages .= ''.$v.' ';
+
+ if($v == $page)
+ $pages .= ''.$v.' ';
+
+ if($v == '...')
+ $pages .= '... ';
+ }
+
+ if($ceil > $page)
+ {
+ if($page < $ceil)
+ {
+ $next = $page+1;
+ $pages .= 'Следующая ';
+ }else
+ $pages .= 'Следующая ';
+ }
+ }
+
+ $html->set('pages', $pages);
+
+ $html->pack('pages');
+
+ return NULL;
+ }
+
+ public static function country($name)
+ {
+ global $cfg;
+
+ $fileimg = file_exists(TPL.'/images/country/'.$name.'.png');
+
+ if($fileimg)
+ return $cfg['http'].'template/images/country/'.$name.'.png';
+
+ return $cfg['http'].'template/images/country/none.png';
+ }
+
+ public static function ipproxy()
+ {
+ global $_SERVER;
+
+ if(isset($_SERVER['HTTP_CF_CONNECTING_IP']) && !empty($_SERVER['HTTP_CF_CONNECTING_IP']))
+ return $_SERVER['HTTP_CF_CONNECTING_IP'];
+
+ return NULL;
+ }
+
+ public static function ip()
+ {
+ $ip = sys::ipproxy();
+
+ if(sys::valid($ip, 'ip'))
+ return $_SERVER['REMOTE_ADDR'];
+
+ return $ip;
+ }
+
+ public static function whois($ip)
+ {
+ $stack = fsockopen('whois.ripe.net', 43, $errno, $errstr);
+
+ if(!$stack)
+ return 'не определена';
+
+ fputs($stack, $ip."\r\n");
+
+ $subnetwork = '';
+
+ while(!feof($stack))
+ {
+ $str = fgets($stack, 128);
+
+ if(strpos($str, 'route:') !== FALSE)
+ {
+ $subnetwork = trim(str_replace('route:', '', $str));
+
+ break;
+ }
+ }
+
+ fclose($stack);
+
+ return isset($subnetwork{0}) ? $subnetwork : 'не определена';
+ }
+
+ public static function rep_act($name, $time = 20)
+ {
+ global $go, $mcache;
+
+ if(!$go)
+ return NULL;
+
+ if($mcache->get($name))
+ sys::outjs(array('e' => sys::text('other', 'mcache')));
+
+ $mcache->set($name, true, false, $time);
+
+ return $name;
+ }
+
+ public static function check_php_config($file, &$error)
+ {
+ exec('php -l '.$file, $error, $code);
+
+ if(!$code)
+ return true;
+
+ return false;
+ }
+
+ public static function cpu_idle($pros_stat = array(), $unit, $fcpu = false, $ctrl = false)
+ {
+ return sys::cpu_get_idle(sys::parse_cpu($pros_stat[0]), sys::parse_cpu($pros_stat[1]), $unit, $fcpu, $ctrl);
+ }
+
+ public static function cpu_get_idle($first, $second, $unit, $fcpu, $ctrl)
+ {
+ global $sql;
+
+ if(count($first) !== count($second))
+ return;
+
+ $cpus = array();
+
+ for($i = 0, $l = count($first); $i < $l; $i+=1)
+ {
+ $dif = array();
+ $dif['use'] = $second[$i]['use']-$first[$i]['use'];
+ $dif['nice'] = $second[$i]['nice']-$first[$i]['nice'];
+ $dif['sys'] = $second[$i]['sys']-$first[$i]['sys'];
+ $dif['idle'] = $second[$i]['idle']-$first[$i]['idle'];
+ $total = array_sum($dif);
+ $cpu = array();
+
+ foreach($dif as $x => $y)
+ $cpu[$x] = $y ? round($y/$total*100, 1) : 0;
+
+ $cpus['cpu'.$i] = $cpu;
+ }
+
+ if($fcpu)
+ return $cpus;
+
+ $threads = array();
+
+ $l = count($first);
+
+ for($i = 0; $i < $l; $i+=1)
+ $threads[$i] = $cpus['cpu'.$i]['idle'];
+
+ if(count($first) > 1)
+ unset($threads[0]);
+
+ $max = max($threads);
+
+ foreach($threads as $idle)
+ {
+ $core = array_search($max, $threads);
+
+ if($ctrl)
+ $sql->query('SELECT `id` FROM `control_servers` WHERE `unit`="'.$unit.'" AND `core_fix`="'.($core+1).'" LIMIT 1');
+ else
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$unit.'" AND `core_fix`="'.($core+1).'" AND `core_fix_one`="1" LIMIT 1');
+ if($sql->num())
+ {
+ unset($threads[$core]);
+
+ if(!count($threads))
+ return NULL;
+
+ $max = max($threads);
+ }
+ }
+
+ return array_search($max, $threads);
+ }
+
+ public static function parse_cpu($data)
+ {
+ $data = explode("\n", $data);
+
+ $cpu = array();
+
+ foreach($data as $line)
+ {
+ if(preg_match('/^cpu[0-9]/', $line))
+ {
+ $info = explode(' ', $line);
+
+ $cpu[] = array(
+ 'use' => $info[1],
+ 'nice' => $info[2],
+ 'sys' => $info[3],
+ 'idle' => $info[4]
+ );
+ }
+ }
+
+ return $cpu;
+ }
+
+ public static function reset_mcache($nmch, $id, $data = array(), $ctrl = false)
+ {
+ global $mcache;
+
+ $cache = array(
+ 'name' => $data['name'],
+ 'status' => sys::status($data['status'], $data['game']),
+ 'online' => $data['online'],
+ 'image' => ' ',
+ );
+
+ $cache = $ctrl ? sys::buttons($id, $data['status'], $data['game'], $ctrl) : sys::buttons($id, $data['status'], $data['game']);
+
+ if(isset($data['players']))
+ $cache['players'] = $data['players'];
+
+ $mcache->set($nmch, $cache, false, 5);
+
+ return NULL;
+ }
+
+ public static function status($status, $game, $map = '', $get = 'text')
+ {
+ global $cfg;
+
+ switch($status)
+ {
+ case 'working':
+ if($get == 'img')
+ {
+ if(in_array($game, array('samp', 'crmp', 'mta', 'mc')))
+ $map = $game;
+
+ return sys::img($map, $game);
+ }
+
+ return 'Карта: '.($map == '' ? '-' : $map);
+
+ case 'off':
+ if($get == 'img')
+ return $cfg['http'].'template/images/status/off.jpg';
+
+ return 'Статус: выключен ';
+
+ case 'start':
+ if($get == 'img')
+ return $cfg['http'].'template/images/status/start.gif';
+
+ return 'Статус: запускается ';
+
+ case 'restart':
+ if($get == 'img')
+ return $cfg['http'].'template/images/status/restart.gif';
+
+ return 'Статус: перезапускается ';
+
+ case 'change':
+ if($get == 'img')
+ return $cfg['http'].'template/images/status/change.gif';
+
+ return 'Статус: меняется карта ';
+
+ case 'install':
+ if($get == 'img')
+ return $cfg['http'].'template/images/status/install.gif';
+
+ return 'Статус: устанавливается ';
+
+ case 'reinstall':
+ if($get == 'img')
+ return $cfg['http'].'template/images/status/reinstall.gif';
+
+ return 'Статус: переустанавливается ';
+
+ case 'update':
+ if($get == 'img')
+ return $cfg['http'].'template/images/status/update.gif';
+
+ return 'Статус: обновляется ';
+
+ case 'recovery':
+ if($get == 'img')
+ return $cfg['http'].'template/images/status/recovery.gif';
+
+ return 'Статус: восстанавливается ';
+
+ case 'overdue':
+ if($get == 'img')
+ return $cfg['http'].'template/images/status/overdue.jpg';
+
+ return 'Статус: просрочен';
+
+ case 'blocked':
+ if($get == 'img')
+ return $cfg['http'].'template/images/status/blocked.jpg';
+
+ return 'Статус: заблокирован';
+ }
+ }
+
+ public static function img($name, $game)
+ {
+ global $cfg;
+
+ $filename = 'http://cdn.enginegp.ru/maps/'.$game.'/'.$name.'.jpg';
+ $file_headers = @get_headers($filename) ;
+ $protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://";
+ if(!$file_headers || $file_headers[0] == 'HTTP/1.1 404 Not Found' ||trim($file_headers[0]) == 'HTTP/1.1 403 Forbidden') {
+ return $cfg['http'].'template/images/status/none.jpg';
+ }
+ else {
+ return '' . $protocol .'cdn.enginegp.ru/maps/'.$game.'/'.$name.'.jpg';
+ }
+ }
+
+ public static function buttons($id, $status, $game = false, $ctrl = false)
+ {
+ global $html;
+
+ if(isset($html->arr['buttons']))
+ unset($html->arr['buttons']);
+
+ $other = in_array($game, array('samp', 'crmp', 'mta', 'mc'));
+
+ $dir = $ctrl ? 'control/servers' : 'servers';
+
+ if(in_array($status, array('working', 'change', 'start', 'restart')))
+ {
+ $html->get('stop', 'sections/'.$dir.'/buttons');
+
+ $html->set('id', $id);
+ if($ctrl)
+ $html->set('ctrl', $ctrl);
+
+ $html->pack('buttons');
+
+ $html->get('restart', 'sections/'.$dir.'/buttons');
+
+ $html->set('id', $id);
+ if($ctrl)
+ $html->set('ctrl', $ctrl);
+
+ $html->pack('buttons');
+
+ if(!$other)
+ {
+ $html->get('change', 'sections/'.$dir.'/buttons');
+
+ $html->set('id', $id);
+ if($ctrl)
+ $html->set('ctrl', $ctrl);
+
+ $html->pack('buttons');
+ }
+
+ return $html->arr['buttons'];
+ }
+
+ if($status == 'off')
+ {
+ $html->get('start', 'sections/'.$dir.'/buttons');
+
+ $html->set('id', $id);
+ if($ctrl)
+ $html->set('ctrl', $ctrl);
+
+ $html->pack('buttons');
+
+ $html->get('reinstall', 'sections/'.$dir.'/buttons');
+
+ $html->set('id', $id);
+ if($ctrl)
+ $html->set('ctrl', $ctrl);
+
+ $html->pack('buttons');
+
+ if(!$other)
+ {
+ $html->get('update', 'sections/'.$dir.'/buttons');
+
+ $html->set('id', $id);
+ if($ctrl)
+ $html->set('ctrl', $ctrl);
+
+ $html->pack('buttons');
+ }
+
+ return $html->arr['buttons'];
+ }
+
+ $html->get('other', 'sections/'.$dir.'/buttons');
+ $html->pack('buttons');
+
+ return $html->arr['buttons'];
+ }
+
+ public static function entoru($month)
+ {
+ $ru = array(
+ 1 => 'Янв', 2 => 'Фев', 3 => 'Мар', 4 => 'Апр',
+ 5 => 'Май', 6 => 'Июн', 7 => 'Июл', 8 => 'Авг',
+ 9 => 'Сен', 10 => 'Окт', 11 => 'Ноя', 12 => 'Дек'
+ );
+
+ return $ru[$month];
+ }
+
+ public static function head($head)
+ {
+ global $route, $header;
+
+ if($head == 'description')
+ {
+ global $description;
+
+ if(isset($description))
+ {
+ $text = str_replace(array('"', '-'), array('', '—'), strip_tags($description));
+
+ if(strlen($text) > 160)
+ {
+ mb_internal_encoding('UTF-8');
+
+ $text = mb_substr($text, 0, 157).'...';
+ }
+
+ return $text;
+ }
+ }else{
+ global $keywords;
+
+ if(isset($keywords))
+ return str_replace(array('"', '-'), array('', '—'), strip_tags($keywords));
+ }
+
+ return array_key_exists($route, $header) ? $header[$route][$head] : $header['index'][$head];
+ }
+
+ public static function tags($tags)
+ {
+ $aTags = explode(',', $tags);
+
+ $text = '';
+
+ foreach($aTags as $tag)
+ $text .= ''.trim($tag).' , ';
+
+ return isset($text{0}) ? substr($text, 0, -2) : 'отсутствуют';
+ }
+
+ public static function benefitblock($id, $nmch = false)
+ {
+ global $cfg, $sql, $start_point;
+
+ if($cfg['benefitblock'])
+ {
+ $sql->query('SELECT `benefit` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $info = $sql->get();
+
+ if($info['benefit'] > $start_point)
+ sys::outjs(array('e' => 'Операция недоступна до '.date('d.m.Y - H:i:s', $info['benefit'])), $nmch);
+ }
+
+ return NULL;
+ }
+
+ function outfile($file, $name, $del = false)
+ {
+ if(file_exists($file))
+ {
+ if(ob_get_level())
+ ob_end_clean();
+
+ header('Content-Description: File Transfer');
+ header('Content-Type: application/octet-stream');
+ header('Content-Disposition: attachment; filename='.$name);
+ header('Content-Transfer-Encoding: binary');
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate');
+ header('Pragma: public');
+ header('Content-Length: '.filesize($file));
+
+ readfile($file);
+
+ if($del)
+ unlink($file);
+
+ exit;
+ }
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/users.php b/system/library/users.php
new file mode 100644
index 0000000..732d68a
--- /dev/null
+++ b/system/library/users.php
@@ -0,0 +1,53 @@
+get('gmenu', 'sections/user');
+
+ $html->set('home', $cfg['http']);
+
+ foreach($aUnit as $unit)
+ if($unit == $active) $html->unit($unit, 1); else $html->unit($unit);
+
+ $html->pack('main');
+
+ $html->get('vmenu', 'sections/user');
+
+ $html->set('home', $cfg['http']);
+
+ foreach($aUnit as $unit)
+ if($unit == $active) $html->unit($unit, 1); else $html->unit($unit);
+
+ $html->pack('vmenu');
+
+ return NULL;
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/web/free.php b/system/library/web/free.php
new file mode 100644
index 0000000..fef8c55
--- /dev/null
+++ b/system/library/web/free.php
@@ -0,0 +1,496 @@
+ 'Дополнительная услуга недоступна для установки.'), $mcache);
+
+ // Проверка на наличие уже установленной выбранной услуги
+ if($sql->num(web::stack($aData, '`id`')))
+ sys::outjs(array('i' => 'Дополнительная услуга уже установлена.'), $mcache);
+
+ // Проверка на наличие уже установленной подобной услуги
+ switch($aWebInstall[$aData['server']['game']][$aData['type']])
+ {
+ case 'server':
+ foreach($aWebOne[$aData['server']['game']][$aData['type']] as $type)
+ {
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$type.'" AND `server`="'.$aData['server']['id'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('i' => 'Подобная услуга уже установлена.', 'type' => $type), $mcache);
+ }
+
+ break;
+
+ case 'user':
+ foreach($aWebOne[$aData['server']['game']][$aData['type']] as $type)
+ {
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$type.'" AND `user`="'.$aData['server']['user'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('i' => 'Подобная услуга уже установлена.', 'type' => $type), $mcache);
+ }
+
+ break;
+
+ case 'unit':
+ foreach($aWebOne[$aData['server']['game']][$aData['type']] as $type)
+ {
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$type.'" AND `user`="'.$aData['server']['user'].'" AND `unit`="'.$aData['server']['unit'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('i' => 'Подобная услуга уже установлена.', 'type' => $type), $mcache);
+ }
+ }
+
+ // Проверка валидности поддомена
+ if(sys::valid($aData['subdomain'], 'other', "/^[a-z0-9]+$/"))
+ sys::outjs(array('e' => 'Адрес должен состоять из букв a-z и цифр.'), $mcache);
+
+ // Проверка длины поддомена
+ if(!isset($aData['subdomain']{3}) || isset($aData['subdomain']{15}))
+ sys::outjs(array('e' => 'Длина адреса не должна превышать 16-и символов и быть не менее 4-х символов.'), $mcache);
+
+ // Проверка запрещенного поддомена
+ if(in_array($aData['subdomain'], $aWebUnit['subdomains']))
+ sys::outjs(array('e' => 'Нельзя создать данный адрес, придумайте другой.'), $mcache);
+
+ // Проверка наличия домена
+ if(!in_array($aData['domain'], $aWebUnit['domains']))
+ sys::outjs(array('e' => 'Выбранный домен не найден.'), $mcache);
+
+ // Проверка поддомена на занятость
+ $sql->query('SELECT `id` FROM `web` WHERE `domain`="'.$aData['subdomain'].'.'.$aData['domain'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный адрес уже занят.'), $mcache);
+
+ // Проверка наличия шаблона
+ if(!array_key_exists($aData['desing'], $aWebParam[$aData['type']]['desing']))
+ sys::outjs(array('e' => 'Выбранный шаблон не найден.'), $mcache);
+
+ if(isset($aData['passwd']))
+ {
+ // Если не указан пароль сгенерировать
+ if($aData['passwd'] == '')
+ $aData['passwd'] = sys::passwd($aWebParam[$aData['type']]['passwd']);
+
+ // Проверка длинны пароля
+ if(!isset($aData['passwd']{5}) || isset($aData['passwd']{15}))
+ sys::outjs(array('e' => 'Необходимо указать пароль длинной не менее 6-и символов и не более 16-и.'), $mcache);
+
+ // Проверка валидности пароля
+ if(sys::valid($aData['passwd'], 'other', "/^[A-Za-z0-9]{6,16}$/"))
+ sys::outjs(array('e' => 'Пароль должен состоять из букв a-z и цифр.'), $mcache);
+ }
+
+ include(LIB.'ssh.php');
+
+ $unit = web::unit($aWebUnit, $aData['type'], $aData['server']['unit']);
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $mcache);
+
+ // Директория файлов услуги
+ $path = $aWebUnit['path'][$aWebUnit['unit'][$aData['type']]][$aData['type']].$aData['desing'];
+
+ // Директория дополнительной услуги
+ $install = $aWebUnit['install'][$aWebUnit['unit'][$aData['type']]][$aData['type']].$aData['subdomain'].'.'.$aData['domain'];
+
+ $sql->query('INSERT INTO `web` set `type`="'.$aData['type'].'", `server`="'.$aData['server']['id'].'", `user`="'.$aData['server']['user'].'", `unit`="'.$aData['server']['unit'].'", `config`=""');
+ $wid = $sql->id();
+ $uid = $wid+10000;
+
+ $sql->query('SELECT `mail` FROM `users` WHERE `id`="'.$aData['server']['user'].'" LIMIT 1');
+ if(!$sql->num())
+ {
+ $sql->query('DELETE FROM `web` WHERE `id`="'.$wid.'" LIMIT 1');
+
+ sys::outjs(array('e' => 'Необходимо указать пользователя сервера.'), $mcache);
+ }
+
+ $u = $sql->get();
+
+ // Данные
+ $login = 'w'.$uid;
+ $passwd = sys::passwd(10);
+ $ip = sys::first(explode(':', $unit['address']));
+ $host = $aWebUnit['unit'][$aData['type']] == 'local' ? '127.0.0.1' : $ip;
+
+ $conf = array(
+ 'address' => $aData['server']['address'],
+ 'install' => $install,
+ 'login' => $login,
+ 'passwd' => $passwd,
+ 'host' => $host,
+ 'url' => $cfg['http'],
+ 'domain' => $aData['subdomain'].'.'.$aData['domain']
+ );
+
+ $aData['config_sql'] = sys::updtext($aData['config_sql'], $conf);
+
+ if(isset($aWebdbConf[$aData['type']]))
+ {
+ $aData['config_php'] = sys::updtext($aData['config_php'], $conf);
+
+ $temp = sys::temp($aData['config_php']);
+ $ssh->setfile($temp, $path.$aWebdbConf[$aData['type']]['file'], $aWebdbConf[$aData['type']]['chmod']);
+
+ unlink($temp);
+ }
+
+ if(isset($aWebothPath[$aData['type']]))
+ {
+ $aData['config_oth'] = sys::updtext($aData['config_oth'], $conf);
+
+ $temp = sys::temp($aData['config_oth']);
+ $ssh->setfile($temp, $path.$aWebothPath[$aData['type']]['file'], $aWebothPath[$aData['type']]['chmod']);
+
+ unlink($temp);
+ }
+
+ // Создание поддомена
+ $result = json_decode(file_get_contents(sys::updtext($aWebUnit['isp']['domain']['create'], array('subdomain' => $aData['subdomain'], 'ip' => $ip, 'domain' => $aData['domain']))), true);
+ if(!isset($result['result']) || strtolower($result['result']) != 'ok')
+ {
+ $sql->query('DELETE FROM `web` WHERE `id`="'.$wid.'" LIMIT 1');
+
+ sys::outjs(array('e' => 'Не удалось создать поддомен, обратитесь в тех.поддержку.'), $mcache);
+ }
+
+ // Создание задания crontab
+ if(isset($aWebUnit['isp']['crontab'][$aData['type']]['install']))
+ {
+ $result = json_decode(file_get_contents(sys::updtext($aWebUnit['isp']['crontab'][$aData['type']]['install'], array('subdomain' => $aData['subdomain'], 'domain' => $aData['domain']))), true);
+ if(!isset($result['result']) || strtolower($result['result']) != 'ok')
+ {
+ $sql->query('DELETE FROM `web` WHERE `id`="'.$wid.'" LIMIT 1');
+
+ sys::outjs(array('e' => 'Не удалось создать задание, обратитесь в тех.поддержку.'), $mcache);
+ }
+ }
+
+ $a2 = ''.PHP_EOL
+ .' ServerName '.$aData['subdomain'].'.'.$aData['domain'].PHP_EOL
+ .' DocumentRoot '.$install.PHP_EOL
+ .' AddType application/x-httpd-php .php .php3 .php4 .php5 .phtml'.PHP_EOL
+ .' AddType application/x-httpd-php-source .phps'.PHP_EOL
+ .' ';
+
+ // Смена прав на файлы/папки
+ $chmod = isset($aWebChmod[$aData['type']]) ? $aWebChmod[$aData['type']] : '';
+
+ $sql_q = '';
+
+ if(isset($aWebSQL[$aData['type']]))
+ {
+ $sql_q .= 'mysql --login-path=local -e "CREATE DATABASE '.$login.';'
+ ."CREATE USER '".$login."'@'%' IDENTIFIED BY '".$passwd."';"
+ .'GRANT ALL PRIVILEGES ON '.$login.' . * TO \''.$login.'\'@\'%\';";'
+ .'mysql --login-path=local '.$login.' < '.$aWebUnit['path'][$aWebUnit['unit'][$aData['type']]][$aData['type']].'dump.sql;';
+
+ if(isset($aWebSQL[$aData['type']]['install']))
+ foreach($aWebSQL[$aData['type']]['install'] as $query)
+ $sql_q .= "mysql --login-path=local ".$login." -e \"".sys::updtext($query,
+ array(
+ 'url' => $cfg['http'],
+ 'passwd' => $aData['passwd'],
+ 'mail' => $u['mail'],
+ 'folder' => $install)
+ )."\";";
+ }
+
+ // Установка
+ $ssh->set('echo "'.$a2.'" > /etc/apache2/sites-enabled/'.$aData['subdomain'].'.'.$aData['domain'].';' // Настроки апач
+ .'mkdir -p '.$install.';' // Создание директории
+ .'useradd -d '.$install.' -g web -u '.$uid.' web'.$uid.';' // Создание пользователя услуги на локации
+ .'chown -R web'.$uid.':999 '.$install.';' // Изменение владельца и группы директории
+ .'cd '.$install.' && sudo -u web'.$uid.' screen -dmS i_w_'.$uid.' sh -c "cp -r '.$path.'/. .; '.$chmod.'";' // Копирование файлов услуги
+ .'screen -dmS apache_reload_'.$uid.' service apache2 reload;' // Перезагрузить конфигурации апач
+ .$sql_q); // sql запросы
+
+ $aData['passwd'] = isset($aData['passwd']) ? $aData['passwd'] : '';
+
+ // Обновление данных
+ $sql->query('UPDATE `web` set `uid`="'.$uid.'", `desing`="'.$aData['desing'].'", '
+ .'`domain`="'.$aData['subdomain'].'.'.$aData['domain'].'", '
+ .'`passwd`="'.$aData['passwd'].'", `config`="'.base64_encode($aData['config_sql']).'", '
+ .'`login`="'.$login.'", `date`="'.$start_point.'" '
+ .'WHERE `id`="'.$wid.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $mcache);
+ }
+
+ public static function update($aData = array(), $mcache)
+ {
+ global $sql, $start_point;
+
+ include(DATA.'web.php');
+
+ $stack = web::stack($aData, '`id`, `uid`, `unit`, `login`, `desing`, `domain`, `update`');
+
+ if(!$sql->num($stack))
+ sys::outjs(array('e' => 'Дополнительная услуга не установлена.'), $mcache);
+
+ $web = $sql->get($stack);
+
+ // Проверка времени последнего обновления
+ include(LIB.'games/games.php');
+
+ $upd = $web['update']+86400;
+
+ if($upd > $start_point)
+ sys::outjs(array('e' => 'Для повторного обновления должно пройти: '.games::date('max', $upd)));
+
+ include(LIB.'ssh.php');
+
+ $unit = web::unit($aWebUnit, $aData['type'], $web['unit']);
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $mcache);
+
+ $install = $aWebUnit['install'][$aWebUnit['unit'][$aData['type']]][$aData['type']].$web['domain'];
+
+ $path = $aWebUnit['path'][$aWebUnit['unit'][$aData['type']]][$aData['type']].$web['desing'];
+
+ $sql->query('SELECT `mail` FROM `users` WHERE `id`="'.$aData['server']['user'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Необходимо указать пользователя сервера.'), $mcache);
+
+ $u = $sql->get();
+
+ // sql запросы
+ $sql_q = '';
+
+ if(isset($aWebSQL[$aData['type']]['update']))
+ foreach($aWebSQL[$aData['type']]['update'] as $query)
+ $sql_q .= "mysql --login-path=local ".$web['login']." -e \"".sys::updtext($query, array('passwd' => $aData['passwd'], 'mail' => $u['mail']))."\";";
+
+ $cat = isset($aWebdbConf[$aData['type']]) ? 'cat '.$install.$aWebdbConf[$aData['type']]['file'].' > '.$path.$aWebdbConf[$aData['type']]['file'].';' : '';
+ $chmod = isset($aWebChmod[$aData['type']]) ? $aWebChmod[$aData['type']] : '';
+
+ $ssh->set($cat
+ .'cd '.$install.' && sudo -u web'.$web['uid'].' screen -dmS u_w_'.$web['uid'].' sh -c "YES | cp -rf '.$path.'/. .; '.$chmod.'";'
+ .$sql_q); // sql запрос
+
+ $sql->query('UPDATE `web` set `update`="'.$start_point.'" WHERE `id`="'.$web['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $mcache);
+ }
+
+ public static function delete($aData = array(), $mcache)
+ {
+ global $sql;
+
+ include(DATA.'web.php');
+
+ $stack = web::stack($aData, '`id`, `uid`, `unit`, `domain`, `login`');
+
+ if(!$sql->num($stack))
+ sys::outjs(array('e' => 'Дополнительная услуга не установлена.'), $mcache);
+
+ $web = $sql->get($stack);
+
+ include(LIB.'ssh.php');
+
+ $unit = web::unit($aWebUnit, $aData['type'], $web['unit']);
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $mcache);
+
+ // Директория дополнительной услуги
+ $delete = '';
+
+ if($web['domain'] != '')
+ $delete = 'screen -dmS r_w_'.$web['uid'].' rm -r '.$aWebUnit['install'][$aWebUnit['unit'][$aData['type']]][$aData['type']].$web['domain'].';';
+
+ $ip = sys::first(explode(':', $unit['address']));
+
+ $aDomain = explode('.', $web['domain']);
+ $zone = array_pop($aDomain);
+
+ // Удаление поддомена
+ if($aData['type'] != 'mysql')
+ {
+ $result = json_decode(file_get_contents(sys::updtext($aWebUnit['isp']['domain']['delete'], array('subdomain' => $web['domain'], 'domain' => end($aDomain).'.'.$zone, 'ip' => $ip))), true);
+
+ if(!isset($result['result']) || strtolower($result['result']) != 'ok')
+ sys::outjs(array('e' => 'Не удалось удалить поддомен, обратитесь в тех.поддержку.'), $mcache);
+ }
+
+ // Удаление задания crontab
+ if(isset($aWebUnit['isp']['crontab'][$aData['type']]['delete']) && isset($aData['cron']))
+ {
+ $result = json_decode(file_get_contents(sys::updtext($aWebUnit['isp']['crontab'][$aData['type']]['delete'], array('data' => $aData['cron']))), true);
+ if(!isset($result['result']) || strtolower($result['result']) != 'ok')
+ sys::outjs(array('e' => 'Не удалось удалить задание, обратитесь в тех.поддержку.'), $mcache);
+ }
+
+ $sql_q = isset($aWebSQL[$aData['type']]) ? "mysql --login-path=local -e \"DROP DATABASE IF EXISTS ".$web['login']."; DROP USER ".$web['login']."\"" : '';
+
+ $ssh->set('rm /etc/apache2/sites-enabled/'.$web['domain'].';' // Удаление настроек апач
+ .$delete // Удаление файлов
+ .'userdel web'.$web['uid'].';' // Удаление пользователя
+ .'screen -dmS apache_reload_'.$web['uid'].' service apache2 reload;' // Перезагрузить конфигурации апач
+ .$sql_q); // sql запрос
+
+ $sql->query('DELETE FROM `web` WHERE `id`="'.$web['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $mcache);
+ }
+
+ public static function connect($aData = array(), $mcache)
+ {
+ global $cfg, $sql, $start_point;
+
+ include(DATA.'web.php');
+
+ $sql->query('SELECT `id`, `uid`, `unit`, `game`, `user`, `tarif`, `address`, `status`, `name` FROM `servers` WHERE `id`="'.$aData['server'].'" AND `user`="'.$aData['user'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Игровой сервер не найден.'), $mcache);
+
+ $server = $sql->get();
+
+ // Проверка статуса игрового сервера
+ if(!in_array($server['status'], array('working', 'off', 'start', 'restart', 'change')))
+ sys::outjs(array('e' => 'Игровой сервер недоступен для подключения.'), $mcache);
+
+ // Проверка установки плагина
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$server['id'].'" AND `plugin`="'.$aWebConnect[$aData['type']][$server['game']].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('i' => 'Для подключения, необходимо установить плагин.', 'pid' => $aWebConnect[$aData['type']][$server['game']]), $mcache);
+
+ $aData['server'] = array_merge($server, array('id' => $aData['server']));
+
+ $stack = web::stack($aData, '`config`, `unit`, `login`');
+
+ if(!$sql->num($stack))
+ sys::outjs(array('e' => 'Дополнительная услуга не установлена.'), $mcache);
+
+ $web = $sql->get($stack);
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `passwd`, `address` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $mcache);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Директория игр. сервера
+ $dir = $tarif['install'].$server['uid'].'/';
+
+ // Взять rcon_password
+ $get = explode(' ', str_replace('"', '', trim($ssh->get('cat '.$dir.$aData['cfg'].' | grep rcon_password'))));
+
+ $rcon = trim(end($get));
+
+ if(!isset($rcon{0}))
+ sys::outjs(array('r' => 'Необходимо установить rcon пароль (rcon_password).', 'url' => $cfg['http'].'servers/id/'.$server['id'].'/section/settings/subsection/server'), $mcache);
+
+ $temp = sys::temp(sys::updtext(base64_decode($web['config']), $aData['orcfg']));
+
+ $ssh->setfile($temp, $dir.$aData['file'], 0644);
+
+ $unit = web::unit($aWebUnit, $aData['type'], $web['unit']);
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $mcache);
+
+ // sql запросы
+ $sql_q = '';
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ if(isset($aWebSQL[$aData['type']]['connect']))
+ foreach($aWebSQL[$aData['type']]['connect'] as $query)
+ $sql_q .= "mysql --login-path=local ".$web['login']." -e \"".sys::updtext($query,
+ array_merge(array('id' => $aData['server']['id'], 'rcon' => $rcon, 'address' => $server['address'], 'ip' => $ip, 'port' => $port, 'name' => $server['name'], 'time' => $start_point), $aData['orsql']))."\";";
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$dir.$aData['file'].';' // Смена владельца файла
+ .$sql_q); // sql запросы
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $mcache);
+ }
+
+ public static function passwd($aData = array(), $mcache)
+ {
+ global $sql;
+
+ include(DATA.'web.php');
+
+ $stack = web::stack($aData);
+
+ if(!$sql->num($stack))
+ sys::outjs(array('e' => 'Дополнительная услуга не установлена.'), $mcache);
+
+ $web = $sql->get($stack);
+
+ $passwd = sys::passwd($aWebParam[$aData['type']]['passwd']);
+
+ include(LIB.'ssh.php');
+
+ $unit = web::unit($aWebUnit, $aData['type'], $web['unit']);
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $mcache);
+
+ $sql_q = '';
+
+ if(isset($aWebSQL[$aData['type']]['passwd']))
+ foreach($aWebSQL[$aData['type']]['passwd'] as $query)
+ $sql_q .= "mysql --login-path=local ".$web['login']." -e \"".sys::updtext($query, array('passwd' => $passwd))."\";";
+
+ $ssh->set($sql_q);
+
+ $sql->query('UPDATE `web` set `passwd`="'.$passwd.'" WHERE `id`="'.$web['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $mcache);
+ }
+
+ public static function stack($aData, $select = '`id`, `unit`, `login`')
+ {
+ global $sql;
+
+ include(DATA.'web.php');
+
+ switch($aWebInstall[$aData['server']['game']][$aData['type']])
+ {
+ case 'server':
+ return $sql->query('SELECT '.$select.' FROM `web` WHERE `type`="'.$aData['type'].'" AND `server`="'.$aData['server']['id'].'" LIMIT 1');
+
+ case 'user':
+ return $sql->query('SELECT '.$select.' FROM `web` WHERE `type`="'.$aData['type'].'" AND `user`="'.$aData['server']['user'].'" LIMIT 1');
+
+ case 'unit':
+ return $sql->query('SELECT '.$select.' FROM `web` WHERE `type`="'.$aData['type'].'" AND `user`="'.$aData['server']['user'].'" AND `unit`="'.$aData['server']['unit'].'" LIMIT 1');
+ }
+
+ return NULL;
+ }
+
+ public static function unit($aWebUnit, $type, $id)
+ {
+ global $sql;
+
+ if($aWebUnit['unit'][$type] == 'local')
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$id.'" LIMIT 1');
+
+ return $sql->get();
+ }
+
+ return array('address' => $aWebUnit['address'], 'passwd' => $aWebUnit['passwd']);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/library/zip.php b/system/library/zip.php
new file mode 100644
index 0000000..8f46a05
--- /dev/null
+++ b/system/library/zip.php
@@ -0,0 +1,5696 @@
+zipname = $p_zipname;
+ $this->zip_fd = 0;
+ $this->magic_quotes_status = -1;
+
+ // ----- Return
+ return;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function :
+ // create($p_filelist, $p_add_dir="", $p_remove_dir="")
+ // create($p_filelist, $p_option, $p_option_value, ...)
+ // Description :
+ // This method supports two different synopsis. The first one is historical.
+ // This method creates a Zip Archive. The Zip file is created in the
+ // filesystem. The files and directories indicated in $p_filelist
+ // are added in the archive. See the parameters description for the
+ // supported format of $p_filelist.
+ // When a directory is in the list, the directory and its content is added
+ // in the archive.
+ // In this synopsis, the function takes an optional variable list of
+ // options. See bellow the supported options.
+ // Parameters :
+ // $p_filelist : An array containing file or directory names, or
+ // a string containing one filename or one directory name, or
+ // a string containing a list of filenames and/or directory
+ // names separated by spaces.
+ // $p_add_dir : A path to add before the real path of the archived file,
+ // in order to have it memorized in the archive.
+ // $p_remove_dir : A path to remove from the real path of the file to archive,
+ // in order to have a shorter path memorized in the archive.
+ // When $p_add_dir and $p_remove_dir are set, $p_remove_dir
+ // is removed first, before $p_add_dir is added.
+ // Options :
+ // PCLZIP_OPT_ADD_PATH :
+ // PCLZIP_OPT_REMOVE_PATH :
+ // PCLZIP_OPT_REMOVE_ALL_PATH :
+ // PCLZIP_OPT_COMMENT :
+ // PCLZIP_CB_PRE_ADD :
+ // PCLZIP_CB_POST_ADD :
+ // Return Values :
+ // 0 on failure,
+ // The list of the added files, with a status of the add action.
+ // (see PclZip::listContent() for list entry format)
+ // --------------------------------------------------------------------------------
+ function create($p_filelist)
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Set default values
+ $v_options = array();
+ $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE;
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+
+ // ----- Look for arguments
+ if ($v_size > 1) {
+ // ----- Get the arguments
+ $v_arg_list = func_get_args();
+
+ // ----- Remove from the options list the first argument
+ array_shift($v_arg_list);
+ $v_size--;
+
+ // ----- Look for first arg
+ if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {
+
+ // ----- Parse the options
+ $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
+ array (PCLZIP_OPT_REMOVE_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
+ PCLZIP_OPT_ADD_PATH => 'optional',
+ PCLZIP_CB_PRE_ADD => 'optional',
+ PCLZIP_CB_POST_ADD => 'optional',
+ PCLZIP_OPT_NO_COMPRESSION => 'optional',
+ PCLZIP_OPT_COMMENT => 'optional',
+ PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
+ PCLZIP_OPT_TEMP_FILE_ON => 'optional',
+ PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
+ //, PCLZIP_OPT_CRYPT => 'optional'
+ ));
+ if ($v_result != 1) {
+ return 0;
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+
+ // ----- Get the first argument
+ $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0];
+
+ // ----- Look for the optional second argument
+ if ($v_size == 2) {
+ $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1];
+ }
+ else if ($v_size > 2) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
+ "Invalid number / type of arguments");
+ return 0;
+ }
+ }
+ }
+
+ // ----- Look for default option values
+ $this->privOptionDefaultThreshold($v_options);
+
+ // ----- Init
+ $v_string_list = array();
+ $v_att_list = array();
+ $v_filedescr_list = array();
+ $p_result_list = array();
+
+ // ----- Look if the $p_filelist is really an array
+ if (is_array($p_filelist)) {
+
+ // ----- Look if the first element is also an array
+ // This will mean that this is a file description entry
+ if (isset($p_filelist[0]) && is_array($p_filelist[0])) {
+ $v_att_list = $p_filelist;
+ }
+
+ // ----- The list is a list of string names
+ else {
+ $v_string_list = $p_filelist;
+ }
+ }
+
+ // ----- Look if the $p_filelist is a string
+ else if (is_string($p_filelist)) {
+ // ----- Create a list from the string
+ $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist);
+ }
+
+ // ----- Invalid variable type for $p_filelist
+ else {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist");
+ return 0;
+ }
+
+ // ----- Reformat the string list
+ if (sizeof($v_string_list) != 0) {
+ foreach ($v_string_list as $v_string) {
+ if ($v_string != '') {
+ $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string;
+ }
+ else {
+ }
+ }
+ }
+
+ // ----- For each file in the list check the attributes
+ $v_supported_attributes
+ = array ( PCLZIP_ATT_FILE_NAME => 'mandatory'
+ ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional'
+ ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional'
+ ,PCLZIP_ATT_FILE_MTIME => 'optional'
+ ,PCLZIP_ATT_FILE_CONTENT => 'optional'
+ ,PCLZIP_ATT_FILE_COMMENT => 'optional'
+ );
+ foreach ($v_att_list as $v_entry) {
+ $v_result = $this->privFileDescrParseAtt($v_entry,
+ $v_filedescr_list[],
+ $v_options,
+ $v_supported_attributes);
+ if ($v_result != 1) {
+ return 0;
+ }
+ }
+
+ // ----- Expand the filelist (expand directories)
+ $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options);
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Call the create fct
+ $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options);
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Return
+ return $p_result_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function :
+ // add($p_filelist, $p_add_dir="", $p_remove_dir="")
+ // add($p_filelist, $p_option, $p_option_value, ...)
+ // Description :
+ // This method supports two synopsis. The first one is historical.
+ // This methods add the list of files in an existing archive.
+ // If a file with the same name already exists, it is added at the end of the
+ // archive, the first one is still present.
+ // If the archive does not exist, it is created.
+ // Parameters :
+ // $p_filelist : An array containing file or directory names, or
+ // a string containing one filename or one directory name, or
+ // a string containing a list of filenames and/or directory
+ // names separated by spaces.
+ // $p_add_dir : A path to add before the real path of the archived file,
+ // in order to have it memorized in the archive.
+ // $p_remove_dir : A path to remove from the real path of the file to archive,
+ // in order to have a shorter path memorized in the archive.
+ // When $p_add_dir and $p_remove_dir are set, $p_remove_dir
+ // is removed first, before $p_add_dir is added.
+ // Options :
+ // PCLZIP_OPT_ADD_PATH :
+ // PCLZIP_OPT_REMOVE_PATH :
+ // PCLZIP_OPT_REMOVE_ALL_PATH :
+ // PCLZIP_OPT_COMMENT :
+ // PCLZIP_OPT_ADD_COMMENT :
+ // PCLZIP_OPT_PREPEND_COMMENT :
+ // PCLZIP_CB_PRE_ADD :
+ // PCLZIP_CB_POST_ADD :
+ // Return Values :
+ // 0 on failure,
+ // The list of the added files, with a status of the add action.
+ // (see PclZip::listContent() for list entry format)
+ // --------------------------------------------------------------------------------
+ function add($p_filelist)
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Set default values
+ $v_options = array();
+ $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE;
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+
+ // ----- Look for arguments
+ if ($v_size > 1) {
+ // ----- Get the arguments
+ $v_arg_list = func_get_args();
+
+ // ----- Remove form the options list the first argument
+ array_shift($v_arg_list);
+ $v_size--;
+
+ // ----- Look for first arg
+ if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {
+
+ // ----- Parse the options
+ $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
+ array (PCLZIP_OPT_REMOVE_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
+ PCLZIP_OPT_ADD_PATH => 'optional',
+ PCLZIP_CB_PRE_ADD => 'optional',
+ PCLZIP_CB_POST_ADD => 'optional',
+ PCLZIP_OPT_NO_COMPRESSION => 'optional',
+ PCLZIP_OPT_COMMENT => 'optional',
+ PCLZIP_OPT_ADD_COMMENT => 'optional',
+ PCLZIP_OPT_PREPEND_COMMENT => 'optional',
+ PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
+ PCLZIP_OPT_TEMP_FILE_ON => 'optional',
+ PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
+ //, PCLZIP_OPT_CRYPT => 'optional'
+ ));
+ if ($v_result != 1) {
+ return 0;
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+
+ // ----- Get the first argument
+ $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0];
+
+ // ----- Look for the optional second argument
+ if ($v_size == 2) {
+ $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1];
+ }
+ else if ($v_size > 2) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");
+
+ // ----- Return
+ return 0;
+ }
+ }
+ }
+
+ // ----- Look for default option values
+ $this->privOptionDefaultThreshold($v_options);
+
+ // ----- Init
+ $v_string_list = array();
+ $v_att_list = array();
+ $v_filedescr_list = array();
+ $p_result_list = array();
+
+ // ----- Look if the $p_filelist is really an array
+ if (is_array($p_filelist)) {
+
+ // ----- Look if the first element is also an array
+ // This will mean that this is a file description entry
+ if (isset($p_filelist[0]) && is_array($p_filelist[0])) {
+ $v_att_list = $p_filelist;
+ }
+
+ // ----- The list is a list of string names
+ else {
+ $v_string_list = $p_filelist;
+ }
+ }
+
+ // ----- Look if the $p_filelist is a string
+ else if (is_string($p_filelist)) {
+ // ----- Create a list from the string
+ $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist);
+ }
+
+ // ----- Invalid variable type for $p_filelist
+ else {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist");
+ return 0;
+ }
+
+ // ----- Reformat the string list
+ if (sizeof($v_string_list) != 0) {
+ foreach ($v_string_list as $v_string) {
+ $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string;
+ }
+ }
+
+ // ----- For each file in the list check the attributes
+ $v_supported_attributes
+ = array ( PCLZIP_ATT_FILE_NAME => 'mandatory'
+ ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional'
+ ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional'
+ ,PCLZIP_ATT_FILE_MTIME => 'optional'
+ ,PCLZIP_ATT_FILE_CONTENT => 'optional'
+ ,PCLZIP_ATT_FILE_COMMENT => 'optional'
+ );
+ foreach ($v_att_list as $v_entry) {
+ $v_result = $this->privFileDescrParseAtt($v_entry,
+ $v_filedescr_list[],
+ $v_options,
+ $v_supported_attributes);
+ if ($v_result != 1) {
+ return 0;
+ }
+ }
+
+ // ----- Expand the filelist (expand directories)
+ $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options);
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Call the create fct
+ $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options);
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Return
+ return $p_result_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : listContent()
+ // Description :
+ // This public method, gives the list of the files and directories, with their
+ // properties.
+ // The properties of each entries in the list are (used also in other functions) :
+ // filename : Name of the file. For a create or add action it is the filename
+ // given by the user. For an extract function it is the filename
+ // of the extracted file.
+ // stored_filename : Name of the file / directory stored in the archive.
+ // size : Size of the stored file.
+ // compressed_size : Size of the file's data compressed in the archive
+ // (without the headers overhead)
+ // mtime : Last known modification date of the file (UNIX timestamp)
+ // comment : Comment associated with the file
+ // folder : true | false
+ // index : index of the file in the archive
+ // status : status of the action (depending of the action) :
+ // Values are :
+ // ok : OK !
+ // filtered : the file / dir is not extracted (filtered by user)
+ // already_a_directory : the file can not be extracted because a
+ // directory with the same name already exists
+ // write_protected : the file can not be extracted because a file
+ // with the same name already exists and is
+ // write protected
+ // newer_exist : the file was not extracted because a newer file exists
+ // path_creation_fail : the file is not extracted because the folder
+ // does not exist and can not be created
+ // write_error : the file was not extracted because there was a
+ // error while writing the file
+ // read_error : the file was not extracted because there was a error
+ // while reading the file
+ // invalid_header : the file was not extracted because of an archive
+ // format error (bad file header)
+ // Note that each time a method can continue operating when there
+ // is an action error on a file, the error is only logged in the file status.
+ // Return Values :
+ // 0 on an unrecoverable failure,
+ // The list of the files in the archive.
+ // --------------------------------------------------------------------------------
+ function listContent()
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ return(0);
+ }
+
+ // ----- Call the extracting fct
+ $p_list = array();
+ if (($v_result = $this->privList($p_list)) != 1)
+ {
+ unset($p_list);
+ return(0);
+ }
+
+ // ----- Return
+ return $p_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function :
+ // extract($p_path="./", $p_remove_path="")
+ // extract([$p_option, $p_option_value, ...])
+ // Description :
+ // This method supports two synopsis. The first one is historical.
+ // This method extract all the files / directories from the archive to the
+ // folder indicated in $p_path.
+ // If you want to ignore the 'root' part of path of the memorized files
+ // you can indicate this in the optional $p_remove_path parameter.
+ // By default, if a newer file with the same name already exists, the
+ // file is not extracted.
+ //
+ // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions
+ // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append
+ // at the end of the path value of PCLZIP_OPT_PATH.
+ // Parameters :
+ // $p_path : Path where the files and directories are to be extracted
+ // $p_remove_path : First part ('root' part) of the memorized path
+ // (if any similar) to remove while extracting.
+ // Options :
+ // PCLZIP_OPT_PATH :
+ // PCLZIP_OPT_ADD_PATH :
+ // PCLZIP_OPT_REMOVE_PATH :
+ // PCLZIP_OPT_REMOVE_ALL_PATH :
+ // PCLZIP_CB_PRE_EXTRACT :
+ // PCLZIP_CB_POST_EXTRACT :
+ // Return Values :
+ // 0 or a negative value on failure,
+ // The list of the extracted files, with a status of the action.
+ // (see PclZip::listContent() for list entry format)
+ // --------------------------------------------------------------------------------
+ function extract()
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ return(0);
+ }
+
+ // ----- Set default values
+ $v_options = array();
+// $v_path = "./";
+ $v_path = '';
+ $v_remove_path = "";
+ $v_remove_all_path = false;
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+
+ // ----- Default values for option
+ $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;
+
+ // ----- Look for arguments
+ if ($v_size > 0) {
+ // ----- Get the arguments
+ $v_arg_list = func_get_args();
+
+ // ----- Look for first arg
+ if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {
+
+ // ----- Parse the options
+ $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
+ array (PCLZIP_OPT_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
+ PCLZIP_OPT_ADD_PATH => 'optional',
+ PCLZIP_CB_PRE_EXTRACT => 'optional',
+ PCLZIP_CB_POST_EXTRACT => 'optional',
+ PCLZIP_OPT_SET_CHMOD => 'optional',
+ PCLZIP_OPT_BY_NAME => 'optional',
+ PCLZIP_OPT_BY_EREG => 'optional',
+ PCLZIP_OPT_BY_PREG => 'optional',
+ PCLZIP_OPT_BY_INDEX => 'optional',
+ PCLZIP_OPT_EXTRACT_AS_STRING => 'optional',
+ PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional',
+ PCLZIP_OPT_REPLACE_NEWER => 'optional'
+ ,PCLZIP_OPT_STOP_ON_ERROR => 'optional'
+ ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional',
+ PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
+ PCLZIP_OPT_TEMP_FILE_ON => 'optional',
+ PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
+ ));
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Set the arguments
+ if (isset($v_options[PCLZIP_OPT_PATH])) {
+ $v_path = $v_options[PCLZIP_OPT_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) {
+ $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) {
+ $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_ADD_PATH])) {
+ // ----- Check for '/' in last path char
+ if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) {
+ $v_path .= '/';
+ }
+ $v_path .= $v_options[PCLZIP_OPT_ADD_PATH];
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+
+ // ----- Get the first argument
+ $v_path = $v_arg_list[0];
+
+ // ----- Look for the optional second argument
+ if ($v_size == 2) {
+ $v_remove_path = $v_arg_list[1];
+ }
+ else if ($v_size > 2) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");
+
+ // ----- Return
+ return 0;
+ }
+ }
+ }
+
+ // ----- Look for default option values
+ $this->privOptionDefaultThreshold($v_options);
+
+ // ----- Trace
+
+ // ----- Call the extracting fct
+ $p_list = array();
+ $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path,
+ $v_remove_all_path, $v_options);
+ if ($v_result < 1) {
+ unset($p_list);
+ return(0);
+ }
+
+ // ----- Return
+ return $p_list;
+ }
+ // --------------------------------------------------------------------------------
+
+
+ // --------------------------------------------------------------------------------
+ // Function :
+ // extractByIndex($p_index, $p_path="./", $p_remove_path="")
+ // extractByIndex($p_index, [$p_option, $p_option_value, ...])
+ // Description :
+ // This method supports two synopsis. The first one is historical.
+ // This method is doing a partial extract of the archive.
+ // The extracted files or folders are identified by their index in the
+ // archive (from 0 to n).
+ // Note that if the index identify a folder, only the folder entry is
+ // extracted, not all the files included in the archive.
+ // Parameters :
+ // $p_index : A single index (integer) or a string of indexes of files to
+ // extract. The form of the string is "0,4-6,8-12" with only numbers
+ // and '-' for range or ',' to separate ranges. No spaces or ';'
+ // are allowed.
+ // $p_path : Path where the files and directories are to be extracted
+ // $p_remove_path : First part ('root' part) of the memorized path
+ // (if any similar) to remove while extracting.
+ // Options :
+ // PCLZIP_OPT_PATH :
+ // PCLZIP_OPT_ADD_PATH :
+ // PCLZIP_OPT_REMOVE_PATH :
+ // PCLZIP_OPT_REMOVE_ALL_PATH :
+ // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and
+ // not as files.
+ // The resulting content is in a new field 'content' in the file
+ // structure.
+ // This option must be used alone (any other options are ignored).
+ // PCLZIP_CB_PRE_EXTRACT :
+ // PCLZIP_CB_POST_EXTRACT :
+ // Return Values :
+ // 0 on failure,
+ // The list of the extracted files, with a status of the action.
+ // (see PclZip::listContent() for list entry format)
+ // --------------------------------------------------------------------------------
+ //function extractByIndex($p_index, options...)
+ function extractByIndex($p_index)
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ return(0);
+ }
+
+ // ----- Set default values
+ $v_options = array();
+// $v_path = "./";
+ $v_path = '';
+ $v_remove_path = "";
+ $v_remove_all_path = false;
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+
+ // ----- Default values for option
+ $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;
+
+ // ----- Look for arguments
+ if ($v_size > 1) {
+ // ----- Get the arguments
+ $v_arg_list = func_get_args();
+
+ // ----- Remove form the options list the first argument
+ array_shift($v_arg_list);
+ $v_size--;
+
+ // ----- Look for first arg
+ if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {
+
+ // ----- Parse the options
+ $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
+ array (PCLZIP_OPT_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_PATH => 'optional',
+ PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
+ PCLZIP_OPT_EXTRACT_AS_STRING => 'optional',
+ PCLZIP_OPT_ADD_PATH => 'optional',
+ PCLZIP_CB_PRE_EXTRACT => 'optional',
+ PCLZIP_CB_POST_EXTRACT => 'optional',
+ PCLZIP_OPT_SET_CHMOD => 'optional',
+ PCLZIP_OPT_REPLACE_NEWER => 'optional'
+ ,PCLZIP_OPT_STOP_ON_ERROR => 'optional'
+ ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional',
+ PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
+ PCLZIP_OPT_TEMP_FILE_ON => 'optional',
+ PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
+ ));
+ if ($v_result != 1) {
+ return 0;
+ }
+
+ // ----- Set the arguments
+ if (isset($v_options[PCLZIP_OPT_PATH])) {
+ $v_path = $v_options[PCLZIP_OPT_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) {
+ $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) {
+ $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH];
+ }
+ if (isset($v_options[PCLZIP_OPT_ADD_PATH])) {
+ // ----- Check for '/' in last path char
+ if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) {
+ $v_path .= '/';
+ }
+ $v_path .= $v_options[PCLZIP_OPT_ADD_PATH];
+ }
+ if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) {
+ $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;
+ }
+ else {
+ }
+ }
+
+ // ----- Look for 2 args
+ // Here we need to support the first historic synopsis of the
+ // method.
+ else {
+
+ // ----- Get the first argument
+ $v_path = $v_arg_list[0];
+
+ // ----- Look for the optional second argument
+ if ($v_size == 2) {
+ $v_remove_path = $v_arg_list[1];
+ }
+ else if ($v_size > 2) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");
+
+ // ----- Return
+ return 0;
+ }
+ }
+ }
+
+ // ----- Trace
+
+ // ----- Trick
+ // Here I want to reuse extractByRule(), so I need to parse the $p_index
+ // with privParseOptions()
+ $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index);
+ $v_options_trick = array();
+ $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick,
+ array (PCLZIP_OPT_BY_INDEX => 'optional' ));
+ if ($v_result != 1) {
+ return 0;
+ }
+ $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX];
+
+ // ----- Look for default option values
+ $this->privOptionDefaultThreshold($v_options);
+
+ // ----- Call the extracting fct
+ if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) {
+ return(0);
+ }
+
+ // ----- Return
+ return $p_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function :
+ // delete([$p_option, $p_option_value, ...])
+ // Description :
+ // This method removes files from the archive.
+ // If no parameters are given, then all the archive is emptied.
+ // Parameters :
+ // None or optional arguments.
+ // Options :
+ // PCLZIP_OPT_BY_INDEX :
+ // PCLZIP_OPT_BY_NAME :
+ // PCLZIP_OPT_BY_EREG :
+ // PCLZIP_OPT_BY_PREG :
+ // Return Values :
+ // 0 on failure,
+ // The list of the files which are still present in the archive.
+ // (see PclZip::listContent() for list entry format)
+ // --------------------------------------------------------------------------------
+ function delete()
+ {
+ $v_result=1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ return(0);
+ }
+
+ // ----- Set default values
+ $v_options = array();
+
+ // ----- Look for variable options arguments
+ $v_size = func_num_args();
+
+ // ----- Look for arguments
+ if ($v_size > 0) {
+ // ----- Get the arguments
+ $v_arg_list = func_get_args();
+
+ // ----- Parse the options
+ $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
+ array (PCLZIP_OPT_BY_NAME => 'optional',
+ PCLZIP_OPT_BY_EREG => 'optional',
+ PCLZIP_OPT_BY_PREG => 'optional',
+ PCLZIP_OPT_BY_INDEX => 'optional' ));
+ if ($v_result != 1) {
+ return 0;
+ }
+ }
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Call the delete fct
+ $v_list = array();
+ if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) {
+ $this->privSwapBackMagicQuotes();
+ unset($v_list);
+ return(0);
+ }
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : deleteByIndex()
+ // Description :
+ // ***** Deprecated *****
+ // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered.
+ // --------------------------------------------------------------------------------
+ function deleteByIndex($p_index)
+ {
+
+ $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index);
+
+ // ----- Return
+ return $p_list;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : properties()
+ // Description :
+ // This method gives the properties of the archive.
+ // The properties are :
+ // nb : Number of files in the archive
+ // comment : Comment associated with the archive file
+ // status : not_exist, ok
+ // Parameters :
+ // None
+ // Return Values :
+ // 0 on failure,
+ // An array with the archive properties.
+ // --------------------------------------------------------------------------------
+ function properties()
+ {
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ $this->privSwapBackMagicQuotes();
+ return(0);
+ }
+
+ // ----- Default properties
+ $v_prop = array();
+ $v_prop['comment'] = '';
+ $v_prop['nb'] = 0;
+ $v_prop['status'] = 'not_exist';
+
+ // ----- Look if file exists
+ if (@is_file($this->zipname))
+ {
+ // ----- Open the zip file
+ if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0)
+ {
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode');
+
+ // ----- Return
+ return 0;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ return 0;
+ }
+
+ // ----- Close the zip file
+ $this->privCloseFd();
+
+ // ----- Set the user attributes
+ $v_prop['comment'] = $v_central_dir['comment'];
+ $v_prop['nb'] = $v_central_dir['entries'];
+ $v_prop['status'] = 'ok';
+ }
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_prop;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : duplicate()
+ // Description :
+ // This method creates an archive by copying the content of an other one. If
+ // the archive already exist, it is replaced by the new one without any warning.
+ // Parameters :
+ // $p_archive : The filename of a valid archive, or
+ // a valid PclZip object.
+ // Return Values :
+ // 1 on success.
+ // 0 or a negative value on error (error code).
+ // --------------------------------------------------------------------------------
+ function duplicate($p_archive)
+ {
+ $v_result = 1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Look if the $p_archive is a PclZip object
+ if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip'))
+ {
+
+ // ----- Duplicate the archive
+ $v_result = $this->privDuplicate($p_archive->zipname);
+ }
+
+ // ----- Look if the $p_archive is a string (so a filename)
+ else if (is_string($p_archive))
+ {
+
+ // ----- Check that $p_archive is a valid zip file
+ // TBC : Should also check the archive format
+ if (!is_file($p_archive)) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'");
+ $v_result = PCLZIP_ERR_MISSING_FILE;
+ }
+ else {
+ // ----- Duplicate the archive
+ $v_result = $this->privDuplicate($p_archive);
+ }
+ }
+
+ // ----- Invalid variable
+ else
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add");
+ $v_result = PCLZIP_ERR_INVALID_PARAMETER;
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : merge()
+ // Description :
+ // This method merge the $p_archive_to_add archive at the end of the current
+ // one ($this).
+ // If the archive ($this) does not exist, the merge becomes a duplicate.
+ // If the $p_archive_to_add archive does not exist, the merge is a success.
+ // Parameters :
+ // $p_archive_to_add : It can be directly the filename of a valid zip archive,
+ // or a PclZip object archive.
+ // Return Values :
+ // 1 on success,
+ // 0 or negative values on error (see below).
+ // --------------------------------------------------------------------------------
+ function merge($p_archive_to_add)
+ {
+ $v_result = 1;
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Check archive
+ if (!$this->privCheckFormat()) {
+ return(0);
+ }
+
+ // ----- Look if the $p_archive_to_add is a PclZip object
+ if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip'))
+ {
+
+ // ----- Merge the archive
+ $v_result = $this->privMerge($p_archive_to_add);
+ }
+
+ // ----- Look if the $p_archive_to_add is a string (so a filename)
+ else if (is_string($p_archive_to_add))
+ {
+
+ // ----- Create a temporary archive
+ $v_object_archive = new PclZip($p_archive_to_add);
+
+ // ----- Merge the archive
+ $v_result = $this->privMerge($v_object_archive);
+ }
+
+ // ----- Invalid variable
+ else
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add");
+ $v_result = PCLZIP_ERR_INVALID_PARAMETER;
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+
+
+ // --------------------------------------------------------------------------------
+ // Function : errorCode()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function errorCode()
+ {
+ if (PCLZIP_ERROR_EXTERNAL == 1) {
+ return(PclErrorCode());
+ }
+ else {
+ return($this->error_code);
+ }
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : errorName()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function errorName($p_with_code=false)
+ {
+ $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR',
+ PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL',
+ PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL',
+ PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER',
+ PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE',
+ PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG',
+ PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP',
+ PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE',
+ PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL',
+ PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION',
+ PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT',
+ PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL',
+ PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL',
+ PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM',
+ PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP',
+ PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE',
+ PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE',
+ PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION',
+ PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION'
+ ,PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE'
+ ,PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION'
+ );
+
+ if (isset($v_name[$this->error_code])) {
+ $v_value = $v_name[$this->error_code];
+ }
+ else {
+ $v_value = 'NoName';
+ }
+
+ if ($p_with_code) {
+ return($v_value.' ('.$this->error_code.')');
+ }
+ else {
+ return($v_value);
+ }
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : errorInfo()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function errorInfo($p_full=false)
+ {
+ if (PCLZIP_ERROR_EXTERNAL == 1) {
+ return(PclErrorString());
+ }
+ else {
+ if ($p_full) {
+ return($this->errorName(true)." : ".$this->error_string);
+ }
+ else {
+ return($this->error_string." [code ".$this->error_code."]");
+ }
+ }
+ }
+ // --------------------------------------------------------------------------------
+
+
+// --------------------------------------------------------------------------------
+// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS *****
+// ***** *****
+// ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY *****
+// --------------------------------------------------------------------------------
+
+
+
+ // --------------------------------------------------------------------------------
+ // Function : privCheckFormat()
+ // Description :
+ // This method check that the archive exists and is a valid zip archive.
+ // Several level of check exists. (futur)
+ // Parameters :
+ // $p_level : Level of check. Default 0.
+ // 0 : Check the first bytes (magic codes) (default value))
+ // 1 : 0 + Check the central directory (futur)
+ // 2 : 1 + Check each file header (futur)
+ // Return Values :
+ // true on success,
+ // false on error, the error code is set.
+ // --------------------------------------------------------------------------------
+ function privCheckFormat($p_level=0)
+ {
+ $v_result = true;
+
+ // ----- Reset the file system cache
+ clearstatcache();
+
+ // ----- Reset the error handler
+ $this->privErrorReset();
+
+ // ----- Look if the file exits
+ if (!is_file($this->zipname)) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'");
+ return(false);
+ }
+
+ // ----- Check that the file is readeable
+ if (!is_readable($this->zipname)) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'");
+ return(false);
+ }
+
+ // ----- Check the magic code
+ // TBC
+
+ // ----- Check the central header
+ // TBC
+
+ // ----- Check each file header
+ // TBC
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privParseOptions()
+ // Description :
+ // This internal methods reads the variable list of arguments ($p_options_list,
+ // $p_size) and generate an array with the options and values ($v_result_list).
+ // $v_requested_options contains the options that can be present and those that
+ // must be present.
+ // $v_requested_options is an array, with the option value as key, and 'optional',
+ // or 'mandatory' as value.
+ // Parameters :
+ // See above.
+ // Return Values :
+ // 1 on success.
+ // 0 on failure.
+ // --------------------------------------------------------------------------------
+ function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false)
+ {
+ $v_result=1;
+
+ // ----- Read the options
+ $i=0;
+ while ($i<$p_size) {
+
+ // ----- Check if the option is supported
+ if (!isset($v_requested_options[$p_options_list[$i]])) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for next option
+ switch ($p_options_list[$i]) {
+ // ----- Look for options that request a path value
+ case PCLZIP_OPT_PATH :
+ case PCLZIP_OPT_REMOVE_PATH :
+ case PCLZIP_OPT_ADD_PATH :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE);
+ $i++;
+ break;
+
+ case PCLZIP_OPT_TEMP_FILE_THRESHOLD :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+ return PclZip::errorCode();
+ }
+
+ // ----- Check for incompatible options
+ if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'");
+ return PclZip::errorCode();
+ }
+
+ // ----- Check the value
+ $v_value = $p_options_list[$i+1];
+ if ((!is_integer($v_value)) || ($v_value<0)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value (and convert it in bytes)
+ $v_result_list[$p_options_list[$i]] = $v_value*1048576;
+ $i++;
+ break;
+
+ case PCLZIP_OPT_TEMP_FILE_ON :
+ // ----- Check for incompatible options
+ if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'");
+ return PclZip::errorCode();
+ }
+
+ $v_result_list[$p_options_list[$i]] = true;
+ break;
+
+ case PCLZIP_OPT_TEMP_FILE_OFF :
+ // ----- Check for incompatible options
+ if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'");
+ return PclZip::errorCode();
+ }
+ // ----- Check for incompatible options
+ if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'");
+ return PclZip::errorCode();
+ }
+
+ $v_result_list[$p_options_list[$i]] = true;
+ break;
+
+ case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ if ( is_string($p_options_list[$i+1])
+ && ($p_options_list[$i+1] != '')) {
+ $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE);
+ $i++;
+ }
+ else {
+ }
+ break;
+
+ // ----- Look for options that request an array of string for value
+ case PCLZIP_OPT_BY_NAME :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ if (is_string($p_options_list[$i+1])) {
+ $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1];
+ }
+ else if (is_array($p_options_list[$i+1])) {
+ $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
+ }
+ else {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ $i++;
+ break;
+
+ // ----- Look for options that request an EREG or PREG expression
+ case PCLZIP_OPT_BY_EREG :
+ // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG
+ // to PCLZIP_OPT_BY_PREG
+ $p_options_list[$i] = PCLZIP_OPT_BY_PREG;
+ case PCLZIP_OPT_BY_PREG :
+ //case PCLZIP_OPT_CRYPT :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ if (is_string($p_options_list[$i+1])) {
+ $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
+ }
+ else {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ $i++;
+ break;
+
+ // ----- Look for options that takes a string
+ case PCLZIP_OPT_COMMENT :
+ case PCLZIP_OPT_ADD_COMMENT :
+ case PCLZIP_OPT_PREPEND_COMMENT :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE,
+ "Missing parameter value for option '"
+ .PclZipUtilOptionText($p_options_list[$i])
+ ."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ if (is_string($p_options_list[$i+1])) {
+ $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
+ }
+ else {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE,
+ "Wrong parameter value for option '"
+ .PclZipUtilOptionText($p_options_list[$i])
+ ."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ $i++;
+ break;
+
+ // ----- Look for options that request an array of index
+ case PCLZIP_OPT_BY_INDEX :
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_work_list = array();
+ if (is_string($p_options_list[$i+1])) {
+
+ // ----- Remove spaces
+ $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', '');
+
+ // ----- Parse items
+ $v_work_list = explode(",", $p_options_list[$i+1]);
+ }
+ else if (is_integer($p_options_list[$i+1])) {
+ $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1];
+ }
+ else if (is_array($p_options_list[$i+1])) {
+ $v_work_list = $p_options_list[$i+1];
+ }
+ else {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Reduce the index list
+ // each index item in the list must be a couple with a start and
+ // an end value : [0,3], [5-5], [8-10], ...
+ // ----- Check the format of each item
+ $v_sort_flag=false;
+ $v_sort_value=0;
+ for ($j=0; $j= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
+ $i++;
+ break;
+
+ // ----- Look for options that request a call-back
+ case PCLZIP_CB_PRE_EXTRACT :
+ case PCLZIP_CB_POST_EXTRACT :
+ case PCLZIP_CB_PRE_ADD :
+ case PCLZIP_CB_POST_ADD :
+ /* for futur use
+ case PCLZIP_CB_PRE_DELETE :
+ case PCLZIP_CB_POST_DELETE :
+ case PCLZIP_CB_PRE_LIST :
+ case PCLZIP_CB_POST_LIST :
+ */
+ // ----- Check the number of parameters
+ if (($i+1) >= $p_size) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Get the value
+ $v_function_name = $p_options_list[$i+1];
+
+ // ----- Check that the value is a valid existing function
+ if (!function_exists($v_function_name)) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Set the attribute
+ $v_result_list[$p_options_list[$i]] = $v_function_name;
+ $i++;
+ break;
+
+ default :
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
+ "Unknown parameter '"
+ .$p_options_list[$i]."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Next options
+ $i++;
+ }
+
+ // ----- Look for mandatory options
+ if ($v_requested_options !== false) {
+ for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) {
+ // ----- Look for mandatory option
+ if ($v_requested_options[$key] == 'mandatory') {
+ // ----- Look if present
+ if (!isset($v_result_list[$key])) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ }
+ }
+ }
+
+ // ----- Look for default values
+ if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) {
+
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privOptionDefaultThreshold()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privOptionDefaultThreshold(&$p_options)
+ {
+ $v_result=1;
+
+ if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD])
+ || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) {
+ return $v_result;
+ }
+
+ // ----- Get 'memory_limit' configuration value
+ $v_memory_limit = ini_get('memory_limit');
+ $v_memory_limit = trim($v_memory_limit);
+ $last = strtolower(substr($v_memory_limit, -1));
+
+ if($last == 'g')
+ //$v_memory_limit = $v_memory_limit*1024*1024*1024;
+ $v_memory_limit = $v_memory_limit*1073741824;
+ if($last == 'm')
+ //$v_memory_limit = $v_memory_limit*1024*1024;
+ $v_memory_limit = $v_memory_limit*1048576;
+ if($last == 'k')
+ $v_memory_limit = $v_memory_limit*1024;
+
+ $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit*PCLZIP_TEMPORARY_FILE_RATIO);
+
+
+ // ----- Sanity check : No threshold if value lower than 1M
+ if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) {
+ unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]);
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privFileDescrParseAtt()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // 1 on success.
+ // 0 on failure.
+ // --------------------------------------------------------------------------------
+ function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false)
+ {
+ $v_result=1;
+
+ // ----- For each file in the list check the attributes
+ foreach ($p_file_list as $v_key => $v_value) {
+
+ // ----- Check if the option is supported
+ if (!isset($v_requested_options[$v_key])) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for attribute
+ switch ($v_key) {
+ case PCLZIP_ATT_FILE_NAME :
+ if (!is_string($v_value)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['filename'] = PclZipUtilPathReduction($v_value);
+
+ if ($p_filedescr['filename'] == '') {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ break;
+
+ case PCLZIP_ATT_FILE_NEW_SHORT_NAME :
+ if (!is_string($v_value)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value);
+
+ if ($p_filedescr['new_short_name'] == '') {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+ break;
+
+ case PCLZIP_ATT_FILE_NEW_FULL_NAME :
+ if (!is_string($v_value)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value);
+
+ if ($p_filedescr['new_full_name'] == '') {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+ break;
+
+ // ----- Look for options that takes a string
+ case PCLZIP_ATT_FILE_COMMENT :
+ if (!is_string($v_value)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['comment'] = $v_value;
+ break;
+
+ case PCLZIP_ATT_FILE_MTIME :
+ if (!is_integer($v_value)) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".PclZipUtilOptionText($v_key)."'");
+ return PclZip::errorCode();
+ }
+
+ $p_filedescr['mtime'] = $v_value;
+ break;
+
+ case PCLZIP_ATT_FILE_CONTENT :
+ $p_filedescr['content'] = $v_value;
+ break;
+
+ default :
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER,
+ "Unknown parameter '".$v_key."'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for mandatory options
+ if ($v_requested_options !== false) {
+ for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) {
+ // ----- Look for mandatory option
+ if ($v_requested_options[$key] == 'mandatory') {
+ // ----- Look if present
+ if (!isset($p_file_list[$key])) {
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")");
+ return PclZip::errorCode();
+ }
+ }
+ }
+ }
+
+ // end foreach
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privFileDescrExpand()
+ // Description :
+ // This method look for each item of the list to see if its a file, a folder
+ // or a string to be added as file. For any other type of files (link, other)
+ // just ignore the item.
+ // Then prepare the information that will be stored for that file.
+ // When its a folder, expand the folder with all the files that are in that
+ // folder (recursively).
+ // Parameters :
+ // Return Values :
+ // 1 on success.
+ // 0 on failure.
+ // --------------------------------------------------------------------------------
+ function privFileDescrExpand(&$p_filedescr_list, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Create a result list
+ $v_result_list = array();
+
+ // ----- Look each entry
+ for ($i=0; $iprivCalculateStoredFilename($v_descr, $p_options);
+
+ // ----- Add the descriptor in result list
+ $v_result_list[sizeof($v_result_list)] = $v_descr;
+
+ // ----- Look for folder
+ if ($v_descr['type'] == 'folder') {
+ // ----- List of items in folder
+ $v_dirlist_descr = array();
+ $v_dirlist_nb = 0;
+ if ($v_folder_handler = @opendir($v_descr['filename'])) {
+ while (($v_item_handler = @readdir($v_folder_handler)) !== false) {
+
+ // ----- Skip '.' and '..'
+ if (($v_item_handler == '.') || ($v_item_handler == '..')) {
+ continue;
+ }
+
+ // ----- Compose the full filename
+ $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler;
+
+ // ----- Look for different stored filename
+ // Because the name of the folder was changed, the name of the
+ // files/sub-folders also change
+ if (($v_descr['stored_filename'] != $v_descr['filename'])
+ && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) {
+ if ($v_descr['stored_filename'] != '') {
+ $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler;
+ }
+ else {
+ $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler;
+ }
+ }
+
+ $v_dirlist_nb++;
+ }
+
+ @closedir($v_folder_handler);
+ }
+ else {
+ // TBC : unable to open folder in read mode
+ }
+
+ // ----- Expand each element of the list
+ if ($v_dirlist_nb != 0) {
+ // ----- Expand
+ if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) {
+ return $v_result;
+ }
+
+ // ----- Concat the resulting list
+ $v_result_list = array_merge($v_result_list, $v_dirlist_descr);
+ }
+ else {
+ }
+
+ // ----- Free local array
+ unset($v_dirlist_descr);
+ }
+ }
+
+ // ----- Get the result list
+ $p_filedescr_list = $v_result_list;
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCreate()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privCreate($p_filedescr_list, &$p_result_list, &$p_options)
+ {
+ $v_result=1;
+ $v_list_detail = array();
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Open the file in write mode
+ if (($v_result = $this->privOpenFd('wb')) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Add the list of files
+ $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options);
+
+ // ----- Close
+ $this->privCloseFd();
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAdd()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privAdd($p_filedescr_list, &$p_result_list, &$p_options)
+ {
+ $v_result=1;
+ $v_list_detail = array();
+
+ // ----- Look if the archive exists or is empty
+ if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0))
+ {
+
+ // ----- Do a create
+ $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options);
+
+ // ----- Return
+ return $v_result;
+ }
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Open the zip file
+ if (($v_result=$this->privOpenFd('rb')) != 1)
+ {
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ @rewind($this->zip_fd);
+
+ // ----- Creates a temporay file
+ $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';
+
+ // ----- Open the temporary file in write mode
+ if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0)
+ {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Copy the files from the archive to the temporary file
+ // TBC : Here I should better append the file and go back to erase the central dir
+ $v_size = $v_central_dir['offset'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = fread($this->zip_fd, $v_read_size);
+ @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Swap the file descriptor
+ // Here is a trick : I swap the temporary fd with the zip fd, in order to use
+ // the following methods on the temporary fil and not the real archive
+ $v_swap = $this->zip_fd;
+ $this->zip_fd = $v_zip_temp_fd;
+ $v_zip_temp_fd = $v_swap;
+
+ // ----- Add the files
+ $v_header_list = array();
+ if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1)
+ {
+ fclose($v_zip_temp_fd);
+ $this->privCloseFd();
+ @unlink($v_zip_temp_name);
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Store the offset of the central dir
+ $v_offset = @ftell($this->zip_fd);
+
+ // ----- Copy the block of file headers from the old archive
+ $v_size = $v_central_dir['size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($v_zip_temp_fd, $v_read_size);
+ @fwrite($this->zip_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Create the Central Dir files header
+ for ($i=0, $v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) {
+ fclose($v_zip_temp_fd);
+ $this->privCloseFd();
+ @unlink($v_zip_temp_name);
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+ $v_count++;
+ }
+
+ // ----- Transform the header to a 'usable' info
+ $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
+ }
+
+ // ----- Zip file comment
+ $v_comment = $v_central_dir['comment'];
+ if (isset($p_options[PCLZIP_OPT_COMMENT])) {
+ $v_comment = $p_options[PCLZIP_OPT_COMMENT];
+ }
+ if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) {
+ $v_comment = $v_comment.$p_options[PCLZIP_OPT_ADD_COMMENT];
+ }
+ if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) {
+ $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT].$v_comment;
+ }
+
+ // ----- Calculate the size of the central header
+ $v_size = @ftell($this->zip_fd)-$v_offset;
+
+ // ----- Create the central dir footer
+ if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1)
+ {
+ // ----- Reset the file list
+ unset($v_header_list);
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Swap back the file descriptor
+ $v_swap = $this->zip_fd;
+ $this->zip_fd = $v_zip_temp_fd;
+ $v_zip_temp_fd = $v_swap;
+
+ // ----- Close
+ $this->privCloseFd();
+
+ // ----- Close the temporary file
+ @fclose($v_zip_temp_fd);
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Delete the zip file
+ // TBC : I should test the result ...
+ @unlink($this->zipname);
+
+ // ----- Rename the temporary file
+ // TBC : I should test the result ...
+ //@rename($v_zip_temp_name, $this->zipname);
+ PclZipUtilRename($v_zip_temp_name, $this->zipname);
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privOpenFd()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function privOpenFd($p_mode)
+ {
+ $v_result=1;
+
+ // ----- Look if already open
+ if ($this->zip_fd != 0)
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Open the zip file
+ if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0)
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCloseFd()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function privCloseFd()
+ {
+ $v_result=1;
+
+ if ($this->zip_fd != 0)
+ @fclose($this->zip_fd);
+ $this->zip_fd = 0;
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAddList()
+ // Description :
+ // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is
+ // different from the real path of the file. This is usefull if you want to have PclTar
+ // running in any directory, and memorize relative path from an other directory.
+ // Parameters :
+ // $p_list : An array containing the file or directory names to add in the tar
+ // $p_result_list : list of added files with their properties (specially the status field)
+ // $p_add_dir : Path to add in the filename path archived
+ // $p_remove_dir : Path to remove in the filename path archived
+ // Return Values :
+ // --------------------------------------------------------------------------------
+// function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options)
+ function privAddList($p_filedescr_list, &$p_result_list, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Add the files
+ $v_header_list = array();
+ if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Store the offset of the central dir
+ $v_offset = @ftell($this->zip_fd);
+
+ // ----- Create the Central Dir files header
+ for ($i=0,$v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) {
+ // ----- Return
+ return $v_result;
+ }
+ $v_count++;
+ }
+
+ // ----- Transform the header to a 'usable' info
+ $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
+ }
+
+ // ----- Zip file comment
+ $v_comment = '';
+ if (isset($p_options[PCLZIP_OPT_COMMENT])) {
+ $v_comment = $p_options[PCLZIP_OPT_COMMENT];
+ }
+
+ // ----- Calculate the size of the central header
+ $v_size = @ftell($this->zip_fd)-$v_offset;
+
+ // ----- Create the central dir footer
+ if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1)
+ {
+ // ----- Reset the file list
+ unset($v_header_list);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAddFileList()
+ // Description :
+ // Parameters :
+ // $p_filedescr_list : An array containing the file description
+ // or directory names to add in the zip
+ // $p_result_list : list of added files with their properties (specially the status field)
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options)
+ {
+ $v_result=1;
+ $v_header = array();
+
+ // ----- Recuperate the current number of elt in list
+ $v_nb = sizeof($p_result_list);
+
+ // ----- Loop on the files
+ for ($j=0; ($jprivAddFile($p_filedescr_list[$j], $v_header,
+ $p_options);
+ if ($v_result != 1) {
+ return $v_result;
+ }
+
+ // ----- Store the file infos
+ $p_result_list[$v_nb++] = $v_header;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAddFile()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privAddFile($p_filedescr, &$p_header, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Working variable
+ $p_filename = $p_filedescr['filename'];
+
+ // TBC : Already done in the fileAtt check ... ?
+ if ($p_filename == "") {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for a stored different filename
+ /* TBC : Removed
+ if (isset($p_filedescr['stored_filename'])) {
+ $v_stored_filename = $p_filedescr['stored_filename'];
+ }
+ else {
+ $v_stored_filename = $p_filedescr['stored_filename'];
+ }
+ */
+
+ // ----- Set the file properties
+ clearstatcache();
+ $p_header['version'] = 20;
+ $p_header['version_extracted'] = 10;
+ $p_header['flag'] = 0;
+ $p_header['compression'] = 0;
+ $p_header['crc'] = 0;
+ $p_header['compressed_size'] = 0;
+ $p_header['filename_len'] = strlen($p_filename);
+ $p_header['extra_len'] = 0;
+ $p_header['disk'] = 0;
+ $p_header['internal'] = 0;
+ $p_header['offset'] = 0;
+ $p_header['filename'] = $p_filename;
+// TBC : Removed $p_header['stored_filename'] = $v_stored_filename;
+ $p_header['stored_filename'] = $p_filedescr['stored_filename'];
+ $p_header['extra'] = '';
+ $p_header['status'] = 'ok';
+ $p_header['index'] = -1;
+
+ // ----- Look for regular file
+ if ($p_filedescr['type']=='file') {
+ $p_header['external'] = 0x00000000;
+ $p_header['size'] = filesize($p_filename);
+ }
+
+ // ----- Look for regular folder
+ else if ($p_filedescr['type']=='folder') {
+ $p_header['external'] = 0x00000010;
+ $p_header['mtime'] = filemtime($p_filename);
+ $p_header['size'] = filesize($p_filename);
+ }
+
+ // ----- Look for virtual file
+ else if ($p_filedescr['type'] == 'virtual_file') {
+ $p_header['external'] = 0x00000000;
+ $p_header['size'] = strlen($p_filedescr['content']);
+ }
+
+
+ // ----- Look for filetime
+ if (isset($p_filedescr['mtime'])) {
+ $p_header['mtime'] = $p_filedescr['mtime'];
+ }
+ else if ($p_filedescr['type'] == 'virtual_file') {
+ $p_header['mtime'] = time();
+ }
+ else {
+ $p_header['mtime'] = filemtime($p_filename);
+ }
+
+ // ------ Look for file comment
+ if (isset($p_filedescr['comment'])) {
+ $p_header['comment_len'] = strlen($p_filedescr['comment']);
+ $p_header['comment'] = $p_filedescr['comment'];
+ }
+ else {
+ $p_header['comment_len'] = 0;
+ $p_header['comment'] = '';
+ }
+
+ // ----- Look for pre-add callback
+ if (isset($p_options[PCLZIP_CB_PRE_ADD])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_header, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header);
+ if ($v_result == 0) {
+ // ----- Change the file status
+ $p_header['status'] = "skipped";
+ $v_result = 1;
+ }
+
+ // ----- Update the informations
+ // Only some fields can be modified
+ if ($p_header['stored_filename'] != $v_local_header['stored_filename']) {
+ $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']);
+ }
+ }
+
+ // ----- Look for empty stored filename
+ if ($p_header['stored_filename'] == "") {
+ $p_header['status'] = "filtered";
+ }
+
+ // ----- Check the path length
+ if (strlen($p_header['stored_filename']) > 0xFF) {
+ $p_header['status'] = 'filename_too_long';
+ }
+
+ // ----- Look if no error, or file not skipped
+ if ($p_header['status'] == 'ok') {
+
+ // ----- Look for a file
+ if ($p_filedescr['type'] == 'file') {
+ // ----- Look for using temporary file to zip
+ if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF]))
+ && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON])
+ || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD])
+ && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) {
+ $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options);
+ if ($v_result < PCLZIP_ERR_NO_ERROR) {
+ return $v_result;
+ }
+ }
+
+ // ----- Use "in memory" zip algo
+ else {
+
+ // ----- Open the source file
+ if (($v_file = @fopen($p_filename, "rb")) == 0) {
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode");
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file content
+ $v_content = @fread($v_file, $p_header['size']);
+
+ // ----- Close the file
+ @fclose($v_file);
+
+ // ----- Calculate the CRC
+ $p_header['crc'] = @crc32($v_content);
+
+ // ----- Look for no compression
+ if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) {
+ // ----- Set header parameters
+ $p_header['compressed_size'] = $p_header['size'];
+ $p_header['compression'] = 0;
+ }
+
+ // ----- Look for normal compression
+ else {
+ // ----- Compress the content
+ $v_content = @gzdeflate($v_content);
+
+ // ----- Set header parameters
+ $p_header['compressed_size'] = strlen($v_content);
+ $p_header['compression'] = 8;
+ }
+
+ // ----- Call the header generation
+ if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
+ @fclose($v_file);
+ return $v_result;
+ }
+
+ // ----- Write the compressed (or not) content
+ @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']);
+
+ }
+
+ }
+
+ // ----- Look for a virtual file (a file from string)
+ else if ($p_filedescr['type'] == 'virtual_file') {
+
+ $v_content = $p_filedescr['content'];
+
+ // ----- Calculate the CRC
+ $p_header['crc'] = @crc32($v_content);
+
+ // ----- Look for no compression
+ if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) {
+ // ----- Set header parameters
+ $p_header['compressed_size'] = $p_header['size'];
+ $p_header['compression'] = 0;
+ }
+
+ // ----- Look for normal compression
+ else {
+ // ----- Compress the content
+ $v_content = @gzdeflate($v_content);
+
+ // ----- Set header parameters
+ $p_header['compressed_size'] = strlen($v_content);
+ $p_header['compression'] = 8;
+ }
+
+ // ----- Call the header generation
+ if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
+ @fclose($v_file);
+ return $v_result;
+ }
+
+ // ----- Write the compressed (or not) content
+ @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']);
+ }
+
+ // ----- Look for a directory
+ else if ($p_filedescr['type'] == 'folder') {
+ // ----- Look for directory last '/'
+ if (@substr($p_header['stored_filename'], -1) != '/') {
+ $p_header['stored_filename'] .= '/';
+ }
+
+ // ----- Set the file properties
+ $p_header['size'] = 0;
+ //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked
+ $p_header['external'] = 0x00000010; // Value for a folder : to be checked
+
+ // ----- Call the header generation
+ if (($v_result = $this->privWriteFileHeader($p_header)) != 1)
+ {
+ return $v_result;
+ }
+ }
+ }
+
+ // ----- Look for post-add callback
+ if (isset($p_options[PCLZIP_CB_POST_ADD])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_header, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header);
+ if ($v_result == 0) {
+ // ----- Ignored
+ $v_result = 1;
+ }
+
+ // ----- Update the informations
+ // Nothing can be modified
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privAddFileUsingTempFile()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options)
+ {
+ $v_result=PCLZIP_ERR_NO_ERROR;
+
+ // ----- Working variable
+ $p_filename = $p_filedescr['filename'];
+
+
+ // ----- Open the source file
+ if (($v_file = @fopen($p_filename, "rb")) == 0) {
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode");
+ return PclZip::errorCode();
+ }
+
+ // ----- Creates a compressed temporary file
+ $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz';
+ if (($v_file_compressed = @gzopen($v_gzip_temp_name, "wb")) == 0) {
+ fclose($v_file);
+ PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode');
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
+ $v_size = filesize($p_filename);
+ while ($v_size != 0) {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($v_file, $v_read_size);
+ //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
+ @gzputs($v_file_compressed, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Close the file
+ @fclose($v_file);
+ @gzclose($v_file_compressed);
+
+ // ----- Check the minimum file size
+ if (filesize($v_gzip_temp_name) < 18) {
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes');
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the compressed attributes
+ if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) {
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the gzip file header
+ $v_binary_data = @fread($v_file_compressed, 10);
+ $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data);
+
+ // ----- Check some parameters
+ $v_data_header['os'] = bin2hex($v_data_header['os']);
+
+ // ----- Read the gzip file footer
+ @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8);
+ $v_binary_data = @fread($v_file_compressed, 8);
+ $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data);
+
+ // ----- Set the attributes
+ $p_header['compression'] = ord($v_data_header['cm']);
+ //$p_header['mtime'] = $v_data_header['mtime'];
+ $p_header['crc'] = $v_data_footer['crc'];
+ $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18;
+
+ // ----- Close the file
+ @fclose($v_file_compressed);
+
+ // ----- Call the header generation
+ if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
+ return $v_result;
+ }
+
+ // ----- Add the compressed data
+ if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0)
+ {
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
+ fseek($v_file_compressed, 10);
+ $v_size = $p_header['compressed_size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($v_file_compressed, $v_read_size);
+ //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
+ @fwrite($this->zip_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Close the file
+ @fclose($v_file_compressed);
+
+ // ----- Unlink the temporary file
+ @unlink($v_gzip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCalculateStoredFilename()
+ // Description :
+ // Based on file descriptor properties and global options, this method
+ // calculate the filename that will be stored in the archive.
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privCalculateStoredFilename(&$p_filedescr, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Working variables
+ $p_filename = $p_filedescr['filename'];
+ if (isset($p_options[PCLZIP_OPT_ADD_PATH])) {
+ $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH];
+ }
+ else {
+ $p_add_dir = '';
+ }
+ if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) {
+ $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH];
+ }
+ else {
+ $p_remove_dir = '';
+ }
+ if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) {
+ $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH];
+ }
+ else {
+ $p_remove_all_dir = 0;
+ }
+
+
+ // ----- Look for full name change
+ if (isset($p_filedescr['new_full_name'])) {
+ // ----- Remove drive letter if any
+ $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']);
+ }
+
+ // ----- Look for path and/or short name change
+ else {
+
+ // ----- Look for short name change
+ // Its when we cahnge just the filename but not the path
+ if (isset($p_filedescr['new_short_name'])) {
+ $v_path_info = pathinfo($p_filename);
+ $v_dir = '';
+ if ($v_path_info['dirname'] != '') {
+ $v_dir = $v_path_info['dirname'].'/';
+ }
+ $v_stored_filename = $v_dir.$p_filedescr['new_short_name'];
+ }
+ else {
+ // ----- Calculate the stored filename
+ $v_stored_filename = $p_filename;
+ }
+
+ // ----- Look for all path to remove
+ if ($p_remove_all_dir) {
+ $v_stored_filename = basename($p_filename);
+ }
+ // ----- Look for partial path remove
+ else if ($p_remove_dir != "") {
+ if (substr($p_remove_dir, -1) != '/')
+ $p_remove_dir .= "/";
+
+ if ( (substr($p_filename, 0, 2) == "./")
+ || (substr($p_remove_dir, 0, 2) == "./")) {
+
+ if ( (substr($p_filename, 0, 2) == "./")
+ && (substr($p_remove_dir, 0, 2) != "./")) {
+ $p_remove_dir = "./".$p_remove_dir;
+ }
+ if ( (substr($p_filename, 0, 2) != "./")
+ && (substr($p_remove_dir, 0, 2) == "./")) {
+ $p_remove_dir = substr($p_remove_dir, 2);
+ }
+ }
+
+ $v_compare = PclZipUtilPathInclusion($p_remove_dir,
+ $v_stored_filename);
+ if ($v_compare > 0) {
+ if ($v_compare == 2) {
+ $v_stored_filename = "";
+ }
+ else {
+ $v_stored_filename = substr($v_stored_filename,
+ strlen($p_remove_dir));
+ }
+ }
+ }
+
+ // ----- Remove drive letter if any
+ $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename);
+
+ // ----- Look for path to add
+ if ($p_add_dir != "") {
+ if (substr($p_add_dir, -1) == "/")
+ $v_stored_filename = $p_add_dir.$v_stored_filename;
+ else
+ $v_stored_filename = $p_add_dir."/".$v_stored_filename;
+ }
+ }
+
+ // ----- Filename (reduce the path of stored name)
+ $v_stored_filename = PclZipUtilPathReduction($v_stored_filename);
+ $p_filedescr['stored_filename'] = $v_stored_filename;
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privWriteFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privWriteFileHeader(&$p_header)
+ {
+ $v_result=1;
+
+ // ----- Store the offset position of the file
+ $p_header['offset'] = ftell($this->zip_fd);
+
+ // ----- Transform UNIX mtime to DOS format mdate/mtime
+ $v_date = getdate($p_header['mtime']);
+ $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
+ $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];
+
+ // ----- Packed data
+ $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50,
+ $p_header['version_extracted'], $p_header['flag'],
+ $p_header['compression'], $v_mtime, $v_mdate,
+ $p_header['crc'], $p_header['compressed_size'],
+ $p_header['size'],
+ strlen($p_header['stored_filename']),
+ $p_header['extra_len']);
+
+ // ----- Write the first 148 bytes of the header in the archive
+ fputs($this->zip_fd, $v_binary_data, 30);
+
+ // ----- Write the variable fields
+ if (strlen($p_header['stored_filename']) != 0)
+ {
+ fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename']));
+ }
+ if ($p_header['extra_len'] != 0)
+ {
+ fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']);
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privWriteCentralFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privWriteCentralFileHeader(&$p_header)
+ {
+ $v_result=1;
+
+ // TBC
+ //for(reset($p_header); $key = key($p_header); next($p_header)) {
+ //}
+
+ // ----- Transform UNIX mtime to DOS format mdate/mtime
+ $v_date = getdate($p_header['mtime']);
+ $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
+ $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];
+
+
+ // ----- Packed data
+ $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50,
+ $p_header['version'], $p_header['version_extracted'],
+ $p_header['flag'], $p_header['compression'],
+ $v_mtime, $v_mdate, $p_header['crc'],
+ $p_header['compressed_size'], $p_header['size'],
+ strlen($p_header['stored_filename']),
+ $p_header['extra_len'], $p_header['comment_len'],
+ $p_header['disk'], $p_header['internal'],
+ $p_header['external'], $p_header['offset']);
+
+ // ----- Write the 42 bytes of the header in the zip file
+ fputs($this->zip_fd, $v_binary_data, 46);
+
+ // ----- Write the variable fields
+ if (strlen($p_header['stored_filename']) != 0)
+ {
+ fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename']));
+ }
+ if ($p_header['extra_len'] != 0)
+ {
+ fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']);
+ }
+ if ($p_header['comment_len'] != 0)
+ {
+ fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']);
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privWriteCentralHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment)
+ {
+ $v_result=1;
+
+ // ----- Packed data
+ $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries,
+ $p_nb_entries, $p_size,
+ $p_offset, strlen($p_comment));
+
+ // ----- Write the 22 bytes of the header in the zip file
+ fputs($this->zip_fd, $v_binary_data, 22);
+
+ // ----- Write the variable fields
+ if (strlen($p_comment) != 0)
+ {
+ fputs($this->zip_fd, $p_comment, strlen($p_comment));
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privList()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privList(&$p_list)
+ {
+ $v_result=1;
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Open the zip file
+ if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0)
+ {
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+
+ // ----- Go to beginning of Central Dir
+ @rewind($this->zip_fd);
+ if (@fseek($this->zip_fd, $v_central_dir['offset']))
+ {
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read each entry
+ for ($i=0; $i<$v_central_dir['entries']; $i++)
+ {
+ // ----- Read the file header
+ if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+ $v_header['index'] = $i;
+
+ // ----- Get the only interesting attributes
+ $this->privConvertHeader2FileInfo($v_header, $p_list[$i]);
+ unset($v_header);
+ }
+
+ // ----- Close the zip file
+ $this->privCloseFd();
+
+ // ----- Magic quotes trick
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privConvertHeader2FileInfo()
+ // Description :
+ // This function takes the file informations from the central directory
+ // entries and extract the interesting parameters that will be given back.
+ // The resulting file infos are set in the array $p_info
+ // $p_info['filename'] : Filename with full path. Given by user (add),
+ // extracted in the filesystem (extract).
+ // $p_info['stored_filename'] : Stored filename in the archive.
+ // $p_info['size'] = Size of the file.
+ // $p_info['compressed_size'] = Compressed size of the file.
+ // $p_info['mtime'] = Last modification date of the file.
+ // $p_info['comment'] = Comment associated with the file.
+ // $p_info['folder'] = true/false : indicates if the entry is a folder or not.
+ // $p_info['status'] = status of the action on the file.
+ // $p_info['crc'] = CRC of the file content.
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privConvertHeader2FileInfo($p_header, &$p_info)
+ {
+ $v_result=1;
+
+ // ----- Get the interesting attributes
+ $v_temp_path = PclZipUtilPathReduction($p_header['filename']);
+ $p_info['filename'] = $v_temp_path;
+ $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']);
+ $p_info['stored_filename'] = $v_temp_path;
+ $p_info['size'] = $p_header['size'];
+ $p_info['compressed_size'] = $p_header['compressed_size'];
+ $p_info['mtime'] = $p_header['mtime'];
+ $p_info['comment'] = $p_header['comment'];
+ $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010);
+ $p_info['index'] = $p_header['index'];
+ $p_info['status'] = $p_header['status'];
+ $p_info['crc'] = $p_header['crc'];
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractByRule()
+ // Description :
+ // Extract a file or directory depending of rules (by index, by name, ...)
+ // Parameters :
+ // $p_file_list : An array where will be placed the properties of each
+ // extracted file
+ // $p_path : Path to add while writing the extracted files
+ // $p_remove_path : Path to remove (from the file memorized path) while writing the
+ // extracted files. If the path does not match the file path,
+ // the file is extracted with its memorized path.
+ // $p_remove_path does not apply to 'list' mode.
+ // $p_path and $p_remove_path are commulative.
+ // Return Values :
+ // 1 on success,0 or less on error (see error code list)
+ // --------------------------------------------------------------------------------
+ function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Magic quotes trick
+ $this->privDisableMagicQuotes();
+
+ // ----- Check the path
+ if ( ($p_path == "")
+ || ( (substr($p_path, 0, 1) != "/")
+ && (substr($p_path, 0, 3) != "../")
+ && (substr($p_path,1,2)!=":/")))
+ $p_path = "./".$p_path;
+
+ // ----- Reduce the path last (and duplicated) '/'
+ if (($p_path != "./") && ($p_path != "/"))
+ {
+ // ----- Look for the path end '/'
+ while (substr($p_path, -1) == "/")
+ {
+ $p_path = substr($p_path, 0, strlen($p_path)-1);
+ }
+ }
+
+ // ----- Look for path to remove format (should end by /)
+ if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/'))
+ {
+ $p_remove_path .= '/';
+ }
+ $p_remove_path_size = strlen($p_remove_path);
+
+ // ----- Open the zip file
+ if (($v_result = $this->privOpenFd('rb')) != 1)
+ {
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ return $v_result;
+ }
+
+ // ----- Start at beginning of Central Dir
+ $v_pos_entry = $v_central_dir['offset'];
+
+ // ----- Read each entry
+ $j_start = 0;
+ for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++)
+ {
+
+ // ----- Read next Central dir entry
+ @rewind($this->zip_fd);
+ if (@fseek($this->zip_fd, $v_pos_entry))
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file header
+ $v_header = array();
+ if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ return $v_result;
+ }
+
+ // ----- Store the index
+ $v_header['index'] = $i;
+
+ // ----- Store the file position
+ $v_pos_entry = ftell($this->zip_fd);
+
+ // ----- Look for the specific extract rules
+ $v_extract = false;
+
+ // ----- Look for extract by name rule
+ if ( (isset($p_options[PCLZIP_OPT_BY_NAME]))
+ && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) {
+
+ // ----- Look if the filename is in the list
+ for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j]))
+ && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) {
+ $v_extract = true;
+ }
+ }
+ // ----- Look for a filename
+ elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) {
+ $v_extract = true;
+ }
+ }
+ }
+
+ // ----- Look for extract by ereg rule
+ // ereg() is deprecated with PHP 5.3
+ /*
+ else if ( (isset($p_options[PCLZIP_OPT_BY_EREG]))
+ && ($p_options[PCLZIP_OPT_BY_EREG] != "")) {
+
+ if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) {
+ $v_extract = true;
+ }
+ }
+ */
+
+ // ----- Look for extract by preg rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_PREG]))
+ && ($p_options[PCLZIP_OPT_BY_PREG] != "")) {
+
+ if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) {
+ $v_extract = true;
+ }
+ }
+
+ // ----- Look for extract by index rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX]))
+ && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) {
+
+ // ----- Look if the index is in the list
+ for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) {
+ $v_extract = true;
+ }
+ if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) {
+ $j_start = $j+1;
+ }
+
+ if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) {
+ break;
+ }
+ }
+ }
+
+ // ----- Look for no rule, which means extract all the archive
+ else {
+ $v_extract = true;
+ }
+
+ // ----- Check compression method
+ if ( ($v_extract)
+ && ( ($v_header['compression'] != 8)
+ && ($v_header['compression'] != 0))) {
+ $v_header['status'] = 'unsupported_compression';
+
+ // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
+ if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
+ && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {
+
+ $this->privSwapBackMagicQuotes();
+
+ PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION,
+ "Filename '".$v_header['stored_filename']."' is "
+ ."compressed by an unsupported compression "
+ ."method (".$v_header['compression'].") ");
+
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Check encrypted files
+ if (($v_extract) && (($v_header['flag'] & 1) == 1)) {
+ $v_header['status'] = 'unsupported_encryption';
+
+ // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
+ if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
+ && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {
+
+ $this->privSwapBackMagicQuotes();
+
+ PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION,
+ "Unsupported encryption for "
+ ." filename '".$v_header['stored_filename']
+ ."'");
+
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Look for real extraction
+ if (($v_extract) && ($v_header['status'] != 'ok')) {
+ $v_result = $this->privConvertHeader2FileInfo($v_header,
+ $p_file_list[$v_nb_extracted++]);
+ if ($v_result != 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+
+ $v_extract = false;
+ }
+
+ // ----- Look for real extraction
+ if ($v_extract)
+ {
+
+ // ----- Go to the file position
+ @rewind($this->zip_fd);
+ if (@fseek($this->zip_fd, $v_header['offset']))
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Look for extraction as string
+ if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) {
+
+ $v_string = '';
+
+ // ----- Extracting the file
+ $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options);
+ if ($v_result1 < 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result1;
+ }
+
+ // ----- Get the only interesting attributes
+ if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ return $v_result;
+ }
+
+ // ----- Set the file content
+ $p_file_list[$v_nb_extracted]['content'] = $v_string;
+
+ // ----- Next extracted file
+ $v_nb_extracted++;
+
+ // ----- Look for user callback abort
+ if ($v_result1 == 2) {
+ break;
+ }
+ }
+ // ----- Look for extraction in standard output
+ elseif ( (isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT]))
+ && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) {
+ // ----- Extracting the file in standard output
+ $v_result1 = $this->privExtractFileInOutput($v_header, $p_options);
+ if ($v_result1 < 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result1;
+ }
+
+ // ----- Get the only interesting attributes
+ if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result;
+ }
+
+ // ----- Look for user callback abort
+ if ($v_result1 == 2) {
+ break;
+ }
+ }
+ // ----- Look for normal extraction
+ else {
+ // ----- Extracting the file
+ $v_result1 = $this->privExtractFile($v_header,
+ $p_path, $p_remove_path,
+ $p_remove_all_path,
+ $p_options);
+ if ($v_result1 < 1) {
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+ return $v_result1;
+ }
+
+ // ----- Get the only interesting attributes
+ if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ return $v_result;
+ }
+
+ // ----- Look for user callback abort
+ if ($v_result1 == 2) {
+ break;
+ }
+ }
+ }
+ }
+
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $this->privSwapBackMagicQuotes();
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractFile()
+ // Description :
+ // Parameters :
+ // Return Values :
+ //
+ // 1 : ... ?
+ // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback
+ // --------------------------------------------------------------------------------
+ function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Read the file header
+ if (($v_result = $this->privReadFileHeader($v_header)) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+
+ // ----- Check that the file header is coherent with $p_entry info
+ if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
+ // TBC
+ }
+
+ // ----- Look for all path to remove
+ if ($p_remove_all_path == true) {
+ // ----- Look for folder entry that not need to be extracted
+ if (($p_entry['external']&0x00000010)==0x00000010) {
+
+ $p_entry['status'] = "filtered";
+
+ return $v_result;
+ }
+
+ // ----- Get the basename of the path
+ $p_entry['filename'] = basename($p_entry['filename']);
+ }
+
+ // ----- Look for path to remove
+ else if ($p_remove_path != "")
+ {
+ if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2)
+ {
+
+ // ----- Change the file status
+ $p_entry['status'] = "filtered";
+
+ // ----- Return
+ return $v_result;
+ }
+
+ $p_remove_path_size = strlen($p_remove_path);
+ if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path)
+ {
+
+ // ----- Remove the path
+ $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size);
+
+ }
+ }
+
+ // ----- Add the path
+ if ($p_path != '') {
+ $p_entry['filename'] = $p_path."/".$p_entry['filename'];
+ }
+
+ // ----- Check a base_dir_restriction
+ if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) {
+ $v_inclusion
+ = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION],
+ $p_entry['filename']);
+ if ($v_inclusion == 0) {
+
+ PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION,
+ "Filename '".$p_entry['filename']."' is "
+ ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION");
+
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Look for pre-extract callback
+ if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header);
+ if ($v_result == 0) {
+ // ----- Change the file status
+ $p_entry['status'] = "skipped";
+ $v_result = 1;
+ }
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ // ----- This status is internal and will be changed in 'skipped'
+ $p_entry['status'] = "aborted";
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+
+ // ----- Update the informations
+ // Only some fields can be modified
+ $p_entry['filename'] = $v_local_header['filename'];
+ }
+
+
+ // ----- Look if extraction should be done
+ if ($p_entry['status'] == 'ok') {
+
+ // ----- Look for specific actions while the file exist
+ if (file_exists($p_entry['filename']))
+ {
+
+ // ----- Look if file is a directory
+ if (is_dir($p_entry['filename']))
+ {
+
+ // ----- Change the file status
+ $p_entry['status'] = "already_a_directory";
+
+ // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
+ // For historical reason first PclZip implementation does not stop
+ // when this kind of error occurs.
+ if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
+ && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {
+
+ PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY,
+ "Filename '".$p_entry['filename']."' is "
+ ."already used by an existing directory");
+
+ return PclZip::errorCode();
+ }
+ }
+ // ----- Look if file is write protected
+ else if (!is_writeable($p_entry['filename']))
+ {
+
+ // ----- Change the file status
+ $p_entry['status'] = "write_protected";
+
+ // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
+ // For historical reason first PclZip implementation does not stop
+ // when this kind of error occurs.
+ if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
+ && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {
+
+ PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL,
+ "Filename '".$p_entry['filename']."' exists "
+ ."and is write protected");
+
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Look if the extracted file is older
+ else if (filemtime($p_entry['filename']) > $p_entry['mtime'])
+ {
+ // ----- Change the file status
+ if ( (isset($p_options[PCLZIP_OPT_REPLACE_NEWER]))
+ && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) {
+ }
+ else {
+ $p_entry['status'] = "newer_exist";
+
+ // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
+ // For historical reason first PclZip implementation does not stop
+ // when this kind of error occurs.
+ if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]))
+ && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) {
+
+ PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL,
+ "Newer version of '".$p_entry['filename']."' exists "
+ ."and option PCLZIP_OPT_REPLACE_NEWER is not selected");
+
+ return PclZip::errorCode();
+ }
+ }
+ }
+ else {
+ }
+ }
+
+ // ----- Check the directory availability and create it if necessary
+ else {
+ if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/'))
+ $v_dir_to_check = $p_entry['filename'];
+ else if (!strstr($p_entry['filename'], "/"))
+ $v_dir_to_check = "";
+ else
+ $v_dir_to_check = dirname($p_entry['filename']);
+
+ if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) {
+
+ // ----- Change the file status
+ $p_entry['status'] = "path_creation_fail";
+
+ // ----- Return
+ //return $v_result;
+ $v_result = 1;
+ }
+ }
+ }
+
+ // ----- Look if extraction should be done
+ if ($p_entry['status'] == 'ok') {
+
+ // ----- Do the extraction (if not a folder)
+ if (!(($p_entry['external']&0x00000010)==0x00000010))
+ {
+ // ----- Look for not compressed file
+ if ($p_entry['compression'] == 0) {
+
+ // ----- Opening destination file
+ if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0)
+ {
+
+ // ----- Change the file status
+ $p_entry['status'] = "write_error";
+
+ // ----- Return
+ return $v_result;
+ }
+
+
+ // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
+ $v_size = $p_entry['compressed_size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($this->zip_fd, $v_read_size);
+ /* Try to speed up the code
+ $v_binary_data = pack('a'.$v_read_size, $v_buffer);
+ @fwrite($v_dest_file, $v_binary_data, $v_read_size);
+ */
+ @fwrite($v_dest_file, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Closing the destination file
+ fclose($v_dest_file);
+
+ // ----- Change the file mtime
+ touch($p_entry['filename'], $p_entry['mtime']);
+
+
+ }
+ else {
+ // ----- TBC
+ // Need to be finished
+ if (($p_entry['flag'] & 1) == 1) {
+ PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.');
+ return PclZip::errorCode();
+ }
+
+
+ // ----- Look for using temporary file to unzip
+ if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF]))
+ && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON])
+ || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD])
+ && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) {
+ $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options);
+ if ($v_result < PCLZIP_ERR_NO_ERROR) {
+ return $v_result;
+ }
+ }
+
+ // ----- Look for extract in memory
+ else {
+
+
+ // ----- Read the compressed file in a buffer (one shot)
+ $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
+
+ // ----- Decompress the file
+ $v_file_content = @gzinflate($v_buffer);
+ unset($v_buffer);
+ if ($v_file_content === FALSE) {
+
+ // ----- Change the file status
+ // TBC
+ $p_entry['status'] = "error";
+
+ return $v_result;
+ }
+
+ // ----- Opening destination file
+ if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
+
+ // ----- Change the file status
+ $p_entry['status'] = "write_error";
+
+ return $v_result;
+ }
+
+ // ----- Write the uncompressed data
+ @fwrite($v_dest_file, $v_file_content, $p_entry['size']);
+ unset($v_file_content);
+
+ // ----- Closing the destination file
+ @fclose($v_dest_file);
+
+ }
+
+ // ----- Change the file mtime
+ @touch($p_entry['filename'], $p_entry['mtime']);
+ }
+
+ // ----- Look for chmod option
+ if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) {
+
+ // ----- Change the mode of the file
+ @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]);
+ }
+
+ }
+ }
+
+ // ----- Change abort status
+ if ($p_entry['status'] == "aborted") {
+ $p_entry['status'] = "skipped";
+ }
+
+ // ----- Look for post-extract callback
+ elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header);
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractFileUsingTempFile()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privExtractFileUsingTempFile(&$p_entry, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Creates a temporary file
+ $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz';
+ if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) {
+ fclose($v_file);
+ PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode');
+ return PclZip::errorCode();
+ }
+
+
+ // ----- Write gz file format header
+ $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3));
+ @fwrite($v_dest_file, $v_binary_data, 10);
+
+ // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
+ $v_size = $p_entry['compressed_size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($this->zip_fd, $v_read_size);
+ //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
+ @fwrite($v_dest_file, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Write gz file format footer
+ $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']);
+ @fwrite($v_dest_file, $v_binary_data, 8);
+
+ // ----- Close the temporary file
+ @fclose($v_dest_file);
+
+ // ----- Opening destination file
+ if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
+ $p_entry['status'] = "write_error";
+ return $v_result;
+ }
+
+ // ----- Open the temporary gz file
+ if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) {
+ @fclose($v_dest_file);
+ $p_entry['status'] = "read_error";
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
+ return PclZip::errorCode();
+ }
+
+
+ // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
+ $v_size = $p_entry['size'];
+ while ($v_size != 0) {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @gzread($v_src_file, $v_read_size);
+ //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
+ @fwrite($v_dest_file, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+ @fclose($v_dest_file);
+ @gzclose($v_src_file);
+
+ // ----- Delete the temporary file
+ @unlink($v_gzip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractFileInOutput()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privExtractFileInOutput(&$p_entry, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Read the file header
+ if (($v_result = $this->privReadFileHeader($v_header)) != 1) {
+ return $v_result;
+ }
+
+
+ // ----- Check that the file header is coherent with $p_entry info
+ if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
+ // TBC
+ }
+
+ // ----- Look for pre-extract callback
+ if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header);
+ if ($v_result == 0) {
+ // ----- Change the file status
+ $p_entry['status'] = "skipped";
+ $v_result = 1;
+ }
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ // ----- This status is internal and will be changed in 'skipped'
+ $p_entry['status'] = "aborted";
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+
+ // ----- Update the informations
+ // Only some fields can be modified
+ $p_entry['filename'] = $v_local_header['filename'];
+ }
+
+ // ----- Trace
+
+ // ----- Look if extraction should be done
+ if ($p_entry['status'] == 'ok') {
+
+ // ----- Do the extraction (if not a folder)
+ if (!(($p_entry['external']&0x00000010)==0x00000010)) {
+ // ----- Look for not compressed file
+ if ($p_entry['compressed_size'] == $p_entry['size']) {
+
+ // ----- Read the file in a buffer (one shot)
+ $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
+
+ // ----- Send the file to the output
+ echo $v_buffer;
+ unset($v_buffer);
+ }
+ else {
+
+ // ----- Read the compressed file in a buffer (one shot)
+ $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
+
+ // ----- Decompress the file
+ $v_file_content = gzinflate($v_buffer);
+ unset($v_buffer);
+
+ // ----- Send the file to the output
+ echo $v_file_content;
+ unset($v_file_content);
+ }
+ }
+ }
+
+ // ----- Change abort status
+ if ($p_entry['status'] == "aborted") {
+ $p_entry['status'] = "skipped";
+ }
+
+ // ----- Look for post-extract callback
+ elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header);
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+ }
+
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privExtractFileAsString()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privExtractFileAsString(&$p_entry, &$p_string, &$p_options)
+ {
+ $v_result=1;
+
+ // ----- Read the file header
+ $v_header = array();
+ if (($v_result = $this->privReadFileHeader($v_header)) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+
+ // ----- Check that the file header is coherent with $p_entry info
+ if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
+ // TBC
+ }
+
+ // ----- Look for pre-extract callback
+ if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header);
+ if ($v_result == 0) {
+ // ----- Change the file status
+ $p_entry['status'] = "skipped";
+ $v_result = 1;
+ }
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ // ----- This status is internal and will be changed in 'skipped'
+ $p_entry['status'] = "aborted";
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+
+ // ----- Update the informations
+ // Only some fields can be modified
+ $p_entry['filename'] = $v_local_header['filename'];
+ }
+
+
+ // ----- Look if extraction should be done
+ if ($p_entry['status'] == 'ok') {
+
+ // ----- Do the extraction (if not a folder)
+ if (!(($p_entry['external']&0x00000010)==0x00000010)) {
+ // ----- Look for not compressed file
+ // if ($p_entry['compressed_size'] == $p_entry['size'])
+ if ($p_entry['compression'] == 0) {
+
+ // ----- Reading the file
+ $p_string = @fread($this->zip_fd, $p_entry['compressed_size']);
+ }
+ else {
+
+ // ----- Reading the file
+ $v_data = @fread($this->zip_fd, $p_entry['compressed_size']);
+
+ // ----- Decompress the file
+ if (($p_string = @gzinflate($v_data)) === FALSE) {
+ // TBC
+ }
+ }
+
+ // ----- Trace
+ }
+ else {
+ // TBC : error : can not extract a folder in a string
+ }
+
+ }
+
+ // ----- Change abort status
+ if ($p_entry['status'] == "aborted") {
+ $p_entry['status'] = "skipped";
+ }
+
+ // ----- Look for post-extract callback
+ elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) {
+
+ // ----- Generate a local information
+ $v_local_header = array();
+ $this->privConvertHeader2FileInfo($p_entry, $v_local_header);
+
+ // ----- Swap the content to header
+ $v_local_header['content'] = $p_string;
+ $p_string = '';
+
+ // ----- Call the callback
+ // Here I do not use call_user_func() because I need to send a reference to the
+ // header.
+// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);');
+ $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header);
+
+ // ----- Swap back the content to header
+ $p_string = $v_local_header['content'];
+ unset($v_local_header['content']);
+
+ // ----- Look for abort result
+ if ($v_result == 2) {
+ $v_result = PCLZIP_ERR_USER_ABORTED;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privReadFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privReadFileHeader(&$p_header)
+ {
+ $v_result=1;
+
+ // ----- Read the 4 bytes signature
+ $v_binary_data = @fread($this->zip_fd, 4);
+ $v_data = unpack('Vid', $v_binary_data);
+
+ // ----- Check signature
+ if ($v_data['id'] != 0x04034b50)
+ {
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the first 42 bytes of the header
+ $v_binary_data = fread($this->zip_fd, 26);
+
+ // ----- Look for invalid block size
+ if (strlen($v_binary_data) != 26)
+ {
+ $p_header['filename'] = "";
+ $p_header['status'] = "invalid_header";
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the values
+ $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data);
+
+ // ----- Get filename
+ $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']);
+
+ // ----- Get extra_fields
+ if ($v_data['extra_len'] != 0) {
+ $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']);
+ }
+ else {
+ $p_header['extra'] = '';
+ }
+
+ // ----- Extract properties
+ $p_header['version_extracted'] = $v_data['version'];
+ $p_header['compression'] = $v_data['compression'];
+ $p_header['size'] = $v_data['size'];
+ $p_header['compressed_size'] = $v_data['compressed_size'];
+ $p_header['crc'] = $v_data['crc'];
+ $p_header['flag'] = $v_data['flag'];
+ $p_header['filename_len'] = $v_data['filename_len'];
+
+ // ----- Recuperate date in UNIX format
+ $p_header['mdate'] = $v_data['mdate'];
+ $p_header['mtime'] = $v_data['mtime'];
+ if ($p_header['mdate'] && $p_header['mtime'])
+ {
+ // ----- Extract time
+ $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
+ $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
+ $v_seconde = ($p_header['mtime'] & 0x001F)*2;
+
+ // ----- Extract date
+ $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
+ $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
+ $v_day = $p_header['mdate'] & 0x001F;
+
+ // ----- Get UNIX date format
+ $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);
+
+ }
+ else
+ {
+ $p_header['mtime'] = time();
+ }
+
+ // TBC
+ //for(reset($v_data); $key = key($v_data); next($v_data)) {
+ //}
+
+ // ----- Set the stored filename
+ $p_header['stored_filename'] = $p_header['filename'];
+
+ // ----- Set the status field
+ $p_header['status'] = "ok";
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privReadCentralFileHeader()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privReadCentralFileHeader(&$p_header)
+ {
+ $v_result=1;
+
+ // ----- Read the 4 bytes signature
+ $v_binary_data = @fread($this->zip_fd, 4);
+ $v_data = unpack('Vid', $v_binary_data);
+
+ // ----- Check signature
+ if ($v_data['id'] != 0x02014b50)
+ {
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the first 42 bytes of the header
+ $v_binary_data = fread($this->zip_fd, 42);
+
+ // ----- Look for invalid block size
+ if (strlen($v_binary_data) != 42)
+ {
+ $p_header['filename'] = "";
+ $p_header['status'] = "invalid_header";
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the values
+ $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data);
+
+ // ----- Get filename
+ if ($p_header['filename_len'] != 0)
+ $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']);
+ else
+ $p_header['filename'] = '';
+
+ // ----- Get extra
+ if ($p_header['extra_len'] != 0)
+ $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']);
+ else
+ $p_header['extra'] = '';
+
+ // ----- Get comment
+ if ($p_header['comment_len'] != 0)
+ $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']);
+ else
+ $p_header['comment'] = '';
+
+ // ----- Extract properties
+
+ // ----- Recuperate date in UNIX format
+ //if ($p_header['mdate'] && $p_header['mtime'])
+ // TBC : bug : this was ignoring time with 0/0/0
+ if (1)
+ {
+ // ----- Extract time
+ $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
+ $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
+ $v_seconde = ($p_header['mtime'] & 0x001F)*2;
+
+ // ----- Extract date
+ $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
+ $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
+ $v_day = $p_header['mdate'] & 0x001F;
+
+ // ----- Get UNIX date format
+ $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);
+
+ }
+ else
+ {
+ $p_header['mtime'] = time();
+ }
+
+ // ----- Set the stored filename
+ $p_header['stored_filename'] = $p_header['filename'];
+
+ // ----- Set default status to ok
+ $p_header['status'] = 'ok';
+
+ // ----- Look if it is a directory
+ if (substr($p_header['filename'], -1) == '/') {
+ //$p_header['external'] = 0x41FF0010;
+ $p_header['external'] = 0x00000010;
+ }
+
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privCheckFileHeaders()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // 1 on success,
+ // 0 on error;
+ // --------------------------------------------------------------------------------
+ function privCheckFileHeaders(&$p_local_header, &$p_central_header)
+ {
+ $v_result=1;
+
+ // ----- Check the static values
+ // TBC
+ if ($p_local_header['filename'] != $p_central_header['filename']) {
+ }
+ if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) {
+ }
+ if ($p_local_header['flag'] != $p_central_header['flag']) {
+ }
+ if ($p_local_header['compression'] != $p_central_header['compression']) {
+ }
+ if ($p_local_header['mtime'] != $p_central_header['mtime']) {
+ }
+ if ($p_local_header['filename_len'] != $p_central_header['filename_len']) {
+ }
+
+ // ----- Look for flag bit 3
+ if (($p_local_header['flag'] & 8) == 8) {
+ $p_local_header['size'] = $p_central_header['size'];
+ $p_local_header['compressed_size'] = $p_central_header['compressed_size'];
+ $p_local_header['crc'] = $p_central_header['crc'];
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privReadEndCentralDir()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privReadEndCentralDir(&$p_central_dir)
+ {
+ $v_result=1;
+
+ // ----- Go to the end of the zip file
+ $v_size = filesize($this->zipname);
+ @fseek($this->zip_fd, $v_size);
+ if (@ftell($this->zip_fd) != $v_size)
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\'');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- First try : look if this is an archive with no commentaries (most of the time)
+ // in this case the end of central dir is at 22 bytes of the file end
+ $v_found = 0;
+ if ($v_size > 26) {
+ @fseek($this->zip_fd, $v_size-22);
+ if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22))
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\'');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read for bytes
+ $v_binary_data = @fread($this->zip_fd, 4);
+ $v_data = @unpack('Vid', $v_binary_data);
+
+ // ----- Check signature
+ if ($v_data['id'] == 0x06054b50) {
+ $v_found = 1;
+ }
+
+ $v_pos = ftell($this->zip_fd);
+ }
+
+ // ----- Go back to the maximum possible size of the Central Dir End Record
+ if (!$v_found) {
+ $v_maximum_size = 65557; // 0xFFFF + 22;
+ if ($v_maximum_size > $v_size)
+ $v_maximum_size = $v_size;
+ @fseek($this->zip_fd, $v_size-$v_maximum_size);
+ if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size))
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\'');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read byte per byte in order to find the signature
+ $v_pos = ftell($this->zip_fd);
+ $v_bytes = 0x00000000;
+ while ($v_pos < $v_size)
+ {
+ // ----- Read a byte
+ $v_byte = @fread($this->zip_fd, 1);
+
+ // ----- Add the byte
+ //$v_bytes = ($v_bytes << 8) | Ord($v_byte);
+ // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number
+ // Otherwise on systems where we have 64bit integers the check below for the magic number will fail.
+ $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte);
+
+ // ----- Compare the bytes
+ if ($v_bytes == 0x504b0506)
+ {
+ $v_pos++;
+ break;
+ }
+
+ $v_pos++;
+ }
+
+ // ----- Look if not found end of central dir
+ if ($v_pos == $v_size)
+ {
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Read the first 18 bytes of the header
+ $v_binary_data = fread($this->zip_fd, 18);
+
+ // ----- Look for invalid block size
+ if (strlen($v_binary_data) != 18)
+ {
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data));
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Extract the values
+ $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data);
+
+ // ----- Check the global size
+ if (($v_pos + $v_data['comment_size'] + 18) != $v_size) {
+
+ // ----- Removed in release 2.2 see readme file
+ // The check of the file size is a little too strict.
+ // Some bugs where found when a zip is encrypted/decrypted with 'crypt'.
+ // While decrypted, zip has training 0 bytes
+ if (0) {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT,
+ 'The central dir is not at the end of the archive.'
+ .' Some trailing bytes exists after the archive.');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+ }
+
+ // ----- Get comment
+ if ($v_data['comment_size'] != 0) {
+ $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']);
+ }
+ else
+ $p_central_dir['comment'] = '';
+
+ $p_central_dir['entries'] = $v_data['entries'];
+ $p_central_dir['disk_entries'] = $v_data['disk_entries'];
+ $p_central_dir['offset'] = $v_data['offset'];
+ $p_central_dir['size'] = $v_data['size'];
+ $p_central_dir['disk'] = $v_data['disk'];
+ $p_central_dir['disk_start'] = $v_data['disk_start'];
+
+ // TBC
+ //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) {
+ //}
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDeleteByRule()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privDeleteByRule(&$p_result_list, &$p_options)
+ {
+ $v_result=1;
+ $v_list_detail = array();
+
+ // ----- Open the zip file
+ if (($v_result=$this->privOpenFd('rb')) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privCloseFd();
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ @rewind($this->zip_fd);
+
+ // ----- Scan all the files
+ // ----- Start at beginning of Central Dir
+ $v_pos_entry = $v_central_dir['offset'];
+ @rewind($this->zip_fd);
+ if (@fseek($this->zip_fd, $v_pos_entry))
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read each entry
+ $v_header_list = array();
+ $j_start = 0;
+ for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++)
+ {
+
+ // ----- Read the file header
+ $v_header_list[$v_nb_extracted] = array();
+ if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1)
+ {
+ // ----- Close the zip file
+ $this->privCloseFd();
+
+ return $v_result;
+ }
+
+
+ // ----- Store the index
+ $v_header_list[$v_nb_extracted]['index'] = $i;
+
+ // ----- Look for the specific extract rules
+ $v_found = false;
+
+ // ----- Look for extract by name rule
+ if ( (isset($p_options[PCLZIP_OPT_BY_NAME]))
+ && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) {
+
+ // ----- Look if the filename is in the list
+ for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j]))
+ && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) {
+ $v_found = true;
+ }
+ elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */
+ && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) {
+ $v_found = true;
+ }
+ }
+ // ----- Look for a filename
+ elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) {
+ $v_found = true;
+ }
+ }
+ }
+
+ // ----- Look for extract by ereg rule
+ // ereg() is deprecated with PHP 5.3
+ /*
+ else if ( (isset($p_options[PCLZIP_OPT_BY_EREG]))
+ && ($p_options[PCLZIP_OPT_BY_EREG] != "")) {
+
+ if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) {
+ $v_found = true;
+ }
+ }
+ */
+
+ // ----- Look for extract by preg rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_PREG]))
+ && ($p_options[PCLZIP_OPT_BY_PREG] != "")) {
+
+ if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) {
+ $v_found = true;
+ }
+ }
+
+ // ----- Look for extract by index rule
+ else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX]))
+ && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) {
+
+ // ----- Look if the index is in the list
+ for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) {
+ $v_found = true;
+ }
+ if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) {
+ $j_start = $j+1;
+ }
+
+ if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) {
+ break;
+ }
+ }
+ }
+ else {
+ $v_found = true;
+ }
+
+ // ----- Look for deletion
+ if ($v_found)
+ {
+ unset($v_header_list[$v_nb_extracted]);
+ }
+ else
+ {
+ $v_nb_extracted++;
+ }
+ }
+
+ // ----- Look if something need to be deleted
+ if ($v_nb_extracted > 0) {
+
+ // ----- Creates a temporay file
+ $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';
+
+ // ----- Creates a temporary zip archive
+ $v_temp_zip = new PclZip($v_zip_temp_name);
+
+ // ----- Open the temporary zip file in write mode
+ if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) {
+ $this->privCloseFd();
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Look which file need to be kept
+ for ($i=0; $izip_fd);
+ if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $v_temp_zip->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Read the file header
+ $v_local_header = array();
+ if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $v_temp_zip->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Check that local file header is same as central file header
+ if ($this->privCheckFileHeaders($v_local_header,
+ $v_header_list[$i]) != 1) {
+ // TBC
+ }
+ unset($v_local_header);
+
+ // ----- Write the file header
+ if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $v_temp_zip->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Read/write the data block
+ if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) {
+ // ----- Close the zip file
+ $this->privCloseFd();
+ $v_temp_zip->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+ }
+
+ // ----- Store the offset of the central dir
+ $v_offset = @ftell($v_temp_zip->zip_fd);
+
+ // ----- Re-Create the Central Dir files header
+ for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) {
+ $v_temp_zip->privCloseFd();
+ $this->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Transform the header to a 'usable' info
+ $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
+ }
+
+
+ // ----- Zip file comment
+ $v_comment = '';
+ if (isset($p_options[PCLZIP_OPT_COMMENT])) {
+ $v_comment = $p_options[PCLZIP_OPT_COMMENT];
+ }
+
+ // ----- Calculate the size of the central header
+ $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset;
+
+ // ----- Create the central dir footer
+ if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) {
+ // ----- Reset the file list
+ unset($v_header_list);
+ $v_temp_zip->privCloseFd();
+ $this->privCloseFd();
+ @unlink($v_zip_temp_name);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Close
+ $v_temp_zip->privCloseFd();
+ $this->privCloseFd();
+
+ // ----- Delete the zip file
+ // TBC : I should test the result ...
+ @unlink($this->zipname);
+
+ // ----- Rename the temporary file
+ // TBC : I should test the result ...
+ //@rename($v_zip_temp_name, $this->zipname);
+ PclZipUtilRename($v_zip_temp_name, $this->zipname);
+
+ // ----- Destroy the temporary archive
+ unset($v_temp_zip);
+ }
+
+ // ----- Remove every files : reset the file
+ else if ($v_central_dir['entries'] != 0) {
+ $this->privCloseFd();
+
+ if (($v_result = $this->privOpenFd('wb')) != 1) {
+ return $v_result;
+ }
+
+ if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) {
+ return $v_result;
+ }
+
+ $this->privCloseFd();
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDirCheck()
+ // Description :
+ // Check if a directory exists, if not it creates it and all the parents directory
+ // which may be useful.
+ // Parameters :
+ // $p_dir : Directory path to check.
+ // Return Values :
+ // 1 : OK
+ // -1 : Unable to create directory
+ // --------------------------------------------------------------------------------
+ function privDirCheck($p_dir, $p_is_dir=false)
+ {
+ $v_result = 1;
+
+
+ // ----- Remove the final '/'
+ if (($p_is_dir) && (substr($p_dir, -1)=='/'))
+ {
+ $p_dir = substr($p_dir, 0, strlen($p_dir)-1);
+ }
+
+ // ----- Check the directory availability
+ if ((is_dir($p_dir)) || ($p_dir == ""))
+ {
+ return 1;
+ }
+
+ // ----- Extract parent directory
+ $p_parent_dir = dirname($p_dir);
+
+ // ----- Just a check
+ if ($p_parent_dir != $p_dir)
+ {
+ // ----- Look for parent directory
+ if ($p_parent_dir != "")
+ {
+ if (($v_result = $this->privDirCheck($p_parent_dir)) != 1)
+ {
+ return $v_result;
+ }
+ }
+ }
+
+ // ----- Create the directory
+ if (!@mkdir($p_dir, 0777))
+ {
+ // ----- Error log
+ PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'");
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privMerge()
+ // Description :
+ // If $p_archive_to_add does not exist, the function exit with a success result.
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privMerge(&$p_archive_to_add)
+ {
+ $v_result=1;
+
+ // ----- Look if the archive_to_add exists
+ if (!is_file($p_archive_to_add->zipname))
+ {
+
+ // ----- Nothing to merge, so merge is a success
+ $v_result = 1;
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Look if the archive exists
+ if (!is_file($this->zipname))
+ {
+
+ // ----- Do a duplicate
+ $v_result = $this->privDuplicate($p_archive_to_add->zipname);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Open the zip file
+ if (($v_result=$this->privOpenFd('rb')) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir = array();
+ if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
+ {
+ $this->privCloseFd();
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ @rewind($this->zip_fd);
+
+ // ----- Open the archive_to_add file
+ if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1)
+ {
+ $this->privCloseFd();
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Read the central directory informations
+ $v_central_dir_to_add = array();
+ if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1)
+ {
+ $this->privCloseFd();
+ $p_archive_to_add->privCloseFd();
+
+ return $v_result;
+ }
+
+ // ----- Go to beginning of File
+ @rewind($p_archive_to_add->zip_fd);
+
+ // ----- Creates a temporay file
+ $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';
+
+ // ----- Open the temporary file in write mode
+ if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0)
+ {
+ $this->privCloseFd();
+ $p_archive_to_add->privCloseFd();
+
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Copy the files from the archive to the temporary file
+ // TBC : Here I should better append the file and go back to erase the central dir
+ $v_size = $v_central_dir['offset'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = fread($this->zip_fd, $v_read_size);
+ @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Copy the files from the archive_to_add into the temporary file
+ $v_size = $v_central_dir_to_add['offset'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size);
+ @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Store the offset of the central dir
+ $v_offset = @ftell($v_zip_temp_fd);
+
+ // ----- Copy the block of file headers from the old archive
+ $v_size = $v_central_dir['size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($this->zip_fd, $v_read_size);
+ @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Copy the block of file headers from the archive_to_add
+ $v_size = $v_central_dir_to_add['size'];
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size);
+ @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Merge the file comments
+ $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment'];
+
+ // ----- Calculate the size of the (new) central header
+ $v_size = @ftell($v_zip_temp_fd)-$v_offset;
+
+ // ----- Swap the file descriptor
+ // Here is a trick : I swap the temporary fd with the zip fd, in order to use
+ // the following methods on the temporary fil and not the real archive fd
+ $v_swap = $this->zip_fd;
+ $this->zip_fd = $v_zip_temp_fd;
+ $v_zip_temp_fd = $v_swap;
+
+ // ----- Create the central dir footer
+ if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1)
+ {
+ $this->privCloseFd();
+ $p_archive_to_add->privCloseFd();
+ @fclose($v_zip_temp_fd);
+ $this->zip_fd = null;
+
+ // ----- Reset the file list
+ unset($v_header_list);
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Swap back the file descriptor
+ $v_swap = $this->zip_fd;
+ $this->zip_fd = $v_zip_temp_fd;
+ $v_zip_temp_fd = $v_swap;
+
+ // ----- Close
+ $this->privCloseFd();
+ $p_archive_to_add->privCloseFd();
+
+ // ----- Close the temporary file
+ @fclose($v_zip_temp_fd);
+
+ // ----- Delete the zip file
+ // TBC : I should test the result ...
+ @unlink($this->zipname);
+
+ // ----- Rename the temporary file
+ // TBC : I should test the result ...
+ //@rename($v_zip_temp_name, $this->zipname);
+ PclZipUtilRename($v_zip_temp_name, $this->zipname);
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDuplicate()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privDuplicate($p_archive_filename)
+ {
+ $v_result=1;
+
+ // ----- Look if the $p_archive_filename exists
+ if (!is_file($p_archive_filename))
+ {
+
+ // ----- Nothing to duplicate, so duplicate is a success.
+ $v_result = 1;
+
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Open the zip file
+ if (($v_result=$this->privOpenFd('wb')) != 1)
+ {
+ // ----- Return
+ return $v_result;
+ }
+
+ // ----- Open the temporary file in write mode
+ if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0)
+ {
+ $this->privCloseFd();
+
+ PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode');
+
+ // ----- Return
+ return PclZip::errorCode();
+ }
+
+ // ----- Copy the files from the archive to the temporary file
+ // TBC : Here I should better append the file and go back to erase the central dir
+ $v_size = filesize($p_archive_filename);
+ while ($v_size != 0)
+ {
+ $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = fread($v_zip_temp_fd, $v_read_size);
+ @fwrite($this->zip_fd, $v_buffer, $v_read_size);
+ $v_size -= $v_read_size;
+ }
+
+ // ----- Close
+ $this->privCloseFd();
+
+ // ----- Close the temporary file
+ @fclose($v_zip_temp_fd);
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privErrorLog()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function privErrorLog($p_error_code=0, $p_error_string='')
+ {
+ if (PCLZIP_ERROR_EXTERNAL == 1) {
+ PclError($p_error_code, $p_error_string);
+ }
+ else {
+ $this->error_code = $p_error_code;
+ $this->error_string = $p_error_string;
+ }
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privErrorReset()
+ // Description :
+ // Parameters :
+ // --------------------------------------------------------------------------------
+ function privErrorReset()
+ {
+ if (PCLZIP_ERROR_EXTERNAL == 1) {
+ PclErrorReset();
+ }
+ else {
+ $this->error_code = 0;
+ $this->error_string = '';
+ }
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privDisableMagicQuotes()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privDisableMagicQuotes()
+ {
+ $v_result=1;
+
+ // ----- Look if function exists
+ if ( (!function_exists("get_magic_quotes_runtime"))
+ || (!function_exists("set_magic_quotes_runtime"))) {
+ return $v_result;
+ }
+
+ // ----- Look if already done
+ if ($this->magic_quotes_status != -1) {
+ return $v_result;
+ }
+
+ // ----- Get and memorize the magic_quote value
+ $this->magic_quotes_status = @get_magic_quotes_runtime();
+
+ // ----- Disable magic_quotes
+ if ($this->magic_quotes_status == 1) {
+ @set_magic_quotes_runtime(0);
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : privSwapBackMagicQuotes()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function privSwapBackMagicQuotes()
+ {
+ $v_result=1;
+
+ // ----- Look if function exists
+ if ( (!function_exists("get_magic_quotes_runtime"))
+ || (!function_exists("set_magic_quotes_runtime"))) {
+ return $v_result;
+ }
+
+ // ----- Look if something to do
+ if ($this->magic_quotes_status != -1) {
+ return $v_result;
+ }
+
+ // ----- Swap back magic_quotes
+ if ($this->magic_quotes_status == 1) {
+ @set_magic_quotes_runtime($this->magic_quotes_status);
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ }
+ // End of class
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilPathReduction()
+ // Description :
+ // Parameters :
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function PclZipUtilPathReduction($p_dir)
+ {
+ $v_result = "";
+
+ // ----- Look for not empty path
+ if ($p_dir != "") {
+ // ----- Explode path by directory names
+ $v_list = explode("/", $p_dir);
+
+ // ----- Study directories from last to first
+ $v_skip = 0;
+ for ($i=sizeof($v_list)-1; $i>=0; $i--) {
+ // ----- Look for current path
+ if ($v_list[$i] == ".") {
+ // ----- Ignore this directory
+ // Should be the first $i=0, but no check is done
+ }
+ else if ($v_list[$i] == "..") {
+ $v_skip++;
+ }
+ else if ($v_list[$i] == "") {
+ // ----- First '/' i.e. root slash
+ if ($i == 0) {
+ $v_result = "/".$v_result;
+ if ($v_skip > 0) {
+ // ----- It is an invalid path, so the path is not modified
+ // TBC
+ $v_result = $p_dir;
+ $v_skip = 0;
+ }
+ }
+ // ----- Last '/' i.e. indicates a directory
+ else if ($i == (sizeof($v_list)-1)) {
+ $v_result = $v_list[$i];
+ }
+ // ----- Double '/' inside the path
+ else {
+ // ----- Ignore only the double '//' in path,
+ // but not the first and last '/'
+ }
+ }
+ else {
+ // ----- Look for item to skip
+ if ($v_skip > 0) {
+ $v_skip--;
+ }
+ else {
+ $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:"");
+ }
+ }
+ }
+
+ // ----- Look for skip
+ if ($v_skip > 0) {
+ while ($v_skip > 0) {
+ $v_result = '../'.$v_result;
+ $v_skip--;
+ }
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilPathInclusion()
+ // Description :
+ // This function indicates if the path $p_path is under the $p_dir tree. Or,
+ // said in an other way, if the file or sub-dir $p_path is inside the dir
+ // $p_dir.
+ // The function indicates also if the path is exactly the same as the dir.
+ // This function supports path with duplicated '/' like '//', but does not
+ // support '.' or '..' statements.
+ // Parameters :
+ // Return Values :
+ // 0 if $p_path is not inside directory $p_dir
+ // 1 if $p_path is inside directory $p_dir
+ // 2 if $p_path is exactly the same as $p_dir
+ // --------------------------------------------------------------------------------
+ function PclZipUtilPathInclusion($p_dir, $p_path)
+ {
+ $v_result = 1;
+
+ // ----- Look for path beginning by ./
+ if ( ($p_dir == '.')
+ || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) {
+ $p_dir = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1);
+ }
+ if ( ($p_path == '.')
+ || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) {
+ $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1);
+ }
+
+ // ----- Explode dir and path by directory separator
+ $v_list_dir = explode("/", $p_dir);
+ $v_list_dir_size = sizeof($v_list_dir);
+ $v_list_path = explode("/", $p_path);
+ $v_list_path_size = sizeof($v_list_path);
+
+ // ----- Study directories paths
+ $i = 0;
+ $j = 0;
+ while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) {
+
+ // ----- Look for empty dir (path reduction)
+ if ($v_list_dir[$i] == '') {
+ $i++;
+ continue;
+ }
+ if ($v_list_path[$j] == '') {
+ $j++;
+ continue;
+ }
+
+ // ----- Compare the items
+ if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) {
+ $v_result = 0;
+ }
+
+ // ----- Next items
+ $i++;
+ $j++;
+ }
+
+ // ----- Look if everything seems to be the same
+ if ($v_result) {
+ // ----- Skip all the empty items
+ while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++;
+ while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++;
+
+ if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) {
+ // ----- There are exactly the same
+ $v_result = 2;
+ }
+ else if ($i < $v_list_dir_size) {
+ // ----- The path is shorter than the dir
+ $v_result = 0;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilCopyBlock()
+ // Description :
+ // Parameters :
+ // $p_mode : read/write compression mode
+ // 0 : src & dest normal
+ // 1 : src gzip, dest normal
+ // 2 : src normal, dest gzip
+ // 3 : src & dest gzip
+ // Return Values :
+ // --------------------------------------------------------------------------------
+ function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0)
+ {
+ $v_result = 1;
+
+ if ($p_mode==0)
+ {
+ while ($p_size != 0)
+ {
+ $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($p_src, $v_read_size);
+ @fwrite($p_dest, $v_buffer, $v_read_size);
+ $p_size -= $v_read_size;
+ }
+ }
+ else if ($p_mode==1)
+ {
+ while ($p_size != 0)
+ {
+ $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @gzread($p_src, $v_read_size);
+ @fwrite($p_dest, $v_buffer, $v_read_size);
+ $p_size -= $v_read_size;
+ }
+ }
+ else if ($p_mode==2)
+ {
+ while ($p_size != 0)
+ {
+ $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @fread($p_src, $v_read_size);
+ @gzwrite($p_dest, $v_buffer, $v_read_size);
+ $p_size -= $v_read_size;
+ }
+ }
+ else if ($p_mode==3)
+ {
+ while ($p_size != 0)
+ {
+ $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
+ $v_buffer = @gzread($p_src, $v_read_size);
+ @gzwrite($p_dest, $v_buffer, $v_read_size);
+ $p_size -= $v_read_size;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilRename()
+ // Description :
+ // This function tries to do a simple rename() function. If it fails, it
+ // tries to copy the $p_src file in a new $p_dest file and then unlink the
+ // first one.
+ // Parameters :
+ // $p_src : Old filename
+ // $p_dest : New filename
+ // Return Values :
+ // 1 on success, 0 on failure.
+ // --------------------------------------------------------------------------------
+ function PclZipUtilRename($p_src, $p_dest)
+ {
+ $v_result = 1;
+
+ // ----- Try to rename the files
+ if (!@rename($p_src, $p_dest)) {
+
+ // ----- Try to copy & unlink the src
+ if (!@copy($p_src, $p_dest)) {
+ $v_result = 0;
+ }
+ else if (!@unlink($p_src)) {
+ $v_result = 0;
+ }
+ }
+
+ // ----- Return
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilOptionText()
+ // Description :
+ // Translate option value in text. Mainly for debug purpose.
+ // Parameters :
+ // $p_option : the option value.
+ // Return Values :
+ // The option text value.
+ // --------------------------------------------------------------------------------
+ function PclZipUtilOptionText($p_option)
+ {
+
+ $v_list = get_defined_constants();
+ for (reset($v_list); $v_key = key($v_list); next($v_list)) {
+ $v_prefix = substr($v_key, 0, 10);
+ if (( ($v_prefix == 'PCLZIP_OPT')
+ || ($v_prefix == 'PCLZIP_CB_')
+ || ($v_prefix == 'PCLZIP_ATT'))
+ && ($v_list[$v_key] == $p_option)) {
+ return $v_key;
+ }
+ }
+
+ $v_result = 'Unknown';
+
+ return $v_result;
+ }
+ // --------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------
+ // Function : PclZipUtilTranslateWinPath()
+ // Description :
+ // Translate windows path by replacing '\' by '/' and optionally removing
+ // drive letter.
+ // Parameters :
+ // $p_path : path to translate.
+ // $p_remove_disk_letter : true | false
+ // Return Values :
+ // The path translated.
+ // --------------------------------------------------------------------------------
+ function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true)
+ {
+ if (stristr(php_uname(), 'windows')) {
+ // ----- Look for potential disk letter
+ if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) {
+ $p_path = substr($p_path, $v_position+1);
+ }
+ // ----- Change potential windows directory separator
+ if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
+ $p_path = strtr($p_path, '\\', '/');
+ }
+ }
+ return $p_path;
+ }
+ // --------------------------------------------------------------------------------
+
+
+?>
diff --git a/system/mdistributor.php b/system/mdistributor.php
new file mode 100644
index 0000000..50922ef
--- /dev/null
+++ b/system/mdistributor.php
@@ -0,0 +1,122 @@
+get('users_auth');
+
+ $user = isset($users[$userkey]) ? $users[$userkey] : 0;
+
+ if(!$user)
+ {
+ if((!sys::valid($aAuth['login'], 'other', $aValid['login'])) && !sys::valid($aAuth['passwd'], 'md5'))
+ {
+ $sql->query('SELECT `id` FROM `users` WHERE `login`="'.$aAuth['login'].'" AND `passwd`="'.$aAuth['passwd'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $sql->query('SELECT `id`, `login`, `passwd`, `balance`, `group`, `level`, `time` FROM `users` WHERE `login`="'.$aAuth['login'].'" AND `passwd`="'.$aAuth['passwd'].'" LIMIT 1');
+ $user = array_merge(array('authkey' => $authkey), $sql->get());
+
+ $auth = 1;
+
+ sys::users($users, $user, $authkey);
+ }
+ }
+
+ if(!$auth)
+ {
+ sys::cookie('egp_login', 'quit', -1);
+ sys::cookie('egp_passwd', 'quit', -1);
+ sys::cookie('egp_authkeycheck', 'quit', -1);
+ }
+ }else{
+ $sql->query('SELECT `balance`, `time` FROM `users` WHERE `id`="'.$user['id'].'" LIMIT 1');
+ $user = array_merge($user, $sql->get());
+
+ sys::user($user);
+
+ $auth = 1;
+ }
+ }
+
+ // Заголовок
+ $title = '';
+
+ // Подключение файла
+ if(in_array($route, $amRoute))
+ include(ENG.'megp/'.$route.'.php');
+ else
+ include(ENG.'megp/index.php');
+
+ // Обновление ссылок
+ if(isset($html->arr['main']))
+ {
+ $html->upd(
+ array(
+ '[home]',
+ '[js]',
+ '[css]',
+ '[img]'
+ ),
+
+ array(
+ $cfg['http'],
+ $cfg['http'].'template/megp/js/',
+ $cfg['http'].'template/megp/css/',
+ $cfg['http'].'template/megp/images/'
+ ),
+ 'main'
+ );
+ }
+
+ // Заготовка выхлопа
+ $html->get('all');
+ $html->set('title', $title.' | '.$cfg['name']);
+ $html->set('home', $cfg['http']);
+ $html->set('js', $cfg['http'].'template/megp/js/');
+ $html->set('css', $cfg['http'].'template/megp/css/');
+ $html->set('img', $cfg['http'].'template/megp/images/');
+
+ if($auth)
+ $html->set('server_menu', isset($html->arr['vmenu']) ? $html->arr['vmenu'] : '');
+ else
+ $html->set('server_menu', '');
+
+ $html->set('main', isset($html->arr['main']) ? $html->arr['main'] : '', true);
+ $html->pack('all');
+
+ // Блоки
+ if($auth)
+ {
+ // Проверка наличия игрового сервера
+ $servers = $sql->query('(SELECT `id` FROM `servers` WHERE `user`="'.$user['id'].'" LIMIT 1) UNION (SELECT `id` FROM `owners` WHERE `user`="'.$user['id'].'" LIMIT 1)');
+
+ if($sql->num())
+ $html->unitall('all', 'servers', 1, 1);
+ else
+ $html->unitall('all', 'servers', 0, 1);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/chat/chats.php b/system/sections/chat/chats.php
new file mode 100644
index 0000000..38e4540
--- /dev/null
+++ b/system/sections/chat/chats.php
@@ -0,0 +1,118 @@
+ 'Необходимо написать сообщение'));
+
+ $msg = $_POST['text'];
+ $sql->query('INSERT INTO `chat` (`userid`, `date`, `msg`) VALUES ("'.$user['id'].'", NOW(), "'.mysqli_real_escape_string($sql->sql_id, $msg).'");');
+ sys::outjs(array('s' => ''));
+ case 'dialog':
+ $q_Msgs = $sql->query('SELECT `chat`.`id`, `userid`, `msg`, `chat`.`date`, `login`, `group` FROM `chat` INNER JOIN `users` ON `chat`.`userid` = `users`.`id` ORDER BY `chat`.`date` ASC LIMIT 30');
+ while($msg = $sql->get($q_Msgs))
+ {
+ $html->get('messages_all', 'sections/chat');
+ if($msg['group'] == 'admin')
+ $group = 'Администратор ';
+ else if($msg['group'] == 'support')
+ $group = 'Тех. Поддержка ';
+ else if($msg['group'] == 'user')
+ $group = 'Клиент ';
+
+ $name = $msg['login'].' ('.$group.')';
+
+ $html->set('id', $msg['id']);
+ $html->set('userid', $msg['userid']);
+ $html->set('name', $name);
+ $html->set('time', $time);
+ $html->set('date', $msg['date']);
+ $html->set('msg', $msg['msg']);
+ $html->set('login', $msg['login']);
+
+ $html->unit('me', $user['id'] == $userid, 1);
+
+ for($i = 1; $i <= 32; $i++)
+ $html->set('emoji_'.$i, ' ');
+
+ global $cfg;
+ $file = 'upload/avatars/' . $resp['uid'] . '.';
+ $link = $cfg['http'] . 'upload/avatars/' . $resp['uid'] . '.';
+ if (file_exists(ROOT . $file . 'jpg'))
+ $html->set('ava', '/upload/avatars/' . $resp['uid'] . '.jpg');
+ elseif (file_exists(ROOT . $file . 'png'))
+ $html->set('ava', '/upload/avatars/' . $resp['uid'] . '.png');
+ elseif (file_exists(ROOT . $file . 'gif'))
+ $html->set('ava', '/upload/avatars/' . $resp['uid'] . '.gif');
+ else
+ $html->set('ava', $cfg['http'] . 'template/images/avatar.png');
+ $html->pack('dialog');
+ }
+ sys::out(isset($html->arr['dialog']) ? $html->arr['dialog'] : '');
+ case 'delete':
+ if($user['group'] != 'admin')
+ sys::outjs(array('e' => 'Недостаточно прав'));
+
+ if(!isset($url['id']))
+ sys::outjs(array('e' => 'Отсутствует идентификатор'));
+
+ if($go)
+ $sql->query('DELETE FROM `chat` WHERE `userid`="'.$url['id'].'";');
+ else
+ $sql->query('DELETE FROM `chat` WHERE `id`="'.$url['id'].'";');
+
+ sys::outjs(array('s' => ''));
+ }
+ }
+
+ $html->nav($title);
+
+ $q_Msgs = $sql->query('SELECT `chat`.`id`, `userid`, `msg`, `chat`.`date`, `login`, `group` FROM `chat` INNER JOIN `users` ON `chat`.`userid` = `users`.`id` ORDER BY `chat`.`date` ASC LIMIT 30');
+ while($msg = $sql->get($q_Msgs))
+ {
+ $html->get('messages_all', 'sections/chat');
+ if($msg['group'] == 'admin')
+ $group = 'Администратор ';
+ else if($msg['group'] == 'support')
+ $group = 'Тех. Поддержка ';
+ else if($msg['group'] == 'user')
+ $group = 'Клиент ';
+
+ $name = $msg['login'].' ('.$group.')';
+
+ $html->set('id', $msg['id']);
+ $html->set('userid', $msg['userid']);
+ $html->set('name', $name);
+ $html->set('time', $time);
+ $html->set('date', $msg['date']);
+ $html->set('msg', $msg['msg']);
+ $html->set('login', $msg['login']);
+
+ $html->unit('me', $user['id'] == $userid, 1);
+
+ for($i = 1; $i <= 32; $i++)
+ $html->set('emoji_'.$i, ' ');
+
+ global $cfg;
+ $file = 'upload/avatars/' . $msg['uid'] . '.';
+ $link = $cfg['http'] . 'upload/avatars/' . $msg['uid'] . '.';
+ if (file_exists(ROOT . $file . 'jpg'))
+ $html->set('ava', '/upload/avatars/' . $msg['uid'] . '.jpg');
+ elseif (file_exists(ROOT . $file . 'png'))
+ $html->set('ava', '/upload/avatars/' . $msg['uid'] . '.png');
+ elseif (file_exists(ROOT . $file . 'gif'))
+ $html->set('ava', '/upload/avatars/' . $msg['uid'] . '.gif');
+ else
+ $html->set('ava', $cfg['http'] . 'template/images/avatar.png');
+ $html->pack('msg_all');
+ }
+
+ $html->get('dialog', 'sections/chat');
+ $html->set('chats', isset($html->arr['msg_all']) ? $html->arr['msg_all'] : '');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/chat/dialog.php b/system/sections/chat/dialog.php
new file mode 100644
index 0000000..338d067
--- /dev/null
+++ b/system/sections/chat/dialog.php
@@ -0,0 +1,39 @@
+query('SELECT * FROM (SELECT `id`, `userid`, `msg`, `date` FROM `chat` ORDER BY `date` DESC LIMIT 30) t ORDER BY `date` ASC;');
+ while($msg = $sql->get($q_Msgs))
+ {
+ if($user['group'] == 'admin')
+ $group = 'Администратор ';
+ //elseif ()
+ // $group = '(Клиент, серверов: 1 шт.) ';
+ else
+ $group = 'Клиент ';
+
+ $name = $user['login'].' ('.$group.')'; // (Администратор)
+ // $time = '[date]';
+
+ $html->get('message', 'chatwin');
+
+ $html->set('id', $msg['id']);
+ $html->set('userid', $msg['userid']);
+ $html->set('name', $name);
+ $html->set('time', $time);
+ $html->set('date', $msg['date']);
+ $html->set('msg', $msg['msg']);
+
+ $html->unit('me', $user['id'] == $userid, 1);
+
+ for($i = 1; $i <= 32; $i++)
+ $html->set('emoji_'.$i, ' ');
+
+ $html->pack('dialog');
+ }
+
+ sys::out(isset($html->arr['dialog']) ? $html->arr['dialog'] : '');
+?>
\ No newline at end of file
diff --git a/system/sections/check/compiler.php b/system/sections/check/compiler.php
new file mode 100644
index 0000000..876f903
--- /dev/null
+++ b/system/sections/check/compiler.php
@@ -0,0 +1,148 @@
+ $value)
+ $aPostFields[$key] = urlencode($key).'='. urlencode($value);
+
+ return implode('&', $aPostFields);
+ }
+
+ $sql->query('SELECT `browser` FROM `users` WHERE `id`="'.$user['id'].'" LIMIT 1');
+ $u_sql = $sql->get();
+
+ $browser = base64_decode($u_sql['browser']);
+
+ $file = $_FILES['file_code'];
+
+ if(substr($file['name'], -4) != '.sma')
+ sys::outjs(array('e' => 'Только .sma разрешается загружать'));
+
+ $text = file_get_contents($file['tmp_name']);
+ $textArray = explode("\n", $text);
+
+ $postFields['fname'] = $file['name'];
+ $postFields['scode'] = $textArray;
+ $postFields['go'] = 'send';
+
+ $ch = curl_init('http://amxmodx.org/webcompiler.cgi');
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; U; Linux i686; pl; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3');
+ curl_setopt($ch, CURLOPT_POSTFIELDS, createPostString($postFields));
+ $tresc = curl_exec($ch);
+
+ sys::out($tresc);
+
+ curl_close($ch);
+
+ if(strpos($tresc, 'Your plugin successfully compiled!'))
+ {
+ $tresc = substr($tresc, strpos($tresc, "http://www.amxmodx.org/webcompiler.cgi?"));
+ $ile = strpos($tresc, "");
+ $link = substr($tresc, 0, $ile);
+ $tresc = substr($tresc, strpos($tresc, "Welcome to the AMX Mod X"));
+ $ile = strpos($tresc, "");
+ $inf = substr($tresc, 0, $ile);
+ $inf = str_replace("\r\n"," ", $inf);
+
+ $out = '
+ Ваш плагин скомпилирован удачно
+ Чтобы скачать пройдите по ссылке
+
+ Посмотреть лог компиляции
+
+ ';
+
+ $good = "good.txt";
+
+ if(!file_exists(FILES.$good))
+ {
+ $handle = fopen(FILES.$good, "w");
+ $count_good = 0;
+ fwrite($handle, $count_good);
+ fclose($handle);
+ }else{
+ $file = file(FILES.$good);
+ $count_good = $file[0];
+ }
+
+ $count_good++;
+
+ $handle = fopen(FILES.$good, "w");
+ fwrite($handle, $count_good);
+ fclose($handle);
+ }else{
+ $ktory = strpos($tresc, "Your plugin failed to compile");
+ $tresc = substr($tresc, $ktory + 63);
+ $ile = strpos($tresc, "");
+ $tresc = substr($tresc, 0, $ile);
+
+ $out = '
+ Ваш плагин скомпилирован неудачно
+
+ Посмотреть лог ошибок
+
+
+ ';
+
+ $failed = "failed.txt";
+
+ if(!file_exists(FILES.$failed))
+ {
+ $handle = fopen(FILES.$failed, "w");
+ $count_failed = 0;
+ fwrite($handle, $count_failed);
+ fclose($handle);
+ }else{
+ $file = file(FILES.$failed);
+ $count_failed = $file[0];
+ }
+
+ $count_failed++;
+
+ $handle = fopen(FILES.$failed, "w");
+ fwrite($handle, $count_failed);
+ fclose($handle);
+ }
+ }else{
+ $good = "good.txt";
+ $failed = "failed.txt";
+
+ if(!file_exists(FILES.$good))
+ {
+ $handle = fopen(FILES.$good, "w");
+ $count_good = 0;
+ fwrite($handle, $count_good);
+ fclose($handle);
+ }else{
+ $file = file(FILES.$good);
+ $count_good = $file[0];
+ }
+
+ if(!file_exists(FILES.$failed))
+ {
+ $handle = fopen(FILES.$failed, "w");
+ $count_failed = 0;
+ fwrite($handle, $count_failed);
+ fclose($handle);
+ }else{
+ $file = file(FILES.$failed);
+ $count_failed = $file[0];
+ }
+
+ $html->get('compiler', 'sections/check');
+ $html->set('success', $count_good);
+ $html->set('failed', $count_failed);
+ $html->pack('compilers');
+
+ include(SEC.'check/index.php');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/check/index.php b/system/sections/check/index.php
new file mode 100644
index 0000000..56d7013
--- /dev/null
+++ b/system/sections/check/index.php
@@ -0,0 +1,105 @@
+ 'Необходимо указать проверяемый файл.'));
+ }
+
+ $file = $_FILES['file'];
+
+ if(substr($file['name'], -4) != '.sma'){
+ sys::outjs(array('e' => 'Только .sma разрешается загружать'));
+ }
+
+ $text = file_get_contents($file['tmp_name']);
+ $textArray = explode("\n", $text);
+
+ $errors = [];
+
+ foreach($textArray as $key => $str){
+ $strNum = $key + 1;
+
+ foreach($badCommands as $cmd){
+ if(strpos($str, $cmd) !== false){
+ $errors[$strNum] = $cmd;
+ }
+ }
+ }
+
+ if(!empty($errors)){
+ $outputErrors .= 'Номер строки Вредоносный код ';
+ foreach($errors as $key => $msg){
+ $outputErrors .= ''.$key.' '.$msg.' ';
+ }
+ $outputErrors .= ' ';
+ }
+ else {
+ if(!isset($outputErrors)){
+ sys::outjs(array('s' => 'Вредоносный код не найден.'));
+ }
+ }
+ sys::outjs(array('sma' => $outputErrors));
+ }
+ }
+
+ $html->nav('Проверка плагинов на наличие бэкдоров');
+ $html->get('check', 'sections/check');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/extend.php b/system/sections/control/extend.php
new file mode 100644
index 0000000..07c7655
--- /dev/null
+++ b/system/sections/control/extend.php
@@ -0,0 +1,65 @@
+query('SELECT `price`, `time` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $ctrl = $sql->get();
+
+ include(LIB.'games/games.php');
+
+ if($go)
+ {
+ if(!isset($url['time']) || !in_array($url['time'], $cfg['control_time']))
+ $url['time'] = array_shift($cfg['control_time']);
+
+ $sum = games::define_sum(false, $ctrl['price'], 1, $url['time']);
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']));
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ $time = $ctrl['time'] < $start_point ? $url['time']*86400 : $url['time']*86400+$ctrl['time'];
+
+ // Обновление информации
+ $sql->query('UPDATE `control` set `time`="'.$time.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'extend_control'), array('days' => games::parse_day($days, true), 'money' => $sum, 'id' => $id)).'", `date`="'.$start_point.'", `type`="extend", `money`="'.$sum.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ if(isset($url['get']))
+ {
+ if(!isset($url['time']) || !in_array($url['time'], $cfg['control_time']))
+ $url['time'] = array_shift($cfg['control_time']);
+
+ sys::out(games::define_sum(false, $ctrl['price'], 1, $url['time']));
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Подключенный сервер #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav('Продление аренды');
+
+ $options = '';
+
+ foreach($cfg['control_time'] as $time)
+ $options .= ''.games::parse_day($time, true).' ';
+
+ $html->get('extend', 'sections/control');
+ $html->set('id', $id);
+ $html->set('time', $options);
+ $html->set('price', $ctrl);
+ $html->set('cur', $cfg['currency']);
+
+ if($cfg['settlement_period'])
+ {
+ $html->set('date', date('d.m.Y', $start_point));
+ $html->unit('settlement_period', true, true);
+ }else
+ $html->unit('settlement_period', false, true);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/index.php b/system/sections/control/index.php
new file mode 100644
index 0000000..ff38e69
--- /dev/null
+++ b/system/sections/control/index.php
@@ -0,0 +1,54 @@
+nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id);
+
+ $sql->query('SELECT '
+ .'`id`,'
+ .'`address`,'
+ .'`game`,'
+ .'`slots`,'
+ .'`online`,'
+ .'`status`,'
+ .'`name`,'
+ .'`map`'
+ .' FROM `control_servers` WHERE `unit`="'.$id.'" ORDER BY `id` ASC');
+
+ $wait_servers = '';
+ $updates_servers = '';
+
+ while($server = $sql->get())
+ {
+ $btn = sys::buttons($server['id'], $server['status'], $server['game'], $id);
+
+ $html->get('list', 'sections/control/servers');
+
+ $html->set('ctrl', $id);
+ $html->set('id', $server['id']);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots']);
+ $html->set('online', $server['online']);
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img', $server['game']));
+ $html->set('btn', $btn);
+
+ $html->pack('list');
+
+ $wait_servers .= $server['id'].':false,';
+ $updates_servers .= 'setTimeout(function() {update_info(\''.$server['id'].'\', \''.$id.'\', true)}, 5000);'
+ .'setTimeout(function() {update_status(\''.$server['id'].'\', \''.$id.'\', true)}, 5000);'
+ .'setTimeout(function() {update_resources(\''.$server['id'].'\', \''.$id.'\', true)}, 3000);';
+ }
+
+ $html->get('servers', 'sections/control/servers');
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : 'Нет установленных серверов');
+ $html->set('wait_servers', $wait_servers);
+ $html->set('updates_servers', $updates_servers);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/install.php b/system/sections/control/install.php
new file mode 100644
index 0000000..5a7ea0f
--- /dev/null
+++ b/system/sections/control/install.php
@@ -0,0 +1,85 @@
+nav('Список подключенных серверов', $cfg['http'].'control');
+
+ if(in_array($ctrl['status'], array('install', 'overdue', 'blocked')))
+ include(SEC.'control/noaccess.php');
+ else{
+ if($go)
+ {
+ $game = isset($url['game']) ? $url['game'] : sys::outjs(array('e' => 'Необходимо указать игру'));
+
+ if(!in_array($game, array('cs', 'cssold', 'css', 'csgo')))
+ sys::outjs(array('e' => 'Указанная игра не найдена'));
+
+ $sql->query('SELECT `address`, `passwd`, `limit` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $ctrl = $sql->get();
+
+ $sql->query('SELECT `id` FROM `control_servers` WHERE `unit`="'.$id.'" LIMIT '.$ctrl['limit']);
+ if($sql->num() == $ctrl['limit'])
+ sys::outjs(array('e' => 'На данном тарифе нельзя установить больше, чем '.$ctrl['limit'].' шт. игровых серверов'));
+
+ $ip = $ctrl['address'];
+ $port = false;
+
+ $port_min = 27015;
+
+ // Проверка наличия свободного порта
+ for($port_min; $port_min <= 30000; $port_min+=1)
+ {
+ $sql->query('SELECT `id` FROM `control_servers` WHERE `unit`="'.$id.'" AND `address`="'.$ip.':'.$port_min.'" LIMIT 1');
+ if(!$sql->num())
+ {
+ $port = $port_min;
+
+ break;
+ }
+ }
+
+ $sql->query('INSERT INTO `control_servers` set '
+ .'`unit`="'.$id.'",'
+ .'`address`="'.$ip.':'.$port.'",'
+ .'`game`="'.$game.'",'
+ .'`slots`="32",'
+ .'`status`="install", '.$cfg['control_install'][$game]);
+
+ $uid = $sql->id()+1000;
+
+ if(in_array($game, array('css', 'csgo')))
+ $screen = 'cd '.$cfg['steamcmd'].'; ./steamcmd.sh +login anonymous +force_install_dir "/servers/'.$uid.'" +app_update '.$cfg['control_steamcmd'][$game].' +quit; cd /servers/'.$uid.';';
+ else{
+ $zip = array_shift(array_keys($cfg['control_packs'][$game])).'.zip';
+
+ $screen = 'rm '.$zip.'; wget '.$cfg['control_server'].'/'.$zip.'; unzip -d . '.$zip.'; rm '.$zip.';';
+ }
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($ctrl['passwd'], $ctrl['address']))
+ sys::outjs(array('e' => 'Неудалось создать связь с физическим сервером'));
+
+ $ssh->set('mkdir /servers/'.$uid.';' // Создание директории
+ .'useradd -s /bin/false -d /servers/'.$uid.' -g servers -u '.$uid.' server'.$uid.';' // Создание пользователя сервера на локации
+ .'chown server'.$uid.':1000 /servers/'.$uid.';' // Изменение владельца и группы директории
+ .'cd /servers/'.$uid.' && sudo -u server'.$uid.' screen -dmS i_'.$uid.' sh -c "'.$screen
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$game].'"');
+
+ $id = $uid-1000;
+
+ $sql->query('UPDATE `control_servers` set `uid`="'.$uid.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok', 'id' => $id));
+ }
+
+ $html->nav('Подключенный сервер #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav('Установка игрового сервера');
+
+ $html->get('install', 'sections/control');
+ $html->set('id', $id);
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/noaccess.php b/system/sections/control/noaccess.php
new file mode 100644
index 0000000..10d8add
--- /dev/null
+++ b/system/sections/control/noaccess.php
@@ -0,0 +1,22 @@
+nav('Раздел недоступен');
+
+ if($ctrl['time'] < $start_point)
+ $html->get('overdue');
+ else{
+ $status = array(
+ 'install' => 'установки',
+ 'reboot' => 'перезагрузки',
+ 'blocked' => 'блокировки'
+ );
+
+ $html->get('noaccess');
+
+ $html->set('status', $status[$ctrl['status']]);
+ }
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/reboot.php b/system/sections/control/reboot.php
new file mode 100644
index 0000000..d993939
--- /dev/null
+++ b/system/sections/control/reboot.php
@@ -0,0 +1,24 @@
+ 'Сервер должен быть в рабочем состоянии'));
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $ctrl = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($ctrl['passwd'], $ctrl['address']))
+ sys::outjs(array('e' => 'Неудалось создать связь с физическим сервером'));
+
+ $ssh->set('screen -dmS reboot reboot');
+
+ $sql->query('UPDATE `control` set `status`="reboot" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('UPDATE `control_servers` set `status`="off" WHERE `unit`="'.$id.'" LIMIT 1');
+
+ $mcache->set('reboot_control_'.$id, true, false, 10);
+
+ sys::outjs(array('s' => 'ok'));
+?>
\ No newline at end of file
diff --git a/system/sections/control/scan.php b/system/sections/control/scan.php
new file mode 100644
index 0000000..73de2b9
--- /dev/null
+++ b/system/sections/control/scan.php
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/system/sections/control/servers/action.php b/system/sections/control/servers/action.php
new file mode 100644
index 0000000..897d1cf
--- /dev/null
+++ b/system/sections/control/servers/action.php
@@ -0,0 +1,74 @@
+query('SELECT `game`, `status` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!isset($url['action']))
+ sys::outjs(array('e' => 'Неверный запрос для выполнения операции'));
+
+ $nmch = 'ctrl_server_action_'.$sid;
+
+ if($mcache->get($nmch))
+ sys::outjs(array('e' => sys::text('other', 'mcache')));
+
+ $mcache->set($nmch, true, false, 10);
+
+ include(LIB.'control/'.$server['game'].'/action.php');
+
+ switch($url['action'])
+ {
+ case 'stop':
+ if(!in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ sys::outjs(array('e' => sys::text('error', 'ser_stop')), $nmch);
+
+ sys::outjs(action::stop($sid), $nmch);
+
+ case 'start':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => sys::text('error', 'ser_start')), $nmch);
+
+ sys::outjs(action::start($sid), $nmch);
+
+ case 'restart':
+ if(!in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ sys::outjs(array('e' => sys::text('error', 'ser_restart')), $nmch);
+
+ sys::outjs(action::start($sid, 'restart'), $nmch);
+
+ case 'change':
+ if($server['status'] != 'working')
+ {
+ if($server['status'] == 'change')
+ sys::outjs(array('e' => sys::text('other', 'mcache')), $nmch);
+
+ sys::outjs(array('e' => sys::text('error', 'ser_change')), $nmch);
+ }
+
+ if(isset($url['change']))
+ sys::outjs(action::change($sid, $url['change']), $nmch);
+
+ sys::outjs(action::change($sid), $nmch);
+
+ case 'reinstall':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => sys::text('error', 'ser_reinstall')), $nmch);
+
+ sys::outjs(action::reinstall($sid), $nmch);
+
+ case 'update':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => sys::text('error', 'ser_update')), $nmch);
+
+ sys::outjs(action::update($sid), $nmch);
+
+ case 'delete':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => sys::text('error', 'ser_delete')), $nmch);
+
+ sys::outjs(action::delete($sid), $nmch);
+ }
+
+ exit;
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/boost.php b/system/sections/control/servers/boost.php
new file mode 100644
index 0000000..07b9806
--- /dev/null
+++ b/system/sections/control/servers/boost.php
@@ -0,0 +1,11 @@
+query('SELECT `address`, `game`, `status` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = $sql->get();
+
+ ctrl::nav($server, $id, $sid, 'boost');
+
+ include(ctrl::route($server, 'boost', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/console.php b/system/sections/control/servers/console.php
new file mode 100644
index 0000000..a3e9dba
--- /dev/null
+++ b/system/sections/control/servers/console.php
@@ -0,0 +1,11 @@
+query('SELECT `address`, `game`, `status` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = $sql->get();
+
+ ctrl::nav($server, $id, $sid, 'console');
+
+ include(ctrl::route($server, 'console', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/copy.php b/system/sections/control/servers/copy.php
new file mode 100644
index 0000000..7e526c8
--- /dev/null
+++ b/system/sections/control/servers/copy.php
@@ -0,0 +1,11 @@
+query('SELECT `uid`, `address`, `game`, `pack`, `status` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = $sql->get();
+
+ ctrl::nav($server, $id, $sid, 'copy');
+
+ include(ctrl::route($server, 'copy', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/boost.php b/system/sections/control/servers/cs/boost.php
new file mode 100644
index 0000000..221c8b7
--- /dev/null
+++ b/system/sections/control/servers/cs/boost.php
@@ -0,0 +1,103 @@
+ 'Необходимо указать сервис.'));
+
+ // Проверка сервиса
+ if(!in_array($aData['site'], $aBoost[$server['game']]['boost']))
+ sys::outjs(array('e' => 'Указанный сервис по раскрутке не найден.'));
+
+ if(isset($url['rating']))
+ {
+ $rating = $url['rating'] == 'up' ? '1' : '-1';
+
+ $sql->query('SELECT `id` FROM `boost_rating` WHERE `boost`="'.$aData['site'].'" AND `user`="'.$user['id'].'" AND `rating`="'.$rating.'" LIMIT 1');
+ if($sql->num())
+ sys::out('err');
+
+ $sql->query('DELETE FROM `boost_rating` WHERE `boost`="'.$aData['site'].'" AND `user`="'.$user['id'].'" LIMIT 1');
+ $sql->query('INSERT INTO `boost_rating` set `boost`="'.$aData['site'].'", `rating`="'.$rating.'", `user`="'.$user['id'].'"');
+
+ $sql->query('SELECT SUM(`rating`) FROM `boost_rating` WHERE `boost`="'.$aData['site'].'"');
+ $sum = $sql->get();
+
+ $rating = (int) $sum['SUM(`rating`)'];
+
+ sys::out($rating, 'ctrl_server_boost_'.$sid);
+ }
+
+ $aData['service'] = isset($url['service']) ? sys::int($url['service']) : sys::outjs(array('e' => 'Необходимо указать номер услуги.'));
+
+ // Проверка номера услуги
+ if(!in_array($aData['service'], $aBoost[$server['game']][$aData['site']]['services']))
+ sys::outjs(array('e' => 'Неправильно указан номер услуги.'));
+
+ // Определение суммы
+ $sum = $aBoost[$server['game']][$aData['site']]['price'][$aData['service']];
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']), $name_mcache);
+
+ include(LIB.'games/boost.php');
+
+ $boost = new boost($aBoost[$server['game']][$aData['site']]['key'], $aBoost[$server['game']][$aData['site']]['api']);
+
+ $buy = $boost->$aBoost[$server['game']][$aData['site']]['type'](array('period' => $aData['service'], 'address' => $server['address']));
+
+ if(is_array($buy))
+ sys::outjs(array('e' => $buy['error']));
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ include(LIB.'games/games.php');
+
+ // Реф. система
+ games::part($user['id'], $sum);
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'ctrl_buy_boost'),
+ array('circles' => $aBoost[$server['game']][$aData['site']]['circles'][$aData['service']],
+ 'money' => $sum, 'site' => $aBoost[$server['game']][$aData['site']]['site'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="boost", `money`="'.$sum.'"');
+
+ $sql->query('INSERT INTO `control_boost` set `user`="'.$user['id'].'", `server`="'.$sid.'", `site`="'.$aData['site'].'", `circles`="'.$aBoost[$server['game']][$aData['site']]['circles'][$aData['service']].'", `money`="'.$sum.'", `date`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Раскрутка');
+
+ if($mcache->get('ctrl_server_boost_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_boost_'.$sid);
+ else{
+ $html->get('boost', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('address', $server['address']);
+
+ foreach($aBoost[$server['game']]['boost'] as $boost)
+ {
+ $sql->query('SELECT SUM(`rating`) FROM `boost_rating` WHERE `boost`="'.$boost.'"');
+ $sum = $sql->get();
+
+ $rating = (int) $sum['SUM(`rating`)'];
+
+ $html->set($boost, $rating);
+ }
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_boost_'.$sid, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/console.php b/system/sections/control/servers/cs/console.php
new file mode 100644
index 0000000..1257a30
--- /dev/null
+++ b/system/sections/control/servers/cs/console.php
@@ -0,0 +1,67 @@
+query('SELECT `uid`, `time_start` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $command = isset($_POST['command']) ? sys::cmd($_POST['command']) : '';
+
+ if($server['status'] == 'off')
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('servers', 'off')));
+
+ sys::out(sys::text('servers', 'off'));
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ sys::out(sys::text('error', 'ssh'));
+ }
+
+ $dir = '/servers/'.$server['uid'].'/cstrike/';
+
+ $filecmd = $dir.'qconsole.log';
+
+ if($command)
+ {
+ if(strtolower($command) == 'clear')
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"Очистка консоли\n\" > '.$filecmd.'"');
+ else
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.$command.'"\015\';'
+ .'sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/control/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/copy.php b/system/sections/control/servers/cs/copy.php
new file mode 100644
index 0000000..19cd939
--- /dev/null
+++ b/system/sections/control/servers/cs/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'control/servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('ctrl_server_copy_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_copy_'.$sid);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/control/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `control_copy` WHERE `user`="'.$ctrl['user'].'_'.$id.'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/control/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_copy_'.$sid, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/index.php b/system/sections/control/servers/cs/index.php
new file mode 100644
index 0000000..65dccf5
--- /dev/null
+++ b/system/sections/control/servers/cs/index.php
@@ -0,0 +1,30 @@
+query('SELECT `slots`, `online`, `players`, `name`, `map` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address']);
+
+ $btn = sys::buttons($sid, $server['status'], $server['game'], $id);
+
+ $html->get('index', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img'));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/plugins.php b/system/sections/control/servers/cs/plugins.php
new file mode 100644
index 0000000..2a96424
--- /dev/null
+++ b/system/sections/control/servers/cs/plugins.php
@@ -0,0 +1,160 @@
+nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/plugins');
+
+ $nmch = sys::rep_act('ctrl_server_plugins_go_'.$sid, 10);
+
+ include(SEC.'control/servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('ctrl_server_plugins_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_plugins_'.$sid);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$sid.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/control/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `control_plugins_install` WHERE `server`="'.$sid.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/control/servers/games');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_plugins_'.$sid, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/rcon.php b/system/sections/control/servers/cs/rcon.php
new file mode 100644
index 0000000..c0a2da0
--- /dev/null
+++ b/system/sections/control/servers/cs/rcon.php
@@ -0,0 +1,56 @@
+ 'Необходимо выбрать игрока.'));
+
+ if($url['action'] == 'kick')
+ rcon::cmd(array_merge($server, array('id' => $id)), 'amx_kick "'.$player.'" "EGP Panel"');
+ else
+ rcon::cmd(array_merge($server, array('id' => $id)), 'amx_slay "'.$player.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ include(LIB.'geo.php');
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $aPlayers = rcon::players(rcon::cmd(array_merge($server, array('id' => $id))));
+
+ foreach($aPlayers as $i => $aPlayer)
+ {
+ $html->get('player', 'sections/control/servers/'.$server['game'].'/rcon');
+
+ $html->set('i', $i);
+ $html->set('name', $aPlayer['name']);
+ $html->set('steamid', $aPlayer['steamid']);
+ $html->set('time', $aPlayer['time']);
+ $html->set('ping', $aPlayer['ping']);
+ $html->set('ip', $aPlayer['ip']);
+ $html->set('ico', $aPlayer['ico']);
+ $html->set('country', $aPlayer['country']);
+
+ $html->pack('players');
+ }
+
+ sys::outjs(array('s' => isset($html->arr['players']) ? $html->arr['players'] : ''));
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Rcon управление игроками');
+
+ $html->get('rcon', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/settings.php b/system/sections/control/servers/cs/settings.php
new file mode 100644
index 0000000..717180f
--- /dev/null
+++ b/system/sections/control/servers/cs/settings.php
@@ -0,0 +1,63 @@
+query('SELECT `uid`, `pack` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+
+ $aSub = array('start', 'server', 'admins', 'bans', 'firewall', 'crontab', 'startlogs', 'debug', 'logs', 'amxlogs', 'pack', 'file');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('ctrl_server_settings_go_'.$id, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'control/servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'control/servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('ctrl_server_settings_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_settings_'.$sid);
+ else{
+ $aEditslist = 1;
+ $ctrlmod = true;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = $cfg['control_packs'][$server['game']];
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ include(SEC.'control/servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/control/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('packs', $packs);
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_settings_'.$sid, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/settings/admins.php b/system/sections/control/servers/cs/settings/admins.php
new file mode 100644
index 0000000..f01a295
--- /dev/null
+++ b/system/sections/control/servers/cs/settings/admins.php
@@ -0,0 +1,134 @@
+nav('Управление администраторами');
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $aData = array();
+
+ $aData['active'] = isset($_POST['active']) ? $_POST['active'] : '';
+ $aData['value'] = isset($_POST['value']) ? $_POST['value'] : '';
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : '';
+ $aData['flags'] = isset($_POST['flags']) ? $_POST['flags'] : '';
+ $aData['type'] = isset($_POST['type']) ? $_POST['type'] : '';
+ $aData['time'] = isset($_POST['time']) ? $_POST['time'] : '';
+ $aData['info'] = isset($_POST['info']) ? $_POST['info'] : '';
+
+ // Удаление текущих записей
+ $sql->query('DELETE FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'"');
+
+ $usini = '';
+
+ foreach($aData['value'] as $index => $val)
+ {
+ if($val != '')
+ {
+ $type = isset($aData['type'][$index]) ? $aData['type'][$index] : 'a';
+ if(!in_array($type, array('c', 'ce', 'de', 'a')))
+ $type = 'a';
+
+ $aDate = isset($aData['time'][$index]) ? explode('.', $aData['time'][$index]) : explode('.', date('d.m.Y', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ $aDate = explode('.', date('d.m.Y', $start_point));
+
+ $time = mktime(0, 0, 0, $aDate[1], $aDate[0], $aDate[2]);
+
+ $aData['active'][$index] = isset($aData['active'][$index]) ? 1 : 0;
+ $aData['passwd'][$index] = isset($aData['passwd'][$index]) ? $aData['passwd'][$index] : '';
+ $aData['flags'][$index] = isset($aData['flags'][$index]) ? $aData['flags'][$index] : '';
+ $aData['info'][$index] = isset($aData['info'][$index]) ? $aData['info'][$index] : '';
+
+ $text = '"'.$val.'" "'.$aData['passwd'][$index].'" "'.$aData['flags'][$index].'" "'.$type.'"';
+
+ $sql->query('INSERT INTO `control_admins_'.$server['game'].'` set'
+ .'`server`="'.$sid.'",'
+ .'`value`="'.htmlspecialchars($val).'",'
+ .'`active`="'.$aData['active'][$index].'",'
+ .'`passwd`="'.htmlspecialchars($aData['passwd'][$index]).'",'
+ .'`flags`="'.htmlspecialchars($aData['flags'][$index]).'",'
+ .'`type`="'.$type.'",'
+ .'`time`="'.$time.'",'
+ .'`text`="'.htmlspecialchars($text).'",'
+ .'`info`="'.htmlspecialchars($aData['info'][$index]).'"');
+
+ if($aData['active'][$index])
+ $usini .= $text.PHP_EOL;
+ }
+ }
+
+ $temp = sys::temp($usini);
+
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/cstrike/addons/amxmodx/configs/users.ini', 0644);
+
+ unlink($temp);
+
+ $ssh->set('chown server'.$server['uid'].':servers /servers/'.$server['uid'].'/cstrike/addons/amxmodx/configs/users.ini');
+
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_reloadadmins\"\015'");
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Построение списка добавленных админов
+ $sql->query('SELECT `id`, `value`, `active`, `passwd`, `flags`, `type`, `time`, `info` FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'" ORDER BY `id` ASC');
+ while($admin = $sql->get())
+ {
+ switch($admin['type'])
+ {
+ case 'c':
+ $type = 'SteamID/Пароль Ник/Пароль SteamID IP Адрес ';
+ break;
+
+ case 'ce':
+ $type = 'SteamID Ник/Пароль SteamID/Пароль IP Адрес ';
+ break;
+
+ case 'de':
+ $type = 'IP Адрес Ник/Пароль SteamID/Пароль SteamID ';
+ break;
+
+ default:
+ $type = 'Ник/Пароль SteamID/Пароль SteamID IP Адрес ';
+ }
+
+ $html->get('list', 'sections/control/servers/'.$server['game'].'/settings/admins');
+
+ if($admin['active'])
+ $html->unit('active', 1);
+ else
+ $html->unit('active');
+
+ $html->set('id', $admin['id']);
+ $html->set('value', $admin['value']);
+ $html->set('passwd', $admin['passwd']);
+ $html->set('flags', $admin['flags']);
+ $html->set('type', $type);
+ $html->set('time', date('d.m.y', $admin['time']));
+ $html->set('info', $admin['info']);
+
+ $html->pack('admins');
+ }
+
+ $sql->query('SELECT `id` FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'" ORDER BY `id` DESC LIMIT 1');
+ $max = $sql->get();
+
+ $html->get('admins', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('admins', isset($html->arr['admins']) ? $html->arr['admins'] : '');
+ $html->set('index', $max['id'] < 1 ? 0 : $max['id']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/settings/amxlogs.php b/system/sections/control/servers/cs/settings/amxlogs.php
new file mode 100644
index 0000000..0d207e9
--- /dev/null
+++ b/system/sections/control/servers/cs/settings/amxlogs.php
@@ -0,0 +1,89 @@
+nav('Логи AMX');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к логам
+ $folder = '/servers/'.$server['uid'].'/cstrike/addons/amxmodx/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['csamxlogs']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/amxlogs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'amxlogs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/amxlogs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', end($name));
+ $html->set('uri', 'amxlogs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/control/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('uri', 'amx');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/settings/bans.php b/system/sections/control/servers/cs/settings/bans.php
new file mode 100644
index 0000000..4e29f56
--- /dev/null
+++ b/system/sections/control/servers/cs/settings/bans.php
@@ -0,0 +1,165 @@
+nav('Бан листы');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к файлам (banned.cfg / listip.cfg)
+ $folder = '/servers/'.$server['uid'].'/cstrike';
+
+ // Если бан/разбан/проверка
+ if($go)
+ {
+ $aData = array();
+
+ $aData['value'] = isset($_POST['value']) ? trim($_POST['value']) : sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+ $aData['amxbans'] = isset($_POST['amxbans']) ? true : false;
+
+ // Проверка входных данных
+ if(sys::valid($aData['value'], 'steamid') AND sys::valid($aData['value'], 'ip'))
+ sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+
+ // Если указан steamid
+ if(sys::valid($aData['value'], 'ip'))
+ {
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен amxbans/csbans
+ if($aData['amxbans'])
+ {
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_ban 0 ".$aData['value']." EGP\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"fb_ban 0 ".$aData['value']." EGP\"\015'");
+ }else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"banid 0.0 ".$aData['value']." kick\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"banid 0.0 '.$aData['value'].'\" >> '.$folder.'/banned.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned.cfg | grep -v '.$aData['value'].' > temp_banned.cfg; echo "" >> temp_banned.cfg && cat temp_banned.cfg > banned.cfg; rm temp_banned.cfg"');
+
+ // Если включен amxbans/csbans
+ if($aData['amxbans'])
+ {
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_unban ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"fb_unban ".$aData['value']."\"\015'");
+ }else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeid ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeid\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный SteamID найден в файле banned.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный SteamID не найден в файле banned.cfg'), $nmch);
+ }
+ }else{
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен amxbans/csbans
+ if($aData['amxbans'])
+ {
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_ban 0 ".$aData['value']." EGP\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"fb_ban 0 ".$aData['value']." EGP\"\015'");
+ }else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"addip 0.0 ".$aData['value']." EGP\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' listip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"addip 0.0 '.$aData['value'].'\" >> '.$folder.'/listip.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из listip.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat listip.cfg | grep -v '.$aData['value'].' > temp_listip.cfg; echo "" >> temp_listip.cfg && cat temp_listip.cfg > listip.cfg; rm temp_listip.cfg"');
+
+ // Если включен amxbans/csbans
+ if($aData['amxbans'])
+ {
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_unban ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"fb_unban ".$aData['value']."\"\015'");
+ }else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeip ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeip\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' listip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный IP найден в файле listip.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный IP не найден в файле listip.cfg'), $nmch);
+ }
+ }
+ }
+
+ // Содержимое banned.cfg
+ $ssh->set('cd '.$folder.' && cat banned.cfg | awk \'{print $3}\' | grep STEAM_');
+ $aBanned = explode("\n", $ssh->get());
+
+ // Содержимое listip.cfg
+ $ssh->set('cd '.$folder.' && cat listip.cfg | awk \'{print $3}\' | egrep "(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}"');
+ $aListip = explode("\n", $ssh->get());
+
+ if(isset($aBanned[count($aBanned)-1]) AND $aBanned[count($aBanned)-1] == '')
+ unset($aBanned[count($aBanned)-1]);
+
+ if(isset($aListip[count($aListip)-1]) AND $aListip[count($aListip)-1] == '')
+ unset($aListip[count($aListip)-1]);
+
+ // Построение списка забаненых по steamid
+ foreach($aBanned as $line => $steam)
+ {
+ $html->get('bans_list', 'sections/control/servers/games/settings');
+
+ $html->set('value', trim($steam));
+
+ $html->pack('banned');
+ }
+
+ // Построение списка забаненых по ip
+ foreach($aListip as $line => $ip)
+ {
+ $html->get('bans_list', 'sections/control/servers/games/settings');
+
+ $html->set('value', trim($ip));
+
+ $html->pack('listip');
+ }
+
+ $html->get('bans', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('banned', isset($html->arr['banned']) ? $html->arr['banned'] : '');
+ $html->set('listip', isset($html->arr['listip']) ? $html->arr['listip'] : '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/settings/debug.php b/system/sections/control/servers/cs/settings/debug.php
new file mode 100644
index 0000000..f9217af
--- /dev/null
+++ b/system/sections/control/servers/cs/settings/debug.php
@@ -0,0 +1,25 @@
+nav('Отладочный лог');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Чтение файла - oldstart.log
+ $file = '/servers/'.$server['uid'].'/debug.log';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep "CRASH: " | grep -ve "^#\|^[[:space:]]*$"');
+
+ $html->get('debug', 'sections/control/servers/games/settings');
+
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/settings/logs.php b/system/sections/control/servers/cs/settings/logs.php
new file mode 100644
index 0000000..56b63ce
--- /dev/null
+++ b/system/sections/control/servers/cs/settings/logs.php
@@ -0,0 +1,89 @@
+nav('Логи');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к логам
+ $folder = '/servers/'.$server['uid'].'/cstrike/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['cslogs']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/logs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+ $html->set('uri', 'logs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/logs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', end($name));
+ $html->set('uri', 'logs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/control/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('uri', '');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/settings/server.php b/system/sections/control/servers/cs/settings/server.php
new file mode 100644
index 0000000..e46d11b
--- /dev/null
+++ b/system/sections/control/servers/cs/settings/server.php
@@ -0,0 +1,116 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ $file = '/servers/'.$server['uid'].'/cstrike/server.cfg';
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ $config_end = $servercfg['\'other\''];
+
+ unset($servercfg['\'other\'']);
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).' "'.$val.'"'."\n";
+
+ // Временый файл
+ $temp = sys::temp($config.$config_end);
+
+ $ssh->setfile($temp, $file, 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$file);
+
+ unlink($temp);
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "exec server.cfg"\015\';');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+ $other = '';
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode(' ', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.' ', "", $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ else
+ $other .= $line."\n";
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/control/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $html->get('servercfg', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('cfg', $html->arr['list']);
+ $html->set('other', $other);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/settings/start.php b/system/sections/control/servers/cs/settings/start.php
new file mode 100644
index 0000000..d80313c
--- /dev/null
+++ b/system/sections/control/servers/cs/settings/start.php
@@ -0,0 +1,185 @@
+query('SELECT `uid`, `slots`, `map_start`, `vac`, `fps`, `fastdl`, `autorestart`, `pingboost`, `core_fix` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'games/games.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($sid, $unit, '/servers/'.$server['uid'].'/cstrike/maps', $server['map_start'], false);
+
+ // Вывод списка потоков
+ if(isset($url['core']))
+ ctrl::cpulist($unit, $server['core_fix']);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($sid, $unit, '/servers/'.$server['uid'].'/cstrike/maps', $map, true, $nmch, true);
+
+ sys::outjs(array('e' => $map.' != '.$server['map_start']), $nmch);
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'address':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => 'Необходимо выключить игровой сервер'), $nmch);
+
+ $address = isset($_POST['address']) ? trim($_POST['address']) : $server['address'];
+
+ if(sys::valid($address, 'other', $aValid['address']))
+ sys::outjs(array('e' => 'Адрес игрового сервера имеет неверный формат'), $nmch);
+
+ $sql->query('SELECT `id` FROM `control_servers` WHERE `unit`="'.$id.'" AND `address`="'.$address.'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный адрес занят другим сервером'), $nmch);
+
+ if($address != $server['address'])
+ $sql->query('UPDATE `control_servers` set `address`="'.$address.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `control_servers` set `vac`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'core_fix':
+ $n = ctrl::cpulist($unit, $server['core_fix'], true);
+
+ if($value > $n)
+ sys::outjs(array('e' => 'На физическом сервере нет такого ядра/потока'), $nmch);
+
+ if($value < 0)
+ $value = 0;
+
+ if($value != $server['core_fix'])
+ $sql->query('UPDATE `control_servers` set `core_fix`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > 32 ? 32 : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots'])
+ $sql->query('UPDATE `control_servers` set `slots`="'.$slots.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `control_servers` set `autorestart`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fps':
+ if(in_array($value, array('300', '500', '1100')))
+ $sql->query('UPDATE `control_servers` set `fps`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'pingboost':
+ if(in_array($value, array(0, 1, 2, 3)))
+ $sql->query('UPDATE `control_servers` set `pingboost`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.$unit['address'].':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, 'servers/'.$server['uid'].'/cstrike/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.'servers/'.$server['uid'].'/cstrike/fastdl.cfg;'
+ .'ln -s '.'servers/'.$server['uid'].'/cstrike /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' '.'servers/'.$server['uid'].'/cstrike/server.cfg;'
+ .'echo "exec fastdl.cfg" >> '.'servers/'.$server['uid'].'/cstrike/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' '.'servers/'.$server['uid'].'/cstrike/server.cfg;'
+ .'rm '.'servers/'.$server['uid'].'/cstrike/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `control_servers` set `fastdl`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= 32; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $fps = '';
+
+ foreach(array('300', '500', '1100') as $value)
+ $fps .= ''.$value.' FPS ';
+
+ $pingboost = str_replace($server['pingboost'].'"', $server['pingboost'].'" selected="select"', 'БЕЗ PINGBOOST PINGBOOST 1 PINGBOOST 2 PINGBOOST 3 ');
+
+ $core_fix = $server['core_fix'] ? '1 ядро/поток ' : 'Автоматическое определение ';
+
+ $html->get('start', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('map', $server['map_start']);
+ $html->set('address', $server['address']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('core_fix', $core_fix);
+ $html->set('slots', str_replace('"'.$server['slots'].'"', '"'.$server['slots'].'" selected="select"', $slots));
+ $html->set('pingboost', $pingboost);
+ $html->set('fps', str_replace($server['fps'].'"', $server['fps'].'" selected="select"', $fps));
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cs/settings/top.php b/system/sections/control/servers/cs/settings/top.php
new file mode 100644
index 0000000..8f10482
--- /dev/null
+++ b/system/sections/control/servers/cs/settings/top.php
@@ -0,0 +1,20 @@
+query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Удаление файла csstats.dat
+ $ssh->set('rm /servers'.$server['uid'].'/cstrike/addons/amxmodx/data/csstats.dat');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ shell_exec('php cron.php '.$cfg['cron_key'].' control_server_action restart cs '.$sid);
+
+ sys::outjs(array('s' => 'ok'));
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/console.php b/system/sections/control/servers/csgo/console.php
new file mode 100644
index 0000000..15438ff
--- /dev/null
+++ b/system/sections/control/servers/csgo/console.php
@@ -0,0 +1,67 @@
+query('SELECT `uid`, `time_start` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $command = isset($_POST['command']) ? sys::cmd($_POST['command']) : '';
+
+ if($server['status'] == 'off')
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('servers', 'off')));
+
+ sys::out(sys::text('servers', 'off'));
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ sys::out(sys::text('error', 'ssh'));
+ }
+
+ $dir = '/servers/'.$server['uid'].'/csgo/';
+
+ $filecmd = $dir.'console.log';
+
+ if($command)
+ {
+ if(strtolower($command) == 'clear')
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"Очистка консоли\n\" > '.$filecmd.'"');
+ else
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.$command.'"\015\';'
+ .'sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/control/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/copy.php b/system/sections/control/servers/csgo/copy.php
new file mode 100644
index 0000000..19cd939
--- /dev/null
+++ b/system/sections/control/servers/csgo/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'control/servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('ctrl_server_copy_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_copy_'.$sid);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/control/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `control_copy` WHERE `user`="'.$ctrl['user'].'_'.$id.'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/control/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_copy_'.$sid, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/index.php b/system/sections/control/servers/csgo/index.php
new file mode 100644
index 0000000..65dccf5
--- /dev/null
+++ b/system/sections/control/servers/csgo/index.php
@@ -0,0 +1,30 @@
+query('SELECT `slots`, `online`, `players`, `name`, `map` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address']);
+
+ $btn = sys::buttons($sid, $server['status'], $server['game'], $id);
+
+ $html->get('index', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img'));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/plugins.php b/system/sections/control/servers/csgo/plugins.php
new file mode 100644
index 0000000..2a96424
--- /dev/null
+++ b/system/sections/control/servers/csgo/plugins.php
@@ -0,0 +1,160 @@
+nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/plugins');
+
+ $nmch = sys::rep_act('ctrl_server_plugins_go_'.$sid, 10);
+
+ include(SEC.'control/servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('ctrl_server_plugins_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_plugins_'.$sid);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$sid.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/control/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `control_plugins_install` WHERE `server`="'.$sid.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/control/servers/games');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_plugins_'.$sid, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/rcon.php b/system/sections/control/servers/csgo/rcon.php
new file mode 100644
index 0000000..14ff694
--- /dev/null
+++ b/system/sections/control/servers/csgo/rcon.php
@@ -0,0 +1,57 @@
+ 'Необходимо выбрать игрока.'));
+
+ if($url['action'] == 'kick')
+ rcon::cmd(array_merge($server, array('id' => $id)), 'kickid "'.$player.'" "EGP Panel"');
+ else
+ rcon::cmd(array_merge($server, array('id' => $id)), 'sm_slay "'.$player.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ include(LIB.'geo.php');
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $aPlayers = rcon::players(rcon::cmd(array_merge($server, array('id' => $id))));
+
+ foreach($aPlayers as $i => $aPlayer)
+ {
+ $html->get('player', 'sections/control/servers/'.$server['game'].'/rcon');
+
+ $html->set('i', $i);
+ $html->set('userid', $aPlayer['userid']);
+ $html->set('name', $aPlayer['name']);
+ $html->set('steamid', $aPlayer['steamid']);
+ $html->set('time', $aPlayer['time']);
+ $html->set('ping', $aPlayer['ping']);
+ $html->set('ip', $aPlayer['ip']);
+ $html->set('ico', $aPlayer['ico']);
+ $html->set('country', $aPlayer['country']);
+
+ $html->pack('players');
+ }
+
+ sys::outjs(array('s' => isset($html->arr['players']) ? $html->arr['players'] : ''));
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Rcon управление игроками');
+
+ $html->get('rcon', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/settings.php b/system/sections/control/servers/csgo/settings.php
new file mode 100644
index 0000000..945b605
--- /dev/null
+++ b/system/sections/control/servers/csgo/settings.php
@@ -0,0 +1,63 @@
+query('SELECT `uid`, `pack` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+
+ $aSub = array('start', 'server', 'admins', 'bans', 'firewall', 'crontab', 'startlogs', 'debug', 'logs', 'smlogs', 'pack', 'file');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('ctrl_server_settings_go_'.$sid, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'control/servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'control/servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('ctrl_server_settings_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_settings_'.$sid);
+ else{
+ $aEditslist = 1;
+ $ctrlmod = true;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = $cfg['control_packs'][$server['game']];
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ include(SEC.'control/servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/control/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('packs', $packs);
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_settings_'.$sid, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/settings/admins.php b/system/sections/control/servers/csgo/settings/admins.php
new file mode 100644
index 0000000..597fc76
--- /dev/null
+++ b/system/sections/control/servers/csgo/settings/admins.php
@@ -0,0 +1,112 @@
+nav('Управление администраторами');
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $aData = array();
+
+ $aData['active'] = isset($_POST['active']) ? $_POST['active'] : '';
+ $aData['value'] = isset($_POST['value']) ? $_POST['value'] : '';
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : '';
+ $aData['flags'] = isset($_POST['flags']) ? $_POST['flags'] : '';
+ $aData['immunity'] = isset($_POST['immunity']) ? sys::int($_POST['immunity']) : '';
+ $aData['time'] = isset($_POST['time']) ? $_POST['time'] : '';
+ $aData['info'] = isset($_POST['info']) ? $_POST['info'] : '';
+
+ // Удаление текущих записей
+ $sql->query('DELETE FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'"');
+
+ $usini = '';
+
+ foreach($aData['value'] as $index => $val)
+ {
+ if($val != '')
+ {
+ $aDate = isset($aData['time'][$index]) ? explode('.', $aData['time'][$index]) : explode('.', date('d.m.Y', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ $aDate = explode('.', date('d.m.Y', $start_point));
+
+ $time = mktime(0, 0, 0, $aDate[1], $aDate[0], $aDate[2]);
+
+ $aData['active'][$index] = isset($aData['active'][$index]) ? 1 : 0;
+ $aData['passwd'][$index] = isset($aData['passwd'][$index]) ? $aData['passwd'][$index] : '';
+ $aData['flags'][$index] = isset($aData['flags'][$index]) ? $aData['flags'][$index] : '';
+ $aData['info'][$index] = isset($aData['info'][$index]) ? $aData['info'][$index] : '';
+
+ $text = '"'.$val.'" "'.$aData['immunity'][$index].':'.$aData['flags'][$index].'" "'.$aData['passwd'][$index].'"';
+
+ $sql->query('INSERT INTO `control_admins_'.$server['game'].'` set'
+ .'`server`="'.$sid.'",'
+ .'`value`="'.htmlspecialchars($val).'",'
+ .'`active`="'.$aData['active'][$index].'",'
+ .'`passwd`="'.htmlspecialchars($aData['passwd'][$index]).'",'
+ .'`flags`="'.htmlspecialchars($aData['flags'][$index]).'",'
+ .'`immunity`="'.$aData['immunity'][$index].'",'
+ .'`time`="'.$time.'",'
+ .'`text`="'.htmlspecialchars($text).'",'
+ .'`info`="'.htmlspecialchars($aData['info'][$index]).'"');
+
+ if($aData['active'][$index])
+ $usini .= $text.PHP_EOL;
+ }
+ }
+
+ $temp = sys::temp($usini);
+
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/csgo/addons/sourcemod/configs/admins_simple.ini', 0644);
+
+ unlink($temp);
+
+ $ssh->set('chown server'.$server['uid'].':servers /servers/'.$server['uid'].'/csgo/addons/sourcemod/configs/admins_simple.ini');
+
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \" sm_reloadadmins\"\015'");
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Построение списка добавленных админов
+ $sql->query('SELECT `id`, `value`, `active`, `passwd`, `flags`, `immunity`, `time`, `info` FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'" ORDER BY `id` ASC');
+ while($admin = $sql->get())
+ {
+ $html->get('list', 'sections/control/servers/'.$server['game'].'/settings/admins');
+
+ if($admin['active'])
+ $html->unit('active', 1);
+ else
+ $html->unit('active');
+
+ $html->set('id', $admin['id']);
+ $html->set('value', $admin['value']);
+ $html->set('passwd', $admin['passwd']);
+ $html->set('flags', $admin['flags']);
+ $html->set('immunity', $admin['immunity']);
+ $html->set('time', date('d.m.y', $admin['time']));
+ $html->set('info', $admin['info']);
+
+ $html->pack('admins');
+ }
+
+ $sql->query('SELECT `id` FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'" ORDER BY `id` DESC LIMIT 1');
+ $max = $sql->get();
+
+ $html->get('admins', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('admins', isset($html->arr['admins']) ? $html->arr['admins'] : '');
+ $html->set('index', $max['id'] < 1 ? 0 : $max['id']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/settings/bans.php b/system/sections/control/servers/csgo/settings/bans.php
new file mode 100644
index 0000000..eed97f7
--- /dev/null
+++ b/system/sections/control/servers/csgo/settings/bans.php
@@ -0,0 +1,158 @@
+nav('Бан листы');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к файлам (banned_user.cfg / banned_ip.cfg)
+ $folder = '/servers/'.$server['uid'].'/csgo';
+
+ // Если бан/разбан/проверка
+ if($go)
+ {
+ $aData = array();
+
+ $aData['value'] = isset($_POST['value']) ? trim($_POST['value']) : sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+ $aData['userid'] = isset($_POST['userid']) ? sys::int($_POST['userid']) : false;
+ $aData['amxbans'] = isset($_POST['amxbans']) ? true : false;
+
+ // Проверка входных данных
+ if(sys::valid($aData['value'], 'steamid') AND sys::valid($aData['value'], 'ip'))
+ sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+
+ // Если указан steamid
+ if(sys::valid($aData['value'], 'ip'))
+ {
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'] AND $aData['userid'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['userid']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"banid 0.0 ".$aData['value']." kick\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"banid 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_user.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_user.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_user.cfg | grep -v '.$aData['value'].' > temp_banned.cfg; echo "" >> temp_banned.cfg && cat temp_banned.cfg > banned_user.cfg; rm temp_banned.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeid ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeid\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный SteamID найден в файле banned_user.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный SteamID не найден в файле banned_user.cfg'), $nmch);
+ }
+ }else{
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['value']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"addip 0.0 ".$aData['value']." EGP\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"addip 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_ip.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_ip.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_ip.cfg | grep -v '.$aData['value'].' > temp_listip.cfg; echo "" >> temp_listip.cfg && cat temp_listip.cfg > banned_ip.cfg; rm temp_listip.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeip ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeip\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный IP найден в файле banned_ip.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный IP не найден в файле banned_ip.cfg'), $nmch);
+ }
+ }
+ }
+
+ // Содержимое banned_user.cfg
+ $ssh->set('cd '.$folder.' && cat banned_user.cfg | awk \'{print $3}\' | grep STEAM_');
+ $aBanned = explode("\n", $ssh->get());
+
+ // Содержимое banned_ip.cfg
+ $ssh->set('cd '.$folder.' && cat banned_ip.cfg | awk \'{print $3}\' | egrep "(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}"');
+ $aListip = explode("\n", $ssh->get());
+
+ if(isset($aBanned[count($aBanned)-1]) AND $aBanned[count($aBanned)-1] == '')
+ unset($aBanned[count($aBanned)-1]);
+
+ if(isset($aListip[count($aListip)-1]) AND $aListip[count($aListip)-1] == '')
+ unset($aListip[count($aListip)-1]);
+
+ // Построение списка забаненых по steamid
+ foreach($aBanned as $line => $steam)
+ {
+ $html->get('bans_list', 'sections/control/servers/games/settings');
+
+ $html->set('value', trim($steam));
+
+ $html->pack('banned');
+ }
+
+ // Построение списка забаненых по ip
+ foreach($aListip as $line => $ip)
+ {
+ $html->get('bans_list', 'sections/control/servers/games/settings');
+
+ $html->set('value', trim($ip));
+
+ $html->pack('listip');
+ }
+
+ $html->get('bans', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('banned', isset($html->arr['banned']) ? $html->arr['banned'] : '');
+ $html->set('listip', isset($html->arr['listip']) ? $html->arr['listip'] : '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/settings/debug.php b/system/sections/control/servers/csgo/settings/debug.php
new file mode 100644
index 0000000..f9217af
--- /dev/null
+++ b/system/sections/control/servers/csgo/settings/debug.php
@@ -0,0 +1,25 @@
+nav('Отладочный лог');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Чтение файла - oldstart.log
+ $file = '/servers/'.$server['uid'].'/debug.log';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep "CRASH: " | grep -ve "^#\|^[[:space:]]*$"');
+
+ $html->get('debug', 'sections/control/servers/games/settings');
+
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/settings/logs.php b/system/sections/control/servers/csgo/settings/logs.php
new file mode 100644
index 0000000..c147110
--- /dev/null
+++ b/system/sections/control/servers/csgo/settings/logs.php
@@ -0,0 +1,89 @@
+nav('Логи');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к логам
+ $folder = '/servers/'.$server['uid'].'/csgo/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['cslogs']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/logs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+ $html->set('uri', 'logs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/logs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', end($name));
+ $html->set('uri', 'logs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/control/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('uri', '');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/settings/server.php b/system/sections/control/servers/csgo/settings/server.php
new file mode 100644
index 0000000..8e41f0f
--- /dev/null
+++ b/system/sections/control/servers/csgo/settings/server.php
@@ -0,0 +1,116 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ $file = '/servers/'.$server['uid'].'/csgo/cfg/server.cfg';
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ $config_end = $servercfg['\'other\''];
+
+ unset($servercfg['\'other\'']);
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).' "'.$val.'"'."\n";
+
+ // Временый файл
+ $temp = sys::temp($config.$config_end);
+
+ $ssh->setfile($temp, $file, 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$file);
+
+ unlink($temp);
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "exec server.cfg"\015\';');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+ $other = '';
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode(' ', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.' ', "", $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ else
+ $other .= $line."\n";
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/control/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $html->get('servercfg', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('cfg', $html->arr['list']);
+ $html->set('other', $other);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/settings/smlogs.php b/system/sections/control/servers/csgo/settings/smlogs.php
new file mode 100644
index 0000000..e13f4d9
--- /dev/null
+++ b/system/sections/control/servers/csgo/settings/smlogs.php
@@ -0,0 +1,89 @@
+nav('Логи SourceMod');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к логам
+ $folder = '/servers/'.$server['uid'].'/csgo/addons/sourcemod/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['csssmlogs']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/smlogs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'smlogs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/smlogs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', end($name));
+ $html->set('uri', 'smlogs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/control/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('uri', 'sm');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/csgo/settings/start.php b/system/sections/control/servers/csgo/settings/start.php
new file mode 100644
index 0000000..8182b60
--- /dev/null
+++ b/system/sections/control/servers/csgo/settings/start.php
@@ -0,0 +1,193 @@
+query('SELECT `uid`, `slots`, `map_start`, `vac`, `fastdl`, `autorestart`, `tickrate`, `core_fix`, `pingboost` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'games/games.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($sid, $unit, '/servers/'.$server['uid'].'/csgo/maps', $server['map_start'], false);
+
+ // Вывод списка потоков
+ if(isset($url['core']))
+ ctrl::cpulist($unit, $server['core_fix']);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($sid, $unit, '/servers/'.$server['uid'].'/csgo/maps', $map, true, $nmch, true);
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'address':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => 'Необходимо выключить игровой сервер'), $nmch);
+
+ $address = isset($_POST['address']) ? trim($_POST['address']) : $server['address'];
+
+ if(sys::valid($address, 'other', $aValid['address']))
+ sys::outjs(array('e' => 'Адрес игрового сервера имеет неверный формат'), $nmch);
+
+ $sql->query('SELECT `id` FROM `control_servers` WHERE `unit`="'.$id.'" AND `address`="'.$address.'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный адрес занят другим сервером'), $nmch);
+
+ if($address != $server['address'])
+ $sql->query('UPDATE `control_servers` set `address`="'.$address.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'mod':
+ if(in_array($value, array(1, 2, 3, 4, 5)))
+ $sql->query('UPDATE `control_servers` set `pingboost`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `control_servers` set `vac`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'core_fix':
+ $n = ctrl::cpulist($unit, $server['core_fix'], true);
+
+ if($value > $n)
+ sys::outjs(array('e' => 'На физическом сервере нет такого ядра/потока'), $nmch);
+
+ if($value < 0)
+ $value = 0;
+
+ if($value != $server['core_fix'])
+ $sql->query('UPDATE `control_servers` set `core_fix`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > 64 ? 64 : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots'])
+ $sql->query('UPDATE `control_servers` set `slots`="'.$slots.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `control_servers` set `autorestart`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'tickrate':
+ if(in_array($value, array('64', '128')))
+ $sql->query('UPDATE `control_servers` set `tickrate`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.$unit['address'].':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/csgo/cfg/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers /servers/'.$server['uid'].'/csgo/cfg/fastdl.cfg;'
+ .'ln -s /servers/'.$server['uid'].'/csgo /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' /servers/'.$server['uid'].'/csgo/cfg/server.cfg;'
+ .'echo "exec fastdl.cfg" >> /servers/'.$server['uid'].'/csgo/cfg/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' /servers/'.$server['uid'].'/csgo/cfg/server.cfg;'
+ .'rm /servers/'.$server['uid'].'/csgo/cfg/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `control_servers` set `fastdl`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= 64; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $tickrate = '';
+
+ foreach(array('64', '128') as $value)
+ $tickrate .= ''.$value.' TickRate ';
+
+ $core_fix = $server['core_fix'] ? '1 ядро/поток ' : 'Автоматическое определение ';
+
+ // Игровой режим
+ $mods = 'Классический обычный '
+ .'Классический соревновательный '
+ .'Гонка вооружений '
+ .'Уничтожение объекта '
+ .'Бой насмерть ';
+
+ if(!$server['pingboost'])
+ $server['pingboost'] = 2;
+
+ $mod = str_replace('value="'.$server['pingboost'], 'value="'.$server['pingboost'].'" selected="select', $mods);
+
+ $html->get('start', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('map', $server['map_start']);
+ $html->set('address', $server['address']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('core_fix', $core_fix);
+ $html->set('mod', $mod);
+ $html->set('slots', str_replace('"'.$server['slots'].'"', '"'.$server['slots'].'" selected="select"', $slots));
+ $html->set('tickrate', str_replace($server['tickrate'].'"', $server['tickrate'].'" selected="select"', $tickrate));
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/console.php b/system/sections/control/servers/css/console.php
new file mode 100644
index 0000000..0900605
--- /dev/null
+++ b/system/sections/control/servers/css/console.php
@@ -0,0 +1,67 @@
+query('SELECT `uid`, `time_start` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $command = isset($_POST['command']) ? sys::cmd($_POST['command']) : '';
+
+ if($server['status'] == 'off')
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('servers', 'off')));
+
+ sys::out(sys::text('servers', 'off'));
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ sys::out(sys::text('error', 'ssh'));
+ }
+
+ $dir = '/servers/'.$server['uid'].'/cstrike/';
+
+ $filecmd = $dir.'console.log';
+
+ if($command)
+ {
+ if(strtolower($command) == 'clear')
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"Очистка консоли\n\" > '.$filecmd.'"');
+ else
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.$command.'"\015\';'
+ .'sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/control/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/copy.php b/system/sections/control/servers/css/copy.php
new file mode 100644
index 0000000..1c41f2e
--- /dev/null
+++ b/system/sections/control/servers/css/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'control/servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('ctrl_server_copy_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_copy_'.$sid);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/control/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `control_copy` WHERE `user`="'.$ctrl['user'].'_'.$id.'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/control/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_copy_'.$sid, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/index.php b/system/sections/control/servers/css/index.php
new file mode 100644
index 0000000..bc4f041
--- /dev/null
+++ b/system/sections/control/servers/css/index.php
@@ -0,0 +1,30 @@
+query('SELECT `slots`, `online`, `players`, `name`, `pack`, `map` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address']);
+
+ $btn = sys::buttons($sid, $server['status'], $server['game'], $id);
+
+ $html->get('index', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img'));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/plugins.php b/system/sections/control/servers/css/plugins.php
new file mode 100644
index 0000000..2a96424
--- /dev/null
+++ b/system/sections/control/servers/css/plugins.php
@@ -0,0 +1,160 @@
+nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/plugins');
+
+ $nmch = sys::rep_act('ctrl_server_plugins_go_'.$sid, 10);
+
+ include(SEC.'control/servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('ctrl_server_plugins_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_plugins_'.$sid);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$sid.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/control/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `control_plugins_install` WHERE `server`="'.$sid.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/control/servers/games');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_plugins_'.$sid, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/rcon.php b/system/sections/control/servers/css/rcon.php
new file mode 100644
index 0000000..14ff694
--- /dev/null
+++ b/system/sections/control/servers/css/rcon.php
@@ -0,0 +1,57 @@
+ 'Необходимо выбрать игрока.'));
+
+ if($url['action'] == 'kick')
+ rcon::cmd(array_merge($server, array('id' => $id)), 'kickid "'.$player.'" "EGP Panel"');
+ else
+ rcon::cmd(array_merge($server, array('id' => $id)), 'sm_slay "'.$player.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ include(LIB.'geo.php');
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $aPlayers = rcon::players(rcon::cmd(array_merge($server, array('id' => $id))));
+
+ foreach($aPlayers as $i => $aPlayer)
+ {
+ $html->get('player', 'sections/control/servers/'.$server['game'].'/rcon');
+
+ $html->set('i', $i);
+ $html->set('userid', $aPlayer['userid']);
+ $html->set('name', $aPlayer['name']);
+ $html->set('steamid', $aPlayer['steamid']);
+ $html->set('time', $aPlayer['time']);
+ $html->set('ping', $aPlayer['ping']);
+ $html->set('ip', $aPlayer['ip']);
+ $html->set('ico', $aPlayer['ico']);
+ $html->set('country', $aPlayer['country']);
+
+ $html->pack('players');
+ }
+
+ sys::outjs(array('s' => isset($html->arr['players']) ? $html->arr['players'] : ''));
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Rcon управление игроками');
+
+ $html->get('rcon', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/settings.php b/system/sections/control/servers/css/settings.php
new file mode 100644
index 0000000..646d5c3
--- /dev/null
+++ b/system/sections/control/servers/css/settings.php
@@ -0,0 +1,63 @@
+query('SELECT `uid`, `pack` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+
+ $aSub = array('start', 'server', 'admins', 'bans', 'firewall', 'crontab', 'startlogs', 'debug', 'logs', 'smlogs', 'pack', 'file');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('ctrl_server_settings_go_'.$sid, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'control/servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'control/servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('ctrl_server_settings_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_settings_'.$sid);
+ else{
+ $aEditslist = 1;
+ $ctrlmod = true;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = $cfg['control_packs'][$server['game']];
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ include(SEC.'control/servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/control/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('packs', $packs);
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_settings_'.$sid, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/settings/admins.php b/system/sections/control/servers/css/settings/admins.php
new file mode 100644
index 0000000..4a85b53
--- /dev/null
+++ b/system/sections/control/servers/css/settings/admins.php
@@ -0,0 +1,112 @@
+nav('Управление администраторами');
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $aData = array();
+
+ $aData['active'] = isset($_POST['active']) ? $_POST['active'] : '';
+ $aData['value'] = isset($_POST['value']) ? $_POST['value'] : '';
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : '';
+ $aData['flags'] = isset($_POST['flags']) ? $_POST['flags'] : '';
+ $aData['immunity'] = isset($_POST['immunity']) ? sys::int($_POST['immunity']) : '';
+ $aData['time'] = isset($_POST['time']) ? $_POST['time'] : '';
+ $aData['info'] = isset($_POST['info']) ? $_POST['info'] : '';
+
+ // Удаление текущих записей
+ $sql->query('DELETE FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'"');
+
+ $usini = '';
+
+ foreach($aData['value'] as $index => $val)
+ {
+ if($val != '')
+ {
+ $aDate = isset($aData['time'][$index]) ? explode('.', $aData['time'][$index]) : explode('.', date('d.m.Y', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ $aDate = explode('.', date('d.m.Y', $start_point));
+
+ $time = mktime(0, 0, 0, $aDate[1], $aDate[0], $aDate[2]);
+
+ $aData['active'][$index] = isset($aData['active'][$index]) ? 1 : 0;
+ $aData['passwd'][$index] = isset($aData['passwd'][$index]) ? $aData['passwd'][$index] : '';
+ $aData['flags'][$index] = isset($aData['flags'][$index]) ? $aData['flags'][$index] : '';
+ $aData['info'][$index] = isset($aData['info'][$index]) ? $aData['info'][$index] : '';
+
+ $text = '"'.$val.'" "'.$aData['immunity'][$index].':'.$aData['flags'][$index].'" "'.$aData['passwd'][$index].'"';
+
+ $sql->query('INSERT INTO `control_admins_'.$server['game'].'` set'
+ .'`server`="'.$sid.'",'
+ .'`value`="'.htmlspecialchars($val).'",'
+ .'`active`="'.$aData['active'][$index].'",'
+ .'`passwd`="'.htmlspecialchars($aData['passwd'][$index]).'",'
+ .'`flags`="'.htmlspecialchars($aData['flags'][$index]).'",'
+ .'`immunity`="'.$aData['immunity'][$index].'",'
+ .'`time`="'.$time.'",'
+ .'`text`="'.htmlspecialchars($text).'",'
+ .'`info`="'.htmlspecialchars($aData['info'][$index]).'"');
+
+ if($aData['active'][$index])
+ $usini .= $text.PHP_EOL;
+ }
+ }
+
+ $temp = sys::temp($usini);
+
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/cstrike/addons/sourcemod/configs/admins_simple.ini', 0644);
+
+ unlink($temp);
+
+ $ssh->set('chown server'.$server['uid'].':servers /servers/'.$server['uid'].'/cstrike/addons/sourcemod/configs/admins_simple.ini');
+
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \" sm_reloadadmins\"\015'");
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Построение списка добавленных админов
+ $sql->query('SELECT `id`, `value`, `active`, `passwd`, `flags`, `immunity`, `time`, `info` FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'" ORDER BY `id` ASC');
+ while($admin = $sql->get())
+ {
+ $html->get('list', 'sections/control/servers/'.$server['game'].'/settings/admins');
+
+ if($admin['active'])
+ $html->unit('active', 1);
+ else
+ $html->unit('active');
+
+ $html->set('id', $admin['id']);
+ $html->set('value', $admin['value']);
+ $html->set('passwd', $admin['passwd']);
+ $html->set('flags', $admin['flags']);
+ $html->set('immunity', $admin['immunity']);
+ $html->set('time', date('d.m.y', $admin['time']));
+ $html->set('info', $admin['info']);
+
+ $html->pack('admins');
+ }
+
+ $sql->query('SELECT `id` FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'" ORDER BY `id` DESC LIMIT 1');
+ $max = $sql->get();
+
+ $html->get('admins', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('admins', isset($html->arr['admins']) ? $html->arr['admins'] : '');
+ $html->set('index', $max['id'] < 1 ? 0 : $max['id']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/settings/bans.php b/system/sections/control/servers/css/settings/bans.php
new file mode 100644
index 0000000..6320100
--- /dev/null
+++ b/system/sections/control/servers/css/settings/bans.php
@@ -0,0 +1,158 @@
+nav('Бан листы');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к файлам (banned_user.cfg / banned_ip.cfg)
+ $folder = '/servers/'.$server['uid'].'/cstrike';
+
+ // Если бан/разбан/проверка
+ if($go)
+ {
+ $aData = array();
+
+ $aData['value'] = isset($_POST['value']) ? trim($_POST['value']) : sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+ $aData['userid'] = isset($_POST['userid']) ? sys::int($_POST['userid']) : false;
+ $aData['amxbans'] = isset($_POST['amxbans']) ? true : false;
+
+ // Проверка входных данных
+ if(sys::valid($aData['value'], 'steamid') AND sys::valid($aData['value'], 'ip'))
+ sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+
+ // Если указан steamid
+ if(sys::valid($aData['value'], 'ip'))
+ {
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'] AND $aData['userid'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['userid']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"banid 0.0 ".$aData['value']." kick\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"banid 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_user.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_user.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_user.cfg | grep -v '.$aData['value'].' > temp_banned.cfg; echo "" >> temp_banned.cfg && cat temp_banned.cfg > banned_user.cfg; rm temp_banned.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeid ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeid\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный SteamID найден в файле banned_user.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный SteamID не найден в файле banned_user.cfg'), $nmch);
+ }
+ }else{
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['value']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"addip 0.0 ".$aData['value']." EGP\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"addip 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_ip.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_ip.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_ip.cfg | grep -v '.$aData['value'].' > temp_listip.cfg; echo "" >> temp_listip.cfg && cat temp_listip.cfg > banned_ip.cfg; rm temp_listip.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeip ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeip\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный IP найден в файле banned_ip.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный IP не найден в файле banned_ip.cfg'), $nmch);
+ }
+ }
+ }
+
+ // Содержимое banned_user.cfg
+ $ssh->set('cd '.$folder.' && cat banned_user.cfg | awk \'{print $3}\' | grep STEAM_');
+ $aBanned = explode("\n", $ssh->get());
+
+ // Содержимое banned_ip.cfg
+ $ssh->set('cd '.$folder.' && cat banned_ip.cfg | awk \'{print $3}\' | egrep "(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}"');
+ $aListip = explode("\n", $ssh->get());
+
+ if(isset($aBanned[count($aBanned)-1]) AND $aBanned[count($aBanned)-1] == '')
+ unset($aBanned[count($aBanned)-1]);
+
+ if(isset($aListip[count($aListip)-1]) AND $aListip[count($aListip)-1] == '')
+ unset($aListip[count($aListip)-1]);
+
+ // Построение списка забаненых по steamid
+ foreach($aBanned as $line => $steam)
+ {
+ $html->get('bans_list', 'sections/control/servers/games/settings');
+
+ $html->set('value', trim($steam));
+
+ $html->pack('banned');
+ }
+
+ // Построение списка забаненых по ip
+ foreach($aListip as $line => $ip)
+ {
+ $html->get('bans_list', 'sections/control/servers/games/settings');
+
+ $html->set('value', trim($ip));
+
+ $html->pack('listip');
+ }
+
+ $html->get('bans', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('banned', isset($html->arr['banned']) ? $html->arr['banned'] : '');
+ $html->set('listip', isset($html->arr['listip']) ? $html->arr['listip'] : '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/settings/debug.php b/system/sections/control/servers/css/settings/debug.php
new file mode 100644
index 0000000..f9217af
--- /dev/null
+++ b/system/sections/control/servers/css/settings/debug.php
@@ -0,0 +1,25 @@
+nav('Отладочный лог');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Чтение файла - oldstart.log
+ $file = '/servers/'.$server['uid'].'/debug.log';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep "CRASH: " | grep -ve "^#\|^[[:space:]]*$"');
+
+ $html->get('debug', 'sections/control/servers/games/settings');
+
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/settings/logs.php b/system/sections/control/servers/css/settings/logs.php
new file mode 100644
index 0000000..56b63ce
--- /dev/null
+++ b/system/sections/control/servers/css/settings/logs.php
@@ -0,0 +1,89 @@
+nav('Логи');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к логам
+ $folder = '/servers/'.$server['uid'].'/cstrike/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['cslogs']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/logs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+ $html->set('uri', 'logs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/logs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', end($name));
+ $html->set('uri', 'logs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/control/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('uri', '');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/settings/server.php b/system/sections/control/servers/css/settings/server.php
new file mode 100644
index 0000000..6ee8918
--- /dev/null
+++ b/system/sections/control/servers/css/settings/server.php
@@ -0,0 +1,116 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ $file = '/servers/'.$server['uid'].'/cstrike/cfg/server.cfg';
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ $config_end = $servercfg['\'other\''];
+
+ unset($servercfg['\'other\'']);
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).' "'.$val.'"'."\n";
+
+ // Временый файл
+ $temp = sys::temp($config.$config_end);
+
+ $ssh->setfile($temp, $file, 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$file);
+
+ unlink($temp);
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "exec server.cfg"\015\';');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+ $other = '';
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode(' ', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.' ', "", $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ else
+ $other .= $line."\n";
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/control/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $html->get('servercfg', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('cfg', $html->arr['list']);
+ $html->set('other', $other);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/settings/smlogs.php b/system/sections/control/servers/css/settings/smlogs.php
new file mode 100644
index 0000000..9b545aa
--- /dev/null
+++ b/system/sections/control/servers/css/settings/smlogs.php
@@ -0,0 +1,89 @@
+nav('Логи SourceMod');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к логам
+ $folder = '/servers/'.$server['uid'].'/cstrike/addons/sourcemod/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['csssmlogs']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/smlogs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'smlogs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/smlogs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', end($name));
+ $html->set('uri', 'smlogs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/control/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('uri', 'sm');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/css/settings/start.php b/system/sections/control/servers/css/settings/start.php
new file mode 100644
index 0000000..afc780f
--- /dev/null
+++ b/system/sections/control/servers/css/settings/start.php
@@ -0,0 +1,173 @@
+query('SELECT `uid`, `slots`, `map_start`, `vac`, `fastdl`, `autorestart`, `tickrate`, `core_fix` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'games/games.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($sid, $unit, '/servers/'.$server['uid'].'/cstrike/maps', $server['map_start'], false);
+
+ // Вывод списка потоков
+ if(isset($url['core']))
+ ctrl::cpulist($unit, $server['core_fix']);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($sid, $unit, '/servers/'.$server['uid'].'/cstrike/maps', $map, true, $nmch, true);
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'address':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => 'Необходимо выключить игровой сервер'), $nmch);
+
+ $address = isset($_POST['address']) ? trim($_POST['address']) : $server['address'];
+
+ if(sys::valid($address, 'other', $aValid['address']))
+ sys::outjs(array('e' => 'Адрес игрового сервера имеет неверный формат'), $nmch);
+
+ $sql->query('SELECT `id` FROM `control_servers` WHERE `unit`="'.$id.'" AND `address`="'.$address.'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный адрес занят другим сервером'), $nmch);
+
+ if($address != $server['address'])
+ $sql->query('UPDATE `control_servers` set `address`="'.$address.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `control_servers` set `vac`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'core_fix':
+ $n = ctrl::cpulist($unit, $server['core_fix'], true);
+
+ if($value > $n)
+ sys::outjs(array('e' => 'На физическом сервере нет такого ядра/потока'), $nmch);
+
+ if($value < 0)
+ $value = 0;
+
+ if($value != $server['core_fix'])
+ $sql->query('UPDATE `control_servers` set `core_fix`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > 64 ? 64 : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots'])
+ $sql->query('UPDATE `control_servers` set `slots`="'.$slots.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `control_servers` set `autorestart`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'tickrate':
+ if(in_array($value, array('66', '100')))
+ $sql->query('UPDATE `control_servers` set `tickrate`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.$unit['address'].':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/cstrike/cfg/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers /servers/'.$server['uid'].'/cstrike/cfg/fastdl.cfg;'
+ .'ln -s /servers/'.$server['uid'].'/cstrike /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' /servers/'.$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'echo "exec fastdl.cfg" >> /servers/'.$server['uid'].'/cstrike/cfg/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' /servers/'.$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'rm /servers/'.$server['uid'].'/cstrike/cfg/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `control_servers` set `fastdl`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= 64; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $tickrate = '';
+
+ foreach(array('66', '100') as $value)
+ $tickrate .= ''.$value.' TickRate ';
+
+ $core_fix = $server['core_fix'] ? '1 ядро/поток ' : 'Автоматическое определение ';
+
+ $html->get('start', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('map', $server['map_start']);
+ $html->set('address', $server['address']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('core_fix', $core_fix);
+ $html->set('slots', str_replace('"'.$server['slots'].'"', '"'.$server['slots'].'" selected="select"', $slots));
+ $html->set('tickrate', str_replace($server['tickrate'].'"', $server['tickrate'].'" selected="select"', $tickrate));
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/console.php b/system/sections/control/servers/cssold/console.php
new file mode 100644
index 0000000..0900605
--- /dev/null
+++ b/system/sections/control/servers/cssold/console.php
@@ -0,0 +1,67 @@
+query('SELECT `uid`, `time_start` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $command = isset($_POST['command']) ? sys::cmd($_POST['command']) : '';
+
+ if($server['status'] == 'off')
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('servers', 'off')));
+
+ sys::out(sys::text('servers', 'off'));
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ sys::out(sys::text('error', 'ssh'));
+ }
+
+ $dir = '/servers/'.$server['uid'].'/cstrike/';
+
+ $filecmd = $dir.'console.log';
+
+ if($command)
+ {
+ if(strtolower($command) == 'clear')
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"Очистка консоли\n\" > '.$filecmd.'"');
+ else
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.$command.'"\015\';'
+ .'sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/control/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/copy.php b/system/sections/control/servers/cssold/copy.php
new file mode 100644
index 0000000..1c41f2e
--- /dev/null
+++ b/system/sections/control/servers/cssold/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'control/servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('ctrl_server_copy_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_copy_'.$sid);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/control/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `control_copy` WHERE `user`="'.$ctrl['user'].'_'.$id.'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/control/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_copy_'.$sid, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/index.php b/system/sections/control/servers/cssold/index.php
new file mode 100644
index 0000000..bc4f041
--- /dev/null
+++ b/system/sections/control/servers/cssold/index.php
@@ -0,0 +1,30 @@
+query('SELECT `slots`, `online`, `players`, `name`, `pack`, `map` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address']);
+
+ $btn = sys::buttons($sid, $server['status'], $server['game'], $id);
+
+ $html->get('index', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img'));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/plugins.php b/system/sections/control/servers/cssold/plugins.php
new file mode 100644
index 0000000..2a96424
--- /dev/null
+++ b/system/sections/control/servers/cssold/plugins.php
@@ -0,0 +1,160 @@
+nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/plugins');
+
+ $nmch = sys::rep_act('ctrl_server_plugins_go_'.$sid, 10);
+
+ include(SEC.'control/servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('ctrl_server_plugins_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_plugins_'.$sid);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$sid.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/control/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `control_plugins_install` WHERE `server`="'.$sid.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/control/servers/games');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_plugins_'.$sid, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/rcon.php b/system/sections/control/servers/cssold/rcon.php
new file mode 100644
index 0000000..14ff694
--- /dev/null
+++ b/system/sections/control/servers/cssold/rcon.php
@@ -0,0 +1,57 @@
+ 'Необходимо выбрать игрока.'));
+
+ if($url['action'] == 'kick')
+ rcon::cmd(array_merge($server, array('id' => $id)), 'kickid "'.$player.'" "EGP Panel"');
+ else
+ rcon::cmd(array_merge($server, array('id' => $id)), 'sm_slay "'.$player.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ include(LIB.'geo.php');
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $aPlayers = rcon::players(rcon::cmd(array_merge($server, array('id' => $id))));
+
+ foreach($aPlayers as $i => $aPlayer)
+ {
+ $html->get('player', 'sections/control/servers/'.$server['game'].'/rcon');
+
+ $html->set('i', $i);
+ $html->set('userid', $aPlayer['userid']);
+ $html->set('name', $aPlayer['name']);
+ $html->set('steamid', $aPlayer['steamid']);
+ $html->set('time', $aPlayer['time']);
+ $html->set('ping', $aPlayer['ping']);
+ $html->set('ip', $aPlayer['ip']);
+ $html->set('ico', $aPlayer['ico']);
+ $html->set('country', $aPlayer['country']);
+
+ $html->pack('players');
+ }
+
+ sys::outjs(array('s' => isset($html->arr['players']) ? $html->arr['players'] : ''));
+ }
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('Rcon управление игроками');
+
+ $html->get('rcon', 'sections/control/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/settings.php b/system/sections/control/servers/cssold/settings.php
new file mode 100644
index 0000000..646d5c3
--- /dev/null
+++ b/system/sections/control/servers/cssold/settings.php
@@ -0,0 +1,63 @@
+query('SELECT `uid`, `pack` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+
+ $aSub = array('start', 'server', 'admins', 'bans', 'firewall', 'crontab', 'startlogs', 'debug', 'logs', 'smlogs', 'pack', 'file');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('ctrl_server_settings_go_'.$sid, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'control/servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'control/servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('ctrl_server_settings_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_settings_'.$sid);
+ else{
+ $aEditslist = 1;
+ $ctrlmod = true;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = $cfg['control_packs'][$server['game']];
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ include(SEC.'control/servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/control/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('packs', $packs);
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_settings_'.$sid, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/settings/admins.php b/system/sections/control/servers/cssold/settings/admins.php
new file mode 100644
index 0000000..4a85b53
--- /dev/null
+++ b/system/sections/control/servers/cssold/settings/admins.php
@@ -0,0 +1,112 @@
+nav('Управление администраторами');
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $aData = array();
+
+ $aData['active'] = isset($_POST['active']) ? $_POST['active'] : '';
+ $aData['value'] = isset($_POST['value']) ? $_POST['value'] : '';
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : '';
+ $aData['flags'] = isset($_POST['flags']) ? $_POST['flags'] : '';
+ $aData['immunity'] = isset($_POST['immunity']) ? sys::int($_POST['immunity']) : '';
+ $aData['time'] = isset($_POST['time']) ? $_POST['time'] : '';
+ $aData['info'] = isset($_POST['info']) ? $_POST['info'] : '';
+
+ // Удаление текущих записей
+ $sql->query('DELETE FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'"');
+
+ $usini = '';
+
+ foreach($aData['value'] as $index => $val)
+ {
+ if($val != '')
+ {
+ $aDate = isset($aData['time'][$index]) ? explode('.', $aData['time'][$index]) : explode('.', date('d.m.Y', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ $aDate = explode('.', date('d.m.Y', $start_point));
+
+ $time = mktime(0, 0, 0, $aDate[1], $aDate[0], $aDate[2]);
+
+ $aData['active'][$index] = isset($aData['active'][$index]) ? 1 : 0;
+ $aData['passwd'][$index] = isset($aData['passwd'][$index]) ? $aData['passwd'][$index] : '';
+ $aData['flags'][$index] = isset($aData['flags'][$index]) ? $aData['flags'][$index] : '';
+ $aData['info'][$index] = isset($aData['info'][$index]) ? $aData['info'][$index] : '';
+
+ $text = '"'.$val.'" "'.$aData['immunity'][$index].':'.$aData['flags'][$index].'" "'.$aData['passwd'][$index].'"';
+
+ $sql->query('INSERT INTO `control_admins_'.$server['game'].'` set'
+ .'`server`="'.$sid.'",'
+ .'`value`="'.htmlspecialchars($val).'",'
+ .'`active`="'.$aData['active'][$index].'",'
+ .'`passwd`="'.htmlspecialchars($aData['passwd'][$index]).'",'
+ .'`flags`="'.htmlspecialchars($aData['flags'][$index]).'",'
+ .'`immunity`="'.$aData['immunity'][$index].'",'
+ .'`time`="'.$time.'",'
+ .'`text`="'.htmlspecialchars($text).'",'
+ .'`info`="'.htmlspecialchars($aData['info'][$index]).'"');
+
+ if($aData['active'][$index])
+ $usini .= $text.PHP_EOL;
+ }
+ }
+
+ $temp = sys::temp($usini);
+
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/cstrike/addons/sourcemod/configs/admins_simple.ini', 0644);
+
+ unlink($temp);
+
+ $ssh->set('chown server'.$server['uid'].':servers /servers/'.$server['uid'].'/cstrike/addons/sourcemod/configs/admins_simple.ini');
+
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \" sm_reloadadmins\"\015'");
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Построение списка добавленных админов
+ $sql->query('SELECT `id`, `value`, `active`, `passwd`, `flags`, `immunity`, `time`, `info` FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'" ORDER BY `id` ASC');
+ while($admin = $sql->get())
+ {
+ $html->get('list', 'sections/control/servers/'.$server['game'].'/settings/admins');
+
+ if($admin['active'])
+ $html->unit('active', 1);
+ else
+ $html->unit('active');
+
+ $html->set('id', $admin['id']);
+ $html->set('value', $admin['value']);
+ $html->set('passwd', $admin['passwd']);
+ $html->set('flags', $admin['flags']);
+ $html->set('immunity', $admin['immunity']);
+ $html->set('time', date('d.m.y', $admin['time']));
+ $html->set('info', $admin['info']);
+
+ $html->pack('admins');
+ }
+
+ $sql->query('SELECT `id` FROM `control_admins_'.$server['game'].'` WHERE `server`="'.$sid.'" ORDER BY `id` DESC LIMIT 1');
+ $max = $sql->get();
+
+ $html->get('admins', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('admins', isset($html->arr['admins']) ? $html->arr['admins'] : '');
+ $html->set('index', $max['id'] < 1 ? 0 : $max['id']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/settings/bans.php b/system/sections/control/servers/cssold/settings/bans.php
new file mode 100644
index 0000000..6320100
--- /dev/null
+++ b/system/sections/control/servers/cssold/settings/bans.php
@@ -0,0 +1,158 @@
+nav('Бан листы');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к файлам (banned_user.cfg / banned_ip.cfg)
+ $folder = '/servers/'.$server['uid'].'/cstrike';
+
+ // Если бан/разбан/проверка
+ if($go)
+ {
+ $aData = array();
+
+ $aData['value'] = isset($_POST['value']) ? trim($_POST['value']) : sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+ $aData['userid'] = isset($_POST['userid']) ? sys::int($_POST['userid']) : false;
+ $aData['amxbans'] = isset($_POST['amxbans']) ? true : false;
+
+ // Проверка входных данных
+ if(sys::valid($aData['value'], 'steamid') AND sys::valid($aData['value'], 'ip'))
+ sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+
+ // Если указан steamid
+ if(sys::valid($aData['value'], 'ip'))
+ {
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'] AND $aData['userid'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['userid']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"banid 0.0 ".$aData['value']." kick\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"banid 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_user.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_user.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_user.cfg | grep -v '.$aData['value'].' > temp_banned.cfg; echo "" >> temp_banned.cfg && cat temp_banned.cfg > banned_user.cfg; rm temp_banned.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeid ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeid\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный SteamID найден в файле banned_user.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный SteamID не найден в файле banned_user.cfg'), $nmch);
+ }
+ }else{
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['value']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"addip 0.0 ".$aData['value']." EGP\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"addip 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_ip.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_ip.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_ip.cfg | grep -v '.$aData['value'].' > temp_listip.cfg; echo "" >> temp_listip.cfg && cat temp_listip.cfg > banned_ip.cfg; rm temp_listip.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeip ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeip\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный IP найден в файле banned_ip.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный IP не найден в файле banned_ip.cfg'), $nmch);
+ }
+ }
+ }
+
+ // Содержимое banned_user.cfg
+ $ssh->set('cd '.$folder.' && cat banned_user.cfg | awk \'{print $3}\' | grep STEAM_');
+ $aBanned = explode("\n", $ssh->get());
+
+ // Содержимое banned_ip.cfg
+ $ssh->set('cd '.$folder.' && cat banned_ip.cfg | awk \'{print $3}\' | egrep "(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}"');
+ $aListip = explode("\n", $ssh->get());
+
+ if(isset($aBanned[count($aBanned)-1]) AND $aBanned[count($aBanned)-1] == '')
+ unset($aBanned[count($aBanned)-1]);
+
+ if(isset($aListip[count($aListip)-1]) AND $aListip[count($aListip)-1] == '')
+ unset($aListip[count($aListip)-1]);
+
+ // Построение списка забаненых по steamid
+ foreach($aBanned as $line => $steam)
+ {
+ $html->get('bans_list', 'sections/control/servers/games/settings');
+
+ $html->set('value', trim($steam));
+
+ $html->pack('banned');
+ }
+
+ // Построение списка забаненых по ip
+ foreach($aListip as $line => $ip)
+ {
+ $html->get('bans_list', 'sections/control/servers/games/settings');
+
+ $html->set('value', trim($ip));
+
+ $html->pack('listip');
+ }
+
+ $html->get('bans', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('banned', isset($html->arr['banned']) ? $html->arr['banned'] : '');
+ $html->set('listip', isset($html->arr['listip']) ? $html->arr['listip'] : '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/settings/debug.php b/system/sections/control/servers/cssold/settings/debug.php
new file mode 100644
index 0000000..f9217af
--- /dev/null
+++ b/system/sections/control/servers/cssold/settings/debug.php
@@ -0,0 +1,25 @@
+nav('Отладочный лог');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Чтение файла - oldstart.log
+ $file = '/servers/'.$server['uid'].'/debug.log';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep "CRASH: " | grep -ve "^#\|^[[:space:]]*$"');
+
+ $html->get('debug', 'sections/control/servers/games/settings');
+
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/settings/logs.php b/system/sections/control/servers/cssold/settings/logs.php
new file mode 100644
index 0000000..56b63ce
--- /dev/null
+++ b/system/sections/control/servers/cssold/settings/logs.php
@@ -0,0 +1,89 @@
+nav('Логи');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к логам
+ $folder = '/servers/'.$server['uid'].'/cstrike/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['cslogs']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/logs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+ $html->set('uri', 'logs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/logs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', end($name));
+ $html->set('uri', 'logs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/control/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('uri', '');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/settings/server.php b/system/sections/control/servers/cssold/settings/server.php
new file mode 100644
index 0000000..6ee8918
--- /dev/null
+++ b/system/sections/control/servers/cssold/settings/server.php
@@ -0,0 +1,116 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ $file = '/servers/'.$server['uid'].'/cstrike/cfg/server.cfg';
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ $config_end = $servercfg['\'other\''];
+
+ unset($servercfg['\'other\'']);
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).' "'.$val.'"'."\n";
+
+ // Временый файл
+ $temp = sys::temp($config.$config_end);
+
+ $ssh->setfile($temp, $file, 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$file);
+
+ unlink($temp);
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "exec server.cfg"\015\';');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+ $other = '';
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode(' ', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.' ', "", $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ else
+ $other .= $line."\n";
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/control/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $html->get('servercfg', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('cfg', $html->arr['list']);
+ $html->set('other', $other);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/settings/smlogs.php b/system/sections/control/servers/cssold/settings/smlogs.php
new file mode 100644
index 0000000..9b545aa
--- /dev/null
+++ b/system/sections/control/servers/cssold/settings/smlogs.php
@@ -0,0 +1,89 @@
+nav('Логи SourceMod');
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к логам
+ $folder = '/servers/'.$server['uid'].'/cstrike/addons/sourcemod/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['csssmlogs']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/smlogs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'smlogs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/smlogs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/control/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', end($name));
+ $html->set('uri', 'smlogs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/control/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('uri', 'sm');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/settings/start.php b/system/sections/control/servers/cssold/settings/start.php
new file mode 100644
index 0000000..1d7e589
--- /dev/null
+++ b/system/sections/control/servers/cssold/settings/start.php
@@ -0,0 +1,186 @@
+query('SELECT `uid`, `slots`, `map_start`, `vac`, `fastdl`, `autorestart`, `fps`, `tickrate`, `core_fix` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'games/games.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($sid, $unit, '/servers/'.$server['uid'].'/cstrike/maps', $server['map_start'], false);
+
+ // Вывод списка потоков
+ if(isset($url['core']))
+ ctrl::cpulist($unit, $server['core_fix']);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($sid, $unit, '/servers/'.$server['uid'].'/cstrike/maps', $map, true, $nmch, true);
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'address':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => 'Необходимо выключить игровой сервер'), $nmch);
+
+ $address = isset($_POST['address']) ? trim($_POST['address']) : $server['address'];
+
+ if(sys::valid($address, 'other', $aValid['address']))
+ sys::outjs(array('e' => 'Адрес игрового сервера имеет неверный формат'), $nmch);
+
+ $sql->query('SELECT `id` FROM `control_servers` WHERE `unit`="'.$id.'" AND `address`="'.$address.'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный адрес занят другим сервером'), $nmch);
+
+ if($address != $server['address'])
+ $sql->query('UPDATE `control_servers` set `address`="'.$address.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `control_servers` set `vac`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'core_fix':
+ $n = ctrl::cpulist($unit, $server['core_fix'], true);
+
+ if($value > $n)
+ sys::outjs(array('e' => 'На физическом сервере нет такого ядра/потока'), $nmch);
+
+ if($value < 0)
+ $value = 0;
+
+ if($value != $server['core_fix'])
+ $sql->query('UPDATE `control_servers` set `core_fix`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > 64 ? 64 : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots'])
+ $sql->query('UPDATE `control_servers` set `slots`="'.$slots.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `control_servers` set `autorestart`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fps':
+ if(in_array($value, array('300', '500', '1100')))
+ $sql->query('UPDATE `control_servers` set `fps`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'tickrate':
+ if(in_array($value, array('33', '66', '100')))
+ $sql->query('UPDATE `control_servers` set `tickrate`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.$unit['address'].':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/cstrike/cfg/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers /servers/'.$server['uid'].'/cstrike/cfg/fastdl.cfg;'
+ .'ln -s /servers/'.$server['uid'].'/cstrike /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' /servers/'.$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'echo "exec fastdl.cfg" >> /servers/'.$server['uid'].'/cstrike/cfg/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' /servers/'.$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'rm /servers/'.$server['uid'].'/cstrike/cfg/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `control_servers` set `fastdl`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= 64; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $fps = '';
+
+ foreach(array('300', '500', '1100') as $value)
+ $fps .= ''.$value.' FPS ';
+
+ $tickrate = '';
+
+ foreach(array('33', '66', '100') as $value)
+ $tickrate .= ''.$value.' TickRate ';
+
+ $core_fix = $server['core_fix'] ? '1 ядро/поток ' : 'Автоматическое определение ';
+
+ $html->get('start', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('map', $server['map_start']);
+ $html->set('address', $server['address']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('core_fix', $core_fix);
+ $html->set('slots', str_replace('"'.$server['slots'].'"', '"'.$server['slots'].'" selected="select"', $slots));
+ $html->set('tickrate', str_replace($server['tickrate'].'"', $server['tickrate'].'" selected="select"', $tickrate));
+ $html->set('fps', str_replace($server['fps'].'"', $server['fps'].'" selected="select"', $fps));
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/cssold/start.php b/system/sections/control/servers/cssold/start.php
new file mode 100644
index 0000000..4e19134
--- /dev/null
+++ b/system/sections/control/servers/cssold/start.php
@@ -0,0 +1,179 @@
+query('SELECT `uid`, `slots`, `map_start`, `vac`, `fastdl`, `autorestart`, `fps`, `tickrate`, `core_fix` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'games/games.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($sid, $unit, '/servers/'.$server['uid'].'/cstrike/maps', $server['map_start'], false);
+
+ // Вывод списка потоков
+ if(isset($url['core']))
+ ctrl::cpulist($unit, $server['core_fix']);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($sid, $unit, '/servers/'.$server['uid'].'/cstrike/maps', $map, true, $nmch, true);
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'address':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => 'Необходимо выключить игровой сервер'), $nmch);
+
+ $address = isset($_POST['address']) ? trim($_POST['address']) : $server['address'];
+
+ if(sys::valid($address, 'other', $aValid['address']))
+ sys::outjs(array('e' => 'Адрес игрового сервера имеет неверный формат'), $nmch);
+
+ $sql->query('SELECT `id` FROM `control_servers` WHERE `unit`="'.$id.'" AND `address`="'.$address.'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный адрес занят другим сервером'), $nmch);
+
+ if($address != $server['address'])
+ $sql->query('UPDATE `control_servers` set `address`="'.$address.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `control_servers` set `vac`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'core_fix':
+ $n = ctrl::cpulist($unit, $server['core_fix'], true);
+
+ if($value > $n)
+ sys::outjs(array('e' => 'На физическом сервере нет такого ядра/потока'), $nmch);
+
+ if($value < 0)
+ $value = 0;
+
+ if($value != $server['core_fix'])
+ $sql->query('UPDATE `control_servers` set `core_fix`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > 64 ? 64 : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots'])
+ $sql->query('UPDATE `control_servers` set `slots`="'.$slots.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `control_servers` set `autorestart`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'tickrate':
+ if(in_array($value, array('66', '100')))
+ $sql->query('UPDATE `control_servers` set `tickrate`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.$unit['address'].':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, '/servers/'.$server['uid'].'/cstrike/cfg/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers /servers/'.$server['uid'].'/cstrike/cfg/fastdl.cfg;'
+ .'ln -s /servers/'.$server['uid'].'/cstrike /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' /servers/'.$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'echo "exec fastdl.cfg" >> /servers/'.$server['uid'].'/cstrike/cfg/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' /servers/'.$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'rm /servers/'.$server['uid'].'/cstrike/cfg/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `control_servers` set `fastdl`="'.$value.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_settings_'.$sid);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= 64; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $tickrate = '';
+
+ foreach(array('66', '100') as $value)
+ $tickrate .= ''.$value.' TickRate ';
+
+ $fps = '';
+
+ foreach(array('300', '500', '1100') as $value)
+ $fps .= ''.$value.' FPS ';
+
+ $core_fix = $server['core_fix'] ? '1 ядро/поток ' : 'Автоматическое определение ';
+
+ $html->get('start', 'sections/control/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('map', $server['map_start']);
+ $html->set('address', $server['address']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('core_fix', $core_fix);
+ $html->set('slots', str_replace('"'.$server['slots'].'"', '"'.$server['slots'].'" selected="select"', $slots));
+ $html->set('tickrate', str_replace($server['tickrate'].'"', $server['tickrate'].'" selected="select"', $tickrate));
+ $html->set('fps', str_replace($server['fps'].'"', $server['fps'].'" selected="select"', $fps));
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/filetp.php b/system/sections/control/servers/filetp.php
new file mode 100644
index 0000000..0c10129
--- /dev/null
+++ b/system/sections/control/servers/filetp.php
@@ -0,0 +1,204 @@
+query('SELECT `uid`, `address`, `game`, `status` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = $sql->get();
+
+ ctrl::nav($server, $id, $sid, 'filetp');
+
+ $frouter = explode('/', ctrl::route($server, 'filetp', $go));
+
+ if(end($frouter) == 'noaccess.php')
+ include(SEC.'control/servers/noaccess.php');
+ else{
+ $sql->query('SELECT `uid`, `ftp`, `ftp_passwd` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+ $ip = sys::first(explode(':', $unit['address']));
+
+ $html->nav('Список подключенных серверов', $cfg['http'].'control');
+ $html->nav('Список игровых серверов #'.$id, $cfg['http'].'control/id/'.$id);
+ $html->nav($server['address'], $cfg['http'].'control/id/'.$id.'/server/'.$sid);
+ $html->nav('FileTP');
+
+ // Путь для Proftpd
+ $homedir = '/servers/'.$server['uid'];
+
+ // Путь для файлового менеджера
+ $dir = '/';
+
+ $aData = array(
+ 'root' => $dir,
+ 'host' => $ip,
+ 'login' => $server['uid'],
+ 'passwd' => $server['ftp_passwd']
+ );
+
+ if($go)
+ {
+ if(isset($url['action']) AND in_array($url['action'], array('on', 'off', 'change', 'logs')))
+ {
+ $sql->query('SELECT `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = array_merge($unit, $sql->get());
+
+ include(LIB.'ssh.php');
+
+ // Проверка соединения с ssh сервером
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/filetp');
+ }else{
+ include(LIB.'ftp.php');
+
+ $ftp = new ftp;
+
+ // Проверка соединения с ftp сервером
+ if(!$ftp->auth($aData['host'], $aData['login'], $aData['passwd']))
+ {
+ if(isset($url['action']))
+ {
+ if($url['action'] == 'search')
+ sys::out('Не удалось соединиться с ftp-сервером.');
+
+ sys::outjs(array('e' => 'Не удалось соединиться с ftp-сервером.'));
+ }
+
+ sys::out();
+ }
+ }
+
+ // Выполнение операций
+ if(isset($url['action']))
+ switch($url['action'])
+ {
+ case 'on':
+ if($server['ftp'])
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/filetp');
+
+ $used = sys::int($ssh->get('cd /servers/'.$server['uid'].' && du -b | tail -1'));
+
+ if($used < 1)
+ sys::back($cfg['http'].'help/action/create');
+
+ $bytes = $server['hdd']*1048576;
+
+ $server['ftp_passwd'] = isset($server['ftp_passwd']{1}) ? $server['ftp_passwd'] : sys::passwd(8);
+
+ $qSql = 'DELETE FROM users WHERE username=\''.$server['uid'].'\';'
+ .'INSERT INTO users set username=\''.$server['uid'].'\', password=\''.$server['ftp_passwd'].'\', uid=\''.$server['uid'].'\', gid=\'1000\', homedir=\''.$homedir.'\', shell=\'/bin/false\';';
+
+ $ssh->set('screen -dmS ftp'.$server['uid'].' mysql -P '.$unit['sql_port'].' -u'.$unit['sql_login'].' -p'.$unit['sql_passwd'].' --database '.$unit['sql_ftp'].' -e "'.$qSql.'"');
+
+ $sql->query('UPDATE `control_servers` SET `ftp`="1", `ftp_passwd`="'.$server['ftp_passwd'].'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_filetp_'.$sid);
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/filetp');
+
+ case 'change':
+ if(!$server['ftp'])
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/filetp');
+
+ $passwd = sys::passwd(8);
+
+ $qSql = "UPDATE users set password='".$passwd."' WHERE username='".$server['uid']."' LIMIT 1";
+
+ $ssh->set('screen -dmS ftp'.$server['uid'].' mysql -P '.$unit['sql_port'].' -u'.$unit['sql_login'].' -p'.$unit['sql_passwd'].' --database '.$unit['sql_ftp'].' -e '.'"'.$qSql.'"');
+
+ $sql->query('UPDATE `control_servers` SET `ftp_passwd`="'.$passwd.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_filetp_'.$sid);
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/filetp');
+
+ case 'off':
+ if(!$server['ftp'])
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/filetp');
+
+ $qSql = 'DELETE FROM users WHERE username=\''.$server['uid'].'\';';
+
+ $ssh->set('screen -dmS ftp'.$server['uid'].' mysql -P '.$unit['sql_port'].' -u'.$unit['sql_login'].' -p'.$unit['sql_passwd'].' --database '.$unit['sql_ftp'].' -e "'.$qSql.'"');
+
+ $sql->query('UPDATE `control_servers` SET `ftp`="0" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ $mcache->delete('ctrl_server_filetp_'.$sid);
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/filetp');
+
+ case 'rename':
+ $ftp->rename(json_decode($_POST['path']), json_decode($_POST['name']), json_decode($_POST['newname']));
+
+ case 'edit':
+ $ftp->edit_file(json_decode($_POST['path']), json_decode($_POST['name']));
+
+ case 'create':
+ if(isset($url['folder']))
+ $ftp->mkdir(json_decode($_POST['path']), json_decode($_POST['name']));
+
+ $ftp->touch(json_decode($_POST['path']), json_decode($_POST['name']), json_decode($_POST['text']));
+
+ case 'delete':
+ if(isset($url['folder']))
+ $ftp->rmdir(json_decode($_POST['path']), json_decode($_POST['name']));
+
+ $ftp->rmfile(json_decode($_POST['path']).'/'.json_decode($_POST['name']));
+
+ case 'chmod':
+ $ftp->chmod(json_decode($_POST['path']), json_decode($_POST['name']), sys::int($_POST['chmod']));
+
+ case 'search':
+ $text = isset($_POST['find']) ? sys::first(explode('.', json_decode($_POST['find']))) : sys::out();
+
+ if(!isset($text{2}))
+ sys::out('Для выполнения поиска, необходимо больше данных');
+
+ $ftp->search($text, $id);
+
+ case 'logs':
+ $logs = $mcache->get('ctrl_filetp_logs_'.$sid);
+
+ if(!$logs)
+ {
+ include(LIB.'ftp.php');
+
+ $ftp = new ftp;
+
+ $logs = $ftp->logs($ssh->get('cat /var/log/proftpd/xferlog | grep "/'.$server['uid'].'/" | awk \'{print $2"\\\"$3"\\\"$4"\\\"$5"\\\"$7"\\\"$8"\\\"$9"\\\"$12}\' | tail -50'), $server['uid']);
+
+ $mcache->set('ctrl_filetp_logs_'.$sid, $logs, false, 300);
+ }
+
+ sys::out($logs);
+ }
+
+ if(!isset($_POST['path'])) $_POST['path'] = json_encode($aData['root']);
+
+ sys::out($ftp->view($ftp->read(json_decode($_POST['path'])), $sid));
+ }
+
+ if($mcache->get('ctrl_server_filetp_'.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_filetp_'.$sid);
+ else{
+ if($server['ftp'])
+ {
+ $html->get('filetp_on', 'sections/control/servers/games/filetp');
+
+ $html->set('address', 'ftp://'.$aData['login'].':'.$aData['passwd'].'@'.$aData['host']);
+ $html->set('server', $aData['host']);
+ $html->set('login', $aData['login']);
+ $html->set('passwd', $aData['passwd']);
+ $html->set('path', $aData['root']);
+ }else
+ $html->get('filetp_off', 'sections/control/servers/games/filetp');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_filetp_'.$sid, $html->arr['main'], false, 10);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/copy/check.php b/system/sections/control/servers/games/copy/check.php
new file mode 100644
index 0000000..60754ed
--- /dev/null
+++ b/system/sections/control/servers/games/copy/check.php
@@ -0,0 +1,26 @@
+get($nmch))
+ sys::outjs(array('e' => sys::text('other', 'mcache')));
+
+ $mcache->set($nmch, true, false, 10);
+
+ $copys = $sql->query('SELECT `id` FROM `control_copy` WHERE `user`="'.$ctrl['user'].'_'.$id.'" AND `status`="0"');
+ if(!$sql->num($copys))
+ sys::outjs(array('e' => 'no find'), $nmch);
+
+ while($copy = $sql->get($copys))
+ {
+ if(!sys::int($ssh->get('ps aux | grep copy_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `control_copy` set `status`="1" WHERE `id`="'.$copy['id'].'" LIMIT 1');
+ }
+
+ // Очистка кеша
+ $mcache->delete('ctrl_server_copy_'.$sid);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/copy/create.php b/system/sections/control/servers/games/copy/create.php
new file mode 100644
index 0000000..59e98e6
--- /dev/null
+++ b/system/sections/control/servers/games/copy/create.php
@@ -0,0 +1,54 @@
+query('SELECT `id` FROM `control_copy` WHERE `server`="'.$sid.'" ORDER BY `id` DESC LIMIT 5');
+ if($sql->num() > 4)
+ sys::outjs(array('e' => 'Для создания новой копии необходимо удалить старые.'), $nmch);
+
+ $sql->query('SELECT `id` FROM `control_copy` WHERE `server`="'.$sid.'" AND `status`="0" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Для создания новой копии дождитесь создания предыдущей.'), $nmch);
+
+ $aSel = array();
+
+ $aData = isset($_POST['copy']) ? $_POST['copy'] : sys::outjs(array('e' => 'Для создания копии необходимо выбрать директории/файлы.'), $nmch);
+
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ if(!isset($aData['\''.$name.'\'']))
+ continue;
+
+ $aSel[] = $name;
+ }
+
+ if(!count($aSel))
+ sys::outjs(array('e' => 'Для создания копии необходимо выбрать директории/файлы.'), $nmch);
+
+ $copy = '';
+ $info = '';
+ $plugins = '';
+
+ foreach($aSel as $name)
+ {
+ $copy .= isset(params::$section_copy[$server['game']]['aCopyDir'][$name]) ? params::$section_copy[$server['game']]['aCopyDir'][$name].' ' : '';
+ $copy .= isset(params::$section_copy[$server['game']]['aCopyFile'][$name]) ? params::$section_copy[$server['game']]['aCopyFile'][$name].' ' : '';
+
+ $info .= $name.', ';
+ }
+
+ $name_copy = md5($start_point.$id.$server['game']);
+
+ $ssh->set('cd /servers/'.$server['uid'].' && screen -dmS copy_'.$server['uid'].' sh -c "tar -cf '.$name_copy.'.tar '.$copy.'; mv '.$name_copy.'.tar /copy"');
+
+ $sql->query('SELECT `plugin`, `upd` FROM `control_plugins_install` WHERE `server`="'.$sid.'"');
+ while($plugin = $sql->get())
+ $plugins .= $plugin['plugin'].'.'.$plugin['upd'].',';
+
+ $sql->query('INSERT INTO `control_copy` set `user`="'.$ctrl['user'].'_'.$id.'", `game`="'.$server['game'].'", `server`="'.$sid.'", `pack`="'.$server['pack'].'", `name`="'.$name_copy.'", `info`="'.substr($info, 0, -2).'", `plugins`="'.substr($plugins, 0, -1).'", `date`="'.$start_point.'", `status`="0"');
+
+ // Очистка кеша
+ $mcache->delete('ctrl_server_copy_'.$sid);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/copy/fullcopy.php b/system/sections/control/servers/games/copy/fullcopy.php
new file mode 100644
index 0000000..e920730
--- /dev/null
+++ b/system/sections/control/servers/games/copy/fullcopy.php
@@ -0,0 +1,25 @@
+query('SELECT `id` FROM `control_copy` WHERE `server`="'.$sid.'" AND `info`="'.params::$section_copy[$server['game']]['CopyFull'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Для создания новой копии необходимо удалить старую.'), $nmch);
+
+ $name_copy = md5($id.$start_point.$sid.$server['game']);
+
+ $ssh->set('cd /servers/'.$server['uid'].' && screen -dmS copy_'.$server['uid'].' sh -c "tar -cf '.$name_copy.'.tar '.params::$section_copy[$server['game']]['CopyFull'].'; mv '.$name_copy.'.tar /copy"');
+
+ $plugins = '';
+
+ $sql->query('SELECT `plugin`, `upd` FROM `control_plugins_install` WHERE `server`="'.$sid.'"');
+ while($plugin = $sql->get())
+ $plugins .= $plugin['plugin'].'.'.$plugin['upd'].',';
+
+ $sql->query('INSERT INTO `control_copy` set `user`="'.$ctrl['user'].'_'.$id.'", `game`="'.$server['game'].'", `server`="'.$sid.'", `pack`="'.$server['pack'].'", `name`="'.$name_copy.'", `info`="'.params::$section_copy[$server['game']]['CopyFull'].'", `plugins`="'.substr($plugins, 0, -1).'", `date`="'.$start_point.'", `status`="0"');
+
+ // Очистка кеша
+ $mcache->delete('ctrl_server_copy_'.$sid);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/copy/recfull.php b/system/sections/control/servers/games/copy/recfull.php
new file mode 100644
index 0000000..3bb8e7d
--- /dev/null
+++ b/system/sections/control/servers/games/copy/recfull.php
@@ -0,0 +1,76 @@
+ 'Выбранная копия не найдена.'), $nmch);
+
+ $sql->query('SELECT `id`, `pack`, `name`, `info`, `plugins`, `date`, `status` FROM `control_copy` WHERE `id`="'.$cid.'" AND `user`="'.$ctrl['user'].'_'.$id.'" AND `game`="'.$server['game'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная копия не найдена.'), $nmch);
+
+ $copy = $sql->get();
+
+ if(!$copy['status'])
+ sys::outjs(array('e' => 'Дождитесь создания резервной копии.'), $nmch);
+
+ if($copy['pack'] != $server['pack'])
+ {
+ $aPack = $cfg['control_packs'][$server['game']];
+
+ sys::outjs(array('e' => 'Для восстановления необходимо установить сборку: '.$aPack[$copy['pack']].'.'), $nmch);
+ }
+
+ if(params::$section_copy[$server['game']]['CopyFull'] == $copy['info'])
+ $rm = 'rm -r '.$copy['info'];
+ else{
+ $rm = '';
+
+ $aInfo = explode(', ', $copy['info']);
+
+ foreach($aInfo as $name)
+ {
+ $rm .= isset(params::$section_copy[$server['game']]['aCopyDir'][$name]) ? 'rm -r '.params::$section_copy[$server['game']]['aCopyDir'][$name].' ' : '';
+ $rm .= isset(params::$section_copy[$server['game']]['aCopyFile'][$name]) ? 'rm '.params::$section_copy[$server['game']]['aCopyFile'][$name].' ' : '';
+ }
+
+ }
+
+ $ssh->set('cd /servers/'.$server['uid'].' && screen -dmS rec_'.$server['uid'].' sh -c "'
+ .$rm.'; cp /copy/'.$copy['name'].'.tar . && tar -xf '.$copy['name'].'.tar; rm '.$copy['name'].'.tar;'
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].';'
+ .'chown -R servers'.$server['uid'].':servers ."');
+
+ // Удаление плагинов
+ $sql->query('DELETE FROM `control_plugins_install` WHERE `server`="'.$sid.'"');
+
+ // Установка плагинов (имитирование)
+ $aPlugins = explode(',', $copy['plugins']);
+
+ foreach($aPlugins as $plugin)
+ {
+ $aPlugin = explode('.', $plugin);
+
+ if(!count($aPlugin != 2))
+ continue;
+
+ if(!$aPlugin[0])
+ continue;
+
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `plugin`="'.$aPlugin[0].'" AND `server`="'.$sid.'" LIMIT 1');
+
+ if(!$aPlugin[1])
+ $aPlugin[1] = 0;
+
+ if(!$sql->num())
+ $sql->query('INSERT INTO `control_plugins_install` set `server`="'.$sid.'", `plugin`="'.$aPlugin[0].'", `upd`="'.$aPlugin[1].'", `time`="'.$copy['date'].'"');
+ }
+
+ // Очистка кеша
+ $mcache->delete('ctrl_server_plugins_'.$sid);
+
+ $sql->query('UPDATE `control_servers` set `status`="recovery" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/copy/recpart.php b/system/sections/control/servers/games/copy/recpart.php
new file mode 100644
index 0000000..98d88f2
--- /dev/null
+++ b/system/sections/control/servers/games/copy/recpart.php
@@ -0,0 +1,50 @@
+ 'Выбранная копия не найдена.'), $nmch);
+
+ $sql->query('SELECT `id`, `pack`, `name`, `plugins`, `date`, `status` FROM `control_copy` WHERE `id`="'.$cid.'" AND `user`="'.$ctrl['user'].'_'.$id.'" AND `game`="'.$server['game'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная копия не найдена.'), $nmch);
+
+ $copy = $sql->get();
+
+ if(!$copy['status'])
+ sys::outjs(array('e' => 'Дождитесь создания резервной копии.'), $nmch);
+
+ if($copy['pack'] != $server['pack'])
+ {
+ $aPack = $cfg['control_packs'][$server['game']];
+
+ sys::outjs(array('e' => 'Для восстановления необходимо установить сборку: '.$aPack[$copy['pack']].'.'), $nmch);
+ }
+
+ $ssh->set('cd /servers/'.$server['uid'].' && screen -dmS rec_'.$server['uid'].' sh -c "'
+ .'cp /copy/'.$copy['name'].'.tar . && tar -xf '.$copy['name'].'.tar; rm '.$copy['name'].'.tar;'
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].';'
+ .'chown -R servers'.$server['uid'].':servers ."');
+
+ // Установка плагинов (имитирование)
+ $aPlugin = explode(',', $copy['plugins']);
+
+ foreach($aPlugin as $plugin)
+ {
+ if(!$plugin)
+ continue;
+
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `plugin`="'.$plugin.'" AND `server`="'.$sid.'" LIMIT 1');
+
+ if(!$sql->num())
+ $sql->query('INSERT INTO `control_plugins_install` set `server`="'.$sid.'", `plugin`="'.$plugin.'", `time`="'.$copy['date'].'"');
+ }
+
+ // Очистка кеша
+ $mcache->delete('ctrl_server_plugins_'.$sid);
+
+ $sql->query('UPDATE `control_servers` set `status`="recovery" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/copy/remove.php b/system/sections/control/servers/games/copy/remove.php
new file mode 100644
index 0000000..f876e81
--- /dev/null
+++ b/system/sections/control/servers/games/copy/remove.php
@@ -0,0 +1,24 @@
+ 'Выбранная копия не найдена.'), $nmch);
+
+ $sql->query('SELECT `name`, `status` FROM `control_copy` WHERE `id`="'.$cid.'" AND `user`="'.$ctrl['user'].'_'.$id.'" AND `game`="'.$server['game'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная копия не найдена.'), $nmch);
+
+ $copy = $sql->get();
+
+ if(!$copy['status'])
+ sys::outjs(array('e' => 'Дождитесь создания резервной копии.'), $nmch);
+
+ $ssh->set('screen -dmS rem_copy_'.$cid.' rm /copy/'.$copy['name'].'.tar');
+
+ $sql->query('DELETE FROM `control_copy` WHERE `id`="'.$cid.'" LIMIT 1');
+
+ // Очистка кеша
+ $mcache->delete('ctrl_server_copy_'.$sid);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/plugins/config.php b/system/sections/control/servers/games/plugins/config.php
new file mode 100644
index 0000000..1ccc740
--- /dev/null
+++ b/system/sections/control/servers/games/plugins/config.php
@@ -0,0 +1,83 @@
+query('SELECT `plugin`, `update`, `file` FROM `plugins_config` WHERE `id`="'.$fid.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $config = $sql->get();
+
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$config['plugin'].'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ // Если обновленный плагин
+ if($config['update'])
+ $sql->query('SELECT `name` FROM `plugins_update` WHERE `id`="'.$config['update'].'" LIMIT 1');
+ else
+ $sql->query('SELECT `name` FROM `plugins` WHERE `id`="'.$config['plugin'].'" LIMIT 1');
+
+ $plugin = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+ }
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Данные файла
+ $file = explode('/', $config['file']);
+
+ // Полный путь файла
+ $path = $tarif['install'].$server['uid'].'/'.$config['file'];
+
+ // Сохранение
+ if($go)
+ {
+ $data = isset($_POST['data']) ? $_POST['data'] : '';
+
+ $temp = sys::temp($data);
+
+ // Отправление файла на сервер
+ $ssh->setfile($temp, $path, 0644);
+
+ // Смена владельца/группы файла
+ $ssh->set('chown server'.$server['uid'].':servers '.$path);
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$path.'; cat '.$path.'"');
+
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+ $html->nav($plugin['name'], $cfg['http'].'servers/id/'.$id.'/section/plugins/subsection/plugin/plugin/'.$config['plugin']);
+
+ $html->get('config', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('file', $fid);
+ $html->set('plugin', $config['plugin']);
+ $html->set('name', end($file));
+ $html->set('data', htmlspecialchars($ssh->get()));
+
+ $html->pack('main');
+
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/plugins/delete.php b/system/sections/control/servers/games/plugins/delete.php
new file mode 100644
index 0000000..a8caba8
--- /dev/null
+++ b/system/sections/control/servers/games/plugins/delete.php
@@ -0,0 +1,84 @@
+query('SELECT `id`, `upd` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$pid.'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Данный плагин не установлен'));
+
+ $plugin = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ // Директория игр. сервера
+ $dir = '/servers/'.$server['uid'].'/';
+
+ // Имя исполняемого файла
+ if($plugin['upd'])
+ {
+ $qsql = 'WHERE `update`="'.$plugin['upd'].'" ORDER BY `id` ASC';
+ $frm = 'u'.$plugin['upd'];
+ }else{
+ $qsql = 'WHERE `plugin`="'.$pid.'" AND `update`="0" ORDER BY `id` ASC';
+ $frm = $pid;
+ }
+
+ // Удаление и установка файлов
+ $ssh->set('cd '.$dir.' && screen -dmS delete_upd_'.$start_point.' '
+ .'sudo -u server'.$server['uid'].' sh -c "'
+ .'wget --no-check-certificate '.$cfg['plugins'].'delete/'.$frm.'.rm && '
+ .'chmod 755 '.$frm.'.rm; ./'.$frm.'.rm; rm '.$frm.'.rm"');
+
+ include(LIB.'control/plugins.php');
+
+ // Удаление добавленного при установке текста в файлах
+ $sql->query('SELECT `text`, `file` FROM `plugins_write` '.$qsql);
+ while($clear = $sql->get())
+ plugins::clear($clear, $server['uid'], $dir);
+
+ unset($clear);
+
+ // Добавление текста при удалении в файлы
+ $sql->query('SELECT `text`, `file`, `top` FROM `plugins_write_del` '.$qsql);
+ while($write = $sql->get())
+ plugins::write($write, $server['uid'], $dir);
+
+ // Удаление записи установленного плагина в базе
+ $sql->query('DELETE FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$pid.'"');
+
+ // Очистка кеша
+ $mcache->delete('ctrl_server_plugins_'.$id);
+
+ if($plugin['upd'])
+ $sql->query('SELECT `install` FROM `plugins_delete_ins` WHERE `update`="'.$plugin['upd'].'" LIMIT 1');
+ else
+ $sql->query('SELECT `install` FROM `plugins_delete_ins` WHERE `plugin`="'.$pid.'" AND `update`="0" LIMIT 1');
+
+ if($sql->num())
+ {
+ $ins = $sql->get();
+
+ $sql->query('SELECT `name` FROM `plugins` WHERE `id`="'.$ins['install'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ sys::outjs(array('i' => $ins['install'], 'pname' => $plugin['name']), $nmch);
+ }
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/plugins/install.php b/system/sections/control/servers/games/plugins/install.php
new file mode 100644
index 0000000..f84784a
--- /dev/null
+++ b/system/sections/control/servers/games/plugins/install.php
@@ -0,0 +1,133 @@
+query('SELECT `name`, `cfg`, `upd`, `incompatible`, `required`, `packs`, `price` FROM `plugins` WHERE `id`="'.$pid.'" AND `game`="'.$server['game'].'" LIMIT 1');
+
+ if(!$sql->num())
+ exit;
+
+ $plugin = $sql->get();
+
+ // Проверка установки плагина
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$pid.'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный плагин уже установлен'));
+
+ $upd = false;
+
+ // Если есть более поздняя версия плагина
+ if($plugin['upd'])
+ {
+ $sql->query('SELECT `name`, `id`, `cfg`, `incompatible`, `required`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$pid.'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = array_merge($plugin, $sql->get());
+
+ $upd = true;
+ }
+ }
+
+ $buy = false;
+
+ // Если платный плагин
+ if($plugin['price'])
+ {
+ // Проверка покупки
+ $sql->query('SELECT `id` FROM `control_plugins_buy` WHERE `plugin`="'.$pid.'" AND `server`="'.$sid.'" LIMIT 1');
+ if($sql->num())
+ $buy = true;
+ else{
+ // Проверка баланса
+ if($user['balance'] < $plugin['price'])
+ sys::outjs(array('e' => 'У вас не хватает '.(round($plugin['price']-$user['balance'], 2)).' '.$cfg['currency']), $name_mcache);
+ }
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ exit;
+
+ include(LIB.'control/plugins.php');
+
+ // Проверка на наличие несовместимости с уже установленными плагинами
+ plugins::incompatible($sid, $plugin['incompatible'], $nmch);
+
+ // Проверка на наличие необходимых установленых плагинов для устанавливаемого дополнения
+ plugins::required($sid, $plugin['required'], $nmch);
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($upd)
+ {
+ $qsql = 'WHERE `update`="'.$plugin['id'].'" ORDER BY `id` ASC';
+ $fzip = 'u'.$plugin['id'];
+ $upd = $plugin['id'];
+ }else{
+ $qsql = 'WHERE `plugin`="'.$pid.'" AND `update`="0" ORDER BY `id` ASC';
+ $fzip = $pid;
+ $upd = 0;
+ }
+
+ // Директория игр. сервера
+ $dir = '/servers/'.$server['uid'].'/';
+
+ // Установка файлов на сервер
+ $ssh->set('cd '.$dir.' && screen -dmS install_'.$start_point.' sudo -u server'.$server['uid'].' sh -c "'
+ .'wget --no-check-certificate '.$cfg['plugins'].'install/'.$fzip.'.zip && unzip -o '.$fzip.'.zip; rm '.$fzip.'.zip;'
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].'"');
+
+ // Удаление файлов
+ $sql->query('SELECT `file` FROM `plugins_delete` '.$qsql);
+ while($delete = $sql->get())
+ $ssh->set('sudo -u server'.$server['uid'].' rm '.$dir.$delete['file']);
+
+ // Удаление текста из файлов
+ $sql->query('SELECT `text`, `file`, `regex` FROM `plugins_clear` '.$qsql);
+ while($clear = $sql->get())
+ plugins::clear($clear, $server['uid'], $dir);
+
+ // Добавление текста в файлы
+ $sql->query('SELECT `text`, `file`, `top` FROM `plugins_write` '.$qsql);
+ while($write = $sql->get())
+ plugins::write($write, $server['uid'], $dir);
+
+ // Если платный плагин
+ if(!$buy AND $plugin['price'])
+ {
+ $sql->query('UPDATE `users` set `balance`=`balance`-"'.$plugin['price'].'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `control_plugins_buy` set `plugin`="'.$pid.'", `key`="'.md5(strip_tags($plugin['name'])).'", `server`="'.$sid.'", `price`="'.$plugin['price'].'", `time`="'.$start_point.'"');
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'ctrl_buy_plugin'),
+ array('plugin' => strip_tags($plugin['name']), 'money' => $plugin['price'], 'id' => $sid)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$plugin['price'].'"');
+ }
+
+ // Запись данных в базу
+ $sql->query('INSERT INTO `control_plugins_install` set `server`="'.$sid.'", `plugin`="'.$pid.'", `upd`="'.$upd.'", `time`="'.$start_point.'"');
+
+ // Очистка кеша
+ $mcache->delete('ctrl_server_plugins_'.$sid);
+
+ if($plugin['cfg'])
+ sys::outjs(array('s' => 'cfg'), $nmch);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/plugins/plugin.php b/system/sections/control/servers/games/plugins/plugin.php
new file mode 100644
index 0000000..7ba4f96
--- /dev/null
+++ b/system/sections/control/servers/games/plugins/plugin.php
@@ -0,0 +1,89 @@
+query('SELECT `id`, `upd` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$pid.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/plugins');
+
+ $install = $sql->get();
+
+ // Если установленно обновление
+ if($install['upd'])
+ $sql->query('SELECT `name`, `info`, `images`, `upd` FROM `plugins_update` WHERE `id`="'.$install['upd'].'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `info`, `images`, `upd` FROM `plugins` WHERE `id`="'.$pid.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/plugins');
+
+ $plugin = $sql->get();
+
+ $html->nav('Плагины', $cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/plugins');
+ $html->nav($plugin['name']);
+
+ // Если есть кеш
+ if($mcache->get('ctrl_server_plugin_'.$pid.$sid) != '')
+ $html->arr['main'] = $mcache->get('ctrl_server_plugin_'.$pid.$sid);
+ else{
+ include(LIB.'control/plugins.php');
+
+ // Построение списка редактируемых файлов
+ $aConf = array();
+
+ $sql->query('SELECT `id`, `file` FROM `plugins_config` WHERE (`plugin`="'.$pid.'" AND `update`="0") OR (`plugin`="'.$pid.'" AND `update`="'.$install['upd'].'") ORDER BY `sort`, `id` ASC');
+ while($config = $sql->get())
+ {
+ // Исключить дублирование, путем проверки массива файлов
+ if(in_array($config['file'], $aConf))
+ continue;
+
+ $aConf[] = $config['file'];
+
+ // Данные файла
+ $file = explode('/', $config['file']);
+
+ $html->get('config_list', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('fid', $config['id']);
+ $html->set('name', end($file));
+ $html->set('file', $config['file']);
+
+ $html->pack('configs');
+ }
+
+ $images = plugins::images($plugin['images'], $pid);
+
+ $html->get('configs', 'sections/control/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', $plugin['name']);
+ $html->set('info', htmlspecialchars_decode($plugin['info']));
+
+ // Картинки
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ // Редактируемые файлы
+ if(isset($html->arr['configs']))
+ {
+ $html->set('configs', $html->arr['configs']);
+ $html->unit('configs', 1);
+ }else
+ $html->unit('configs');
+
+ $html->pack('main');
+
+ $mcache->set('ctrl_server_plugin_'.$pid.$sid, $html->arr['main'], false, 60);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/plugins/search.php b/system/sections/control/servers/games/plugins/search.php
new file mode 100644
index 0000000..bfe215b
--- /dev/null
+++ b/system/sections/control/servers/games/plugins/search.php
@@ -0,0 +1,201 @@
+ ''));
+
+ $mkey = md5($sid.$text.$id);
+
+ if($mcache->get($mkey) != '')
+ sys::outjs(array('s' => $mcache->get($mkey)));
+
+ if(!isset($text{2}))
+ sys::outjs(array('s' => 'Для выполнения поиска, необходимо больше данных', $nmch));
+
+ $sPlugins = array();
+ $sUpdate = array();
+
+ // Поиск по плагинам
+ $plugins = $sql->query('SELECT `id`, `packs` FROM `plugins` WHERE `game`="'.$server['game'].'" AND `name` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') OR `desc` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') LIMIT 5');
+
+ // Поиск по обновлениям
+ $update = false;
+
+ if(!$sql->num($plugins))
+ {
+ $plugins = $sql->query('SELECT `id`, `plugin`, `packs` FROM `plugins_update` WHERE `game`="'.$server['game'].'" AND (`name` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') OR `desc` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\')) AND `upd`="0" LIMIT 5');
+ $update = true;
+ }
+
+ // Если нет ниодного совпадения по вводимому тексту
+ if(!$sql->num($plugins))
+ {
+ // Поиск по словам
+ if(strpos($text, ' '))
+ {
+ // Массив слов
+ $aText = explode(' ', $text);
+
+ // Метка, которая изменится в процессе, если будет найдено хоть одно совпадение
+ $sWord = false;
+
+ foreach($aText as $word)
+ {
+ if($word == '' || !isset($word{2}))
+ continue;
+
+ // Поиск по плагинам
+ $plugins = $sql->query('SELECT `id`, `packs` FROM `plugins` WHERE `name` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') OR `desc` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') LIMIT 5');
+
+ // Поиск по обновлениям
+ $update = false;
+
+ if(!$sql->num($plugins))
+ {
+ $plugins = $sql->query('SELECT `id`, `plugin`, `packs` FROM `plugins_update` WHERE (`name` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') OR `desc` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\')) AND `upd`="0" LIMIT 5');
+ $update = true;
+ }
+
+ if($sql->num($plugins))
+ {
+ if(!$sWord) $sWord = true;
+
+ $sPlugins[] = $plugins;
+ $sUpdate[] = $update;
+ }
+ }
+
+ // Если нет ниодного совпадения
+ if(!$sWord)
+ {
+ $mcache->set($mkey, 'По вашему запросу ничего не найдено', false, 15);
+
+ sys::outjs(array('s' => 'По вашему запросу ничего не найдено'));
+ }
+ }else{
+ $mcache->set($mkey, 'По вашему запросу ничего не найдено', false, 15);
+
+ sys::outjs(array('s' => 'По вашему запросу ничего не найдено'));
+ }
+ }else{
+ $sPlugins[] = $plugins;
+ $sUpdate[] = $update;
+ }
+
+ // Массив для исклуючения дублирования
+ $aPlugins = array();
+
+ foreach($sPlugins as $index => $plugins)
+ {
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка дублирования
+ if(($sUpdate[$index] AND in_array($plugin['plugin'], $aPlugins)) || !$sUpdate[$index] AND in_array($plugin['id'], $aPlugins))
+ continue;
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':', $plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $install = false; // не установлен плагин
+ $upd = false; // не обновлен плагин
+
+ if($sUpdate[$index])
+ {
+ $sql->query('SELECT `id`, `upd`, `time` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $aPlugins[] = $plugin['plugin'];
+ }else{
+ $sql->query('SELECT `id`, `upd`, `time` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+
+ $aPlugins[] = $plugin['id'];
+ }
+
+ // Проверка на установку
+ if($sql->num())
+ {
+ $install = $sql->get();
+
+ $upd = $install['upd'];
+ $time = sys::today($install['time']);
+
+ $install = true;
+ }
+
+ // Если установлен обновленный плагин
+ if($upd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$upd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['id'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ $html->get('search', 'sections/control/servers/games/plugins');
+
+ // Если установлен
+ if($install)
+ {
+ // Если есть обновление
+ if($plugin['upd'] > $upd) $html->unit('update', 1); else $html->unit('update');
+
+ // Если есть редактируемые файлы
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ $html->unit('install', 1);
+ $html->unit('!install');
+ }else{
+ // Обновление данных на более позднею версию плагина
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" AND `upd`="0" LIMIT 1');
+ if($sql->num())
+ {
+ $upd = $sql->get();
+
+ $plugin['name'] = $upd['name'];
+ $plugin['desc'] = $upd['desc'];
+ $plugin['status'] = $upd['status'];
+ $plugin['cfg'] = $upd['cfg'];
+ }
+
+ $html->unit('install');
+ $html->unit('!install', 1);
+ }
+
+ if(!$plugin['status'])
+ {
+ $html->unit('unstable');
+ $html->unit('stable', 1);
+ $html->unit('testing');
+ }elseif($plugin['status'] == 2){
+ $html->unit('unstable');
+ $html->unit('stable');
+ $html->unit('testing', 1);
+ }else{
+ $html->unit('unstable', 1);
+ $html->unit('stable');
+ $html->unit('testing');
+ }
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('plugin', $plugin['id']);
+
+ if($install)
+ $html->set('time', $time);
+
+ $html->set('name', sys::find(htmlspecialchars_decode($plugin['name']), $text));
+ $html->set('desc', sys::find(htmlspecialchars_decode($plugin['desc']), $text));
+
+ $html->pack('plugins');
+ }
+ }
+
+ $html->arr['plugins'] = isset($html->arr['plugins']) ? $html->arr['plugins'] : '';
+
+ $mcache->set($mkey, $html->arr['plugins'], false, 15);
+
+ sys::outjs(array('s' => $html->arr['plugins']), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/plugins/update.php b/system/sections/control/servers/games/plugins/update.php
new file mode 100644
index 0000000..3d9fe1b
--- /dev/null
+++ b/system/sections/control/servers/games/plugins/update.php
@@ -0,0 +1,85 @@
+query('SELECT `id` FROM `plugins_update` WHERE `plugin`="'.$pid.'" ORDER BY `id` DESC LIMIT 1');
+
+ if(!$sql->num())
+ exit();
+
+ $plugin = $sql->get();
+
+ // Проверка установки плагина
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$pid.'" LIMIT 1');
+ if(!$sql->num())
+ exit();
+
+ // Проверка установки обновления плагина
+ $sql->query('SELECT `id` FROM `control_plugins_install` WHERE `server`="'.$sid.'" AND `plugin`="'.$pid.'" AND `upd`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный плагин уже обновлен'));
+
+ // Данные обновления
+ $sql->query('SELECT `id`, `cfg`, `incompatible`, `required` FROM `plugins_update` WHERE `id`="'.$plugin['id'].'" LIMIT 1');
+
+ $plugin = $sql->get();
+
+ include(LIB.'control/plugins.php');
+
+ // Проверка на наличие несовместимости с уже установленными плагинами
+ plugins::incompatible($sid, $plugin['incompatible'], $nmch);
+
+ // Проверка на наличие необходимых установленых плагинов для устанавливаемого плагина
+ plugins::required($sid, $plugin['required'], $nmch);
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ // Директория игр. сервера
+ $dir = '/servers/'.$server['uid'].'/';
+
+ // Установка файлов на сервер
+ $ssh->set('cd '.$dir.' && screen -dmS update_'.$start_point.' sudo -u server'.$server['uid'].' sh -c "wget --no-check-certificate '.$cfg['plugins'].'update/'.$plugin['id'].'.zip && unzip -o '.$plugin['id'].'.zip; rm '.$plugin['id'].'.zip"');
+
+ // Удаление файлов
+ $sql->query('SELECT `file` FROM `plugins_delete` WHERE `update`="'.$plugin['id'].'"');
+ while($delete = $sql->get())
+ $ssh->set('sudo -u server'.$server['uid'].' rm '.$dir.$delete['file']);
+
+ unset($delete);
+
+ // Удаление текста из файлов
+ $sql->query('SELECT `text`, `file`, `regex` FROM `plugins_clear` WHERE `update`="'.$plugin['id'].'"');
+ while($clear = $sql->get())
+ plugins::clear($clear, $server['uid'], $dir);
+
+ unset($clear);
+
+ // Добавление текста в файлы
+ $sql->query('SELECT `text`, `file`, `top` FROM `plugins_write` WHERE `update`="'.$plugin['id'].'" ORDER BY `id` ASC');
+ while($write = $sql->get())
+ plugins::write($write, $server['uid'], $dir);
+
+ // Обновление данных в базе
+ $sql->query('UPDATE `control_plugins_install` set `upd`="'.$plugin['id'].'", `time`="'.$start_point.'" WHERE `server`="'.$sid.'" AND `plugin`="'.$pid.'" LIMIT 1');
+
+ // Очистка кеша
+ $mcache->delete('server_plugins_'.$id);
+
+ if($plugin['cfg'])
+ sys::outjs(array('s' => 'cfg'), $nmch);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/settings/crontab.php b/system/sections/control/servers/games/settings/crontab.php
new file mode 100644
index 0000000..8d534ba
--- /dev/null
+++ b/system/sections/control/servers/games/settings/crontab.php
@@ -0,0 +1,109 @@
+nav('Планировщик задач');
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `panel` LIMIT 1');
+ $panel = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($panel['passwd'], $panel['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ // Удаление задания
+ if(isset($url['action']) AND $url['action'] == 'delete')
+ {
+ $task = isset($_POST['task']) ? sys::int($_POST['task']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ $sql->query('SELECT `cron` FROM `control_crontab` WHERE `id`="'.$task.'" AND `server`="'.$sid.'" LIMIT 1');
+ if(!$sql->num())
+ $sys->outjs(array('s' => 'ok'), $nmch);
+
+ $cron = $sql->get();
+
+ $ssh->set('touch /etc/crontab; cat /etc/crontab');
+ $crontab = str_replace($cron['cron'], '', $ssh->get());
+
+ // Временный файл
+ $temp = sys::temp($crontab);
+
+ $ssh->setfile($temp, '/etc/crontab', 0644);
+
+ $ssh->set("sed -i '/^$/d' /etc/crontab;"
+ .'crontab -u root /etc/crontab');
+
+ unlink($temp);
+
+ $sql->query('DELETE FROM `control_crontab` WHERE `id`="'.$task.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Добавление задания
+ $sql->query('SELECT `id` FROM `control_crontab` WHERE `server`="'.$sid.'" LIMIT 5');
+ if($sql->num() == $cfg['crontabs'])
+ sys::outjs(array('e' => sys::text('servers', 'crontab')), $nmch);
+
+ $data = array();
+
+ $data['task'] = isset($_POST['task']) ? $_POST['task'] : 'start';
+
+ $task = in_array($server['game'], array('samp', 'crmp')) ? array('start', 'restart', 'stop') : array('start', 'restart', 'stop', 'console');
+
+ if(!in_array($data['task'], $task))
+ $data['task'] = 'start';
+
+ $data['commands'] = isset($_POST['commands']) ? base64_encode(htmlspecialchars($_POST['commands'])) : '';
+ $data['allhour'] = isset($_POST['allhour']) ? true : false;
+ $data['hour'] = isset($_POST['hour']) ? $_POST['hour'] : '00';
+ $data['minute'] = isset($_POST['minute']) ? $_POST['minute'] : '00';
+ $data['week'] = (isset($_POST['week']) AND is_array($_POST['week'])) ? $_POST['week'] : array();
+
+ $sql->query('INSERT INTO `control_crontab` set `server`="'.$sid.'"');
+ $cid = $sql->id();
+
+ include(LIB.'games/games.php');
+
+ $cron_rule = ctrl::crontab($data, $sid, $cid);
+
+ $ssh->set('echo "'.$cron_rule.'" >> /etc/crontab;'
+ ."sed -i '/^$/d' /etc/crontab;"
+ .'crontab -u root /etc/crontab');
+
+ $time = games::crontab_time($data['allhour'], $data['hour'], $data['minute']);
+ $week = games::crontab_week($data['week']);
+
+ $sql->query('UPDATE `control_crontab` set `server`="'.$sid.'", `task`="'.$data['task'].'", `cron`="'.$cron_rule.'", `week`="'.$week.'", `time`="'.$time.'", `commands`="'.$data['commands'].'" WHERE `id`="'.$cid.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $aTask = array(
+ 'start' => 'Включение сервера',
+ 'stop' => 'Выключение сервера',
+ 'restart' => 'Перезагрузка сервера',
+ 'console' => 'Отправка команд на сервер'
+ );
+
+ $sql->query('SELECT `id`, `task`, `week`, `time` FROM `control_crontab` WHERE `server`="'.$sid.'" ORDER BY `id` ASC');
+ while($crontab = $sql->get())
+ {
+ $html->get('crontab_list', 'sections/control/servers/games/settings');
+ $html->set('id', $crontab['id']);
+ $html->set('task', $aTask[$crontab['task']]);
+ $html->set('week', $crontab['week']);
+ $html->set('time', $crontab['time']);
+ $html->pack('crontab');
+ }
+
+ $html->get('crontab', 'sections/control/servers/'.$server['game'].'/settings');
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('time', date('H:i:s', $start_point));
+ $html->set('crontab', isset($html->arr['crontab']) ? $html->arr['crontab'] : '');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/settings/file.php b/system/sections/control/servers/games/settings/file.php
new file mode 100644
index 0000000..30b7c1e
--- /dev/null
+++ b/system/sections/control/servers/games/settings/file.php
@@ -0,0 +1,59 @@
+nav('Редактирование файла: '.$file);
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+ }
+
+ // Полный путь файла
+ $path = '/servers/'.$server['uid'].'/'.$aEdits[$server['game']]['all']['path'][$file].$file;
+
+ if($go)
+ {
+ $data = isset($_POST['data']) ? $_POST['data'] : '';
+
+ $temp = sys::temp($data);
+
+ // Отправление файла на сервер
+ $ssh->setfile($temp, $path, 0644);
+
+ // Смена владельца/группы файла
+ $ssh->set('chown server'.$server['uid'].':servers '.$path);
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$path.'; cat '.$path.'"');
+
+ $html->get('file', 'sections/control/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('file', $file);
+ $html->set('data', htmlspecialchars($ssh->get()));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/settings/firewall.php b/system/sections/control/servers/games/settings/firewall.php
new file mode 100644
index 0000000..28da443
--- /dev/null
+++ b/system/sections/control/servers/games/settings/firewall.php
@@ -0,0 +1,39 @@
+nav('Блокировка на оборудовании');
+
+ if(isset($url['action']))
+ {
+ include(LIB.'games/games.php');
+
+ // Получение информации адреса
+ if($url['action'] == 'info')
+ games::iptables_whois($nmch);
+
+ // Добавление / удаление правил
+ if($go && in_array($url['action'], array('block', 'unblock')))
+ {
+ $address = isset($_POST['address']) ? trim($_POST['address']) : sys::outjs(array('e' => sys::text('servers', 'firewall')), $nmch);
+ $snw = isset($_POST['subnetwork']) ? true : false;
+
+ sys::outjs(ctrl::iptables($sid, $url['action'], $address, explode(':', $server['address']), $id, $snw), $nmch);
+ }
+ }
+
+ $sql->query('SELECT `id`, `sip` FROM `control_firewall` WHERE `server`="'.$sid.'" ORDER BY `id` ASC');
+ while($firewall = $sql->get())
+ {
+ $html->get('list', 'sections/control/servers/games/settings/firewall');
+ $html->set('id', $firewall['id']);
+ $html->set('address', $firewall['sip']);
+ $html->pack('firewall');
+ }
+
+ $html->get('firewall', 'sections/control/servers/games/settings');
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('firewall', isset($html->arr['firewall']) ? $html->arr['firewall'] : '');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/settings/pack.php b/system/sections/control/servers/games/settings/pack.php
new file mode 100644
index 0000000..a70c43d
--- /dev/null
+++ b/system/sections/control/servers/games/settings/pack.php
@@ -0,0 +1,19 @@
+ 'ok'));
+
+ // Проверка сборки
+ if(!array_key_exists($pack, $aPacks))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ $sql->query('UPDATE `control_servers` set `pack`="'.$pack.'" WHERE `id`="'.$sid.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), 'ctrl_server_settings_'.$sid);
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/games/settings/startlogs.php b/system/sections/control/servers/games/settings/startlogs.php
new file mode 100644
index 0000000..1ad5b61
--- /dev/null
+++ b/system/sections/control/servers/games/settings/startlogs.php
@@ -0,0 +1,93 @@
+nav('Снимки консоли');
+
+ $sql->query('SELECT `ftp`, `ftp_root`, `ftp_passwd` FROM `servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings');
+
+ // Путь к логам
+ $folder = '/servers/'.$server['uid'].'/'.$aSLdir[$server['game']];
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['startlogs']))
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/startlogs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/control/servers/games/settings/logs');
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+ $html->set('uri', 'startlogs');
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'control/id/'.$id.'/server/'.$sid.'/section/settings/subsection/startlogs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/control/servers/games/settings/startlogs');
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('name', end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ if($server['ftp'])
+ {
+ $html->unit('download', true, true);
+
+ $html->set('url', 'ftp://'.$server['uid'].':'.$server['ftp_passwd'].'@'.sys::first(explode(':', $unit['address'])).'/'.$aSLdir[$server['game']].'/'.end($name));
+ }else
+ $html->unit('download', false, true);
+ $html->pack('logs');
+ }
+
+ $html->get('startlogs', 'sections/control/servers/games/settings');
+ $html->set('id', $id);
+ $html->set('server', $sid);
+ $html->set('uri', 'start');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/index.php b/system/sections/control/servers/index.php
new file mode 100644
index 0000000..bdde65d
--- /dev/null
+++ b/system/sections/control/servers/index.php
@@ -0,0 +1,11 @@
+query('SELECT `address`, `game`, `status` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = $sql->get();
+
+ ctrl::nav($server, $id, $sid, 'index');
+
+ include(SEC.'control/servers/'.$server['game'].'/index.php');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/noaccess.php b/system/sections/control/servers/noaccess.php
new file mode 100644
index 0000000..30a9d9b
--- /dev/null
+++ b/system/sections/control/servers/noaccess.php
@@ -0,0 +1,17 @@
+nav('Раздел недоступен');
+
+ $status = array(
+ 'install' => 'установки',
+ 'reinstall' => 'переустановки',
+ 'update' => 'обновления',
+ 'recovery' => 'восстановления'
+ );
+
+ $html->get('noaccess');
+ $html->set('status', $status[$server['status']]);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/plugins.php b/system/sections/control/servers/plugins.php
new file mode 100644
index 0000000..803a21d
--- /dev/null
+++ b/system/sections/control/servers/plugins.php
@@ -0,0 +1,11 @@
+query('SELECT `uid`, `address`, `game`, `status` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = $sql->get();
+
+ ctrl::nav($server, $id, $sid, 'plugins');
+
+ include(ctrl::route($server, 'plugins', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/rcon.php b/system/sections/control/servers/rcon.php
new file mode 100644
index 0000000..b286ad8
--- /dev/null
+++ b/system/sections/control/servers/rcon.php
@@ -0,0 +1,11 @@
+query('SELECT `uid`, `unit`, `address`, `game`, `status` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = $sql->get();
+
+ ctrl::nav($server, $id, $sid, 'rcon');
+
+ include(ctrl::route($server, 'rcon', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/scan.php b/system/sections/control/servers/scan.php
new file mode 100644
index 0000000..d59fc6a
--- /dev/null
+++ b/system/sections/control/servers/scan.php
@@ -0,0 +1,27 @@
+query('SELECT `game` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = $sql->get();
+
+ include(LIB.'control/'.$server['game'].'/scan.php');
+
+ // Запрошена информация (статус, онлайн, название)
+ if(isset($url['mon']))
+ sys::outjs(scan::mon($sid));
+
+ // Запрошена информация (статус, онлайн, название, игроки)
+ if(isset($url['fmon']))
+ sys::outjs(scan::mon($sid, true));
+
+ // Запрошена информация (cpu, ram, hdd)
+ if(isset($url['resources']))
+ sys::outjs(scan::resources($sid));
+
+ // Запрошена информация (работает, меняется карта, переустанавливается)
+ if(isset($url['status']))
+ sys::outjs(scan::status($sid));
+
+ exit;
+?>
\ No newline at end of file
diff --git a/system/sections/control/servers/settings.php b/system/sections/control/servers/settings.php
new file mode 100644
index 0000000..8b77a3b
--- /dev/null
+++ b/system/sections/control/servers/settings.php
@@ -0,0 +1,11 @@
+query('SELECT `address`, `game`, `status`, `pack` FROM `control_servers` WHERE `id`="'.$sid.'" LIMIT 1');
+ $server = $sql->get();
+
+ ctrl::nav($server, $id, $sid, 'settings');
+
+ include(ctrl::route($server, 'settings', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/control/settings.php b/system/sections/control/settings.php
new file mode 100644
index 0000000..318b8bb
--- /dev/null
+++ b/system/sections/control/settings.php
@@ -0,0 +1,56 @@
+nav('Список подключенных серверов', $cfg['http'].'control');
+
+ if(in_array($ctrl['status'], array('install', 'overdue', 'blocked')))
+ include(SEC.'control/noaccess.php');
+ else{
+ $sql->query('SELECT `address`, `passwd`, `fcpu`, `ram`, `hdd` FROM `control` WHERE `id`="'.$id.'" LIMIT 1');
+ $ctrl = $sql->get();
+
+ if($go)
+ {
+ $aData = array();
+
+ $aData['passwd'] = isset($_POST['passwd']) ? trim($_POST['passwd']) : $ctrl['passwd'];
+ $aData['fcpu'] = isset($_POST['fcpu']) ? sys::int($_POST['fcpu']) : $ctrl['fcpu'];
+ $aData['ram'] = isset($_POST['ram']) ? sys::int($_POST['ram']) : $ctrl['ram'];
+ $aData['hdd'] = isset($_POST['hdd']) ? sys::int($_POST['hdd']) : $ctrl['hdd'];
+
+ include(LIB.'ssh.php');
+
+ if(sys::strlen($aData['passwd']) > 32)
+ sys::outjs(array('e' => 'Указанный пароль слишком длинный'));
+
+ if(sys::valid($aData['passwd'], 'other', $aValid['passwd']))
+ sys::outjs(array('e' => 'Пожалуйста, поменяйте пароль используя только латинские буквы и цифры'));
+
+ if(!$ssh->auth($aData['passwd'], $ctrl['address']))
+ sys::outjs(array('e' => 'Неудалось создать связь с физическим сервером'));
+
+ $aData['fcpu'] = $aData['fcpu'] == 1 ? 1 : 0;
+
+ if($aData['ram'] < 1 || $aData['ram'] > 9999999)
+ $aData['ram'] = 0;
+
+ if($aData['hdd'] < 1 || $aData['hdd'] > 9999999)
+ $aData['hdd'] = 0;
+
+ $sql->query('UPDATE `control` set `passwd`="'.$aData['passwd'].'", `fcpu`="'.$aData['fcpu'].'", `ram`="'.$aData['ram'].'", `hdd`="'.$aData['hdd'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $html->nav('Параметры сервера #'.$id);
+
+ $html->get('settings', 'sections/control');
+ $html->set('id', $id);
+ $html->set('passwd', $ctrl['passwd']);
+ $html->set('ram', $ctrl['ram']);
+ $html->set('hdd', $ctrl['hdd']);
+ $html->set('fcpu', $ctrl['fcpu'] ? 'Активный Пассивный ' : 'Пассивный Активный ');
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/help/action/close.php b/system/sections/help/action/close.php
new file mode 100644
index 0000000..b450420
--- /dev/null
+++ b/system/sections/help/action/close.php
@@ -0,0 +1,19 @@
+ 'У вас нет доступа к данному действию.'));
+
+ if($id)
+ {
+ if(in_array($user['group'], array('admin', 'support')))
+ $sql->query('UPDATE `help` set `close`="1" WHERE `id`="'.$id.'" LIMIT 1');
+ else
+ $sql->query('UPDATE `help` set `close`="1" WHERE `id`="'.$id.'" AND `user`="'.$user['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ sys::outjs(array('e' => 'Вопрос не найден в базе.'));
+?>
\ No newline at end of file
diff --git a/system/sections/help/action/delete.php b/system/sections/help/action/delete.php
new file mode 100644
index 0000000..849a1da
--- /dev/null
+++ b/system/sections/help/action/delete.php
@@ -0,0 +1,31 @@
+ 'У вас нет доступа к данному действию.'));
+
+ if($id)
+ {
+ $sql->query('DELETE FROM `help` WHERE `id`="'.$id.'" LIMIT 1');
+
+ $dialogs = $sql->query('SELECT `id`, `img` FROM `help_dialogs` WHERE `help`="'.$id.'"');
+ while($dialog = $sql->get($dialogs))
+ {
+ $aImg = sys::b64djs($dialog['img']);
+
+ foreach($aImg as $img)
+ {
+ $sql->query('DELETE FROM `help_upload` WHERE `name`="'.$img.'" LIMIT 1');
+
+ unlink(ROOT.'upload/'.$img);
+ }
+
+ $sql->query('DELETE FROM `help_dialogs` WHERE `id`="'.$dialog['id'].'" LIMIT 1');
+ }
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ sys::outjs(array('e' => 'Вопрос не найден в базе.'));
+?>
\ No newline at end of file
diff --git a/system/sections/help/action/open.php b/system/sections/help/action/open.php
new file mode 100644
index 0000000..aba7e2d
--- /dev/null
+++ b/system/sections/help/action/open.php
@@ -0,0 +1,19 @@
+ 'У вас нет доступа к данному действию.'));
+
+ if($id)
+ {
+ if(in_array($user['group'], array('admin', 'support')))
+ $sql->query('UPDATE `help` set `close`="0", `time`="'.$start_point.'" WHERE `id`="'.$id.'" LIMIT 1');
+ else
+ $sql->query('UPDATE `help` set `close`="0", `time`="'.$start_point.'" WHERE `id`="'.$id.'" AND `user`="'.$user['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ sys::outjs(array('e' => 'Вопрос не найден в базе.'));
+?>
\ No newline at end of file
diff --git a/system/sections/help/action/read.php b/system/sections/help/action/read.php
new file mode 100644
index 0000000..a764380
--- /dev/null
+++ b/system/sections/help/action/read.php
@@ -0,0 +1,49 @@
+ 'Администратор',
+ 'support' => 'Техническая поддержка',
+ 'user' => 'Клиент'
+ );
+
+ if($id)
+ {
+ $nmch = 'read_help_'.$id;
+
+ $cache = $mcache->get($nmch);
+
+ // Если кеш создан
+ if($cache)
+ {
+ $cache[$user['id']] = $user['group'].'|'.$start_point;
+
+ $mcache->replace($nmch, $cache, false, 10);
+ }else
+ $mcache->set($nmch, array($user['id'] => $user['group'].'|'.$start_point), false, 10);
+
+ if($user['group'] == 'user')
+ sys::out('У вас нет доступа к данной информации.');
+
+ // Обработка кеша
+ $cache = $mcache->get($nmch);
+
+ $read_now = '';
+
+ foreach($cache as $reader => $data)
+ {
+ list($group, $time) = explode('|', $data);
+
+ if($time+9 > $start_point)
+ $read_now .= '#'.$reader.' ('.$aGroup[$group].') , ';
+ }
+
+ if(isset($read_now{1}))
+ $read_now = substr($read_now, 0, -2);
+
+ sys::out($read_now);
+ }
+
+ sys::out('Необходимо передать номер вопроса.');
+?>
\ No newline at end of file
diff --git a/system/sections/help/action/remove.php b/system/sections/help/action/remove.php
new file mode 100644
index 0000000..5d826dc
--- /dev/null
+++ b/system/sections/help/action/remove.php
@@ -0,0 +1,33 @@
+ 'У вас нет доступа к данному действию.'));
+
+ if($id)
+ {
+ $msg = isset($url['msg']) ? sys::int($url['msg']) : sys::outjs(array('s' => 'ok'));
+
+ $sql->query('SELECT `img` FROM `help_dialogs` WHERE `id`="'.$msg.'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('s' => 'ok'));
+
+ $images = $sql->get();
+
+ $aImg = sys::b64djs($images['img']);
+
+ foreach($aImg as $img)
+ {
+ $sql->query('DELETE FROM `help_upload` WHERE `name`="'.$img.'" LIMIT 1');
+
+ unlink(ROOT.'upload/'.$img);
+ }
+
+ $sql->query('DELETE FROM `help_dialogs` WHERE `id`="'.$msg.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ sys::outjs(array('e' => 'Вопрос не найден в базе.'));
+?>
\ No newline at end of file
diff --git a/system/sections/help/action/reply.php b/system/sections/help/action/reply.php
new file mode 100644
index 0000000..3993b04
--- /dev/null
+++ b/system/sections/help/action/reply.php
@@ -0,0 +1,112 @@
+get($nmch))
+ sys::outjs(array('e' => sys::text('other', 'mcache')), $nmch);
+
+ // Создание сессии
+ $mcache->set($nmch, 1, false, 10);
+
+ if($id)
+ {
+ if(in_array($user['group'], array('admin', 'support')))
+ $sql->query('SELECT `user` FROM `help` WHERE `id`="'.$id.'" AND `close`="0" LIMIT 1');
+ else
+ $sql->query('SELECT `user` FROM `help` WHERE `id`="'.$id.'" AND `close`="0" AND `user`="'.$user['id'].'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('с' => 'Вопрос не открыт чтобы вести диалог.'), $nmch);
+
+ $help = $sql->get();
+ }else
+ sys::outjs(array('e' => 'Вопрос не найден в базе.'), $nmch);
+
+ $aData = array();
+
+ $aData['text'] = isset($_POST['text']) ? $_POST['text'] : sys::outjs(array('e' => 'Сообщение не найдено.'), $nmch);
+ $aData['images'] = isset($_POST['img']) ? $_POST['img'] : array();
+
+ $aData['img'] = array();
+
+ // Проверка сообщения
+ if(iconv_strlen($aData['text'], 'UTF-8') < 2 || iconv_strlen(str_replace(array(' ', "\t", "\n"),'', $aData['text']), 'UTF-8') > 1000)
+ sys::outjs(array('e' => 'Длина сообщения не должна быть менее 2 и не превышать 1000 символов.'), $nmch);
+
+ include(LIB.'help.php');
+
+ // Обработка сообщения
+ $aData['text'] = help::text($aData['text']);
+
+ // Проверка изображений
+ if(is_array($aData['images']) AND count($aData['images']))
+ {
+ foreach($aData['images'] as $img)
+ {
+ $key = explode('.', $img);
+
+ if(!is_array($key) || sys::valid($key[0], 'md5') || !in_array($key[1], array('png', 'gif', 'jpg', 'bmp')))
+ continue;
+
+ $sql->query('SELECT `id` FROM `help_upload` WHERE `name`="'.$img.'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $image = $sql->get();
+
+ $sql->query('UPDATE `help_upload` set `status`="1" WHERE `id`="'.$image['id'].'" LIMIT 1');
+
+ $aData['img'][] = $img;
+ }
+ }
+
+ // Система контроля спама
+ if($user['group'] == 'user')
+ {
+ $i = 0;
+ $n = 3;
+ $sql->query('SELECT `user` FROM `help_dialogs` WHERE `help`="'.$id.'" ORDER BY `id` DESC LIMIT 3');
+ while($msg = $sql->get())
+ {
+ if(!$i AND !$msg['user'])
+ sys::outjs(array('i' => 'Пожалуйста, дождитесь ответа технической поддержки.'), $nmch);
+
+ $i+=1;
+
+ if($msg['user'] == $help['user'])
+ $n-=1;
+ }
+
+ if(!$n)
+ {
+ $sql->query('INSERT INTO `help_dialogs` set `help`="'.$id.'", `user`="0", `text`="Пожалуйста, дождитесь ответа технической поддержки.", `img`="", `time`="'.$start_point.'"');
+
+ sys::outjs(array('i' => 'Пожалуйста, дождитесь ответа технической поддержки.'), $nmch);
+ }
+ }
+
+ $sql->query('SELECT `text` FROM `help_dialogs` WHERE `help`="'.$id.'" ORDER BY `id` DESC LIMIT 1');
+ $msg = $sql->get();
+
+ if(md5($msg['text']) == md5($aData['text']))
+ sys::outjs(array('e' => 'Такое сообщение уже отправлено.'), $nmch);
+
+ $sql->query('INSERT INTO `help_dialogs` set '
+ .'`help`="'.$id.'",'
+ .'`user`="'.$user['id'].'",'
+ .'`text`="'.$aData['text'].'",'
+ .'`img`="'.sys::b64js($aData['img']).'",'
+ .'`time`="'.$start_point.'"');
+
+ if($user['group'] != 'user')
+ $sql->query('UPDATE `help` set `status`="0" WHERE `id`="'.$id.'" LIMIT 1');
+ else{
+ $sql->query('UPDATE `help` set `status`="1" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('UPDATE `help` set `notice_admin`="2" WHERE `id`="'.$id.'" LIMIT 1');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/help/action/write.php b/system/sections/help/action/write.php
new file mode 100644
index 0000000..7d1a946
--- /dev/null
+++ b/system/sections/help/action/write.php
@@ -0,0 +1,57 @@
+ 'Администратор',
+ 'support' => 'Техническая поддержка',
+ 'user' => 'Клиент'
+ );
+
+ $write_st = isset($url['write']) ? true : false;
+
+ if($id)
+ {
+ $nmch = 'write_help_'.$id;
+
+ $cache = $mcache->get($nmch);
+
+ // Если кеш создан
+ if($cache)
+ {
+ if($write_st)
+ $cache[$user['id']] = $user['group'].'|'.$start_point;
+ else
+ unset($cache[$user['id']]);
+
+ $mcache->replace($nmch, $cache, false, 10);
+ }else{
+ if($write_st)
+ $mcache->set($nmch, array($user['id'] => $user['group'].'|'.$start_point), false, 10);
+ }
+
+ if($user['group'] == 'user')
+ sys::out('У вас нет доступа к данной информации.');
+
+ // Обработка кеша
+ $cache = $mcache->get($nmch);
+
+ $write_now = '';
+
+ if(is_array($cache))
+ foreach($cache as $writer => $data)
+ {
+ list($group, $time) = explode('|', $data);
+
+ if($time+9 > $start_point)
+ $write_now .= '#'.$writer.' ('.$aGroup[$group].') , ';
+ }
+
+ if(isset($write_now{1}))
+ $write_now = substr($write_now, 0, -2);
+
+ sys::out($write_now);
+ }
+
+ sys::out('Необходимо передать номер вопроса.');
+?>
\ No newline at end of file
diff --git a/system/sections/help/close.php b/system/sections/help/close.php
new file mode 100644
index 0000000..5bc1b24
--- /dev/null
+++ b/system/sections/help/close.php
@@ -0,0 +1,103 @@
+query('SELECT `id`, `user`, `type`, `service`, `date`, `time` FROM `help` WHERE `close`="1"');
+ else
+ $sql->query('SELECT `id`, `type`, `service`, `date`, `time` FROM `help` WHERE `user`="'.$user['id'].'" AND `close`="1"');
+
+ $aPage = sys::page($page, $sql->num(), 20);
+
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'help/section/close');
+
+ if(in_array($user['group'], array('admin', 'support')))
+ $helps = $sql->query('SELECT `id`, `user`, `type`, `service`, `date`, `time` FROM `help` WHERE `close`="1" ORDER BY `id` DESC LIMIT '.$aPage['num'].', 20');
+ else
+ $helps = $sql->query('SELECT `id`, `type`, `service`, `date`, `time` FROM `help` WHERE `user`="'.$user['id'].'" AND `close`="1" ORDER BY `id` DESC LIMIT '.$aPage['num'].', 20');
+
+ // Массив пользователей
+ $uArr = array();
+
+ while($help = $sql->get($helps))
+ {
+ // Создатель вопроса
+ if(in_array($user['group'], array('admin', 'support')) AND !isset($uArr[$help['user']]))
+ {
+ $sql->query('SELECT `login` FROM `users` WHERE `id`="'.$help['user'].'" LIMIT 1');
+
+ if(!$sql->num())
+ $uArr[$help['user']] = 'Пользователь удален';
+ else{
+ $us = $sql->get();
+ $uArr[$help['user']] = $us['login'];
+ }
+ }
+
+ // Краткая информация вопроса
+ switch($help['type'])
+ {
+ case 'server':
+ $sql->query('SELECT `address` FROM `servers` WHERE `id`="'.$help['service'].'" LIMIT 1');
+ if(!$sql->num())
+ $name = 'Игровой сервер: #'.$help['service'].' (не найден)';
+ else{
+ $ser = $sql->get();
+ $name = 'Игровой сервер: #'.$help['service'].' '.$ser['address'];
+ }
+
+ break;
+
+ case 'hosting':
+ $name = 'Виртуальных хостинг: #'.$help['service'];
+
+ break;
+
+ default:
+ $name = 'Вопрос без определенной услуги';
+ }
+
+ $html->get('question', 'sections/help/close');
+
+ $html->set('id', $help['id']);
+
+ if(array_key_exists('user', $help))
+ {
+ $html->set('uid', $help['user']);
+ $html->set('login', $uArr[$help['user']]);
+ }
+
+ $html->set('name', $name);
+ $html->set('status', 'Вопрос решен');
+ $html->set('date', sys::today($help['date']));
+ $html->set('time', sys::today($help['time']));
+
+ $html->pack('question');
+ }
+
+ $html->get('close', 'sections/help');
+
+ $html->set('question', isset($html->arr['question']) ? $html->arr['question'] : '');
+
+ $html->set('pages', isset($html->arr['pages']) ? $html->arr['pages'] : '');
+
+ $html->pack('main');
+
+ if(!in_array($user['group'], array('admin', 'support')))
+ {
+ $html->unitall('main', 'user', 1);
+ $html->unitall('main', 'support');
+ }else{
+ $html->unitall('main', 'user');
+ $html->unitall('main', 'support', 1);
+ }
+
+ if($user['group'] == 'admin')
+ $html->unitall('main', 'admin', 1);
+ else
+ $html->unitall('main', 'admin');
+?>
\ No newline at end of file
diff --git a/system/sections/help/create.php b/system/sections/help/create.php
new file mode 100644
index 0000000..d99a172
--- /dev/null
+++ b/system/sections/help/create.php
@@ -0,0 +1,140 @@
+get($nmch))
+ sys::outjs(array('e' => $text['mcache']), $nmch);
+
+ // Создание сессии
+ $mcache->set($nmch, 1, false, 10);
+
+ $aData = array();
+
+ $aData['service'] = isset($_POST['service']) ? explode('_', $_POST['service']) : exit();
+ $aData['title'] = isset($_POST['title']) ? strip_tags(trim($_POST['title'])) : '';
+ $aData['text'] = isset($_POST['text']) ? $_POST['text'] : exit();
+ $aData['images'] = isset($_POST['img']) ? $_POST['img'] : array();
+
+ $aData['img'] = array();
+
+ /*
+ Проверка входных данных
+ */
+
+ // Проверка услуги
+ if(count($aData['service']) != 2)
+ {
+ if($aData['service'][0] != 'none')
+ sys::outjs(array('e' => 'Необходимо выбрать услугу связанную с вопросом.'), $nmch);
+
+ $aData['type'] = 'none';
+ $aData['service'] = 0;
+ }else{
+ if(!in_array($aData['service'][0], array('server', 'hosting')))
+ sys::outjs(array('e' => 'Необходимо выбрать услугу связанную с вопросом.'), $nmch);
+
+ $aData['type'] = $aData['service'][0];
+ $aData['service'] = sys::int($aData['service'][1]);
+
+ switch($aData['type'])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$aData['service'].'" AND `user`="'.$user['id'].'" LIMIT 1');
+ break;
+
+ case 'hosting':
+ $sql->query('SELECT `id` FROM `hosting` WHERE `id`="'.$aData['service'].'" AND `user`="'.$user['id'].'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная услуга не найдена в базе.'), $nmch);
+
+ // Защита от дублирования темы вопроса
+ $sql->query('SELECT `id` FROM `help` WHERE `user`="'.$user['id'].'" AND `type`="'.$aData['type'].'" AND `service`="'.$aData['service'].'" AND `close`="0" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'По выбранной услуге уже есть открытый диалог.'), $nmch);
+ }
+
+ // Проверка заголовка, если указан
+ if(!empty($aData['title']))
+ {
+ if(iconv_strlen($aData['title'], 'UTF-8') < 3 || iconv_strlen($aData['title'], 'UTF-8') > 40)
+ sys::outjs(array('e' => 'Длина загловка не должна быть менее 3 и не превышать 40 символов.'), $nmch);
+ }
+
+ // Проверка сообщения
+ if(iconv_strlen($aData['text'], 'UTF-8') < 10 || iconv_strlen($aData['text'], 'UTF-8') > 1000)
+ sys::outjs(array('e' => 'Длина сообщения не должна быть менее 10 и не превышать 1000 символов.'), $nmch);
+
+ include(LIB.'help.php');
+
+ // Обработка сообщения
+ $aData['text'] = help::text($aData['text']);
+
+ // Проверка изображений
+ if(is_array($aData['images']) AND count($aData['images']))
+ {
+ foreach($aData['images'] as $img)
+ {
+ $key = explode('.', $img);
+
+ if(!is_array($key) || sys::valid($key[0], 'md5') || !in_array($key[1], array('png', 'gif', 'jpg', 'jpeg','bmp')))
+ continue;
+
+ $sql->query('SELECT `id` FROM `help_upload` WHERE `name`="'.$img.'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $image = $sql->get();
+
+ $sql->query('UPDATE `help_upload` set `status`="1" WHERE `id`="'.$image['id'].'" LIMIT 1');
+
+ $aData['img'][] = $img;
+ }
+ }
+
+ // Проверка открытых сообщений
+ $sql->query('SELECT `id` FROM `help` WHERE `user`="'.$user['id'].'" AND `close`="0" LIMIT 3');
+ if($sql->num() == 3)
+ sys::outjs(array('e' => 'У вас уже открыто 3 вопроса, чтобы создать новый необходимо их закрыть.'), $nmch);
+
+ $sql->query('INSERT INTO `help` set '
+ .'`user`="'.$user['id'].'",'
+ .'`type`="'.$aData['type'].'",'
+ .'`service`="'.$aData['service'].'",'
+ .'`status`="1",'
+ .'`date`="'.$start_point.'",'
+ .'`time`="'.$start_point.'",'
+ .'`title`="'.htmlspecialchars($aData['title']).'",'
+ .'`close`="0"');
+
+ $help = $sql->id();
+
+ $sql->query('INSERT INTO `help_dialogs` set '
+ .'`help`="'.$help.'",'
+ .'`user`="'.$user['id'].'",'
+ .'`text`="'.$aData['text'].'",'
+ .'`img`="'.sys::b64js($aData['img']).'",'
+ .'`time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => $help), $nmch);
+ }
+
+ $services = '';
+
+ $sql->query('SELECT `id`, `address` FROM `servers` WHERE `user`="'.$user['id'].'" LIMIT 10');
+ while($server = $sql->get())
+ $services .= 'Игровой сервер #'.$server['id'].' ('.$server['address'].') ';
+
+ $html->get('create', 'sections/help');
+
+ $html->set('id', $user['id']);
+ $html->set('services', $services);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/help/dialog.php b/system/sections/help/dialog.php
new file mode 100644
index 0000000..d92e8a0
--- /dev/null
+++ b/system/sections/help/dialog.php
@@ -0,0 +1,203 @@
+query('SELECT `type`, `service`, `status`, `date`, `close` FROM `help` WHERE `id`="'.$id.'" LIMIT 1');
+ else
+ $sql->query('SELECT `type`, `service`, `status`, `date`, `close` FROM `help` WHERE `id`="'.$id.'" AND `user`="'.$user['id'].'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'help/section/open');
+
+ $help = $sql->get();
+
+ // Смена статуса вопроса на "Прочитан"
+ if($user['group'] == 'user' AND !$help['status'])
+ {
+ $sql->query('UPDATE `help` set `status`="2", `notice`="1" WHERE `id`="'.$id.'" LIMIT 1');
+ $help['status'] = 2;
+ }
+
+ $aGroup = array(
+ 'admin' => 'Администратор',
+ 'support' => 'Техническая поддержка',
+ 'user' => 'Клиент'
+ );
+
+ include(LIB.'help.php');
+ include(LIB.'users.php');
+
+ $aSender = array();
+
+ $dialogs = $sql->query('SELECT `id`, `user`, `text`, `img`, `time` FROM `help_dialogs` WHERE `help`="'.$id.'" ORDER BY `id` DESC LIMIT 50');
+ while($dialog = $sql->get($dialogs))
+ {
+ unset($html->arr['attachment']);
+
+ $images = sys::b64djs($dialog['img']);
+
+ if(is_array($images))
+ foreach($images as $img)
+ {
+ $html->get('attachment', 'sections/help/dialog');
+
+ $html->set('img', $img);
+ $html->set('home', $cfg['http']);
+
+ $html->pack('attachment');
+ }
+
+ $html->get('msg', 'sections/help/dialog');
+
+ if($user['id'] != $dialog['user'])
+ {
+ if(!$dialog['user'])
+ $html->set('sender', 'Автоматическое сообщение');
+ else{
+ if(isset($aSender[$dialog['user']]))
+ $html->set('sender', $aSender[$dialog['user']]);
+ else{
+ switch($iHelp)
+ {
+ case 1:
+ $sql->query('SELECT `name`, `group`, `support_info` FROM `users` WHERE `id`="'.$dialog['user'].'" LIMIT 1');
+ $us = $sql->get();
+
+ if($us['support_info'] != '')
+ $aSender[$dialog['user']] = $us['name'].' ('.$us['support_info'].')';
+ else
+ $aSender[$dialog['user']] = $us['name'].' ('.$aGroup[$us['group']].')';
+
+ break;
+ case 2:
+ $sql->query('SELECT `login`, `group`, `support_info` FROM `users` WHERE `id`="'.$dialog['user'].'" LIMIT 1');
+ $us = $sql->get();
+
+ if($us['support_info'] != '')
+ $aSender[$dialog['user']] = $us['login'].' ('.$us['support_info'].')';
+ else
+ $aSender[$dialog['user']] = $us['login'].' ('.$aGroup[$us['group']].')';
+
+ break;
+ case 3:
+ $sql->query('SELECT `mail`, `group`, `support_info` FROM `users` WHERE `id`="'.$dialog['user'].'" LIMIT 1');
+ $us = $sql->get();
+
+ if($us['support_info'] != '')
+ $aSender[$dialog['user']] = $us['mail'].' ('.$us['support_info'].')';
+ else
+ $aSender[$dialog['user']] = $us['mail'].' ('.$aGroup[$us['group']].')';
+
+ break;
+ default:
+ $sql->query('SELECT `name`, `patronymic`, `group`, `support_info` FROM `users` WHERE `id`="'.$dialog['user'].'" LIMIT 1');
+ $us = $sql->get();
+
+ if($us['support_info'] != '')
+ $aSender[$dialog['user']] = $us['name'].' '.$us['patronymic'].' ('.$us['support_info'].')';
+ else
+ $aSender[$dialog['user']] = $us['name'].' '.$us['patronymic'].' ('.$aGroup[$us['group']].')';
+ }
+
+ $html->set('sender', $aSender[$dialog['user']]);
+ }
+ }
+ }else
+ $html->set('sender', 'Я');
+
+ $html->set('id', $dialog['id']);
+ $html->set('uid', $dialog['user']);
+ $html->set('help', $id);
+ $html->set('home', $cfg['http']);
+ $html->set('ava', users::ava($dialog['user']));
+ $html->set('text', $dialog['text']);
+
+ if($tHelp)
+ $html->set('time', $dialog['time'] < ($start_point-600) ? sys::today($dialog['time']) : help::ago($dialog['time']));
+ else
+ $html->set('time', sys::today($dialog['time']).' '.help::ago($dialog['time'], true));
+
+ if(isset($html->arr['attachment']))
+ {
+ $html->set('img', $html->arr['attachment']);
+ $html->unit('img', 1);
+ }else
+ $html->unit('img');
+
+ if($user['group'] == 'admin')
+ $html->unit('admin', 1);
+ else
+ $html->unit('admin');
+
+ $html->pack('dialog');
+ }
+
+ // Массив статусов вопроса
+ $status = array(
+ 0 => 'Есть ответ',
+ 1 => 'Ожидается ответ',
+ 2 => 'Прочитан'
+ );
+
+ if(isset($url['ajax']))
+ sys::outjs(array('dialog' => (isset($html->arr['dialog']) ? $html->arr['dialog'] : ''), 'status' => ($help['close'] ? 'Вопрос решен' : $status[$help['status']])));
+
+ // Краткая информация вопроса
+ switch($help['type'])
+ {
+ case 'server':
+ $sql->query('SELECT `address` FROM `servers` WHERE `id`="'.$help['service'].'" LIMIT 1');
+ if(!$sql->num())
+ $service = 'Игровой сервер: #'.$help['service'].' (не найден)';
+ else{
+ $ser = $sql->get();
+ $service = 'Игровой сервер: #'.$help['service'].' '.$ser['address'].' ';
+ }
+
+ break;
+
+ case 'hosting':
+ $service = 'Виртуальных хостинг: #'.$help['service'].' ';
+
+ break;
+
+ default:
+ $service = 'Вопрос без определенной услуги';
+ }
+
+ $html->get('dialog', 'sections/help');
+
+ $html->set('id', $id);
+ $html->set('date', sys::today($help['date']));
+ $html->set('status', $help['close'] ? 'Вопрос решен' : $status[$help['status']]);
+ $html->set('service', $service);
+ $html->set('dialog', isset($html->arr['dialog']) ? $html->arr['dialog'] : '');
+
+ if($user['group'] == 'user')
+ {
+ $html->unit('!user');
+ $html->unit('user', 1);
+ }else{
+ $html->unit('!user', 1);
+ $html->unit('user');
+ }
+
+ if($help['close'])
+ {
+ $html->unit('open');
+ $html->unit('close', 1);
+ }else{
+ $html->unit('open', 1);
+ $html->unit('close');
+ }
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/help/notice.php b/system/sections/help/notice.php
new file mode 100644
index 0000000..f1ff116
--- /dev/null
+++ b/system/sections/help/notice.php
@@ -0,0 +1,49 @@
+query('SELECT `id` FROM `help` WHERE `user`="'.$user['id'].'" AND `status`="0" AND `close`="0" LIMIT 1');
+ else
+ $sql->query('SELECT `id` FROM `help` WHERE `status`="1" AND `close`="0" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('empty' => ''));
+
+ if($user['group'] != 'user')
+ {
+ $sql->query('SELECT `time` FROM `help` WHERE `status`="1" AND `close`="0" ORDER BY `time` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $help = $sql->get();
+
+ sys::outjs(array('reply' => $help['time']));
+ }
+
+ sys::outjs(array('empty' => ''));
+ }
+
+ $help = $sql->get();
+
+ $sql->query('SELECT `text`, `time` FROM `help_dialogs` WHERE `help`="'.$help['id'].'" AND `user`!="'.$user['id'].'" AND `time`>"'.($start_point-15).'" ORDER BY `id` DESC LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('reply' => ''));
+
+ $msg = $sql->get();
+
+ if(strip_tags($msg['text'], ' ,') != $msg['text'])
+ sys::outjs(array('reply' => ''));
+
+ include(LIB.'help.php');
+
+ $html->get('notice', 'sections/help');
+
+ $html->set('id', $help['id']);
+ $html->set('home', $cfg['http']);
+ $html->set('text', $msg['text']);
+ $html->set('ago', help::ago($msg['time']));
+
+ $html->pack('notice');
+
+ sys::outjs(array('notice' => $html->arr['notice']));
+?>
\ No newline at end of file
diff --git a/system/sections/help/open.php b/system/sections/help/open.php
new file mode 100644
index 0000000..f682d9a
--- /dev/null
+++ b/system/sections/help/open.php
@@ -0,0 +1,113 @@
+ 'Есть ответ',
+ 1 => 'Ожидается ответ',
+ 2 => 'Прочитан'
+ );
+
+ if(in_array($user['group'], array('admin', 'support')))
+ $sql->query('SELECT `id` FROM `help` WHERE `close`="0"');
+ else
+ $sql->query('SELECT `id` FROM `help` WHERE `user`="'.$user['id'].'" AND `close`="0"');
+
+ $aPage = sys::page($page, $sql->num(), 20);
+
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'help/section/open');
+
+ if(in_array($user['group'], array('admin', 'support')))
+ $helps = $sql->query('SELECT `id`, `user`, `type`, `service`, `status`, `date`, `time`, `title` FROM `help` WHERE `close`="0" ORDER BY `id` DESC LIMIT '.$aPage['num'].', 20');
+ else
+ $helps = $sql->query('SELECT `id`, `type`, `service`, `status`, `date`, `time`, `title` FROM `help` WHERE `user`="'.$user['id'].'" AND `close`="0" ORDER BY `id` DESC LIMIT '.$aPage['num'].', 20');
+
+ // Массив пользователей
+ $uArr = array();
+
+ while($help = $sql->get($helps))
+ {
+ // Создатель вопроса
+ if(in_array($user['group'], array('admin', 'support')) AND !isset($uArr[$help['user']]))
+ {
+ $sql->query('SELECT `login` FROM `users` WHERE `id`="'.$help['user'].'" LIMIT 1');
+
+ if(!$sql->num())
+ $uArr[$help['user']] = 'Пользователь удален';
+ else{
+ $us = $sql->get();
+ $uArr[$help['user']] = $us['login'];
+ }
+ }
+
+ // Краткая информация вопроса
+ switch($help['type'])
+ {
+ case 'server':
+ $sql->query('SELECT `address` FROM `servers` WHERE `id`="'.$help['service'].'" LIMIT 1');
+ if(!$sql->num())
+ $name = 'Игровой сервер: #'.$help['service'].' (не найден)';
+ else{
+ $ser = $sql->get();
+ $name = 'Игровой сервер: #'.$help['service'].' '.$ser['address'];
+ }
+
+ break;
+
+ case 'hosting':
+ $name = 'Виртуальных хостинг: #'.$help['service'];
+
+ break;
+
+ default:
+ $name = 'Вопрос без определенной услуги';
+ }
+
+ if(!empty($help['title']))
+ $name = $help['title'];
+
+ $html->get('question', 'sections/help/open');
+
+ $html->set('id', $help['id']);
+
+ if(array_key_exists('user', $help))
+ {
+ $html->set('uid', $help['user']);
+ $html->set('login', $uArr[$help['user']]);
+ }
+
+ $html->set('name', $name);
+ $html->set('status', $status[$help['status']]);
+ $html->set('date', sys::today($help['date']));
+ $html->set('time', sys::today($help['time']));
+
+ $html->pack('question');
+ }
+
+ $html->get('open', 'sections/help');
+
+ $html->set('question', isset($html->arr['question']) ? $html->arr['question'] : '');
+
+ $html->set('pages', isset($html->arr['pages']) ? $html->arr['pages'] : '');
+
+ $html->pack('main');
+
+ if(!in_array($user['group'], array('admin', 'support')))
+ {
+ $html->unitall('main', 'user', 1);
+ $html->unitall('main', 'support');
+ }else{
+ $html->unitall('main', 'user');
+ $html->unitall('main', 'support', 1);
+ }
+
+ if($user['group'] == 'admin')
+ $html->unitall('main', 'admin', 1);
+ else
+ $html->unitall('main', 'admin');
+?>
\ No newline at end of file
diff --git a/system/sections/help/upload.php b/system/sections/help/upload.php
new file mode 100644
index 0000000..8c43514
--- /dev/null
+++ b/system/sections/help/upload.php
@@ -0,0 +1,31 @@
+query('INSERT INTO `help_upload` set `user`="'.$user['id'].'", `name`="'.$rdmName.'", `time`="'.$start_point.'", `status`="0"');
+
+ exit($rdmName.':ok');
+ }
+
+ exit('Ошибка загрузки: убедитесь, что изображение не повреждено и имеет правильный формат.');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/news/index.php b/system/sections/megp/news/index.php
new file mode 100644
index 0000000..9059079
--- /dev/null
+++ b/system/sections/megp/news/index.php
@@ -0,0 +1,28 @@
+query('SELECT `id` FROM `news`');
+
+ $aPage = sys::page($page, $sql->num(), $cfg['news_page']);
+
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'news');
+
+ $sql->query('SELECT `id`, `name`, `text`, `views`, `tags`, `date` FROM `news` ORDER BY `id` DESC LIMIT '.$aPage['num'].', '.$cfg['news_page']);
+ while($news = $sql->get())
+ {
+ $html->get('list', 'sections/news');
+ $html->set('id', $news['id']);
+ $html->set('name', htmlspecialchars_decode($news['name']));
+ $html->set('text', htmlspecialchars_decode($news['text']));
+ $html->set('views', $news['views']);
+ $html->set('tags', sys::tags($news['tags']));
+ $html->set('date', sys::today($news['date']));
+ $html->pack('news');
+ }
+
+ $html->get('all', 'sections/news');
+ $html->set('list', isset($html->arr['news']) ? $html->arr['news'] : '');
+ $html->set('pages', isset($html->arr['pages']) ? $html->arr['pages'] : '');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/news/news.php b/system/sections/megp/news/news.php
new file mode 100644
index 0000000..e5b8378
--- /dev/null
+++ b/system/sections/megp/news/news.php
@@ -0,0 +1,32 @@
+query('SELECT `id`, `name`, `full_text`, `views`, `tags`, `date` FROM `news` WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ include(ENG.'404.php');
+
+ $news = $sql->get();
+
+ $sql->query('UPDATE `news` set `views`="'.($news['views']+1).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $text = htmlspecialchars_decode($news['full_text']);
+
+ $title = $news['name'];
+ $description = $text;
+ $keywords = $news['tags'];
+
+ $html->nav($news['name']);
+
+ $html->get('news', 'sections/news');
+
+ $html->set('id', $news['id']);
+ $html->set('name', htmlspecialchars_decode($news['name']));
+ $html->set('text', $text);
+ $html->set('views', $news['views']);
+ $html->set('tags', sys::tags($news['tags']));
+ $html->set('date', sys::today($news['date']));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/action.php b/system/sections/megp/servers/action.php
new file mode 100644
index 0000000..85e1c4b
--- /dev/null
+++ b/system/sections/megp/servers/action.php
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/system/sections/megp/servers/boost.php b/system/sections/megp/servers/boost.php
new file mode 100644
index 0000000..3f9d4aa
--- /dev/null
+++ b/system/sections/megp/servers/boost.php
@@ -0,0 +1,11 @@
+query('SELECT `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'boost');
+
+ include(sys::route($server, 'boost', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/console.php b/system/sections/megp/servers/console.php
new file mode 100644
index 0000000..f49ac21
--- /dev/null
+++ b/system/sections/megp/servers/console.php
@@ -0,0 +1,11 @@
+query('SELECT `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'console');
+
+ include(sys::route($server, 'console', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/crmp/console.php b/system/sections/megp/servers/crmp/console.php
new file mode 100644
index 0000000..78da5ef
--- /dev/null
+++ b/system/sections/megp/servers/crmp/console.php
@@ -0,0 +1,14 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/crmp/index.php b/system/sections/megp/servers/crmp/index.php
new file mode 100644
index 0000000..2025c89
--- /dev/null
+++ b/system/sections/megp/servers/crmp/index.php
@@ -0,0 +1,35 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status'], $server['game']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name']);
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+ $html->set('btn', $btn);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/crmp/settings.php b/system/sections/megp/servers/crmp/settings.php
new file mode 100644
index 0000000..7d7260d
--- /dev/null
+++ b/system/sections/megp/servers/crmp/settings.php
@@ -0,0 +1,46 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $aSub = array('start', 'pack');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ $dir = $url['subsection'] == 'start' ? 'megp/' : '';
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.$dir.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/crmp/settings/start.php b/system/sections/megp/servers/crmp/settings/start.php
new file mode 100644
index 0000000..c64a63b
--- /dev/null
+++ b/system/sections/megp/servers/crmp/settings/start.php
@@ -0,0 +1,44 @@
+query('SELECT `uid`, `slots`, `slots_start`, `autorestart` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ include(LIB.'games/games.php');
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/crmp/tarif.php b/system/sections/megp/servers/crmp/tarif.php
new file mode 100644
index 0000000..7d95fa2
--- /dev/null
+++ b/system/sections/megp/servers/crmp/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'slots');
+
+ include(SEC.'megp/servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/crmp/tarif/extend.php b/system/sections/megp/servers/crmp/tarif/extend.php
new file mode 100644
index 0000000..cf31b1e
--- /dev/null
+++ b/system/sections/megp/servers/crmp/tarif/extend.php
@@ -0,0 +1,48 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ // Цена за 30 дней 1 слота
+ $price = $tarif['price'];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/crmp/tarif/slots.php b/system/sections/megp/servers/crmp/tarif/slots.php
new file mode 100644
index 0000000..3a60d63
--- /dev/null
+++ b/system/sections/megp/servers/crmp/tarif/slots.php
@@ -0,0 +1,31 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $tarif['price']/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($tarif['price']/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cs/boost.php b/system/sections/megp/servers/cs/boost.php
new file mode 100644
index 0000000..a05aec2
--- /dev/null
+++ b/system/sections/megp/servers/cs/boost.php
@@ -0,0 +1,68 @@
+ 'Необходимо указать сервис.'));
+ $aData['service'] = isset($url['service']) ? sys::int($url['service']) : sys::outjs(array('e' => 'Необходимо указать номер услуги.'));
+
+ include(DATA.'boost.php');
+
+ // Проверка сервиса
+ if(!array_key_exists($aData['site'], $aBoost[$server['game']]))
+ sys::outjs(array('e' => 'Указанный сервис по раскрутке не найден.'));
+
+ // Проверка номера услуги
+ if(!in_array($aData['service'], $aBoost[$server['game']][$aData['site']]['services']))
+ sys::outjs(array('e' => 'Неправильно указан номер услуги.'));
+
+ // Определение суммы
+ $sum = $aBoost[$server['game']][$aData['site']]['price'][$aData['service']];
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']), $name_mcache);
+
+ include(LIB.'games/boost.php');
+
+ $boost = new boost($aBoost[$server['game']][$aData['site']]['key'], $aBoost[$server['game']][$aData['site']]['api']);
+
+ $buy = $boost->$aBoost[$server['game']][$aData['site']]['type'](array('period' => $aData['service'], 'address' => $server['address']));
+
+ if(is_array($buy))
+ sys::outjs(array('e' => $buy['error']));
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ include(LIB.'games/games.php');
+
+ // Реф. система
+ games::part($user['id'], $sum);
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_boost'),
+ array('circles' => $aBoost[$server['game']][$aData['site']]['circles'][$aData['service']],
+ 'money' => $sum, 'site' => $aBoost[$server['game']][$aData['site']]['site'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="boost", `money`="'.$sum.'"');
+
+ $sql->query('INSERT INTO `boost` set `user`="'.$user['id'].'", `server`="'.$id.'", `site`="'.$aData['site'].'", `circles`="'.$aBoost[$server['game']][$aData['site']]['circles'][$aData['service']].'", `money`="'.$sum.'", `date`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+ }
+
+ if($mcache->get('server_boost_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_boost_'.$id);
+ else{
+ $html->get('boost', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('address', $server['address']);
+ $html->set('cur', $cfg['currency']);
+
+ $html->pack('main');
+
+ $mcache->set('server_boost_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cs/console.php b/system/sections/megp/servers/cs/console.php
new file mode 100644
index 0000000..78da5ef
--- /dev/null
+++ b/system/sections/megp/servers/cs/console.php
@@ -0,0 +1,14 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cs/index.php b/system/sections/megp/servers/cs/index.php
new file mode 100644
index 0000000..bd883c4
--- /dev/null
+++ b/system/sections/megp/servers/cs/index.php
@@ -0,0 +1,41 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `fps`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name'].' / '.$server['fps'].' FPS');
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cs/settings.php b/system/sections/megp/servers/cs/settings.php
new file mode 100644
index 0000000..113f956
--- /dev/null
+++ b/system/sections/megp/servers/cs/settings.php
@@ -0,0 +1,51 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $aSub = array('start', 'pack', 'antiddos');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ $dir = $url['subsection'] == 'start' ? 'megp/' : '';
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.$dir.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $antiddos = 'Индивидуальная защита отключена '
+ .'Индивидуальная защита (Заблокировать всех кроме: RU, UA) '
+ .'Индивидуальная защита (Заблокировать всех кроме: AM, BY, UA, RU, KZ) ';
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('antiddos', str_replace($server['ddos'], $server['ddos'].'" selected="select', $antiddos));
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cs/settings/start.php b/system/sections/megp/servers/cs/settings/start.php
new file mode 100644
index 0000000..93c5828
--- /dev/null
+++ b/system/sections/megp/servers/cs/settings/start.php
@@ -0,0 +1,159 @@
+query('SELECT `uid`, `slots`, `slots_start`, `map_start`, `vac`, `fps`, `fastdl`, `autorestart`, `pingboost` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install`, `fps`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'games/games.php');
+ include(LIB.'games/tarifs.php');
+ include(LIB.'games/'.$server['game'].'/tarif.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/cstrike/maps', $server['map_start'], false);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/cstrike/maps', $map, true, $nmch);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `servers` set `vac`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `servers` set `autorestart`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fps':
+ if(!tarif::price($tarif['price']) AND in_array($value, explode(':', $tarif['fps'])))
+ $sql->query('UPDATE `servers` set `fps`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'pingboost':
+ if($cfg['change_pingboost'] AND in_array($value, array(1, 2, 3)))
+ $sql->query('UPDATE `servers` set `pingboost`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.sys::first(explode(':', $unit['address'])).':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/fastdl.cfg;'
+ .'ln -s '.$tarif['install'].$server['uid'].'/cstrike /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/cstrike/server.cfg;'
+ .'echo "exec fastdl.cfg" >> '.$tarif['install'].$server['uid'].'/cstrike/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/cstrike/server.cfg;'
+ .'rm '.$tarif['install'].$server['uid'].'/cstrike/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `servers` set `fastdl`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $fps = ''.$server['fps'].' FPS ';
+
+ if(!tarif::price($tarif['price']))
+ {
+ $aFps = explode(':', $tarif['fps']);
+
+ unset($aFps[array_search($server['fps'], $aFps)]);
+
+ if(count($aFps))
+ foreach($aFps as $value)
+ $fps .= ''.$value.' FPS ';
+ }
+
+ if($cfg['change_pingboost'])
+ $pingboost = str_replace($server['pingboost'].'"', $server['pingboost'].'" selected="select"', 'PINGBOOST 1 PINGBOOST 2 PINGBOOST 3 ');
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('map', $server['map_start']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ if($cfg['change_pingboost'])
+ {
+ $html->unit('pingboost', true);
+ $html->set('pingboost', $pingboost);
+ }else
+ $html->unit('pingboost');
+
+ if(!tarif::price($tarif['price']))
+ {
+ $html->unit('fps', true);
+ $html->set('fps', $fps);
+ }else
+ $html->unit('fps');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cs/tarif.php b/system/sections/megp/servers/cs/tarif.php
new file mode 100644
index 0000000..49e6b9c
--- /dev/null
+++ b/system/sections/megp/servers/cs/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `fps`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'plan', 'slots');
+
+ include(SEC.'megp/servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cs/tarif/extend.php b/system/sections/megp/servers/cs/tarif/extend.php
new file mode 100644
index 0000000..0e66ff6
--- /dev/null
+++ b/system/sections/megp/servers/cs/tarif/extend.php
@@ -0,0 +1,52 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['fps'] = $server['fps'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aFPS = explode(':', $tarif['fps']);
+
+ // Цена за 30 дней 1 слота
+ $price = $aPrice[array_search($server['fps'], $aFPS)];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cs/tarif/plan.php b/system/sections/megp/servers/cs/tarif/plan.php
new file mode 100644
index 0000000..09170db
--- /dev/null
+++ b/system/sections/megp/servers/cs/tarif/plan.php
@@ -0,0 +1,64 @@
+ 'Переданые не все данные'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aFPS = explode(':', $tarif['fps']);
+
+ // Проверка плана
+ if(array_search($plan, $aFPS) === FALSE)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ if($plan == $server['fps'])
+ sys::outjs(array('e' => 'Смысла в этой операции нет'), $nmch);
+
+ if(!tarif::price($tarif['price']))
+ sys::outjs(array('e' => 'Чтобы изменить тариф, перейдите в настройки запуска'), $nmch);
+
+ if($server['time'] < $start_point+86400)
+ $time = $server['time'];
+ else{
+ // Цена за 1 день аренды (по новому тарифному плану)
+ $price = $aPrice[array_search($plan, $aFPS)]/30*$server['slots'];
+
+ // Цена за 1 день аренды (по старому тарифному плану)
+ $price_old = $aPrice[array_search($server['fps'], $aFPS)]/30*$server['slots'];
+
+ // Остаток дней аренды
+ $days = ($server['time']-$start_point)/86400;
+
+ $time = date('H:i:s', $server['time']);
+ $date = date('d.m.Y', round($start_point+$days*$price_old/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+ $aTime = explode(':', $time);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2]);
+ }
+
+ // Выполнение смена тарифного плана
+ if($go)
+ {
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `fps`="'.$plan.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_plan').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cs/tarif/slots.php b/system/sections/megp/servers/cs/tarif/slots.php
new file mode 100644
index 0000000..b32de8a
--- /dev/null
+++ b/system/sections/megp/servers/cs/tarif/slots.php
@@ -0,0 +1,34 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aFPS = explode(':', $tarif['fps']);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день 1 слота
+ $price = $aPrice[array_search($server['fps'], $aFPS)]/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($aPrice[array_search($server['fps'], $aFPS)]/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/csgo/console.php b/system/sections/megp/servers/csgo/console.php
new file mode 100644
index 0000000..78da5ef
--- /dev/null
+++ b/system/sections/megp/servers/csgo/console.php
@@ -0,0 +1,14 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/csgo/index.php b/system/sections/megp/servers/csgo/index.php
new file mode 100644
index 0000000..bd883c4
--- /dev/null
+++ b/system/sections/megp/servers/csgo/index.php
@@ -0,0 +1,41 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `fps`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name'].' / '.$server['fps'].' FPS');
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/csgo/settings.php b/system/sections/megp/servers/csgo/settings.php
new file mode 100644
index 0000000..113f956
--- /dev/null
+++ b/system/sections/megp/servers/csgo/settings.php
@@ -0,0 +1,51 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $aSub = array('start', 'pack', 'antiddos');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ $dir = $url['subsection'] == 'start' ? 'megp/' : '';
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.$dir.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $antiddos = 'Индивидуальная защита отключена '
+ .'Индивидуальная защита (Заблокировать всех кроме: RU, UA) '
+ .'Индивидуальная защита (Заблокировать всех кроме: AM, BY, UA, RU, KZ) ';
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('antiddos', str_replace($server['ddos'], $server['ddos'].'" selected="select', $antiddos));
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/csgo/settings/start.php b/system/sections/megp/servers/csgo/settings/start.php
new file mode 100644
index 0000000..1238750
--- /dev/null
+++ b/system/sections/megp/servers/csgo/settings/start.php
@@ -0,0 +1,143 @@
+query('SELECT `uid`, `slots`, `slots_start`, `map_start`, `vac`, `fastdl`, `autorestart`, `fps`, `tickrate` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install`, `tickrate`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'games/games.php');
+ include(LIB.'games/tarifs.php');
+ include(LIB.'games/'.$server['game'].'/tarif.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/csgo/maps', $server['map_start'], false);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/csgo/maps', $map, true, $nmch);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `servers` set `vac`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `servers` set `autorestart`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'tickrate':
+ if(!tarif::price($tarif['price']) AND in_array($value, explode(':', $tarif['tickrate'])))
+ $sql->query('UPDATE `servers` set `tickrate`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.sys::first(explode(':', $unit['address'])).':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/csgo/cfg/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/csgo/cfg/fastdl.cfg;'
+ .'ln -s '.$tarif['install'].$server['uid'].'/csgo /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/csgo/cfg/server.cfg;'
+ .'echo "exec fastdl.cfg" >> '.$tarif['install'].$server['uid'].'/csgo/cfg/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/csgo/cfg/server.cfg;'
+ .'rm '.$tarif['install'].$server['uid'].'/csgo/cfg/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `servers` set `fastdl`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $tickrate = ''.$server['tickrate'].' TickRate ';
+
+ if(!tarif::price($tarif['price']))
+ {
+ $aTick = explode(':', $tarif['tickrate']);
+
+ unset($aTick[array_search($server['tickrate'], $aTick)]);
+
+ if(count($aTick))
+ foreach($aTick as $value)
+ $tickrate .= ''.$value.' TickRate ';
+ }
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('map', $server['map_start']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ if(!tarif::price($tarif['price']))
+ {
+ $html->unit('tickrate', true);
+ $html->set('tickrate', $tickrate);
+ }else
+ $html->unit('tickrate');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/csgo/tarif.php b/system/sections/megp/servers/csgo/tarif.php
new file mode 100644
index 0000000..49e6b9c
--- /dev/null
+++ b/system/sections/megp/servers/csgo/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `fps`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'plan', 'slots');
+
+ include(SEC.'megp/servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/csgo/tarif/extend.php b/system/sections/megp/servers/csgo/tarif/extend.php
new file mode 100644
index 0000000..9d060bc
--- /dev/null
+++ b/system/sections/megp/servers/csgo/tarif/extend.php
@@ -0,0 +1,51 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['tickrate'] = $server['tickrate'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ $aPrice = explode(':', $tarif['price']);
+
+ // Цена за 30 дней 1 слота
+ $price = $aPrice[array_search($server['tickrate'], explode(':', $tarif['tickrate']))];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/csgo/tarif/plan.php b/system/sections/megp/servers/csgo/tarif/plan.php
new file mode 100644
index 0000000..33988c4
--- /dev/null
+++ b/system/sections/megp/servers/csgo/tarif/plan.php
@@ -0,0 +1,64 @@
+ 'Переданые не все данные'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ // Проверка плана
+ if(array_search($plan, $aTICK) === FALSE)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ if($plan == $server['tickrate'])
+ sys::outjs(array('e' => 'Смысла в этой операции нет'), $nmch);
+
+ if(!tarif::price($tarif['price']))
+ sys::outjs(array('e' => 'Чтобы изменить тариф, перейдите в настройки запуска'), $nmch);
+
+ if($server['time'] < $start_point+86400)
+ $time = $server['time'];
+ else{
+ // Цена за 1 день аренды (по новому тарифному плану)
+ $price = $aPrice[array_search($plan, $aTICK)]/30*$server['slots'];
+
+ // Цена за 1 день аренды (по старому тарифному плану)
+ $price_old = $aPrice[array_search($server['tickrate'], $aTICK)]/30*$server['slots'];
+
+ // Остаток дней аренды
+ $days = ($server['time']-$start_point)/86400;
+
+ $time = date('H:i:s', $server['time']);
+ $date = date('d.m.Y', round($start_point+$days*$price_old/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+ $aTime = explode(':', $time);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2]);
+ }
+
+ // Выполнение смена тарифного плана
+ if($go)
+ {
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `tickrate`="'.$plan.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_plan').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/csgo/tarif/slots.php b/system/sections/megp/servers/csgo/tarif/slots.php
new file mode 100644
index 0000000..e457286
--- /dev/null
+++ b/system/sections/megp/servers/csgo/tarif/slots.php
@@ -0,0 +1,34 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $aPrice[array_search($server['tickrate'], $aTICK)]/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($aPrice[array_search($server['tickrate'], $aTICK)]/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/css/.htaccess b/system/sections/megp/servers/css/.htaccess
new file mode 100644
index 0000000..e69de29
diff --git a/system/sections/megp/servers/cssold/console.php b/system/sections/megp/servers/cssold/console.php
new file mode 100644
index 0000000..78da5ef
--- /dev/null
+++ b/system/sections/megp/servers/cssold/console.php
@@ -0,0 +1,14 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cssold/index.php b/system/sections/megp/servers/cssold/index.php
new file mode 100644
index 0000000..ff85fde
--- /dev/null
+++ b/system/sections/megp/servers/cssold/index.php
@@ -0,0 +1,41 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `fps`, `tickrate`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name'].' / '.$server['fps'].' FPS / '.$server['tickrate'].' TickRate');
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cssold/settings.php b/system/sections/megp/servers/cssold/settings.php
new file mode 100644
index 0000000..113f956
--- /dev/null
+++ b/system/sections/megp/servers/cssold/settings.php
@@ -0,0 +1,51 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $aSub = array('start', 'pack', 'antiddos');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ $dir = $url['subsection'] == 'start' ? 'megp/' : '';
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.$dir.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $antiddos = 'Индивидуальная защита отключена '
+ .'Индивидуальная защита (Заблокировать всех кроме: RU, UA) '
+ .'Индивидуальная защита (Заблокировать всех кроме: AM, BY, UA, RU, KZ) ';
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('antiddos', str_replace($server['ddos'], $server['ddos'].'" selected="select', $antiddos));
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cssold/settings/start.php b/system/sections/megp/servers/cssold/settings/start.php
new file mode 100644
index 0000000..a681458
--- /dev/null
+++ b/system/sections/megp/servers/cssold/settings/start.php
@@ -0,0 +1,163 @@
+query('SELECT `uid`, `slots`, `slots_start`, `map_start`, `vac`, `fastdl`, `autorestart`, `fps`, `tickrate` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install`, `fps`, `tickrate`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'games/games.php');
+ include(LIB.'games/tarifs.php');
+ include(LIB.'games/'.$server['game'].'/tarif.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/cstrike/maps', $server['map_start'], false);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/cstrike/maps', $map, true, $nmch);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `servers` set `vac`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `servers` set `autorestart`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fps':
+ if(!tarif::price($tarif['price']) AND in_array($value, explode(':', $tarif['fps'])))
+ $sql->query('UPDATE `servers` set `fps`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'tickrate':
+ if(!tarif::price($tarif['price']) AND in_array($value, explode(':', $tarif['tickrate'])))
+ $sql->query('UPDATE `servers` set `tickrate`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.sys::first(explode(':', $unit['address'])).':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/cfg/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/cfg/fastdl.cfg;'
+ .'ln -s '.$tarif['install'].$server['uid'].'/cstrike /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'echo "exec fastdl.cfg" >> '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'rm '.$tarif['install'].$server['uid'].'/cstrike/cfg/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `servers` set `fastdl`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $fps = ''.$server['fps'].' FPS ';
+ $tickrate = ''.$server['tickrate'].' TickRate ';
+
+ if(!tarif::price($tarif['price']))
+ {
+ $aFps = explode(':', $tarif['fps']);
+
+ unset($aFps[array_search($server['fps'], $aFps)]);
+
+ if(count($aFps))
+ foreach($aFps as $value)
+ $fps .= ''.$value.' FPS ';
+
+ $aTick = explode(':', $tarif['tickrate']);
+
+ unset($aTick[array_search($server['tickrate'], $aTick)]);
+
+ if(count($aTick))
+ foreach($aTick as $value)
+ $tickrate .= ''.$value.' TickRate ';
+ }
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('map', $server['map_start']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ if(!tarif::price($tarif['price']))
+ {
+ $html->unit('fps', true);
+ $html->set('fps', $fps);
+
+ $html->unit('tickrate', true);
+ $html->set('tickrate', $tickrate);
+ }else{
+ $html->unit('fps');
+ $html->unit('tickrate');
+ }
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cssold/tarif.php b/system/sections/megp/servers/cssold/tarif.php
new file mode 100644
index 0000000..a24eff3
--- /dev/null
+++ b/system/sections/megp/servers/cssold/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `fps`, `tickrate`, `ram`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'plan', 'slots');
+
+ include(SEC.'megp/servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cssold/tarif/extend.php b/system/sections/megp/servers/cssold/tarif/extend.php
new file mode 100644
index 0000000..5125e5f
--- /dev/null
+++ b/system/sections/megp/servers/cssold/tarif/extend.php
@@ -0,0 +1,52 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['fps'] = $server['fps'];
+ $aData['tickrate'] = $server['tickrate'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ $aPrice = sys::b64djs($tarif['price']);
+
+ // Цена за 30 дней 1 слота
+ $price = $aPrice[$server['tickrate'].'_'.$server['fps']];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cssold/tarif/plan.php b/system/sections/megp/servers/cssold/tarif/plan.php
new file mode 100644
index 0000000..2ed7cd4
--- /dev/null
+++ b/system/sections/megp/servers/cssold/tarif/plan.php
@@ -0,0 +1,65 @@
+ 'Переданые не все данные'), $nmch);
+
+ $aPrice = sys::b64djs($tarif['price']);
+
+ // Проверка плана
+ if(!array_key_exists($plan, $aPrice))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ list($tickrate, $fps) = explode('_', $plan);
+
+ if($tickrate == $server['tickrate'] AND $fps == $server['fps'])
+ sys::outjs(array('e' => 'Смысла в этой операции нет'), $nmch);
+
+ if(!tarif::price($tarif['price']))
+ sys::outjs(array('e' => 'Чтобы изменить тариф, перейдите в настройки запуска'), $nmch);
+
+ if($server['time'] < $start_point+86400)
+ $time = $server['time'];
+ else{
+ // Цена за 1 день (по новому тарифному плану)
+ $price = $aPrice[$plan]/30*$server['slots'];
+
+ // Цена аренды за остаток дней
+ $price_old = $aPrice[$server['tickrate'].'_'.$server['fps']]/30*$server['slots'];
+
+ // Остаток дней аренды
+ $days = ($server['time']-$start_point)/86400;
+
+ $time = date('H:i:s', $server['time']);
+ $date = date('d.m.Y', round($start_point+$days*$price_old/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+ $aTime = explode(':', $time);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2]);
+ }
+
+ // Выполнение смена тарифного плана
+ if($go)
+ {
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `fps`="'.$fps.'", `tickrate`="'.$tickrate.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_plan').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/cssold/tarif/slots.php b/system/sections/megp/servers/cssold/tarif/slots.php
new file mode 100644
index 0000000..fae705c
--- /dev/null
+++ b/system/sections/megp/servers/cssold/tarif/slots.php
@@ -0,0 +1,33 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $aPrice = sys::b64djs($tarif['price']);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $aPrice[$server['tickrate'].'_'.$server['fps']]/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($aPrice[$server['tickrate'].'_'.$server['fps']]/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/games/tarif.php b/system/sections/megp/servers/games/tarif.php
new file mode 100644
index 0000000..9ecbcb9
--- /dev/null
+++ b/system/sections/megp/servers/games/tarif.php
@@ -0,0 +1,50 @@
+get('tarif', 'sections/servers/games');
+
+ $html->set('id', $id);
+
+ $html->pack('main');
+
+ // Шаблон продления
+ if($cfg['settlement_period'])
+ tarif::extend_sp($server, $tarif, $id);
+ else{
+ $options = games::parse_time(explode(':', $tarif['timext']), $tarif['discount'], $server['tarif'], 'extend');
+
+ tarif::extend($options, $server, $tarif['name'], $id);
+ }
+
+ // Если не тестовый период
+ if(!$server['test'])
+ {
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Шаблон смены тарифа (если аренда не менее 1 дня и цены планов различны)
+ if($server['time'] > $start_point+86400 AND tarif::price($tarif['price']))
+ tarif::plan($server, $tarif['name'], $id);
+
+ // Шаблон изменения кол-ва слот
+ if($tarif['slots_min'] != $tarif['slots_max'])
+ tarif::slots($server, array('min' => $tarif['slots_min'], 'max' => $tarif['slots_max']), $id);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/games/tarif/extend.php b/system/sections/megp/servers/games/tarif/extend.php
new file mode 100644
index 0000000..e63104f
--- /dev/null
+++ b/system/sections/megp/servers/games/tarif/extend.php
@@ -0,0 +1,71 @@
+ с учетом промо-кода)
+ if(isset($url['promo']) || $aData['promo'] != '')
+ $promo = games::define_promo(
+ $aData['promo'],
+ $aData,
+ $tarif['discount'],
+ $sum,
+ 'extend'
+ );
+
+ // Использование промо-кода
+ if(is_array($promo))
+ {
+ if(array_key_exists('sum', $promo))
+ $sum = $promo['sum'];
+ else
+ $aData['time'] += $promo['days']*86400; // Кол-во дней аренды с учетом подарочных (промо-код)
+ }
+
+ // Выполнение продления
+ if($go)
+ {
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']), $nmch);
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Реф. система
+ games::part($user['id'], $sum);
+
+ $status = $server['status'] == 'overdue' ? '`status`="off",' : '';
+
+ // Время аренды
+ $time = $server['time'] < $start_point ? $start_point+$aData['time']*86400 : $server['time']+$aData['time']*86400;
+
+ // Обновление информации
+ $sql->query('UPDATE `servers` set '.$status.' `time`="'.$time.'", `test`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Продление адреса на 30 дней
+ if($add_sum)
+ $sql->query('UPDATE `address_buy` set `time`=`time`+"2592000" WHERE `server`="'.$id.'" LIMIT 1');
+
+ // Запись логов
+ if(!is_array($promo))
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'extend_server'),
+ array('days' => $days,
+ 'money' => $sum,
+ 'id' => $id)).'", `date`="'.$start_point.'", `type`="extend", `money`="'.$sum.'"');
+ else{
+ $sql->query('INSERT INTO `promo_use` set `promo`="'.$promo['id'].'", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'extend_server_promo'),
+ array('days' => $days,
+ 'money' => $sum,
+ 'promo' => $promo['cod'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="extend", `money`="'.$sum.'"');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп цены
+ sys::outjs(array('s' => $sum));
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/games/tarif/slots.php b/system/sections/megp/servers/games/tarif/slots.php
new file mode 100644
index 0000000..dbfe315
--- /dev/null
+++ b/system/sections/megp/servers/games/tarif/slots.php
@@ -0,0 +1,124 @@
+ ''));
+
+ if($go)
+ {
+ $start = $server['slots_start'] > $slots ? ', `slots_start`="'.$slots.'"' : '';
+
+ $sql->query('UPDATE `servers` set `slots`="'.$slots.'" '.$start.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_slots').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // При возможности уменьшить
+ if($cfg['change_slots'][$server['game']]['down'] || $overdue)
+ {
+ // Проверка кол-ва слот
+ if($slots < $tarif['slots_min'] || $slots > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданые неверные данные.'), $nmch);
+
+ if($server['slots'] == $slots)
+ {
+ if($go)
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ sys::outjs(array('s' => 'Сервер будет арендован до: '.date('d.m.Y - H:i', $server['time']).' ('.sys::date('min', $server['time']).')'), $nmch);
+ }
+ }else{
+ // Установлено макс. значение
+ if($server['slots'] == $tarif['slots_max'] AND !$overdue)
+ sys::outjs(array('e' => 'На игровом сервере установлено максимальное значение.'), $nmch);
+
+ if($slots < 1 || $slots > $max)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ $slots += $server['slots'];
+ }
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$price_old/($price*$slots)*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ // При уменьшении кол-ва слот не добавлять дни
+ if($slots < $server['slots'] AND ($cfg['change_slots'][$server['game']]['days'] AND $cfg['change_slots'][$server['game']]['down'] AND !$cfg['change_slots'][$server['game']]['add']))
+ $time = $server['time'];
+
+ // Выполнение операции
+ if($go)
+ {
+ $start = $server['slots_start'] > $slots ? ', `slots_start`="'.$slots.'"' : '';
+
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `slots`="'.$slots.'" '.$start.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')) AND $slots < $server['slots_start'])
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_slots').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => 'Сервер будет арендован до: '.$arenda.' '.date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'));
+ }
+
+ if($slots < 1 || $slots > $max)
+ sys::outjs(array('e' => 'Переданые неверные данные'), $nmch);
+
+ // Выполнение операции
+ if($go)
+ {
+ $slots_new = $server['slots']+$slots;
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']), $nmch);
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Реф. система
+ games::part($user['id'], $sum);
+
+ $start = $server['slots_start'] == $server['slots'] ? ', `slots_start`="'.$slots_new.'"' : '';
+
+ // Обновление информации
+ $sql->query('UPDATE `servers` set `slots`="'.$slots_new.'" '.$start.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')) AND $slots_new != $server['slots_start'])
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_slots'),
+ array('slots' => $slots, 'money' => $sum, 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$sum.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => 'Цена за дополнительные слоты: '.$sum.' '.$cfg['currency']));
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/index.php b/system/sections/megp/servers/index.php
new file mode 100644
index 0000000..0b71c47
--- /dev/null
+++ b/system/sections/megp/servers/index.php
@@ -0,0 +1,11 @@
+query('SELECT `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'index');
+
+ include(SEC.'megp/servers/'.$server['game'].'/index.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/list.php b/system/sections/megp/servers/list.php
new file mode 100644
index 0000000..58c7acf
--- /dev/null
+++ b/system/sections/megp/servers/list.php
@@ -0,0 +1,90 @@
+query('SELECT `unit`, `tarif` FROM `servers` WHERE `user`="'.$user['id'].'" ORDER BY `id` ASC');
+
+ $n = $sql->num($q_Servers);
+
+ $aUnits = array();
+ $aTarifs = array();
+
+ // Проверка массивов в кеше
+ if(is_array($mcache->get('aut_'.$user['id'])) AND $mcache->get('nser_'.$user['id']) == $n)
+ {
+ $aUT = $mcache->get('aut_'.$user['id']);
+ $aUnits = $aUT[0];
+ $aTarifs = $aUT[1];
+ }else{
+ while($server = $sql->get($q_Servers))
+ {
+ if(!array_key_exists($server['unit'], $aUnits))
+ {
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $aUnits[$server['unit']] = array(
+ 'name' => $unit['name']
+ );
+ }
+
+ if(!array_key_exists($server['tarif'], $aTarifs))
+ {
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aTarifs[$server['tarif']] = array(
+ 'name' => $tarif['name'],
+ 'packs' => sys::b64djs($tarif['packs'])
+ );
+ }
+ }
+
+ // Запись массивов в кеш
+ $mcache->set('aut_'.$user['id'], array($aUnits, $aTarifs), false, 60);
+
+ // Запись кол-во серверов в кеш
+ $mcache->set('nser_'.$user['id'], $n, false, 60);
+ }
+
+ include(LIB.'games/games.php');
+
+ $sql->query('SELECT '
+ .'`id`,'
+ .'`unit`,'
+ .'`tarif`,'
+ .'`address`,'
+ .'`game`,'
+ .'`slots_start`,'
+ .'`online`,'
+ .'`status`,'
+ .'`name`,'
+ .'`map`,'
+ .'`time`,'
+ .'`overdue`'
+ .' FROM `servers` WHERE `user`="'.$user['id'].'" ORDER BY `id` ASC');
+
+ $wait_servers = '';
+ $updates_servers = '';
+
+ while($server = $sql->get())
+ {
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('list', 'sections/servers');
+
+ $html->set('id', $server['id']);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('time_end', $time_end);
+
+ $html->pack('list');
+
+ $wait_servers .= $server['id'].':false,';
+ $updates_servers .= 'setTimeout(function() {update_info(\''.$server['id'].'\', true)}, 5000); setTimeout(function() {update_status(\''.$server['id'].'\', true)}, 10000);';
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mc/console.php b/system/sections/megp/servers/mc/console.php
new file mode 100644
index 0000000..78da5ef
--- /dev/null
+++ b/system/sections/megp/servers/mc/console.php
@@ -0,0 +1,14 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mc/index.php b/system/sections/megp/servers/mc/index.php
new file mode 100644
index 0000000..8537694
--- /dev/null
+++ b/system/sections/megp/servers/mc/index.php
@@ -0,0 +1,40 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `map`, `time`, `date`, `overdue`, `ram` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status'], $server['game']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name'].' / '.$server['ram'].' RAM');
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mc/settings.php b/system/sections/megp/servers/mc/settings.php
new file mode 100644
index 0000000..7d7260d
--- /dev/null
+++ b/system/sections/megp/servers/mc/settings.php
@@ -0,0 +1,46 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $aSub = array('start', 'pack');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ $dir = $url['subsection'] == 'start' ? 'megp/' : '';
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.$dir.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mc/settings/start.php b/system/sections/megp/servers/mc/settings/start.php
new file mode 100644
index 0000000..c64a63b
--- /dev/null
+++ b/system/sections/megp/servers/mc/settings/start.php
@@ -0,0 +1,44 @@
+query('SELECT `uid`, `slots`, `slots_start`, `autorestart` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ include(LIB.'games/games.php');
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mc/tarif.php b/system/sections/megp/servers/mc/tarif.php
new file mode 100644
index 0000000..7d95fa2
--- /dev/null
+++ b/system/sections/megp/servers/mc/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'slots');
+
+ include(SEC.'megp/servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mc/tarif/extend.php b/system/sections/megp/servers/mc/tarif/extend.php
new file mode 100644
index 0000000..1c37b4a
--- /dev/null
+++ b/system/sections/megp/servers/mc/tarif/extend.php
@@ -0,0 +1,53 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $ram = $server['slots_fix'] ? $server['ram'] : $server['ram']/$server['slots'];
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['ram'] = $ram;
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ $aPrice = explode(':', $tarif['price']);
+
+ // Цена за 30 дней 1 слота
+ $price = $aPrice[array_search($ram, explode(':', $tarif['ram']))];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mc/tarif/plan.php b/system/sections/megp/servers/mc/tarif/plan.php
new file mode 100644
index 0000000..6f4e3e2
--- /dev/null
+++ b/system/sections/megp/servers/mc/tarif/plan.php
@@ -0,0 +1,68 @@
+ 'Переданые не все данные'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aRAM = explode(':', $tarif['ram']);
+
+ // Проверка плана
+ if(array_search($plan, $aRAM) === FALSE)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ $ram = $server['slots_fix'] ? $server['ram'] : $server['ram']/$server['slots'];
+
+ if($plan == $ram)
+ sys::outjs(array('e' => 'Смысла в этой операции нет'), $nmch);
+
+ if(!tarif::price($tarif['price']))
+ sys::outjs(array('e' => 'Чтобы изменить тариф, перейдите в настройки запуска'), $nmch);
+
+ if($server['time'] < $start_point+86400)
+ $time = $server['time'];
+ else{
+ // Цена за 1 день аренды (по новому тарифному плану)
+ $price = $aPrice[array_search($plan, $aRAM)]/30*$server['slots'];
+
+ // Цена за 1 день аренды (по старому тарифному плану)
+ $price_old = $aPrice[array_search($ram, $aRAM)]/30*$server['slots'];
+
+ // Остаток дней аренды
+ $days = ($server['time']-$start_point)/86400;
+
+ $time = date('H:i:s', $server['time']);
+ $date = date('d.m.Y', round($start_point+$days*$price_old/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+ $aTime = explode(':', $time);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2]);
+ }
+
+ $plan = $server['slots_fix'] ? $plan : $plan*$server['slots'];
+
+ // Выполнение смена тарифного плана
+ if($go)
+ {
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `ram`="'.$plan.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart')))
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_plan').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mc/tarif/slots.php b/system/sections/megp/servers/mc/tarif/slots.php
new file mode 100644
index 0000000..5126e84
--- /dev/null
+++ b/system/sections/megp/servers/mc/tarif/slots.php
@@ -0,0 +1,158 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aRAM = explode(':', $tarif['ram']);
+
+ $ram = $server['ram_fix'] ? $server['ram'] : $server['ram']/$server['slots'];
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $aPrice[array_search($ram, $aRAM)]/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($aPrice[array_search($ram, $aRAM)]/30)*$slots, 2);
+
+ // Изменение кол-ва слот за счет пересчета дней аренды или закончился срок аренды (иначе аренда дополнительных слот)
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ $ram = $server['ram_fix'] ? $server['ram'] : $server['ram']/$server['slots']*$slots;
+
+ // Если просрочен
+ if($overdue)
+ {
+ sys::outjs(array('i' => ''));
+
+ if($go)
+ {
+ $start = $server['slots_start'] > $slots ? ', `slots_start`="'.$slots.'"' : '';
+
+ $sql->query('UPDATE `servers` set `slots`="'.$slots.'" '.$start.', `ram`='.$ram.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_slots').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // При возможности уменьшить
+ if($cfg['change_slots'][$server['game']]['down'] || $overdue)
+ {
+ // Проверка кол-ва слот
+ if($slots < $tarif['slots_min'] || $slots > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданые неверные данные.'), $nmch);
+
+ if($server['slots'] == $slots)
+ {
+ if($go)
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ sys::outjs(array('s' => 'Сервер будет арендован до: '.date('d.m.Y - H:i', $server['time']).' ('.sys::date('min', $server['time']).')'), $nmch);
+ }
+ }else{
+ // Установлено макс. значение
+ if($server['slots'] == $tarif['slots_max'] AND !$overdue)
+ sys::outjs(array('e' => 'На игровом сервере установлено максимальное значение.'), $nmch);
+
+ if($slots < 1 || $slots > $max)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ $slots += $server['slots'];
+ }
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$price_old/($price*$slots)*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ // При уменьшении кол-ва слот не добавлять дни
+ if($slots < $server['slots'] AND ($cfg['change_slots'][$server['game']]['days'] AND $cfg['change_slots'][$server['game']]['down'] AND !$cfg['change_slots'][$server['game']]['add']))
+ $time = $server['time'];
+
+ // Выполнение операции
+ if($go)
+ {
+ $start = $server['slots_start'] > $slots ? ', `slots_start`="'.$slots.'"' : '';
+
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `slots`="'.$slots.'" '.$start.', `ram`='.$ram.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')) AND $slots < $server['slots_start'])
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_slots').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => 'Сервер будет арендован до: '.$arenda.' '.date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'));
+ }
+
+ if($slots < 1 || $slots > $max)
+ sys::outjs(array('e' => 'Переданые неверные данные'), $nmch);
+
+ // Выполнение операции
+ if($go)
+ {
+ $slots_new = $server['slots']+$slots;
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']), $nmch);
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Реф. система
+ games::part($user['id'], $sum);
+
+ $start = $server['slots_start'] == $server['slots'] ? ', `slots_start`="'.$slots_new.'"' : '';
+
+ $ram = $server['ram_fix'] ? $server['ram'] : $server['ram']/$server['slots']*$slots_new;
+
+ // Обновление информации
+ $sql->query('UPDATE `servers` set `slots`="'.$slots_new.'" '.$start.', `ram`='.$ram.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart')) AND $slots_new != $server['slots_start'])
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_slots'),
+ array('slots' => $slots, 'money' => $sum, 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$sum.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => 'Цена за дополнительные слоты: '.$sum.' '.$cfg['currency']));
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mta/console.php b/system/sections/megp/servers/mta/console.php
new file mode 100644
index 0000000..78da5ef
--- /dev/null
+++ b/system/sections/megp/servers/mta/console.php
@@ -0,0 +1,14 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mta/index.php b/system/sections/megp/servers/mta/index.php
new file mode 100644
index 0000000..2025c89
--- /dev/null
+++ b/system/sections/megp/servers/mta/index.php
@@ -0,0 +1,35 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status'], $server['game']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name']);
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+ $html->set('btn', $btn);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mta/settings.php b/system/sections/megp/servers/mta/settings.php
new file mode 100644
index 0000000..7d7260d
--- /dev/null
+++ b/system/sections/megp/servers/mta/settings.php
@@ -0,0 +1,46 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $aSub = array('start', 'pack');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ $dir = $url['subsection'] == 'start' ? 'megp/' : '';
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.$dir.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mta/settings/start.php b/system/sections/megp/servers/mta/settings/start.php
new file mode 100644
index 0000000..7721155
--- /dev/null
+++ b/system/sections/megp/servers/mta/settings/start.php
@@ -0,0 +1,50 @@
+query('SELECT `uid`, `slots`, `slots_start`, `autorestart` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ include(LIB.'games/games.php');
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `servers` set `autorestart`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mta/tarif.php b/system/sections/megp/servers/mta/tarif.php
new file mode 100644
index 0000000..7d95fa2
--- /dev/null
+++ b/system/sections/megp/servers/mta/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'slots');
+
+ include(SEC.'megp/servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mta/tarif/extend.php b/system/sections/megp/servers/mta/tarif/extend.php
new file mode 100644
index 0000000..cf31b1e
--- /dev/null
+++ b/system/sections/megp/servers/mta/tarif/extend.php
@@ -0,0 +1,48 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ // Цена за 30 дней 1 слота
+ $price = $tarif['price'];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/mta/tarif/slots.php b/system/sections/megp/servers/mta/tarif/slots.php
new file mode 100644
index 0000000..3a60d63
--- /dev/null
+++ b/system/sections/megp/servers/mta/tarif/slots.php
@@ -0,0 +1,31 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $tarif['price']/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($tarif['price']/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/noaccess.php b/system/sections/megp/servers/noaccess.php
new file mode 100644
index 0000000..eb3d106
--- /dev/null
+++ b/system/sections/megp/servers/noaccess.php
@@ -0,0 +1,22 @@
+get('overdue');
+ else{
+ $status = array(
+ 'install' => 'установки',
+ 'reinstall' => 'переустановки',
+ 'update' => 'обновления',
+ 'recovery' => 'восстановления',
+ 'blocked' => 'блокировки'
+ );
+
+ $html->get('noaccess');
+
+ $html->set('status', $status[$server['status']]);
+ }
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/owners_list.php b/system/sections/megp/servers/owners_list.php
new file mode 100644
index 0000000..b08c2b4
--- /dev/null
+++ b/system/sections/megp/servers/owners_list.php
@@ -0,0 +1,102 @@
+query('SELECT `server` FROM `owners` WHERE `user`="'.$user['id'].'" AND `time`>"'.$start_point.'" ORDER BY `id` ASC');
+
+ $n = $sql->num($owners);
+
+ $aUnits = array();
+ $aTarifs = array();
+
+ // Проверка массивов в кеше
+ if(is_array($mcache->get('owners_aut_'.$user['id'])) AND $mcache->get('owners_nser_'.$user['id']) == $n)
+ {
+ $aUT = $mcache->get('owners_aut_'.$user['id']);
+ $aUnits = $aUT[0];
+ $aTarifs = $aUT[1];
+ }else{
+ while($owner = $sql->get($owners))
+ {
+ $server_sql = $sql->query('SELECT `unit`, `tarif` FROM `servers` WHERE `id`="'.$owner['server'].'"');
+
+ while($server = $sql->get($server_sql))
+ {
+ if(!array_key_exists($server['unit'], $aUnits))
+ {
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $aUnits[$server['unit']] = array(
+ 'name' => $unit['name']
+ );
+ }
+
+ if(!array_key_exists($server['tarif'], $aTarifs))
+ {
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aTarifs[$server['tarif']] = array(
+ 'name' => $tarif['name'],
+ 'packs' => sys::b64djs($tarif['packs'])
+ );
+ }
+ }
+ }
+
+ // Запись массивов в кеш
+ $mcache->set('owners_aut_'.$user['id'], array($aUnits, $aTarifs), false, 60);
+
+ // Запись кол-во серверов в кеш
+ $mcache->set('owners_nser_'.$user['id'], $n, false, 60);
+ }
+
+ $owners = $sql->query('SELECT `id`, `server`, `time` FROM `owners` WHERE `user`="'.$user['id'].'" ORDER BY `id` ASC');
+
+ while($owner = $sql->get($owners))
+ {
+ if($owner['time'] < $start_point)
+ {
+ $sql->query('DELETE FROM `owners` WHERE `id`="'.$owner['id'].'" LIMIT 1');
+
+ continue;
+ }
+
+ $sql->query('SELECT '
+ .'`id`,'
+ .'`unit`,'
+ .'`tarif`,'
+ .'`address`,'
+ .'`game`,'
+ .'`slots_start`,'
+ .'`online`,'
+ .'`status`,'
+ .'`name`,'
+ .'`map`,'
+ .'`time`,'
+ .'`overdue`'
+ .' FROM `servers` WHERE `id`="'.$owner['server'].'" LIMIT 1');
+
+ while($server = $sql->get())
+ {
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('list', 'sections/servers');
+
+ $html->set('id', $server['id']);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('time_end', $time_end);
+
+ $html->pack('list');
+
+ $wait_servers .= $server['id'].':false,';
+ $updates_servers .= 'setTimeout(function() {update_info(\''.$server['id'].'\', true)}, 5000); setTimeout(function() {update_status(\''.$server['id'].'\', true)}, 10000);';
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/samp/console.php b/system/sections/megp/servers/samp/console.php
new file mode 100644
index 0000000..78da5ef
--- /dev/null
+++ b/system/sections/megp/servers/samp/console.php
@@ -0,0 +1,14 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/samp/index.php b/system/sections/megp/servers/samp/index.php
new file mode 100644
index 0000000..2025c89
--- /dev/null
+++ b/system/sections/megp/servers/samp/index.php
@@ -0,0 +1,35 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status'], $server['game']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name']);
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+ $html->set('btn', $btn);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/samp/settings.php b/system/sections/megp/servers/samp/settings.php
new file mode 100644
index 0000000..7d7260d
--- /dev/null
+++ b/system/sections/megp/servers/samp/settings.php
@@ -0,0 +1,46 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $aSub = array('start', 'pack');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ $dir = $url['subsection'] == 'start' ? 'megp/' : '';
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.$dir.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/samp/settings/start.php b/system/sections/megp/servers/samp/settings/start.php
new file mode 100644
index 0000000..c64a63b
--- /dev/null
+++ b/system/sections/megp/servers/samp/settings/start.php
@@ -0,0 +1,44 @@
+query('SELECT `uid`, `slots`, `slots_start`, `autorestart` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ include(LIB.'games/games.php');
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/samp/tarif.php b/system/sections/megp/servers/samp/tarif.php
new file mode 100644
index 0000000..7d95fa2
--- /dev/null
+++ b/system/sections/megp/servers/samp/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'slots');
+
+ include(SEC.'megp/servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/samp/tarif/extend.php b/system/sections/megp/servers/samp/tarif/extend.php
new file mode 100644
index 0000000..cf31b1e
--- /dev/null
+++ b/system/sections/megp/servers/samp/tarif/extend.php
@@ -0,0 +1,48 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ // Цена за 30 дней 1 слота
+ $price = $tarif['price'];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/samp/tarif/slots.php b/system/sections/megp/servers/samp/tarif/slots.php
new file mode 100644
index 0000000..3a60d63
--- /dev/null
+++ b/system/sections/megp/servers/samp/tarif/slots.php
@@ -0,0 +1,31 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $tarif['price']/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($tarif['price']/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/scan.php b/system/sections/megp/servers/scan.php
new file mode 100644
index 0000000..f2e2f23
--- /dev/null
+++ b/system/sections/megp/servers/scan.php
@@ -0,0 +1,27 @@
+query('SELECT `game` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ include(LIB.'games/'.$server['game'].'/scan.php');
+
+ // Запрошена информация (статус, онлайн, название)
+ if(isset($url['mon']))
+ sys::outjs(scan::mon($id));
+
+ // Запрошена информация (статус, онлайн, название, игроки)
+ if(isset($url['fmon']))
+ sys::outjs(scan::mon($id, true));
+
+ // Запрошена информация (cpu, ram, hdd)
+ if(isset($url['resources']))
+ sys::outjs(scan::resources($id));
+
+ // Запрошена информация (работает, меняется карта, переустанавливается)
+ if(isset($url['status']))
+ sys::outjs(scan::status($id));
+
+ exit;
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/settings.php b/system/sections/megp/servers/settings.php
new file mode 100644
index 0000000..d8dd817
--- /dev/null
+++ b/system/sections/megp/servers/settings.php
@@ -0,0 +1,11 @@
+query('SELECT `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'settings');
+
+ include(sys::route($server, 'settings', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/megp/servers/tarif.php b/system/sections/megp/servers/tarif.php
new file mode 100644
index 0000000..63c161a
--- /dev/null
+++ b/system/sections/megp/servers/tarif.php
@@ -0,0 +1,18 @@
+query('SELECT `uid`, `unit`, `user`, `tarif`, `address`, `port`, `game`, `status`, `slots`, `slots_start`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time`, `test`, `fps`, `tickrate`, `ram`, `ram_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'tarif');
+
+ if($server['status'] == 'blocked')
+ {
+ if($go)
+ sys::out('Раздел недоступен');
+
+ include(SEC.'megp/servers/noaccess.php');
+ }else
+ include(SEC.'megp/servers/'.$server['game'].'/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/monitoring/all.php b/system/sections/monitoring/all.php
new file mode 100644
index 0000000..69d25be
--- /dev/null
+++ b/system/sections/monitoring/all.php
@@ -0,0 +1,119 @@
+nav('Мониторинг');
+
+// Дополнительная переменная
+$type = false;
+$i = 0;
+
+// Получаем значение пагинации
+if(isset($url['page'])){
+ $page = sys::clean($url['page'], "int");
+}else{
+ $page = 1;
+}
+
+// Проверяем задано ли у нас фильтрация по типу игры
+if(isset($url['game']) AND in_array($url['game'], array('cs', 'css', 'cssold', 'csgo', 'samp', 'crmp', 'mta', 'mc'))){
+ $type = $url['game'];
+}
+
+// Если идет сортировка по игре
+if($type){
+
+ // SQL запрос для выборки
+ $qSql = "game = '{$type}' AND status = 'working'";
+
+ // Задаем переменной колличество серверов всего, результат кэша
+ $all = $mcache->get('monitoring_list_count_'.$type);
+
+ // Если кэш пуст
+ if(!$all){
+
+ // Получаем инфу из бд, кооличество серверов всего
+ $sql->query("SELECT id FROM servers WHERE {$qSql}");
+ $all = $sql->num();
+
+ // Закидываем значеие в кэш на 2 минуты
+ $mcache->set('monitoring_list_count_'.$type, $all, false, 120);
+ }
+
+ // Массив для построения страниц
+ $aPage = sys::page($page, $all, 30);
+
+ // Генерация массива ($html->arr['pages']) страниц
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'monitoring/type/'.$type);
+}else{
+
+ // SQL запрос для выборки
+ $qSql = "status = 'working'";
+
+ // Задаем переменной колличество серверов всего, результат кэша
+ $all = $mcache->get('monitoring_list_count');
+
+ // Если кэш пуст
+ if(!$all){
+
+ // Получаем инфу из бд, кооличество серверов всего
+ $sql->query("SELECT id FROM servers WHERE {$qSql}");
+ $all = $sql->num();
+
+ // Закидываем значеие в кэш на 2 минуты
+ $mcache->set('monitoring_list_count', $all, false, 120);
+ }
+
+ // Массив для построения страниц
+ $aPage = sys::page($page, $all, 30);
+
+ // Генерация массива ($html->arr['pages']) страниц
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'monitoring');
+}
+
+
+// Получаем список серверов
+$sql->query("SELECT `id`, `address`, `name`, `map`, `slots_start`, `online` FROM servers WHERE {$qSql} ORDER BY `id` ASC LIMIT {$aPage['num']}, 30");
+
+// Циклически собираем шаблон серверов
+while($server = $sql->get()) {
+
+ // Увеличиваем значение ID
+ $i+=1;
+
+ // Собираем шаблон
+ $html->get('list', 'sections/monitoring');
+ $html->set('id', $i);
+ $html->set('server', $server['id']);
+ $html->set('address', $server['address']);
+ $html->set('name', $server['name']);
+ $html->set('map', $server['map']);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->pack('monitoring_list');
+}
+
+$games = array('cs', 'cssold', 'css', 'csgo', 'samp', 'crmp', 'mta', 'mc');
+ $online = array();
+ foreach($games as $game)
+ {
+ $sql->query('SELECT SUM(`online`) AS `online` FROM `servers` WHERE (`status`="working" OR `status`="change") AND `game`="'.$game.'"');
+ $online[$game] = $sql->get()['online'];
+ }
+
+// Подготовка страницы
+$html->get('all', 'sections/monitoring');
+foreach($games as $game)
+ if(!empty($online[$game]))
+ $html->set('o_'.$game, $online[$game]);
+ else
+ $html->set('o_'.$game, '0');
+ $html->set('list', isset($html->arr['monitoring_list']) ? $html->arr['monitoring_list'] : '');
+ $html->set('pages', isset($html->arr['pages']) ? $html->arr['pages'] : '');
+$html->pack('main');
\ No newline at end of file
diff --git a/system/sections/monitoring/server.php b/system/sections/monitoring/server.php
new file mode 100644
index 0000000..e79554b
--- /dev/null
+++ b/system/sections/monitoring/server.php
@@ -0,0 +1,77 @@
+nav('Мониторинг', $cfg['http'].'monitoring');
+$html->nav('Сервер #'.$id);
+
+// Получаем информацию о сервере
+$sql->query("SELECT `id`, `unit`, `tarif`, `address`, `name`, `map`, `slots_start`, `online`, `players`, `status`, `game`, `pack`, `date` FROM servers WHERE id = '{$id}' LIMIT 1");
+$server = $sql->get();
+
+// Если результат пустой
+if(empty($server)){
+ header('Refresh: 0; URL='.$cfg['http'].'monitoring');
+ exit();
+}
+
+// Получаем название локации
+$sql->query("SELECT name FROM units WHERE id = '{$server['unit']}' LIMIT 1");
+$unit = $sql->get();
+
+// Получаем название тарифа и доступные сборки
+$sql->query("SELECT name, packs FROM tarifs WHERE id = '{$server['tarif']}' LIMIT 1");
+$tarif = $sql->get();
+
+// Получаем массив сборок
+$aPacks = json_decode(base64_decode($tarif['packs']), true);
+
+// Получаем ключ для графиков
+$sql->query("SELECT `key` FROM graph WHERE server = '{$id}' LIMIT 1");
+
+// Если ключ отсуствует, создаем
+if(!$sql->num()){
+
+ // Генерируем ключ
+ $key = md5($id.sys::key('graph'));
+
+ // Добавляем в DB
+ $sql->query("INSERT INTO graph SET `server` = '{id}', `key` = '{key}', `time` = '0'");
+}else{
+
+ // Получаем ключ из бд
+ $graph = $sql->get();
+ $key = $graph['key'];
+}
+
+// Подготовка страницы
+$html->get('server', 'sections/monitoring');
+ $html->set('id', $server['id']);
+ $html->set('key', $key);
+ $html->set('address', $server['address']);
+ $html->set('name', $server['name']);
+ $html->set('map', $server['map']);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name']);
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img'));
+ $html->set('pack', $aPacks[$server['pack']]);
+ $html->set('create', date("d.m.Y H:m", $server['date']));
+$html->pack('main');
\ No newline at end of file
diff --git a/system/sections/news/index.php b/system/sections/news/index.php
new file mode 100644
index 0000000..dd42140
--- /dev/null
+++ b/system/sections/news/index.php
@@ -0,0 +1,30 @@
+nav('Список новостей');
+
+ $sql->query('SELECT `id` FROM `news`');
+
+ $aPage = sys::page($page, $sql->num(), $cfg['news_page']);
+
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'news');
+
+ $sql->query('SELECT `id`, `name`, `text`, `views`, `tags`, `date` FROM `news` ORDER BY `id` DESC LIMIT '.$aPage['num'].', '.$cfg['news_page']);
+ while($news = $sql->get())
+ {
+ $html->get('list', 'sections/news');
+ $html->set('id', $news['id']);
+ $html->set('name', htmlspecialchars_decode($news['name']));
+ $html->set('text', htmlspecialchars_decode($news['text']));
+ $html->set('views', $news['views']);
+ $html->set('tags', sys::tags($news['tags']));
+ $html->set('date', sys::today($news['date']));
+ $html->pack('news');
+ }
+
+ $html->get('all', 'sections/news');
+ $html->set('list', isset($html->arr['news']) ? $html->arr['news'] : '');
+ $html->set('pages', isset($html->arr['pages']) ? $html->arr['pages'] : '');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/news/news.php b/system/sections/news/news.php
new file mode 100644
index 0000000..d0b7738
--- /dev/null
+++ b/system/sections/news/news.php
@@ -0,0 +1,34 @@
+nav('Список новостей', $cfg['http'].'news');
+
+ $sql->query('SELECT `id`, `name`, `full_text`, `views`, `tags`, `date` FROM `news` WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ include(ENG.'404.php');
+
+ $news = $sql->get();
+
+ $sql->query('UPDATE `news` set `views`="'.($news['views']+1).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $text = htmlspecialchars_decode($news['full_text']);
+
+ $title = $news['name'];
+ $description = $text;
+ $keywords = $news['tags'];
+
+ $html->nav($news['name']);
+
+ $html->get('news', 'sections/news');
+
+ $html->set('id', $news['id']);
+ $html->set('name', htmlspecialchars_decode($news['name']));
+ $html->set('text', $text);
+ $html->set('views', $news['views']);
+ $html->set('tags', sys::tags($news['tags']));
+ $html->set('date', sys::today($news['date']));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/action.php b/system/sections/servers/action.php
new file mode 100644
index 0000000..e8004d3
--- /dev/null
+++ b/system/sections/servers/action.php
@@ -0,0 +1,68 @@
+query('SELECT `game`, `status` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!isset($url['action']))
+ sys::outjs(array('e' => 'Неверный запрос для выполнения операции'));
+
+ $nmch = 'server_action_'.$id;
+
+ if($mcache->get($nmch))
+ sys::outjs(array('e' => sys::text('other', 'mcache')));
+
+ $mcache->set($nmch, true, false, 10);
+
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ switch($url['action'])
+ {
+ case 'stop':
+ if(!in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ sys::outjs(array('e' => sys::text('error', 'ser_stop')), $nmch);
+
+ sys::outjs(action::stop($id), $nmch);
+
+ case 'start':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => sys::text('error', 'ser_start')), $nmch);
+
+ sys::outjs(action::start($id), $nmch);
+
+ case 'restart':
+ if(!in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ sys::outjs(array('e' => sys::text('error', 'ser_restart')), $nmch);
+
+ sys::outjs(action::start($id, 'restart'), $nmch);
+
+ case 'change':
+ if($server['status'] != 'working')
+ {
+ if($server['status'] == 'change')
+ sys::outjs(array('e' => sys::text('other', 'mcache')), $nmch);
+
+ sys::outjs(array('e' => sys::text('error', 'ser_change')), $nmch);
+ }
+
+ if(isset($url['change']))
+ sys::outjs(action::change($id, $url['change']), $nmch);
+
+ sys::outjs(action::change($id), $nmch);
+
+ case 'reinstall':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => sys::text('error', 'ser_reinstall')), $nmch);
+
+ sys::outjs(action::reinstall($id), $nmch);
+
+ case 'update':
+ if($server['status'] != 'off')
+ sys::outjs(array('e' => sys::text('error', 'ser_update')), $nmch);
+
+ sys::outjs(action::update($id), $nmch);
+ }
+
+ exit;
+?>
\ No newline at end of file
diff --git a/system/sections/servers/boost.php b/system/sections/servers/boost.php
new file mode 100644
index 0000000..3f9d4aa
--- /dev/null
+++ b/system/sections/servers/boost.php
@@ -0,0 +1,11 @@
+query('SELECT `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'boost');
+
+ include(sys::route($server, 'boost', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/console.php b/system/sections/servers/console.php
new file mode 100644
index 0000000..f49ac21
--- /dev/null
+++ b/system/sections/servers/console.php
@@ -0,0 +1,11 @@
+query('SELECT `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'console');
+
+ include(sys::route($server, 'console', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/copy.php b/system/sections/servers/copy.php
new file mode 100644
index 0000000..f075051
--- /dev/null
+++ b/system/sections/servers/copy.php
@@ -0,0 +1,11 @@
+query('SELECT `uid`, `unit`, `tarif`, `user`, `address`, `game`, `pack`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'copy');
+
+ include(sys::route($server, 'copy', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/console.php b/system/sections/servers/crmp/console.php
new file mode 100644
index 0000000..3c42b22
--- /dev/null
+++ b/system/sections/servers/crmp/console.php
@@ -0,0 +1,44 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if($server['status'] == 'off')
+ sys::out(sys::text('servers', 'off'));
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::out(sys::text('error', 'ssh'));
+
+ $dir = $tarif['install'].$server['uid'].'/';
+
+ $filecmd = $dir.'server_log.txt';
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/copy.php b/system/sections/servers/crmp/copy.php
new file mode 100644
index 0000000..7a9c216
--- /dev/null
+++ b/system/sections/servers/crmp/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('server_copy_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_copy_'.$id);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `copy` WHERE `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_copy_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/graph.php b/system/sections/servers/crmp/graph.php
new file mode 100644
index 0000000..d8d8df8
--- /dev/null
+++ b/system/sections/servers/crmp/graph.php
@@ -0,0 +1,71 @@
+query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+ $graph = $sql->get();
+
+ $nmch = 'server_graph_full_'.$id;
+
+ $time = isset($url['time']) ? $url['time'] : 'day';
+
+ if(!in_array($time, array('day', 'week', 'month')))
+ $time = 'day';
+
+ // Выхлоп кеш графика
+ if($mcache->get($nmch) AND file_exists(TEMP.(md5($graph['key'].'full_'.$time)).'.png'))
+ {
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ include(LIB.'games/graph/pData.php');
+ include(LIB.'games/graph/pDraw.php');
+ include(LIB.'games/graph/pImage.php');
+
+ include(LIB.'games/graph.php');
+
+ graph::full($id, $server['slots_start'], $graph['key'], $time);
+
+ $mcache->set($nmch, true, false, 300);
+
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Графики');
+
+ if($mcache->get('server_graph_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_graph_'.$id);
+ else{
+ $sql->query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+
+ // Если отсутствует ключ, создать
+ if(!$sql->num())
+ {
+ // Генерация ключа
+ $key = md5($id.sys::key('graph'));
+
+ $sql->query('INSERT INTO `graph` set `server`="'.$id.'", `key`="'.$key.'", `time`="0"');
+ }else{
+ $graph = $sql->get();
+
+ $key = $graph['key'];
+ }
+
+ $html->get('graph', 'sections/servers/games');
+
+ $html->set('id', $id);
+
+ $html->set('key', $key);
+ $html->set('address', $server['address']);
+ $html->set('_img', '[img]');
+
+ $html->pack('main');
+
+ $mcache->set('server_graph_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/index.php b/system/sections/servers/crmp/index.php
new file mode 100644
index 0000000..3f35c48
--- /dev/null
+++ b/system/sections/servers/crmp/index.php
@@ -0,0 +1,44 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address']);
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status'], $server['game']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name']);
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], 'crmp', 'img'));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/plugins.php b/system/sections/servers/crmp/plugins.php
new file mode 100644
index 0000000..a116ca1
--- /dev/null
+++ b/system/sections/servers/crmp/plugins.php
@@ -0,0 +1,158 @@
+query('SELECT `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $nmch = sys::rep_act('server_plugins_go_'.$id, 10);
+
+ include(SEC.'servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('server_plugins_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_plugins_'.$id);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$id.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `plugins_install` WHERE `server`="'.$id.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_plugins_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/rcon.php b/system/sections/servers/crmp/rcon.php
new file mode 100644
index 0000000..1a0de70
--- /dev/null
+++ b/system/sections/servers/crmp/rcon.php
@@ -0,0 +1,54 @@
+ 'Необходимо выбрать игрока.'));
+
+ if($url['action'] == 'kick')
+ rcon::cmd(array_merge($server, array('id' => $id)), 'kickid "'.$player.'" "EGP Panel"');
+ else
+ rcon::cmd(array_merge($server, array('id' => $id)), 'sm_slay "'.$player.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ include(LIB.'geo.php');
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $aPlayers = rcon::players(rcon::cmd(array_merge($server, array('id' => $id))));
+
+ foreach($aPlayers as $i => $aPlayer)
+ {
+ $html->get('player', 'sections/servers/'.$server['game'].'/rcon');
+
+ $html->set('i', $i);
+ $html->set('userid', $aPlayer['userid']);
+ $html->set('name', $aPlayer['name']);
+ $html->set('steamid', $aPlayer['steamid']);
+ $html->set('time', $aPlayer['time']);
+ $html->set('ping', $aPlayer['ping']);
+ $html->set('ip', $aPlayer['ip']);
+ $html->set('ico', $aPlayer['ico']);
+ $html->set('country', $aPlayer['country']);
+
+ $html->pack('players');
+ }
+
+ sys::outjs(array('s' => isset($html->arr['players']) ? $html->arr['players'] : ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Rcon управление игроками');
+
+ $html->get('rcon', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/settings.php b/system/sections/servers/crmp/settings.php
new file mode 100644
index 0000000..132d29b
--- /dev/null
+++ b/system/sections/servers/crmp/settings.php
@@ -0,0 +1,72 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $aSub = array('start', 'server', 'firewall', 'crontab', 'startlogs', 'pack', 'file', 'api');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aEditslist = 1;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ include(SEC.'servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+
+ $sql->query('SELECT `key` FROM `api` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ {
+ $api = $sql->get();
+
+ $html->set('api', $api['key']);
+ $html->unit('api', 1, 1);
+ }else
+ $html->unit('api', 0, 1);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/settings/server.php b/system/sections/servers/crmp/settings/server.php
new file mode 100644
index 0000000..c9b07a5
--- /dev/null
+++ b/system/sections/servers/crmp/settings/server.php
@@ -0,0 +1,119 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ $config_end = $servercfg['\'other\''];
+
+ unset($servercfg['\'other\'']);
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).' '.$val."\n";
+
+ // Временый файл
+ $temp = sys::temp($config.$config_end);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/server.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/server.cfg');
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Чтение файла - server.cfg
+ $file = $tarif['install'].$server['uid'].'/server.cfg';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+ $other = '';
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode(' ', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.' ', "", $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ else
+ $other .= $line."\n";
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $sql->query('UPDATE `servers` set `map_start`="'.htmlspecialchars($servercfg['rcon_password']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $html->get('servercfg', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('cfg', $html->arr['list']);
+ $html->set('other', $other);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/settings/start.php b/system/sections/servers/crmp/settings/start.php
new file mode 100644
index 0000000..862c835
--- /dev/null
+++ b/system/sections/servers/crmp/settings/start.php
@@ -0,0 +1,45 @@
+query('SELECT `uid`, `slots`, `slots_start`, `autorestart` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ include(LIB.'games/games.php');
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/tarif.php b/system/sections/servers/crmp/tarif.php
new file mode 100644
index 0000000..c321da7
--- /dev/null
+++ b/system/sections/servers/crmp/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'address', 'addextend', 'unit', 'slots');
+
+ include(SEC.'servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/tarif/extend.php b/system/sections/servers/crmp/tarif/extend.php
new file mode 100644
index 0000000..cf31b1e
--- /dev/null
+++ b/system/sections/servers/crmp/tarif/extend.php
@@ -0,0 +1,48 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ // Цена за 30 дней 1 слота
+ $price = $tarif['price'];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/tarif/slots.php b/system/sections/servers/crmp/tarif/slots.php
new file mode 100644
index 0000000..3a60d63
--- /dev/null
+++ b/system/sections/servers/crmp/tarif/slots.php
@@ -0,0 +1,31 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $tarif['price']/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($tarif['price']/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/tarif/unit.php b/system/sections/servers/crmp/tarif/unit.php
new file mode 100644
index 0000000..b840bc7
--- /dev/null
+++ b/system/sections/servers/crmp/tarif/unit.php
@@ -0,0 +1,50 @@
+ 'Переданы не все данные.'), $nmch);
+
+ if(!$cfg['change_unit'][$server['game']] || $server['time'] < $start_point+86400 || $server['test'])
+ exit;
+
+ $sql->query('SELECT `id`, `unit`, `packs`, `tickrate`, `price` FROM `tarifs` WHERE `unit`="'.$uid.'" AND `game`="'.$server['game'].'" AND `name`="'.$tarif['name'].'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Не найден подходящий тариф.'), $nmch);
+
+ $oldTarif = $tarif;
+
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $oldUnit = $sql->get();
+
+ $aPriceold = explode(':', $oldTarif['price']);
+ $aTICKold = explode(':', $oldTarif['tickrate']);
+
+ $sql->query('SELECT `id` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная локация не доступна.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ if(!in_array($server['tickrate'], $aTICK))
+ sys::outjs(array('e' => 'Не найден подходящий тарифный план.'), $nmch);
+
+ // Цена за 1 день (при новом тарифном плане)
+ $price = $aPrice[array_search($server['tickrate'], $aTICK)]/30*$server['slots'];
+
+ // Цена аренды за остаток дней (с текущим тарифным планом)
+ $oldprice = ($server['time']-$start_point)/86400*($aPriceold[array_search($server['tickrate'], $aTICKold)]/30*$server['slots']);
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$oldprice/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ include(SEC.'servers/games/tarif/unit.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/crmp/web.php b/system/sections/servers/crmp/web.php
new file mode 100644
index 0000000..634e9b6
--- /dev/null
+++ b/system/sections/servers/crmp/web.php
@@ -0,0 +1,87 @@
+nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ include(DATA.'web.php');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub) AND in_array($url['action'], array_merge($aAction[$url['subsection']], array('install', 'manage'))))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_web_go_'.$id, 10);
+ else
+ $html->nav('Web', $cfg['http'].'servers/id/'.$id.'/section/web');
+
+ include(SEC.'web/'.$url['subsection'].'/free/'.$url['action'].'.php');
+ }else{
+ $html->nav('Web');
+
+ if($mcache->get('server_web_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_web_'.$id);
+ else{
+ // Услуги
+ foreach($aWeb[$server['game']] as $service => $active)
+ {
+ if($active)
+ {
+ if($service == 'hosting')
+ {
+ if(!$aWebVHTtype && !in_array($server['tarif'], $aWebVHT))
+ continue;
+
+ if($aWebVHTtype && in_array($server['tarif'], $aWebVHT))
+ continue;
+ }
+
+ // Проверка на установку
+ switch($aWebInstall[$server['game']][$service])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ }
+
+ if($sql->num())
+ $html->get('list_install', 'sections/servers/games/web');
+ else
+ $html->get('list', 'sections/servers/games/web');
+
+ $html->set('id', $id);
+ $html->set('service', $service);
+ $html->set('name', $aWebname[$service]);
+ $html->set('desc', $aWebDesc[$service]);
+
+ $html->pack($aWebType[$service]);
+ }
+ }
+
+ // Блоки услуг
+ foreach($aWebTypeInfo[$server['game']] as $type => $name)
+ {
+ if(!isset($html->arr[$type]))
+ continue;
+
+ $html->get('block', 'sections/servers/games/web');
+ $html->set('name', $name);
+ $html->set('list', $html->arr[$type]);
+ $html->pack('web');
+ }
+
+ $html->get('web', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('web', isset($html->arr['web']) ? $html->arr['web'] : 'Дополнительные услуги отсутствуют');
+ $html->pack('main');
+
+ $mcache->set('server_web_'.$id, $html->arr['main'], false, 4);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/boost.php b/system/sections/servers/cs/boost.php
new file mode 100644
index 0000000..d539440
--- /dev/null
+++ b/system/sections/servers/cs/boost.php
@@ -0,0 +1,100 @@
+ 'Необходимо указать сервис.'));
+
+ // Проверка сервиса
+ if(!in_array($aData['site'], $aBoost[$server['game']]['boost']))
+ sys::outjs(array('e' => 'Указанный сервис по раскрутке не найден.'));
+
+ if(isset($url['rating']))
+ {
+ $rating = $url['rating'] == 'up' ? '1' : '-1';
+
+ $sql->query('SELECT `id` FROM `boost_rating` WHERE `boost`="'.$aData['site'].'" AND `user`="'.$user['id'].'" AND `rating`="'.$rating.'" LIMIT 1');
+ if($sql->num())
+ sys::out('err');
+
+ $sql->query('DELETE FROM `boost_rating` WHERE `boost`="'.$aData['site'].'" AND `user`="'.$user['id'].'" LIMIT 1');
+ $sql->query('INSERT INTO `boost_rating` set `boost`="'.$aData['site'].'", `rating`="'.$rating.'", `user`="'.$user['id'].'"');
+
+ $sql->query('SELECT SUM(`rating`) FROM `boost_rating` WHERE `boost`="'.$aData['site'].'"');
+ $sum = $sql->get();
+
+ $rating = (int) $sum['SUM(`rating`)'];
+
+ sys::out($rating, 'server_boost_'.$id);
+ }
+
+ $aData['service'] = isset($url['service']) ? sys::int($url['service']) : sys::outjs(array('e' => 'Необходимо указать номер услуги.'));
+
+ // Проверка номера услуги
+ if(!in_array($aData['service'], $aBoost[$server['game']][$aData['site']]['services']))
+ sys::outjs(array('e' => 'Неправильно указан номер услуги.'));
+
+ // Определение суммы
+ $sum = $aBoost[$server['game']][$aData['site']]['price'][$aData['service']];
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']), $name_mcache);
+
+ include(LIB.'games/boost.php');
+
+ $boost = new boost($aBoost[$server['game']][$aData['site']]['key'], $aBoost[$server['game']][$aData['site']]['api']);
+
+ $buy = $boost->$aBoost[$server['game']][$aData['site']]['type'](array('period' => $aData['service'], 'address' => $server['address']));
+
+ if(is_array($buy))
+ sys::outjs(array('e' => $buy['error']));
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ include(LIB.'games/games.php');
+
+ // Реф. система
+ games::part($user['id'], $sum);
+
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_boost'),
+ array('circles' => $aBoost[$server['game']][$aData['site']]['circles'][$aData['service']],
+ 'money' => $sum, 'site' => $aBoost[$server['game']][$aData['site']]['site'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="boost", `money`="'.$sum.'"');
+
+ $sql->query('INSERT INTO `boost` set `user`="'.$user['id'].'", `server`="'.$id.'", `site`="'.$aData['site'].'", `circles`="'.$aBoost[$server['game']][$aData['site']]['circles'][$aData['service']].'", `money`="'.$sum.'", `date`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Раскрутка');
+
+ if($mcache->get('server_boost_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_boost_'.$id);
+ else{
+ $html->get('boost', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('address', $server['address']);
+
+ foreach($aBoost[$server['game']]['boost'] as $boost)
+ {
+ $sql->query('SELECT SUM(`rating`) FROM `boost_rating` WHERE `boost`="'.$boost.'"');
+ $sum = $sql->get();
+
+ $rating = (int) $sum['SUM(`rating`)'];
+
+ $html->set($boost, $rating);
+ }
+
+ $html->pack('main');
+
+ $mcache->set('server_boost_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/console.php b/system/sections/servers/cs/console.php
new file mode 100644
index 0000000..fec0e74
--- /dev/null
+++ b/system/sections/servers/cs/console.php
@@ -0,0 +1,67 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $command = isset($_POST['command']) ? sys::cmd($_POST['command']) : '';
+
+ if($server['status'] == 'off')
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('servers', 'off')));
+
+ sys::out(sys::text('servers', 'off'));
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ sys::out(sys::text('error', 'ssh'));
+ }
+
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ $filecmd = $dir.'qconsole.log';
+
+ if($command)
+ {
+ if(strtolower($command) == 'clear')
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"Очистка консоли\n\" > '.$filecmd.'"');
+ else
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.$command.'"\015\';'
+ .'sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/copy.php b/system/sections/servers/cs/copy.php
new file mode 100644
index 0000000..7a9c216
--- /dev/null
+++ b/system/sections/servers/cs/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('server_copy_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_copy_'.$id);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `copy` WHERE `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_copy_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/graph.php b/system/sections/servers/cs/graph.php
new file mode 100644
index 0000000..c106d08
--- /dev/null
+++ b/system/sections/servers/cs/graph.php
@@ -0,0 +1,70 @@
+query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+ $graph = $sql->get();
+
+ $nmch = 'server_graph_full_'.$id;
+
+ $time = isset($url['time']) ? $url['time'] : 'day';
+
+ if(!in_array($time, array('day', 'week', 'month')))
+ $time = 'day';
+
+ // Выхлоп кеш графика
+ if($mcache->get($nmch) AND file_exists(TEMP.(md5($graph['key'].'full_'.$time)).'.png'))
+ {
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ include(LIB.'games/graph/pData.php');
+ include(LIB.'games/graph/pDraw.php');
+ include(LIB.'games/graph/pImage.php');
+
+ include(LIB.'games/graph.php');
+
+ graph::full($id, $server['slots_start'], $graph['key'], $time);
+
+ $mcache->set($nmch, true, false, 300);
+
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Графики');
+
+ if($mcache->get('server_graph_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_graph_'.$id);
+ else{
+ $sql->query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+
+ // Если отсутствует ключ, создать
+ if(!$sql->num())
+ {
+ // Генерация ключа
+ $key = md5($id.sys::key('graph'));
+
+ $sql->query('INSERT INTO `graph` set `server`="'.$id.'", `key`="'.$key.'", `time`="0"');
+ }else{
+ $graph = $sql->get();
+
+ $key = $graph['key'];
+ }
+
+ $html->get('graph', 'sections/servers/games');
+
+ $html->set('id', $id);
+
+ $html->set('key', $key);
+ $html->set('address', $server['address']);
+
+ $html->pack('main');
+
+ $mcache->set('server_graph_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/index.php b/system/sections/servers/cs/index.php
new file mode 100644
index 0000000..affa171
--- /dev/null
+++ b/system/sections/servers/cs/index.php
@@ -0,0 +1,44 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `fps`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address']);
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name'].' / '.$server['fps'].' FPS');
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img'));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/maps.php b/system/sections/servers/cs/maps.php
new file mode 100644
index 0000000..de85c4d
--- /dev/null
+++ b/system/sections/servers/cs/maps.php
@@ -0,0 +1,87 @@
+query('SELECT `unit`, `tarif` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'list', 'listing', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Карты', $cfg['http'].'servers/id/'.$id.'/section/maps');
+
+ if($go)
+ $nmch = sys::rep_act('server_maps_go_'.$id, 10);
+
+ include(SEC.'servers/'.$server['game'].'/maps/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Карты');
+
+ // Построение списка установленных карт
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id);
+ }
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $ssh->set('cd '.$tarif['install'].$server['uid'].'/cstrike/maps/ && ls | grep -e "\.bsp$"');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace('.bsp', '', $maps));
+
+ // Сортировка карт
+ sort($aMaps);
+ reset($aMaps);
+
+ $mapsjs = '';
+ $i = 0;
+
+ foreach($aMaps as $index => $map)
+ {
+ if(!isset($map{3}))
+ continue;
+
+ $mapjs = str_replace('$', '-_-', $map);
+
+ $i+=1;
+ $mapsjs .= $i.' : "'.$mapjs.'",';
+
+ $html->get('map_server', 'sections/servers/games/maps');
+ $html->set('img', sys::img($map, $server['game']));
+ $html->set('map', $mapjs);
+ $html->set('name', $map);
+ $html->pack('maps');
+ }
+
+ // Если есть кеш
+ if($mcache->get('server_maps_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_maps_'.$id);
+ else{
+ $html->get('maps', 'sections/servers/games');
+ $html->set('id', $id);
+ $html->set('types', isset($html->arr['types']) ? $html->arr['types'] : '');
+ $html->set('maps', isset($html->arr['maps']) ? $html->arr['maps'] : '');
+ $html->set('mapsjs', $mapsjs);
+ $html->pack('main');
+
+ $mcache->set('server_maps_'.$id, $html->arr['main'], false, 3);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/maps/delete.php b/system/sections/servers/cs/maps/delete.php
new file mode 100644
index 0000000..7b0173a
--- /dev/null
+++ b/system/sections/servers/cs/maps/delete.php
@@ -0,0 +1,70 @@
+query('SELECT `unit`, `tarif`, `map_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ // Генерация списка карт
+ $ssh->set('cd '.$dir.'maps/ && ls | grep -iE "\.bsp$"');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace('.bsp', '', $maps));
+
+ // Массив переданных карт
+ $in_aMaps = isset($_POST['maps']) ? $_POST['maps'] : array();
+
+ // Обработка выборки
+ foreach($in_aMaps as $name => $sel)
+ if($sel)
+ {
+ $map = str_replace(array("\\", "'", "'", '-_-'), array('', '', '', '$'), $name);
+
+ // Проверка наличия карты
+ if(!in_array($map, $aMaps))
+ continue;
+
+ // Проверка: является ли карта стартовой
+ if($server['map_start'] == $map)
+ continue;
+
+ $ssh->set('cd /path/maps/'.$server['game'].'/'.sys::map($map).' && du -a | grep -iE "\.[a-z]{1,3}$" | awk \'{print $2}\'');
+
+ $aFiles = explode("\n", str_replace('./', '', $ssh->get()));
+
+ if(isset($aFiles[count($aFiles)-1]) AND $aFiles[count($aFiles)-1] == '')
+ unset($aFiles[count($aFiles)-1]);
+
+ $files = '';
+
+ foreach($aFiles as $file)
+ $files .= $dir.$file.' ';
+
+ $rm = '';
+ $aFlrm = explode(' ', $dir.'maps/'.$map.'.* '.trim($files));
+
+ foreach($aFlrm as $flrm)
+ $rm .= sys::map($flrm).' ';
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -dmS md'.$start_point.$id.' sh -c \'rm '.trim($rm).'\'');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
diff --git a/system/sections/servers/cs/maps/install.php b/system/sections/servers/cs/maps/install.php
new file mode 100644
index 0000000..b54e0fd
--- /dev/null
+++ b/system/sections/servers/cs/maps/install.php
@@ -0,0 +1,56 @@
+query('SELECT `unit`, `tarif`, `hdd` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ // Проверить наличие свободного места
+ $ssh->set('cd '.$dir.' && du -ms');
+ $hdd = ceil(sys::int($ssh->get())/($server['hdd']/100));
+ $hdd = $hdd > 100 ? 100 : $hdd;
+
+ if($hdd == 100)
+ sys::outjs(array('e' => 'Невозможно выполнить установку, нет свободного места'), $nmch);
+
+ // Массив переданных карт
+ $in_aMaps = isset($_POST['maps']) ? $_POST['maps'] : array();
+
+ // Обработка выборки
+ foreach($in_aMaps as $mid => $sel)
+ if($sel)
+ {
+ $map = sys::int($mid);
+
+ // Проверка наличия карты
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `id`="'.$map.'" AND `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $map = $sql->get();
+
+ $cp = 'cp /path/maps/'.$server['game'].'/'.sys::map($map['name']).'.* '.$dir.'maps/;'
+ .'cd /path/maps/'.$server['game'].'/'.sys::map($map['name']).'/ && cp -r * '.$dir;
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -dmS mc'.$start_point.$id.' sh -c \''.$cp.'\'');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
diff --git a/system/sections/servers/cs/maps/list.php b/system/sections/servers/cs/maps/list.php
new file mode 100644
index 0000000..f3c28dc
--- /dev/null
+++ b/system/sections/servers/cs/maps/list.php
@@ -0,0 +1,85 @@
+nav('Установка карт');
+
+ // Категории для быстрой сортировки
+ $html->get('types', 'sections/servers/'.$server['game'].'/maps');
+ $html->set('id', $id);
+ $html->pack('types');
+
+ $type = false;
+
+ include(DATA.'maps.php');
+ include(LIB.'games/games.php');
+
+ if(isset($url['type']) AND array_key_exists($url['type'], $aFindMap[$server['game']]))
+ $type = $url['type'];
+
+ if($type)
+ {
+ $qsql = games::mapsql($aFindMap[$server['game']][$type]);
+
+ $all = $mcache->get('maps_'.$server['game'].'_'.$type);
+
+ if(!$all)
+ {
+ $sql->query('SELECT `id` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" '.$qsql);
+ $all = $sql->num();
+
+ $mcache->set('maps_'.$server['game'].'_'.$type, $all, false, 120);
+ }
+
+ // Массив для построения страниц
+ $aPage = sys::page($page, $all, 60);
+
+ // Генерация массива ($html->arr['pages']) страниц
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'servers/id/'.$id.'/section/maps/subsection/list/type/'.$type);
+
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" '.$qsql.' ORDER BY `name` ASC LIMIT '.$aPage['num'].', 30');
+ }else{
+ $all = $mcache->get('maps_'.$server['game']);
+
+ if(!$all)
+ {
+ $sql->query('SELECT `id` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'"');
+ $all = $sql->num();
+
+ $mcache->set('maps_'.$server['game'], $all, false, 120);
+ }
+
+ // Массив для построения страниц
+ $aPage = sys::page($page, $all, 30);
+
+ // Генерация массива ($html->arr['pages']) страниц
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'servers/id/'.$id.'/section/maps/subsection/list');
+
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `name` ASC LIMIT '.$aPage['num'].', 30');
+ }
+
+ $mapsjs = '';
+ $i = 0;
+
+ while($map = $sql->get())
+ {
+ $i+=1;
+
+ $mapsjs .= $i.' : "'.$map['id'].'",';
+
+ $html->get('map_install', 'sections/servers/games/maps');
+ $html->set('id', $map['id']);
+ $html->set('img', sys::img($map['name'], $server['game']));
+ $html->set('name', $map['name']);
+ $html->pack('maps');
+ }
+
+ $html->get('install', 'sections/servers/games/maps');
+ $html->set('id', $id);
+ $html->set('types', isset($html->arr['types']) ? $html->arr['types'] : '');
+ $html->set('maps', isset($html->arr['maps']) ? $html->arr['maps'] : 'К сожалению карты не найдены в базе');
+ $html->set('amaps', $mapsjs);
+ $html->set('pages', isset($html->arr['pages']) ? $html->arr['pages'] : '');
+ $html->set('cdn', $cfg['cdn']);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/maps/listing.php b/system/sections/servers/cs/maps/listing.php
new file mode 100644
index 0000000..93dec91
--- /dev/null
+++ b/system/sections/servers/cs/maps/listing.php
@@ -0,0 +1,94 @@
+nav('Списки карт');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/maps');
+ }
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Директория сервера
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ // Генерация списка
+ if($go AND isset($url['gen']))
+ {
+ $ssh->set('cd '.$dir.'maps/ && ls | grep -e "\.bsp$"');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace(array('./', '.bsp'), '', $maps));
+
+ sort($aMaps);
+ reset($aMaps);
+
+ $list = '';
+
+ foreach($aMaps as $index => $map)
+ {
+ $aMap = explode('/', $map);
+ $name = end($aMap);
+ if(strlen($name) < 4)
+ continue;
+
+ $list .= $map."\n";
+ }
+
+ sys::outjs(array('s' => $list), $nmch);
+ }
+
+ $aFiles = array(
+ 'mapcycle' => 'mapcycle.txt',
+ 'maps' => 'addons/amxmodx/configs/maps.ini'
+ );
+
+ // Сохранение
+ if($go AND isset($url['file']))
+ {
+ if(!array_key_exists($url['file'], $aFiles))
+ exit;
+
+ $data = isset($_POST['data']) ? $_POST['data'] : '';
+
+ $temp = sys::temp($data);
+
+ // Отправление файла на сервер
+ $ssh->setfile($temp, $dir.$aFiles[$url['file']], 0644);
+
+ // Смена владельца/группы файла
+ $ssh->set('chown server'.$server['uid'].':servers '.$dir.$aFiles[$url['file']]);
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$dir.$aFiles['mapcycle'].'; cat '.$dir.$aFiles['mapcycle'].'"');
+ $mapcycle = $ssh->get();
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$dir.$aFiles['maps'].'; cat '.$dir.$aFiles['maps'].'"');
+ $maps = $ssh->get();
+
+ $html->get('listing', 'sections/servers/'.$server['game'].'/maps');
+
+ $html->set('id', $id);
+
+ $html->set('mapcycle', $mapcycle);
+ $html->set('maps', $maps);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/maps/search.php b/system/sections/servers/cs/maps/search.php
new file mode 100644
index 0000000..866ca47
--- /dev/null
+++ b/system/sections/servers/cs/maps/search.php
@@ -0,0 +1,64 @@
+get($mkey);
+
+ if(is_array($cache))
+ {
+ if($go)
+ sys::outjs($cache, $nmch);
+
+ sys::outjs($cache);
+ }
+
+ if(!isset($text{2}))
+ {
+ if($go)
+ sys::outjs(array('e' => 'Для выполнения поиска, необходимо больше данных'), $nmch);
+
+ sys::outjs(array('e' => ''));
+ }
+
+ // Поиск по картам
+ if($text{0} == '^')
+ {
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` REGEXP FROM_BASE64(\''.base64_encode(str_replace('_', '\_', $text).'').'\') ORDER BY `name` ASC LIMIT 12');
+ $text = substr($text, 1);
+ }else
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` LIKE FROM_BASE64(\''.base64_encode('%'.str_replace('_', '\_', $text).'%').'\') ORDER BY `name` ASC LIMIT 12');
+
+ if(!$sql->num())
+ {
+ if($go)
+ sys::outjs(array('e' => 'По вашему запросу ничего не найдено'), $nmch);
+
+ sys::outjs(array('e' => 'По вашему запросу ничего не найдено'));
+ }
+
+ $i = 0;
+ $mapsjs = '';
+
+ while($map = $sql->get())
+ {
+ $i+=1;
+
+ $mapsjs[$i] = 's'.$map['id'];
+
+ $html->get('map_search', 'sections/servers/games/maps');
+
+ $html->set('id', 's'.$map['id']);
+ $html->set('img', sys::img($map['name'], $server['game']));
+ $html->set('name', sys::find($map['name'], $text));
+
+ $html->pack('maps');
+ }
+
+ $mcache->set($mkey, array('maps' => $html->arr['maps'], 'mapsjs' => $mapsjs), false, 15);
+
+ sys::outjs(array('maps' => $html->arr['maps'], 'mapsjs' => $mapsjs));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/plugins.php b/system/sections/servers/cs/plugins.php
new file mode 100644
index 0000000..a116ca1
--- /dev/null
+++ b/system/sections/servers/cs/plugins.php
@@ -0,0 +1,158 @@
+query('SELECT `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $nmch = sys::rep_act('server_plugins_go_'.$id, 10);
+
+ include(SEC.'servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('server_plugins_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_plugins_'.$id);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$id.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `plugins_install` WHERE `server`="'.$id.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_plugins_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/rcon.php b/system/sections/servers/cs/rcon.php
new file mode 100644
index 0000000..9c2389c
--- /dev/null
+++ b/system/sections/servers/cs/rcon.php
@@ -0,0 +1,53 @@
+ 'Необходимо выбрать игрока.'));
+
+ if($url['action'] == 'kick')
+ rcon::cmd(array_merge($server, array('id' => $id)), 'amx_kick "'.$player.'" "EGP Panel"');
+ else
+ rcon::cmd(array_merge($server, array('id' => $id)), 'amx_slay "'.$player.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ include(LIB.'geo.php');
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $aPlayers = rcon::players(rcon::cmd(array_merge($server, array('id' => $id))));
+
+ foreach($aPlayers as $i => $aPlayer)
+ {
+ $html->get('player', 'sections/servers/'.$server['game'].'/rcon');
+
+ $html->set('i', $i);
+ $html->set('name', $aPlayer['name']);
+ $html->set('steamid', $aPlayer['steamid']);
+ $html->set('time', $aPlayer['time']);
+ $html->set('ping', $aPlayer['ping']);
+ $html->set('ip', $aPlayer['ip']);
+ $html->set('ico', $aPlayer['ico']);
+ $html->set('country', $aPlayer['country']);
+
+ $html->pack('players');
+ }
+
+ sys::outjs(array('s' => isset($html->arr['players']) ? $html->arr['players'] : ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Rcon управление игроками');
+
+ $html->get('rcon', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings.php b/system/sections/servers/cs/settings.php
new file mode 100644
index 0000000..c5f3a92
--- /dev/null
+++ b/system/sections/servers/cs/settings.php
@@ -0,0 +1,77 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $aSub = array('start', 'server', 'admins', 'bans', 'firewall', 'crontab', 'startlogs', 'debug', 'logs', 'amxlogs', 'pack', 'file', 'antiddos', 'privileges', 'api');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aEditslist = 1;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $antiddos = 'Индивидуальная защита отключена '
+ .'Индивидуальная защита (Заблокировать всех кроме: RU, UA) '
+ .'Индивидуальная защита (Заблокировать всех кроме: AM, BY, UA, RU, KZ) ';
+
+ include(SEC.'servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('antiddos', str_replace($server['ddos'], $server['ddos'].'" selected="select', $antiddos));
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+
+ $sql->query('SELECT `key` FROM `api` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ {
+ $api = $sql->get();
+
+ $html->set('api', $api['key']);
+ $html->unit('api', 1, 1);
+ }else
+ $html->unit('api', 0, 1);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings/admins.php b/system/sections/servers/cs/settings/admins.php
new file mode 100644
index 0000000..83edc7d
--- /dev/null
+++ b/system/sections/servers/cs/settings/admins.php
@@ -0,0 +1,151 @@
+nav('Управление администраторами');
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $aData = array();
+
+ $aData['active'] = isset($_POST['active']) ? $_POST['active'] : '';
+ $aData['value'] = isset($_POST['value']) ? $_POST['value'] : '';
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : '';
+ $aData['flags'] = isset($_POST['flags']) ? $_POST['flags'] : '';
+ $aData['type'] = isset($_POST['type']) ? $_POST['type'] : '';
+ $aData['time'] = isset($_POST['time']) ? $_POST['time'] : '';
+ $aData['info'] = isset($_POST['info']) ? $_POST['info'] : '';
+
+ // Удаление текущих записей
+ $sql->query('DELETE FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'"');
+
+ $usini = '';
+
+ foreach($aData['value'] as $index => $val)
+ {
+ if($val != '')
+ {
+ $type = isset($aData['type'][$index]) ? $aData['type'][$index] : 'a';
+ if(!in_array($type, array('c', 'ce', 'de', 'a')))
+ $type = 'a';
+
+ $aDate = isset($aData['time'][$index]) ? explode('.', $aData['time'][$index]) : explode('.', date('d.m.Y', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ $aDate = explode('.', date('d.m.Y', $start_point));
+
+ $time = mktime(0, 0, 0, $aDate[1], $aDate[0], $aDate[2]);
+
+ $aData['active'][$index] = isset($aData['active'][$index]) ? 1 : 0;
+ $aData['passwd'][$index] = isset($aData['passwd'][$index]) ? $aData['passwd'][$index] : '';
+ $aData['flags'][$index] = isset($aData['flags'][$index]) ? $aData['flags'][$index] : '';
+ $aData['info'][$index] = isset($aData['info'][$index]) ? $aData['info'][$index] : '';
+
+ $text = '"'.$val.'" "'.$aData['passwd'][$index].'" "'.$aData['flags'][$index].'" "'.$type.'"';
+
+ $sql->query('INSERT INTO `admins_'.$server['game'].'` set'
+ .'`server`="'.$id.'",'
+ .'`value`="'.htmlspecialchars($val).'",'
+ .'`active`="'.$aData['active'][$index].'",'
+ .'`passwd`="'.htmlspecialchars($aData['passwd'][$index]).'",'
+ .'`flags`="'.htmlspecialchars($aData['flags'][$index]).'",'
+ .'`type`="'.$type.'",'
+ .'`time`="'.$time.'",'
+ .'`text`="'.htmlspecialchars($text).'",'
+ .'`info`="'.htmlspecialchars($aData['info'][$index]).'"');
+
+ if($aData['active'][$index])
+ $usini .= $text.PHP_EOL;
+ }
+ }
+
+ $temp = sys::temp($usini);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/addons/amxmodx/configs/users.ini', 0644);
+
+ unlink($temp);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/addons/amxmodx/configs/users.ini');
+
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_reloadadmins\"\015'");
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Построение списка добавленных админов
+ $sql->query('SELECT `id`, `value`, `active`, `passwd`, `flags`, `type`, `time`, `info` FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'" ORDER BY `id` ASC');
+ while($admin = $sql->get())
+ {
+ switch($admin['type'])
+ {
+ case 'c':
+ $type = 'SteamID/Пароль Ник/Пароль SteamID IP Адрес ';
+ break;
+
+ case 'ce':
+ $type = 'SteamID Ник/Пароль SteamID/Пароль IP Адрес ';
+ break;
+
+ case 'de':
+ $type = 'IP Адрес Ник/Пароль SteamID/Пароль SteamID ';
+ break;
+
+ default:
+ $type = 'Ник/Пароль SteamID/Пароль SteamID IP Адрес ';
+ }
+
+ $html->get('list', 'sections/servers/'.$server['game'].'/settings/admins');
+
+ if($admin['active'])
+ $html->unit('active', 1);
+ else
+ $html->unit('active');
+
+ $html->set('id', $admin['id']);
+ $html->set('value', $admin['value']);
+ $html->set('passwd', $admin['passwd']);
+ $html->set('flags', $admin['flags']);
+ $html->set('type', $type);
+ $html->set('time', date('d.m.y', $admin['time']));
+ $html->set('info', $admin['info']);
+
+ $html->pack('admins');
+ }
+
+ $sql->query('SELECT `id` FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'" ORDER BY `id` DESC LIMIT 1');
+ $max = $sql->get();
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $html->get('admins', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('admins', isset($html->arr['admins']) ? $html->arr['admins'] : '');
+ $html->set('index', $max['id'] < 1 ? 0 : $max['id']);
+ $html->set('address', 'ip/'.$ip.'/port/'.$port);
+
+ $sql->query('SELECT `active` FROM `privileges` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ {
+ $privilege = $sql->get();
+
+ if($privilege['active'])
+ $html->unit('privileges', 1);
+ else
+ $html->unit('privileges');
+ }else
+ $html->unit('privileges');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings/amxlogs.php b/system/sections/servers/cs/settings/amxlogs.php
new file mode 100644
index 0000000..e02ee9a
--- /dev/null
+++ b/system/sections/servers/cs/settings/amxlogs.php
@@ -0,0 +1,89 @@
+nav('Логи AMX');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к логам
+ $folder = $tarif['install'].$server['uid'].'/cstrike/addons/amxmodx/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['csamxlogs']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/amxlogs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'amxlogs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/amxlogs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"\/"$1"\/"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('\/', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', end($name));
+ $html->set('uri', 'amxlogs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('uri', 'amx');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings/antiddos.php b/system/sections/servers/cs/settings/antiddos.php
new file mode 100644
index 0000000..6451e5d
--- /dev/null
+++ b/system/sections/servers/cs/settings/antiddos.php
@@ -0,0 +1,73 @@
+query('SELECT `id` FROM `units` WHERE `id`="'.$server['unit'].'" AND `ddos`="1" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'В данный момент нельзя изменить параметр, т.к. включена защита на всю локацию.'), $name_mcache);
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($url['type']) || !in_array($url['type'], array('0', '1', '2')))
+ sys::outjs(array('e' => 'Неправильно передан параметр.'), $name_mcache);
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh', $user['group'])), $name_mcache);
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $geo = $cfg['iptables'].'_geo';
+
+ if($url['type'] == 2)
+ {
+ if($server['ddos'] == 2)
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ $cmd = '';
+
+ if($server['ddos'])
+ $cmd = 'iptables -D INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country UA,RU -j DROP;'
+ .'sed "`nl '.$geo.' | grep \"#'.$id.'\" | awk \'{print $1","$1+1}\'`d" '.$geo.' > '.$geo.'_temp; cat '.$geo.'_temp > '.$geo.'; rm '.$geo.'_temp;';
+
+ $rule = 'iptables -I INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country AM,BY,UA,RU,KZ -j DROP;';
+
+ $ssh->set($cmd.$rule.' echo -e "#'.$id.';\n'.$rule.'" >> '.$geo);
+
+ $sql->query('UPDATE `servers` set `ddos`="2" WHERE `id`="'.$id.'" LIMIT 1');
+ }elseif($url['type'] == 1){
+ if($server['ddos'] == 1)
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ $cmd = '';
+
+ if($server['ddos'])
+ $cmd = 'iptables -D INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country AM,BY,UA,RU,KZ -j DROP;'
+ .'sed "`nl '.$geo.' | grep \"#'.$id.'\" | awk \'{print $1","$1+1}\'`d" '.$geo.' > '.$geo.'_temp; cat '.$geo.'_temp > '.$geo.'; rm '.$geo.'_temp;';
+
+ $rule = 'iptables -I INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country UA,RU -j DROP;';
+
+ $ssh->set($cmd.$rule.' echo -e "#'.$id.';\n'.$rule.'" >> '.$geo);
+
+ $sql->query('UPDATE `servers` set `ddos`="1" WHERE `id`="'.$id.'" LIMIT 1');
+ }else{
+ if(!$server['ddos'])
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ $country = $server['ddos'] == 2 ? 'AM,BY,UA,RU,KZ' : 'UA,RU';
+
+ $ssh->set('iptables -D INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country '.$country.' -j DROP;'
+ .'sed "`nl '.$geo.' | grep \"#'.$id.'\" | awk \'{print $1","$1+1}\'`d" '.$geo.' > '.$geo.'_temp; cat '.$geo.'_temp > '.$geo.'; rm '.$geo.'_temp;');
+
+ $sql->query('UPDATE `servers` set `ddos`="0" WHERE `id`="'.$id.'" LIMIT 1');
+ }
+
+ $mcache->delete('server_settings_'.$id);
+
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings/bans.php b/system/sections/servers/cs/settings/bans.php
new file mode 100644
index 0000000..ee82294
--- /dev/null
+++ b/system/sections/servers/cs/settings/bans.php
@@ -0,0 +1,167 @@
+nav('Бан листы');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к файлам (banned.cfg / listip.cfg)
+ $folder = $tarif['install'].$server['uid'].'/cstrike';
+
+ // Если бан/разбан/проверка
+ if($go)
+ {
+ $aData = array();
+
+ $aData['value'] = isset($_POST['value']) ? trim($_POST['value']) : sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+ $aData['amxbans'] = isset($_POST['amxbans']) ? true : false;
+
+ // Проверка входных данных
+ if(sys::valid($aData['value'], 'steamid') AND sys::valid($aData['value'], 'ip'))
+ sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+
+ // Если указан steamid
+ if(sys::valid($aData['value'], 'ip'))
+ {
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен amxbans/csbans
+ if($aData['amxbans'])
+ {
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_ban 0 ".$aData['value']." EGP\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"fb_ban 0 ".$aData['value']." EGP\"\015'");
+ }else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"banid 0.0 ".$aData['value']." kick\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"banid 0.0 '.$aData['value'].'\" >> '.$folder.'/banned.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned.cfg | grep -v '.$aData['value'].' > temp_banned.cfg; echo "" >> temp_banned.cfg && cat temp_banned.cfg > banned.cfg; rm temp_banned.cfg"');
+
+ // Если включен amxbans/csbans
+ if($aData['amxbans'])
+ {
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_unban ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"fb_unban ".$aData['value']."\"\015'");
+ }else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeid ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeid\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный SteamID найден в файле banned.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный SteamID не найден в файле banned.cfg'), $nmch);
+ }
+ }else{
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен amxbans/csbans
+ if($aData['amxbans'])
+ {
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_ban 0 ".$aData['value']." EGP\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"fb_ban 0 ".$aData['value']." EGP\"\015'");
+ }else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"addip 0.0 ".$aData['value']." EGP\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' listip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"addip 0.0 '.$aData['value'].'\" >> '.$folder.'/listip.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из listip.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat listip.cfg | grep -v '.$aData['value'].' > temp_listip.cfg; echo "" >> temp_listip.cfg && cat temp_listip.cfg > listip.cfg; rm temp_listip.cfg"');
+
+ // Если включен amxbans/csbans
+ if($aData['amxbans'])
+ {
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_unban ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"fb_unban ".$aData['value']."\"\015'");
+ }else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeip ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeip\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' listip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный IP найден в файле listip.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный IP не найден в файле listip.cfg'), $nmch);
+ }
+ }
+ }
+
+ // Содержимое banned.cfg
+ $ssh->set('cd '.$folder.' && cat banned.cfg | awk \'{print $3}\' | grep STEAM_');
+ $aBanned = explode("\n", $ssh->get());
+
+ // Содержимое listip.cfg
+ $ssh->set('cd '.$folder.' && cat listip.cfg | awk \'{print $3}\' | egrep "(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}"');
+ $aListip = explode("\n", $ssh->get());
+
+ if(isset($aBanned[count($aBanned)-1]) AND $aBanned[count($aBanned)-1] == '')
+ unset($aBanned[count($aBanned)-1]);
+
+ if(isset($aListip[count($aListip)-1]) AND $aListip[count($aListip)-1] == '')
+ unset($aListip[count($aListip)-1]);
+
+ // Построение списка забаненых по steamid
+ foreach($aBanned as $line => $steam)
+ {
+ $html->get('bans_list', 'sections/servers/games/settings');
+
+ $html->set('value', trim($steam));
+
+ $html->pack('banned');
+ }
+
+ // Построение списка забаненых по ip
+ foreach($aListip as $line => $ip)
+ {
+ $html->get('bans_list', 'sections/servers/games/settings');
+
+ $html->set('value', trim($ip));
+
+ $html->pack('listip');
+ }
+
+ $html->get('bans', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('banned', isset($html->arr['banned']) ? $html->arr['banned'] : '');
+ $html->set('listip', isset($html->arr['listip']) ? $html->arr['listip'] : '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings/debug.php b/system/sections/servers/cs/settings/debug.php
new file mode 100644
index 0000000..180f9d0
--- /dev/null
+++ b/system/sections/servers/cs/settings/debug.php
@@ -0,0 +1,28 @@
+nav('Отладочный лог');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Чтение файла - oldstart.log
+ $file = $tarif['install'].$server['uid'].'/debug.log';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep "CRASH: " | grep -ve "^#\|^[[:space:]]*$"');
+
+ $html->get('debug', 'sections/servers/games/settings');
+
+ $html->set('log', htmlspecialchars($ssh->get()));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings/logs.php b/system/sections/servers/cs/settings/logs.php
new file mode 100644
index 0000000..94ba493
--- /dev/null
+++ b/system/sections/servers/cs/settings/logs.php
@@ -0,0 +1,89 @@
+nav('Логи');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к логам
+ $folder = $tarif['install'].$server['uid'].'/cstrike/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['cslogs']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/logs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+ $html->set('uri', 'logs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/logs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', end($name));
+ $html->set('uri', 'logs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('uri', '');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings/privileges.php b/system/sections/servers/cs/settings/privileges.php
new file mode 100644
index 0000000..de8716a
--- /dev/null
+++ b/system/sections/servers/cs/settings/privileges.php
@@ -0,0 +1,138 @@
+query('SELECT `active` FROM `privileges` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ {
+ $privilege = $sql->get();
+
+ if(!$privilege['active'])
+ {
+ $sql->query('SELECT `id` FROM `privileges_list` WHERE `server`="'.$id.'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Необходимо настроить привилегии'));
+
+ $sql->query('UPDATE `privileges` set `active`="1" WHERE `server`="'.$id.'" LIMIT 1');
+ }else
+ $sql->query('UPDATE `privileges` set `active`="0" WHERE `server`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ sys::outjs(array('e' => 'Необходимо настроить привилегии'));
+ }
+
+ if(isset($url['delete']))
+ {
+ $sql->query('DELETE FROM `privileges_list` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ $sql->query('SELECT `id` FROM `privileges_list` WHERE `server`="'.$id.'" LIMIT 1');
+ if(!$sql->num())
+ $sql->query('UPDATE `privileges` set `active`="0" WHERE `server`="'.$id.'" LIMIT 1');
+
+ sys::out();
+ }
+
+ if($go)
+ {
+ $sql->query('SELECT `id` FROM `privileges_list` WHERE `server`="'.$id.'" LIMIT 10');
+ if($sql->num() > 9)
+ sys::outjs(array('e' => 'Нельзя добавить больше 10-и привилегий'), $name_mcache);
+
+ $aData = array();
+
+ $aData['name'] = isset($_POST['name']) ? trim($_POST['name']) : sys::outjs(array('e' => 'Необходимо заполнить все поля'), $name_mcache);
+ $aData['flags'] = isset($_POST['flags']) ? trim($_POST['flags']) : sys::outjs(array('e' => 'Необходимо заполнить все поля'), $name_mcache);
+ $aData['time'] = isset($_POST['time']) ? $_POST['time'] : sys::outjs(array('e' => 'Необходимо заполнить все поля'), $name_mcache);
+ $aData['price'] = isset($_POST['price']) ? $_POST['price'] : sys::outjs(array('e' => 'Необходимо заполнить все поля'), $name_mcache);
+
+ if(sys::strlen($aData['name']) < 3 || sys::strlen($aData['name']) > 30)
+ sys::outjs(array('e' => 'Длина названия должна быть от 3-х до 30-и символов'), $name_mcache);
+
+ if(sys::valid($aData['name'], 'other', '/[А-яA-z0-9]+$/u'))
+ sys::outjs(array('e' => 'Неверное указано название, доступны латинские, русские буквы и цифры.'), $name_mcache);
+
+ if(sys::valid($aData['flags'], 'other', '/^[a-z]+$/') || (sys::strlen($aData['flags']) < 1 || sys::strlen($aData['flags']) > 22))
+ sys::outjs(array('e' => 'Неверное указаны флаги AmxModX.'), $name_mcache);
+
+ foreach(count_chars($aData['flags'], 1) as $val)
+ if($val > 1)
+ sys::outjs(array('e' => 'Неверное указаны флаги AmxModX, флаг не должен повторяться дважды.'), $name_mcache);
+
+ if((!is_array($aData['time']) || !is_array($aData['price'])) || (count($aData['time']) < 1|| count($aData['time']) > 5) || (count($aData['time']) != count($aData['price'])))
+ sys::outjs(array('e' => 'Неверное переданы данные.'), $name_mcache);
+
+ $keys = array();
+ $data = array();
+
+ foreach($aData['time'] as $key => $val)
+ {
+ $val = intval($val);
+
+ if($val > 1000)
+ $val = 1000;
+
+ if(in_array($val, $keys))
+ continue;
+
+ $aData['price'][$key] = intval($aData['price'][$key]);
+
+ if($aData['price'][$key] < 1)
+ continue;
+
+ $data[$val] = $aData['price'][$key];
+ $keys[] = $val;
+ }
+
+ if(!count($data))
+ sys::outjs(array('e' => 'Неверное переданы данные.'), $name_mcache);
+
+ $sql->query('SELECT `id` FROM `privileges` WHERE `server`="'.$id.'" LIMIT 1');
+ if(!$sql->num())
+ $sql->query('INSERT INTO `privileges` set `server`="'.$id.'", `active`="0"');
+
+ $sql->query('INSERT INTO `privileges_list` set `server`="'.$id.'", `name`="'.$aData['name'].'", `flags`="'.$aData['flags'].'", `data`="'.sys::b64js($data).'"');
+
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+ }
+
+ $html->nav('Управление администраторами', $cfg['http'].'servers/id/'.$id.'/section/settings/subsection/admins');
+ $html->nav('Настройка платных привилегий');
+
+ $sql->query('SELECT `id`, `name`, `flags`, `data` FROM `privileges_list` WHERE `server`="'.$id.'" ORDER BY `id` ASC LIMIT 10');
+ while($privilege = $sql->get())
+ {
+ $data = sys::b64djs($privilege['data']);
+
+ $time = '';
+
+ if(isset($data[0]))
+ {
+ $time = 'Навсегда / '.$data[0].' '.$cfg['currency'].'; ';
+
+ unset($data[0]);
+ }
+
+ foreach($data as $days => $price)
+ $time .= $days.' '.sys::day($time).' / '.$price.' '.$cfg['currency'].'; ';
+
+ $html->get('list', 'sections/servers/'.$server['game'].'/settings/privileges');
+
+ $html->set('id', $privilege['id']);
+ $html->set('name', $privilege['name']);
+ $html->set('flags', $privilege['flags']);
+ $html->set('time', $time);
+
+ $html->pack('list');
+ }
+
+ $html->get('privileges', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('list', $html->arr['list']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings/server.php b/system/sections/servers/cs/settings/server.php
new file mode 100644
index 0000000..158ec18
--- /dev/null
+++ b/system/sections/servers/cs/settings/server.php
@@ -0,0 +1,119 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ $config_end = $servercfg['\'other\''];
+
+ unset($servercfg['\'other\'']);
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).' "'.$val.'"'."\n";
+
+ // Временый файл
+ $temp = sys::temp($config.$config_end);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/server.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/server.cfg');
+
+ unlink($temp);
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "exec server.cfg"\015\';');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Чтение файла - server.cfg
+ $file = $tarif['install'].$server['uid'].'/cstrike/server.cfg';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+ $other = '';
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode(' ', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.' ', "", $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ else
+ $other .= $line."\n";
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $html->get('servercfg', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('cfg', $html->arr['list']);
+ $html->set('other', $other);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings/start.php b/system/sections/servers/cs/settings/start.php
new file mode 100644
index 0000000..25d83e2
--- /dev/null
+++ b/system/sections/servers/cs/settings/start.php
@@ -0,0 +1,166 @@
+query('SELECT `uid`, `slots`, `slots_start`, `map_start`, `vac`, `fps`, `fastdl`, `autorestart`, `pingboost` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install`, `fps`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'games/games.php');
+ include(LIB.'games/tarifs.php');
+ include(LIB.'games/'.$server['game'].'/tarif.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/cstrike/maps', $server['map_start'], false);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/cstrike/maps', $map, true, $nmch);
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `servers` set `vac`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `servers` set `autorestart`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fps':
+ if(!tarif::price($tarif['price']) AND in_array($value, explode(':', $tarif['fps'])))
+ $sql->query('UPDATE `servers` set `fps`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'pingboost':
+ if($cfg['change_pingboost'] AND in_array($value, array(1, 2, 3)))
+ $sql->query('UPDATE `servers` set `pingboost`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.sys::first(explode(':', $unit['address'])).':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/fastdl.cfg;'
+ .'ln -s '.$tarif['install'].$server['uid'].'/cstrike /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/cstrike/server.cfg;'
+ .'echo "exec fastdl.cfg" >> '.$tarif['install'].$server['uid'].'/cstrike/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/cstrike/server.cfg;'
+ .'rm '.$tarif['install'].$server['uid'].'/cstrike/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `servers` set `fastdl`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $fps = ''.$server['fps'].' FPS ';
+
+ if(!tarif::price($tarif['price']))
+ {
+ $aFps = explode(':', $tarif['fps']);
+
+ unset($aFps[array_search($server['fps'], $aFps)]);
+
+ if(count($aFps))
+ foreach($aFps as $value)
+ $fps .= ''.$value.' FPS ';
+ }
+
+ if($cfg['change_pingboost'])
+ $pingboost = str_replace($server['pingboost'].'"', $server['pingboost'].'" selected="select"', 'PINGBOOST 1 PINGBOOST 2 PINGBOOST 3 ');
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('map', $server['map_start']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ if($cfg['change_pingboost'])
+ {
+ $html->unit('pingboost', true);
+ $html->set('pingboost', $pingboost);
+ }else
+ $html->unit('pingboost');
+
+ if(!tarif::price($tarif['price']))
+ {
+ $html->unit('fps', true);
+ $html->set('fps', $fps);
+ }else
+ $html->unit('fps');
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/settings/top.php b/system/sections/servers/cs/settings/top.php
new file mode 100644
index 0000000..e0e1d61
--- /dev/null
+++ b/system/sections/servers/cs/settings/top.php
@@ -0,0 +1,25 @@
+query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Удаление файла csstats.dat
+ $ssh->set('rm '.$tarif['install'].$server['uid'].'/cstrike/addons/amxmodx/data/csstats.dat');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ {
+ shell_exec('php cron.php '.$cfg['cron_key'].' server_action restart cs '.$id);
+
+ sys::outjs(array('s' => 'ok'));
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/tarif.php b/system/sections/servers/cs/tarif.php
new file mode 100644
index 0000000..bff952e
--- /dev/null
+++ b/system/sections/servers/cs/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `fps`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'plan', 'address', 'addextend', 'unit', 'slots');
+
+ include(SEC.'servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/tarif/extend.php b/system/sections/servers/cs/tarif/extend.php
new file mode 100644
index 0000000..0e66ff6
--- /dev/null
+++ b/system/sections/servers/cs/tarif/extend.php
@@ -0,0 +1,52 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['fps'] = $server['fps'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aFPS = explode(':', $tarif['fps']);
+
+ // Цена за 30 дней 1 слота
+ $price = $aPrice[array_search($server['fps'], $aFPS)];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/tarif/plan.php b/system/sections/servers/cs/tarif/plan.php
new file mode 100644
index 0000000..d847cdc
--- /dev/null
+++ b/system/sections/servers/cs/tarif/plan.php
@@ -0,0 +1,66 @@
+ 'Переданые не все данные'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aFPS = explode(':', $tarif['fps']);
+
+ // Проверка плана
+ if(array_search($plan, $aFPS) === FALSE)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ if($plan == $server['fps'])
+ sys::outjs(array('e' => 'Смысла в этой операции нет'), $nmch);
+
+ if(!tarif::price($tarif['price']))
+ sys::outjs(array('e' => 'Чтобы изменить тариф, перейдите в настройки запуска'), $nmch);
+
+ if($server['time'] < $start_point+86400)
+ $time = $server['time'];
+ else{
+ // Цена за 1 день аренды (по новому тарифному плану)
+ $price = $aPrice[array_search($plan, $aFPS)]/30*$server['slots'];
+
+ // Цена за 1 день аренды (по старому тарифному плану)
+ $price_old = $aPrice[array_search($server['fps'], $aFPS)]/30*$server['slots'];
+
+ // Остаток дней аренды
+ $days = ($server['time']-$start_point)/86400;
+
+ $time = date('H:i:s', $server['time']);
+ $date = date('d.m.Y', round($start_point+$days*$price_old/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+ $aTime = explode(':', $time);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2]);
+ }
+
+ // Выполнение смена тарифного плана
+ if($go)
+ {
+ sys::benefitblock($id, $nmch);
+
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `fps`="'.$plan.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_plan').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/tarif/slots.php b/system/sections/servers/cs/tarif/slots.php
new file mode 100644
index 0000000..b32de8a
--- /dev/null
+++ b/system/sections/servers/cs/tarif/slots.php
@@ -0,0 +1,34 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aFPS = explode(':', $tarif['fps']);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день 1 слота
+ $price = $aPrice[array_search($server['fps'], $aFPS)]/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($aPrice[array_search($server['fps'], $aFPS)]/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/tarif/unit.php b/system/sections/servers/cs/tarif/unit.php
new file mode 100644
index 0000000..1ea53a9
--- /dev/null
+++ b/system/sections/servers/cs/tarif/unit.php
@@ -0,0 +1,50 @@
+ 'Переданы не все данные.'), $nmch);
+
+ if(!$cfg['change_unit'][$server['game']] || $server['time'] < $start_point+86400 || $server['test'])
+ exit;
+
+ $sql->query('SELECT `id`, `unit`, `packs`, `fps`, `price` FROM `tarifs` WHERE `unit`="'.$uid.'" AND `game`="'.$server['game'].'" AND `name`="'.$tarif['name'].'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Не найден подходящий тариф.'), $nmch);
+
+ $oldTarif = $tarif;
+
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $oldUnit = $sql->get();
+
+ $aPriceold = explode(':', $oldTarif['price']);
+ $aFPSold = explode(':', $oldTarif['fps']);
+
+ $sql->query('SELECT `id` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная локация не доступна.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aFPS = explode(':', $tarif['fps']);
+
+ if(!in_array($server['fps'], $aFPS))
+ sys::outjs(array('e' => 'Не найден подходящий тарифный план.'), $nmch);
+
+ // Цена за 1 день (при новом тарифном плане)
+ $price = $aPrice[array_search($server['fps'], $aFPS)]/30*$server['slots'];
+
+ // Цена аренды за остаток дней (с текущим тарифным планом)
+ $oldprice = ($server['time']-$start_point)/86400*($aPriceold[array_search($server['fps'], $aFPSold)]/30*$server['slots']);
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$oldprice/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ include(SEC.'servers/games/tarif/unit.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cs/web.php b/system/sections/servers/cs/web.php
new file mode 100644
index 0000000..d987a62
--- /dev/null
+++ b/system/sections/servers/cs/web.php
@@ -0,0 +1,86 @@
+nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ include(DATA.'web.php');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub) AND in_array($url['action'], array_merge($aAction[$url['subsection']], array('install', 'manage'))))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_web_go_'.$id, 10);
+ else
+ $html->nav('Web', $cfg['http'].'servers/id/'.$id.'/section/web');
+
+ include(SEC.'web/'.$url['subsection'].'/free/'.$url['action'].'.php');
+ }else{
+ $html->nav('Web');
+
+ if($mcache->get('server_web_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_web_'.$id);
+ else{
+ // Услуги
+ foreach($aWeb[$server['game']] as $service => $active)
+ {
+ if($active)
+ {
+ if($service == 'hosting')
+ {
+ if(!$aWebVHTtype && !in_array($server['tarif'], $aWebVHT))
+ continue;
+
+ if($aWebVHTtype && in_array($server['tarif'], $aWebVHT))
+ continue;
+ }
+
+ // Проверка на установку
+ switch($aWebInstall[$server['game']][$service])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ }
+
+ if($sql->num())
+ $html->get('list_install', 'sections/servers/games/web');
+ else
+ $html->get('list', 'sections/servers/games/web');
+
+ $html->set('id', $id);
+ $html->set('service', $service);
+ $html->set('name', $aWebname[$service]);
+ $html->set('desc', $aWebDesc[$service]);
+ $html->pack($aWebType[$service]);
+ }
+ }
+
+ // Блоки услуг
+ foreach($aWebTypeInfo[$server['game']] as $type => $name)
+ {
+ if(!isset($html->arr[$type]))
+ continue;
+
+ $html->get('block', 'sections/servers/games/web');
+ $html->set('name', $name);
+ $html->set('list', $html->arr[$type]);
+ $html->pack('web');
+ }
+
+ $html->get('web', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('web', isset($html->arr['web']) ? $html->arr['web'] : 'Дополнительные услуги отсутствуют');
+ $html->pack('main');
+
+ $mcache->set('server_web_'.$id, $html->arr['main'], false, 4);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/console.php b/system/sections/servers/csgo/console.php
new file mode 100644
index 0000000..6f548f0
--- /dev/null
+++ b/system/sections/servers/csgo/console.php
@@ -0,0 +1,67 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $command = isset($_POST['command']) ? sys::cmd($_POST['command']) : '';
+
+ if($server['status'] == 'off')
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('servers', 'off')));
+
+ sys::out(sys::text('servers', 'off'));
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ sys::out(sys::text('error', 'ssh'));
+ }
+
+ $dir = $tarif['install'].$server['uid'].'/csgo/';
+
+ $filecmd = $dir.'console.log';
+
+ if($command)
+ {
+ if(strtolower($command) == 'clear')
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"Очистка консоли\n\" > '.$filecmd.'"');
+ else
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.$command.'"\015\';'
+ .'sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/copy.php b/system/sections/servers/csgo/copy.php
new file mode 100644
index 0000000..7a9c216
--- /dev/null
+++ b/system/sections/servers/csgo/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('server_copy_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_copy_'.$id);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `copy` WHERE `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_copy_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/graph.php b/system/sections/servers/csgo/graph.php
new file mode 100644
index 0000000..d8d8df8
--- /dev/null
+++ b/system/sections/servers/csgo/graph.php
@@ -0,0 +1,71 @@
+query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+ $graph = $sql->get();
+
+ $nmch = 'server_graph_full_'.$id;
+
+ $time = isset($url['time']) ? $url['time'] : 'day';
+
+ if(!in_array($time, array('day', 'week', 'month')))
+ $time = 'day';
+
+ // Выхлоп кеш графика
+ if($mcache->get($nmch) AND file_exists(TEMP.(md5($graph['key'].'full_'.$time)).'.png'))
+ {
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ include(LIB.'games/graph/pData.php');
+ include(LIB.'games/graph/pDraw.php');
+ include(LIB.'games/graph/pImage.php');
+
+ include(LIB.'games/graph.php');
+
+ graph::full($id, $server['slots_start'], $graph['key'], $time);
+
+ $mcache->set($nmch, true, false, 300);
+
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Графики');
+
+ if($mcache->get('server_graph_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_graph_'.$id);
+ else{
+ $sql->query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+
+ // Если отсутствует ключ, создать
+ if(!$sql->num())
+ {
+ // Генерация ключа
+ $key = md5($id.sys::key('graph'));
+
+ $sql->query('INSERT INTO `graph` set `server`="'.$id.'", `key`="'.$key.'", `time`="0"');
+ }else{
+ $graph = $sql->get();
+
+ $key = $graph['key'];
+ }
+
+ $html->get('graph', 'sections/servers/games');
+
+ $html->set('id', $id);
+
+ $html->set('key', $key);
+ $html->set('address', $server['address']);
+ $html->set('_img', '[img]');
+
+ $html->pack('main');
+
+ $mcache->set('server_graph_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/index.php b/system/sections/servers/csgo/index.php
new file mode 100644
index 0000000..e16beb5
--- /dev/null
+++ b/system/sections/servers/csgo/index.php
@@ -0,0 +1,44 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `tickrate`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address']);
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name'].' / '.$server['tickrate'].' TickRate');
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img'));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/maps.php b/system/sections/servers/csgo/maps.php
new file mode 100644
index 0000000..9fc1ba3
--- /dev/null
+++ b/system/sections/servers/csgo/maps.php
@@ -0,0 +1,97 @@
+query('SELECT `unit`, `tarif` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'list', 'listing', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Карты', $cfg['http'].'servers/id/'.$id.'/section/maps');
+
+ $nmch = sys::rep_act('server_maps_go_'.$id, 10);
+
+ include(SEC.'servers/'.$server['game'].'/maps/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Карты');
+
+ // Построение списка установленных карт
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id);
+ }
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $ssh->set('cd '.$tarif['install'].$server['uid'].'/csgo/maps/ && du -ah | grep -e "\.bsp$" | awk \'{print $2}\'');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace('.bsp', '', $maps));
+
+ // Сортировка карт
+ sort($aMaps);
+ reset($aMaps);
+
+ $mapsjs = '';
+ $i = 0;
+
+ foreach($aMaps as $index => $map)
+ {
+ if(!isset($map{3}))
+ continue;
+
+ $map = str_replace('./', '', $map);
+
+ $mapjs = str_replace('$', '-_-', $map);
+
+ $aName = explode('/', $map);
+ $name = end($aName);
+
+ $html->get('map_server', 'sections/servers/csgo/maps');
+ $html->set('img', sys::img($name, $server['game']));
+ $html->set('map', $mapjs);
+ $html->set('name', $name);
+
+ if(count($aName) > 1)
+ $html->unit('workshop', true, true);
+ else{
+ $i+=1;
+ $mapsjs .= $i.' : "'.$mapjs.'",';
+
+ $html->unit('workshop', false, true);
+ }
+ $html->pack('maps');
+ }
+
+ // Если есть кеш
+ if($mcache->get('server_maps_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_maps_'.$id);
+ else{
+ $html->get('maps', 'sections/servers/games');
+ $html->set('id', $id);
+ $html->set('types', isset($html->arr['types']) ? $html->arr['types'] : '');
+ $html->set('maps', isset($html->arr['maps']) ? $html->arr['maps'] : '');
+ $html->set('mapsjs', $mapsjs);
+ $html->pack('main');
+
+ $mcache->set('server_maps_'.$id, $html->arr['main'], false, 3);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/maps/delete.php b/system/sections/servers/csgo/maps/delete.php
new file mode 100644
index 0000000..0cbf4ec
--- /dev/null
+++ b/system/sections/servers/csgo/maps/delete.php
@@ -0,0 +1,70 @@
+query('SELECT `unit`, `tarif`, `map_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $dir = $tarif['install'].$server['uid'].'/csgo/';
+
+ // Генерация списка карт
+ $ssh->set('cd '.$dir.'maps/ && ls | grep -iE "\.bsp$"');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace('.bsp', '', $maps));
+
+ // Массив переданных карт
+ $in_aMaps = isset($_POST['maps']) ? $_POST['maps'] : array();
+
+ // Обработка выборки
+ foreach($in_aMaps as $name => $sel)
+ if($sel)
+ {
+ $map = str_replace(array("\\", "'", "'", '-_-'), array('', '', '', '$'), $name);
+
+ // Проверка наличия карты
+ if(!in_array($map, $aMaps))
+ continue;
+
+ // Проверка: является ли карта стартовой
+ if($server['map_start'] == $map)
+ continue;
+
+ $ssh->set('cd /path/maps/'.$server['game'].'/'.sys::map($map).' && du -a | grep -iE "\.[a-z]{1,3}$" | awk \'{print $2}\'');
+
+ $aFiles = explode("\n", str_replace('./', '', $ssh->get()));
+
+ if(isset($aFiles[count($aFiles)-1]) AND $aFiles[count($aFiles)-1] == '')
+ unset($aFiles[count($aFiles)-1]);
+
+ $files = '';
+
+ foreach($aFiles as $file)
+ $files .= $dir.$file.' ';
+
+ $rm = '';
+ $aFlrm = explode(' ', $dir.'maps/'.$map.'.* '.trim($files));
+
+ foreach($aFlrm as $flrm)
+ $rm .= sys::map($flrm).' ';
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -dmS md'.$start_point.$id.' sh -c \'rm '.trim($rm).'\'');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
diff --git a/system/sections/servers/csgo/maps/install.php b/system/sections/servers/csgo/maps/install.php
new file mode 100644
index 0000000..7aae0c8
--- /dev/null
+++ b/system/sections/servers/csgo/maps/install.php
@@ -0,0 +1,56 @@
+query('SELECT `unit`, `tarif`, `hdd` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $dir = $tarif['install'].$server['uid'].'/csgo/';
+
+ // Проверить наличие свободного места
+ $ssh->set('cd '.$dir.' && du -ms');
+ $hdd = ceil(sys::int($ssh->get())/($server['hdd']/100));
+ $hdd = $hdd > 100 ? 100 : $hdd;
+
+ if($hdd == 100)
+ sys::outjs(array('e' => 'Невозможно выполнить установку, нет свободного места'), $nmch);
+
+ // Массив переданных карт
+ $in_aMaps = isset($_POST['maps']) ? $_POST['maps'] : array();
+
+ // Обработка выборки
+ foreach($in_aMaps as $mid => $sel)
+ if($sel)
+ {
+ $map = sys::int($mid);
+
+ // Проверка наличия карты
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `id`="'.$map.'" AND `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $map = $sql->get();
+
+ $cp = 'cp /path/maps/'.$server['game'].'/'.sys::map($map['name']).'.* '.$dir.'maps/;'
+ .'cd /path/maps/'.$server['game'].'/'.sys::map($map['name']).'/ && cp -r * '.$dir;
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -dmS mc'.$start_point.$id.' sh -c \''.$cp.'\'');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
diff --git a/system/sections/servers/csgo/maps/list.php b/system/sections/servers/csgo/maps/list.php
new file mode 100644
index 0000000..abb251a
--- /dev/null
+++ b/system/sections/servers/csgo/maps/list.php
@@ -0,0 +1,62 @@
+nav('Установка карт');
+
+ // Категории для быстрой сортировки
+ $html->get('types', 'sections/servers/'.$server['game'].'/maps');
+
+ $html->set('id', $id);
+
+ $html->pack('types');
+
+ $type = false;
+
+ if(isset($url['type']) AND in_array($url['type'], array('de', 'cs', 'aim', 'awp', 'bhop', 'csde', 'deathrun', 'jail')))
+ $type = '^'.$url['type'].'\_';
+
+ if($type)
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` REGEXP FROM_BASE64(\''.base64_encode($type).'\') ORDER BY `name` ASC LIMIT 72');
+ else{
+ $sql->query('SELECT `id` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'"');
+
+ // Массив для построения страниц
+ $aPage = sys::page($page, $sql->num(), 30);
+
+ // Генерация массива ($html->arr['pages']) страниц
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'servers/id/'.$id.'/section/maps/subsection/list');
+
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `name` ASC LIMIT '.$aPage['num'].', 30');
+ }
+
+ $mapsjs = '';
+ $i = 0;
+
+ while($map = $sql->get())
+ {
+ $i+=1;
+
+ $mapsjs .= $i.' : "'.$map['id'].'",';
+
+ $html->get('map_install', 'sections/servers/games/maps');
+
+ $html->set('id', $map['id']);
+ $html->set('img', sys::img($map['name'], $server['game']));
+ $html->set('name', $map['name']);
+
+ $html->pack('maps');
+ }
+
+ $html->get('install', 'sections/servers/games/maps');
+
+ $html->set('id', $id);
+
+ $html->set('types', isset($html->arr['types']) ? $html->arr['types'] : '');
+ $html->set('maps', isset($html->arr['maps']) ? $html->arr['maps'] : 'К сожалению карты не найдены в базе');
+ $html->set('amaps', $mapsjs);
+ $html->set('pages', isset($html->arr['pages']) ? $html->arr['pages'] : '');
+ $html->set('cdn', $cfg['cdn']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/maps/listing.php b/system/sections/servers/csgo/maps/listing.php
new file mode 100644
index 0000000..664ea1e
--- /dev/null
+++ b/system/sections/servers/csgo/maps/listing.php
@@ -0,0 +1,94 @@
+nav('Списки карт');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/maps');
+ }
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Директория сервера
+ $dir = $tarif['install'].$server['uid'].'/csgo/';
+
+ // Генерация списка
+ if($go AND isset($url['gen']))
+ {
+ $ssh->set('cd '.$dir.'maps/ && du -ah | grep -e "\.bsp$" | awk \'{print $2}\'');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace(array('./', '.bsp'), '', $maps));
+
+ sort($aMaps);
+ reset($aMaps);
+
+ $list = '';
+
+ foreach($aMaps as $index => $map)
+ {
+ $aMap = explode('/', $map);
+ $name = end($aMap);
+ if(strlen($name) < 4)
+ continue;
+
+ $list .= $map."\n";
+ }
+
+ sys::outjs(array('s' => $list), $nmch);
+ }
+
+ $aFiles = array(
+ 'mapcycle' => 'mapcycle.txt',
+ 'maps' => 'maplist.txt'
+ );
+
+ // Сохранение
+ if($go AND isset($url['file']))
+ {
+ if(!array_key_exists($url['file'], $aFiles))
+ exit;
+
+ $data = isset($_POST['data']) ? $_POST['data'] : '';
+
+ $temp = sys::temp($data);
+
+ // Отправление файла на сервер
+ $ssh->setfile($temp, $dir.$aFiles[$url['file']], 0644);
+
+ // Смена владельца/группы файла
+ $ssh->set('chown server'.$server['uid'].':servers '.$dir.$aFiles[$url['file']]);
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$dir.$aFiles['mapcycle'].'; cat '.$dir.$aFiles['mapcycle'].'"');
+ $mapcycle = $ssh->get();
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$dir.$aFiles['maps'].'; cat '.$dir.$aFiles['maps'].'"');
+ $maps = $ssh->get();
+
+ $html->get('listing', 'sections/servers/'.$server['game'].'/maps');
+
+ $html->set('id', $id);
+
+ $html->set('mapcycle', $mapcycle);
+ $html->set('maps', $maps);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/maps/search.php b/system/sections/servers/csgo/maps/search.php
new file mode 100644
index 0000000..866ca47
--- /dev/null
+++ b/system/sections/servers/csgo/maps/search.php
@@ -0,0 +1,64 @@
+get($mkey);
+
+ if(is_array($cache))
+ {
+ if($go)
+ sys::outjs($cache, $nmch);
+
+ sys::outjs($cache);
+ }
+
+ if(!isset($text{2}))
+ {
+ if($go)
+ sys::outjs(array('e' => 'Для выполнения поиска, необходимо больше данных'), $nmch);
+
+ sys::outjs(array('e' => ''));
+ }
+
+ // Поиск по картам
+ if($text{0} == '^')
+ {
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` REGEXP FROM_BASE64(\''.base64_encode(str_replace('_', '\_', $text).'').'\') ORDER BY `name` ASC LIMIT 12');
+ $text = substr($text, 1);
+ }else
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` LIKE FROM_BASE64(\''.base64_encode('%'.str_replace('_', '\_', $text).'%').'\') ORDER BY `name` ASC LIMIT 12');
+
+ if(!$sql->num())
+ {
+ if($go)
+ sys::outjs(array('e' => 'По вашему запросу ничего не найдено'), $nmch);
+
+ sys::outjs(array('e' => 'По вашему запросу ничего не найдено'));
+ }
+
+ $i = 0;
+ $mapsjs = '';
+
+ while($map = $sql->get())
+ {
+ $i+=1;
+
+ $mapsjs[$i] = 's'.$map['id'];
+
+ $html->get('map_search', 'sections/servers/games/maps');
+
+ $html->set('id', 's'.$map['id']);
+ $html->set('img', sys::img($map['name'], $server['game']));
+ $html->set('name', sys::find($map['name'], $text));
+
+ $html->pack('maps');
+ }
+
+ $mcache->set($mkey, array('maps' => $html->arr['maps'], 'mapsjs' => $mapsjs), false, 15);
+
+ sys::outjs(array('maps' => $html->arr['maps'], 'mapsjs' => $mapsjs));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/owners.php b/system/sections/servers/csgo/owners.php
new file mode 100644
index 0000000..71a5157
--- /dev/null
+++ b/system/sections/servers/csgo/owners.php
@@ -0,0 +1,172 @@
+ 'Включение',
+ 'stop' => 'Выключение',
+ 'restart' => 'Перезагрузка',
+ 'change' => 'Смена карты',
+ 'reinstall' => 'Переустановка',
+ 'update' => 'Обновление',
+ 'console' => 'Раздел "Консоль"',
+ 'settings' => 'Раздел "Настройки"',
+ 'plugins' => 'Раздел "Плагины"',
+ 'maps' => 'Раздел "Карты"'
+ );
+
+ $aAccess = array('start', 'stop', 'restart', 'change', 'reinstall', 'update', 'console', 'settings', 'plugins', 'maps');
+
+ // Проверка прав
+ if(isset($url['rights']) AND $url['rights'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['rights']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Совладелец не найден.'));
+
+ $owner = $sql->get();
+
+ $aRights = sys::b64djs($owner['rights']);
+
+ $rights = '';
+
+ foreach($aAccess as $access)
+ if($aRights[$access]) $rights .= $aAccessI[$access].', ';
+
+ sys::outjs(array('s' => substr($rights, 0, -2)));
+ }
+
+ // Удаление совладельца
+ if(isset($url['delete']) AND $url['delete'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if($sql->num())
+ $sql->query('DELETE FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/owners');
+ }
+
+ // Добавление совладельца
+ if($go)
+ {
+ $nmch = sys::rep_act('server_owners_go_'.$id, 5);
+
+ $aData = (isset($_POST['owner']) AND is_array($_POST['owner'])) ? $_POST['owner'] : array();
+
+ $aDate = isset($aData['\'time\'']) ? explode('.', $aData['\'time\'']) : explode('.', date('d.m.Y', $start_point));
+ $aTime = explode(':', date('H:i:s', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ sys::outjs(array('e' => 'Дата доступа указана неверно.'), $nmch);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2])+3600;
+
+ if($time < $start_point)
+ sys::outjs(array('e' => 'Время доступа не может быть меньше 60 минут.'), $nmch);
+
+ // Проверка пользователя
+ if(!isset($aData['\'user\'']))
+ sys::outjs(array('e' => 'Необходимо указать пользователя.'), $nmch);
+
+ if(is_numeric($aData['\'user\'']))
+ $sql->query('SELECT `id` FROM `users` WHERE `id`="'.$aData['\'user\''].'" LIMIT 1');
+ else{
+ if(sys::valid($aData['\'user\''], 'other', $aValid['login']))
+ sys::outjs(array('e' => sys::text('input', 'login_valid')), $nmch);
+
+ $sql->query('SELECT `id` FROM `users` WHERE `login`="'.$aData['\'user\''].'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Пользователь не найден в базе.'), $nmch);
+
+ $uowner = $sql->get();
+
+ $owner = $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+
+ // Если не обновление доступа совладельца, проверить кол-во
+ if(!$sql->num($owner))
+ {
+ $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" LIMIT 5');
+
+ if($sql->num() == 5)
+ sys::outjs(array('e' => 'Вы добавили максимально число совладельцев.'), $nmch);
+ }
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Владельца сервера нельзя добавить в совладельцы.'), $nmch);
+
+ $aRights = array();
+
+ $check = 0;
+
+ foreach($aAccess as $access)
+ {
+ $aRights[$access] = isset($aData['\''.$access.'\'']) ? 1 : 0;
+
+ $check += $aRights[$access];
+ }
+
+ if(!$check)
+ sys::outjs(array('e' => 'Необходимо включить минимум одно разрешение.'), $nmch);
+
+
+
+ if($sql->num($owner))
+ $sql->query('UPDATE `owners` set `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'" WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ else
+ $sql->query('INSERT INTO `owners` set `server`="'.$id.'", `user`="'.$uowner['id'].'", `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $html->nav('Друзья');
+
+ $cache = $mcache->get('server_owners_'.$id);
+
+ if($cache != '')
+ $html->arr['main'] = $cache;
+ else{
+ $owners = $sql->query('SELECT `id`, `user`, `rights`, `time` FROM `owners` WHERE `server`="'.$id.'" AND `time`>"'.$start_point.'" ORDER BY `id` ASC LIMIT 5');
+
+ if($sql->num())
+ include(LIB.'games/games.php');
+
+ while($owner = $sql->get($owners))
+ {
+ $sql->query('SELECT `login` FROM `users` WHERE `id`="'.$owner['user'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $uowner = $sql->get();
+
+ $rights = games::owners(sys::b64djs($owner['rights']));
+
+ $html->get('owners_list', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('oid', $owner['id']);
+ $html->set('user', $uowner['login']);
+ $html->set('rights', $rights);
+ $html->set('time', date('d.m.Y - H:i', $owner['time']));
+
+ $html->pack('owners');
+ }
+
+ $html->get('owners', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('time', date('d.m.Y', $start_point));
+
+ $html->set('owners', isset($html->arr['owners']) ? $html->arr['owners'] : 'Для данного сервера совладельцы отсутсвуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_owners_'.$id, $html->arr['main'], false, 1);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/plugins.php b/system/sections/servers/csgo/plugins.php
new file mode 100644
index 0000000..a116ca1
--- /dev/null
+++ b/system/sections/servers/csgo/plugins.php
@@ -0,0 +1,158 @@
+query('SELECT `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $nmch = sys::rep_act('server_plugins_go_'.$id, 10);
+
+ include(SEC.'servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('server_plugins_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_plugins_'.$id);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$id.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `plugins_install` WHERE `server`="'.$id.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_plugins_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/rcon.php b/system/sections/servers/csgo/rcon.php
new file mode 100644
index 0000000..1a0de70
--- /dev/null
+++ b/system/sections/servers/csgo/rcon.php
@@ -0,0 +1,54 @@
+ 'Необходимо выбрать игрока.'));
+
+ if($url['action'] == 'kick')
+ rcon::cmd(array_merge($server, array('id' => $id)), 'kickid "'.$player.'" "EGP Panel"');
+ else
+ rcon::cmd(array_merge($server, array('id' => $id)), 'sm_slay "'.$player.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ include(LIB.'geo.php');
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $aPlayers = rcon::players(rcon::cmd(array_merge($server, array('id' => $id))));
+
+ foreach($aPlayers as $i => $aPlayer)
+ {
+ $html->get('player', 'sections/servers/'.$server['game'].'/rcon');
+
+ $html->set('i', $i);
+ $html->set('userid', $aPlayer['userid']);
+ $html->set('name', $aPlayer['name']);
+ $html->set('steamid', $aPlayer['steamid']);
+ $html->set('time', $aPlayer['time']);
+ $html->set('ping', $aPlayer['ping']);
+ $html->set('ip', $aPlayer['ip']);
+ $html->set('ico', $aPlayer['ico']);
+ $html->set('country', $aPlayer['country']);
+
+ $html->pack('players');
+ }
+
+ sys::outjs(array('s' => isset($html->arr['players']) ? $html->arr['players'] : ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Rcon управление игроками');
+
+ $html->get('rcon', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/settings.php b/system/sections/servers/csgo/settings.php
new file mode 100644
index 0000000..b775147
--- /dev/null
+++ b/system/sections/servers/csgo/settings.php
@@ -0,0 +1,77 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $aSub = array('start', 'server', 'admins', 'bans', 'firewall', 'crontab', 'startlogs', 'debug', 'logs', 'smlogs', 'pack', 'file', 'antiddos', 'api');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aEditslist = 1;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $antiddos = 'Индивидуальная защита отключена '
+ .'Индивидуальная защита (Заблокировать всех кроме: RU, UA) '
+ .'Индивидуальная защита (Заблокировать всех кроме: AM, BY, UA, RU, KZ) ';
+
+ include(SEC.'servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('antiddos', str_replace($server['ddos'], $server['ddos'].'" selected="select', $antiddos));
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+
+ $sql->query('SELECT `key` FROM `api` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ {
+ $api = $sql->get();
+
+ $html->set('api', $api['key']);
+ $html->unit('api', 1, 1);
+ }else
+ $html->unit('api', 0, 1);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/settings/admins.php b/system/sections/servers/csgo/settings/admins.php
new file mode 100644
index 0000000..a60bca1
--- /dev/null
+++ b/system/sections/servers/csgo/settings/admins.php
@@ -0,0 +1,114 @@
+nav('Управление администраторами');
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $aData = array();
+
+ $aData['active'] = isset($_POST['active']) ? $_POST['active'] : '';
+ $aData['value'] = isset($_POST['value']) ? $_POST['value'] : '';
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : '';
+ $aData['flags'] = isset($_POST['flags']) ? $_POST['flags'] : '';
+ $aData['immunity'] = isset($_POST['immunity']) ? sys::int($_POST['immunity']) : '';
+ $aData['time'] = isset($_POST['time']) ? $_POST['time'] : '';
+ $aData['info'] = isset($_POST['info']) ? $_POST['info'] : '';
+
+ // Удаление текущих записей
+ $sql->query('DELETE FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'"');
+
+ $usini = '';
+
+ foreach($aData['value'] as $index => $val)
+ {
+ if($val != '')
+ {
+ $aDate = isset($aData['time'][$index]) ? explode('.', $aData['time'][$index]) : explode('.', date('d.m.Y', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ $aDate = explode('.', date('d.m.Y', $start_point));
+
+ $time = mktime(0, 0, 0, $aDate[1], $aDate[0], $aDate[2]);
+
+ $aData['active'][$index] = isset($aData['active'][$index]) ? 1 : 0;
+ $aData['passwd'][$index] = isset($aData['passwd'][$index]) ? $aData['passwd'][$index] : '';
+ $aData['flags'][$index] = isset($aData['flags'][$index]) ? $aData['flags'][$index] : '';
+ $aData['info'][$index] = isset($aData['info'][$index]) ? $aData['info'][$index] : '';
+
+ $text = '"'.$val.'" "'.$aData['immunity'][$index].':'.$aData['flags'][$index].'" "'.$aData['passwd'][$index].'"';
+
+ $sql->query('INSERT INTO `admins_'.$server['game'].'` set'
+ .'`server`="'.$id.'",'
+ .'`value`="'.htmlspecialchars($val).'",'
+ .'`active`="'.$aData['active'][$index].'",'
+ .'`passwd`="'.htmlspecialchars($aData['passwd'][$index]).'",'
+ .'`flags`="'.htmlspecialchars($aData['flags'][$index]).'",'
+ .'`immunity`="'.$aData['immunity'][$index].'",'
+ .'`time`="'.$time.'",'
+ .'`text`="'.htmlspecialchars($text).'",'
+ .'`info`="'.htmlspecialchars($aData['info'][$index]).'"');
+
+ if($aData['active'][$index])
+ $usini .= $text.PHP_EOL;
+ }
+ }
+
+ $temp = sys::temp($usini);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/csgo/addons/sourcemod/configs/admins_simple.ini', 0644);
+
+ unlink($temp);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/csgo/addons/sourcemod/configs/admins_simple.ini');
+
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \" sm_reloadadmins\"\015'");
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Построение списка добавленных админов
+ $sql->query('SELECT `id`, `value`, `active`, `passwd`, `flags`, `immunity`, `time`, `info` FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'" ORDER BY `id` ASC');
+ while($admin = $sql->get())
+ {
+ $html->get('list', 'sections/servers/'.$server['game'].'/settings/admins');
+
+ if($admin['active'])
+ $html->unit('active', 1);
+ else
+ $html->unit('active');
+
+ $html->set('id', $admin['id']);
+ $html->set('value', $admin['value']);
+ $html->set('passwd', $admin['passwd']);
+ $html->set('flags', $admin['flags']);
+ $html->set('immunity', $admin['immunity']);
+ $html->set('time', date('d.m.y', $admin['time']));
+ $html->set('info', $admin['info']);
+
+ $html->pack('admins');
+ }
+
+ $sql->query('SELECT `id` FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'" ORDER BY `id` DESC LIMIT 1');
+ $max = $sql->get();
+
+ $html->get('admins', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('admins', isset($html->arr['admins']) ? $html->arr['admins'] : '');
+ $html->set('index', $max['id'] < 1 ? 0 : $max['id']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/settings/bans.php b/system/sections/servers/csgo/settings/bans.php
new file mode 100644
index 0000000..e715728
--- /dev/null
+++ b/system/sections/servers/csgo/settings/bans.php
@@ -0,0 +1,160 @@
+nav('Бан листы');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к файлам (banned_user.cfg / banned_ip.cfg)
+ $folder = $tarif['install'].$server['uid'].'/csgo';
+
+ // Если бан/разбан/проверка
+ if($go)
+ {
+ $aData = array();
+
+ $aData['value'] = isset($_POST['value']) ? trim($_POST['value']) : sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+ $aData['userid'] = isset($_POST['userid']) ? sys::int($_POST['userid']) : false;
+ $aData['amxbans'] = isset($_POST['amxbans']) ? true : false;
+
+ // Проверка входных данных
+ if(sys::valid($aData['value'], 'steamid') AND sys::valid($aData['value'], 'steamid3') AND sys::valid($aData['value'], 'ip'))
+ sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+
+ // Если указан steamid
+ if(sys::valid($aData['value'], 'ip'))
+ {
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'] AND $aData['userid'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['userid']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"banid 0.0 ".$aData['value']." kick\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"banid 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_user.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_user.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_user.cfg | grep -v '.$aData['value'].' > temp_banned.cfg; echo "" >> temp_banned.cfg && cat temp_banned.cfg > banned_user.cfg; rm temp_banned.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeid ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeid\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный SteamID найден в файле banned_user.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный SteamID не найден в файле banned_user.cfg'), $nmch);
+ }
+ }else{
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['value']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"addip 0.0 ".$aData['value']." EGP\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"addip 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_ip.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_ip.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_ip.cfg | grep -v '.$aData['value'].' > temp_listip.cfg; echo "" >> temp_listip.cfg && cat temp_listip.cfg > banned_ip.cfg; rm temp_listip.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeip ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeip\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный IP найден в файле banned_ip.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный IP не найден в файле banned_ip.cfg'), $nmch);
+ }
+ }
+ }
+
+ // Содержимое banned_user.cfg
+ $ssh->set('cd '.$folder.' && cat banned_user.cfg | awk \'{print $3}\' | egrep "^\[U:[01]:[0-9]{3,12}\]$"');
+ $aBanned = explode("\n", $ssh->get());
+
+ // Содержимое banned_ip.cfg
+ $ssh->set('cd '.$folder.' && cat banned_ip.cfg | awk \'{print $3}\' | egrep "(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}"');
+ $aListip = explode("\n", $ssh->get());
+
+ if(isset($aBanned[count($aBanned)-1]) AND $aBanned[count($aBanned)-1] == '')
+ unset($aBanned[count($aBanned)-1]);
+
+ if(isset($aListip[count($aListip)-1]) AND $aListip[count($aListip)-1] == '')
+ unset($aListip[count($aListip)-1]);
+
+ // Построение списка забаненых по steamid
+ foreach($aBanned as $line => $steam)
+ {
+ $html->get('bans_list', 'sections/servers/games/settings');
+
+ $html->set('value', trim($steam));
+
+ $html->pack('banned');
+ }
+
+ // Построение списка забаненых по ip
+ foreach($aListip as $line => $ip)
+ {
+ $html->get('bans_list', 'sections/servers/games/settings');
+
+ $html->set('value', trim($ip));
+
+ $html->pack('listip');
+ }
+
+ $html->get('bans', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('banned', isset($html->arr['banned']) ? $html->arr['banned'] : '');
+ $html->set('listip', isset($html->arr['listip']) ? $html->arr['listip'] : '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/settings/debug.php b/system/sections/servers/csgo/settings/debug.php
new file mode 100644
index 0000000..180f9d0
--- /dev/null
+++ b/system/sections/servers/csgo/settings/debug.php
@@ -0,0 +1,28 @@
+nav('Отладочный лог');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Чтение файла - oldstart.log
+ $file = $tarif['install'].$server['uid'].'/debug.log';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep "CRASH: " | grep -ve "^#\|^[[:space:]]*$"');
+
+ $html->get('debug', 'sections/servers/games/settings');
+
+ $html->set('log', htmlspecialchars($ssh->get()));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/settings/logs.php b/system/sections/servers/csgo/settings/logs.php
new file mode 100644
index 0000000..6f3acd2
--- /dev/null
+++ b/system/sections/servers/csgo/settings/logs.php
@@ -0,0 +1,89 @@
+nav('Логи');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к логам
+ $folder = $tarif['install'].$server['uid'].'/csgo/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['cslogs']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/logs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'logs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/logs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"\/"$1"\/"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('\/', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', end($name));
+ $html->set('uri', 'logs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('uri', '');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/settings/server.php b/system/sections/servers/csgo/settings/server.php
new file mode 100644
index 0000000..62faef8
--- /dev/null
+++ b/system/sections/servers/csgo/settings/server.php
@@ -0,0 +1,117 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ $config_end = $servercfg['\'other\''];
+
+ unset($servercfg['\'other\'']);
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).' "'.$val.'"'."\n";
+
+ // Временый файл
+ $temp = sys::temp($config.$config_end);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/csgo/cfg/server.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/csgo/cfg/server.cfg');
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Чтение файла - server.cfg
+ $file = $tarif['install'].$server['uid'].'/csgo/cfg/server.cfg';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+ $other = '';
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode(' ', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.' ', "", $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ else
+ $other .= $line."\n";
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $html->get('servercfg', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('cfg', $html->arr['list']);
+ $html->set('other', $other);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/settings/smlogs.php b/system/sections/servers/csgo/settings/smlogs.php
new file mode 100644
index 0000000..784e3a8
--- /dev/null
+++ b/system/sections/servers/csgo/settings/smlogs.php
@@ -0,0 +1,89 @@
+nav('Логи SourceMod');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к логам
+ $folder = $tarif['install'].$server['uid'].'/csgo/addons/sourcemod/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['csssmlogs']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/smlogs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'smlogs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/smlogs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"\/"$1"\/"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('\/', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', end($name));
+ $html->set('uri', 'smlogs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('uri', 'sm');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/settings/start.php b/system/sections/servers/csgo/settings/start.php
new file mode 100644
index 0000000..ea9406b
--- /dev/null
+++ b/system/sections/servers/csgo/settings/start.php
@@ -0,0 +1,169 @@
+query('SELECT `uid`, `slots`, `slots_start`, `map_start`, `vac`, `fastdl`, `autorestart`, `fps`, `tickrate`, `pingboost` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install`, `tickrate`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'games/games.php');
+ include(LIB.'games/tarifs.php');
+ include(LIB.'games/'.$server['game'].'/tarif.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/csgo/maps', $server['map_start'], false);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/csgo/maps', $map, true, $nmch);
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `servers` set `vac`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'mod':
+ if(in_array($value, array(1, 2, 3, 4, 5)))
+ $sql->query('UPDATE `servers` set `pingboost`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `servers` set `autorestart`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'tickrate':
+ if(!tarif::price($tarif['price']) AND in_array($value, explode(':', $tarif['tickrate'])))
+ $sql->query('UPDATE `servers` set `tickrate`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.sys::first(explode(':', $unit['address'])).':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/csgo/cfg/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/csgo/cfg/fastdl.cfg;'
+ .'ln -s '.$tarif['install'].$server['uid'].'/csgo /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/csgo/cfg/server.cfg;'
+ .'echo "exec fastdl.cfg" >> '.$tarif['install'].$server['uid'].'/csgo/cfg/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/csgo/cfg/server.cfg;'
+ .'rm '.$tarif['install'].$server['uid'].'/csgo/cfg/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `servers` set `fastdl`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $tickrate = ''.$server['tickrate'].' TickRate ';
+
+ if(!tarif::price($tarif['price']))
+ {
+ $aTick = explode(':', $tarif['tickrate']);
+
+ unset($aTick[array_search($server['tickrate'], $aTick)]);
+
+ if(count($aTick))
+ foreach($aTick as $value)
+ $tickrate .= ''.$value.' TickRate ';
+ }
+
+ // Игровой режим
+ $mods = 'Классический обычный '
+ .'Классический соревновательный '
+ .'Гонка вооружений '
+ .'Уничтожение объекта '
+ .'Бой насмерть ';
+
+ if(!$server['pingboost'])
+ $server['pingboost'] = 2;
+
+ $mod = str_replace('value="'.$server['pingboost'], 'value="'.$server['pingboost'].'" selected="select', $mods);
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('map', $server['map_start']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('mod', $mod);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ if(!tarif::price($tarif['price']))
+ {
+ $html->unit('tickrate', true);
+ $html->set('tickrate', $tickrate);
+ }else
+ $html->unit('tickrate');
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/tarif.php b/system/sections/servers/csgo/tarif.php
new file mode 100644
index 0000000..30cc896
--- /dev/null
+++ b/system/sections/servers/csgo/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `fps`, `tickrate`, `ram`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'plan', 'address', 'addextend', 'unit', 'slots');
+
+ include(SEC.'servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/tarif/extend.php b/system/sections/servers/csgo/tarif/extend.php
new file mode 100644
index 0000000..9d060bc
--- /dev/null
+++ b/system/sections/servers/csgo/tarif/extend.php
@@ -0,0 +1,51 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['tickrate'] = $server['tickrate'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ $aPrice = explode(':', $tarif['price']);
+
+ // Цена за 30 дней 1 слота
+ $price = $aPrice[array_search($server['tickrate'], explode(':', $tarif['tickrate']))];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/tarif/plan.php b/system/sections/servers/csgo/tarif/plan.php
new file mode 100644
index 0000000..3eed81c
--- /dev/null
+++ b/system/sections/servers/csgo/tarif/plan.php
@@ -0,0 +1,66 @@
+ 'Переданые не все данные'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ // Проверка плана
+ if(array_search($plan, $aTICK) === FALSE)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ if($plan == $server['tickrate'])
+ sys::outjs(array('e' => 'Смысла в этой операции нет'), $nmch);
+
+ if(!tarif::price($tarif['price']))
+ sys::outjs(array('e' => 'Чтобы изменить тариф, перейдите в настройки запуска'), $nmch);
+
+ if($server['time'] < $start_point+86400)
+ $time = $server['time'];
+ else{
+ // Цена за 1 день аренды (по новому тарифному плану)
+ $price = $aPrice[array_search($plan, $aTICK)]/30*$server['slots'];
+
+ // Цена за 1 день аренды (по старому тарифному плану)
+ $price_old = $aPrice[array_search($server['tickrate'], $aTICK)]/30*$server['slots'];
+
+ // Остаток дней аренды
+ $days = ($server['time']-$start_point)/86400;
+
+ $time = date('H:i:s', $server['time']);
+ $date = date('d.m.Y', round($start_point+$days*$price_old/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+ $aTime = explode(':', $time);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2]);
+ }
+
+ // Выполнение смена тарифного плана
+ if($go)
+ {
+ sys::benefitblock($id, $nmch);
+
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `tickrate`="'.$plan.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_plan').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/tarif/slots.php b/system/sections/servers/csgo/tarif/slots.php
new file mode 100644
index 0000000..e457286
--- /dev/null
+++ b/system/sections/servers/csgo/tarif/slots.php
@@ -0,0 +1,34 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $aPrice[array_search($server['tickrate'], $aTICK)]/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($aPrice[array_search($server['tickrate'], $aTICK)]/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/tarif/unit.php b/system/sections/servers/csgo/tarif/unit.php
new file mode 100644
index 0000000..b840bc7
--- /dev/null
+++ b/system/sections/servers/csgo/tarif/unit.php
@@ -0,0 +1,50 @@
+ 'Переданы не все данные.'), $nmch);
+
+ if(!$cfg['change_unit'][$server['game']] || $server['time'] < $start_point+86400 || $server['test'])
+ exit;
+
+ $sql->query('SELECT `id`, `unit`, `packs`, `tickrate`, `price` FROM `tarifs` WHERE `unit`="'.$uid.'" AND `game`="'.$server['game'].'" AND `name`="'.$tarif['name'].'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Не найден подходящий тариф.'), $nmch);
+
+ $oldTarif = $tarif;
+
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $oldUnit = $sql->get();
+
+ $aPriceold = explode(':', $oldTarif['price']);
+ $aTICKold = explode(':', $oldTarif['tickrate']);
+
+ $sql->query('SELECT `id` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная локация не доступна.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ if(!in_array($server['tickrate'], $aTICK))
+ sys::outjs(array('e' => 'Не найден подходящий тарифный план.'), $nmch);
+
+ // Цена за 1 день (при новом тарифном плане)
+ $price = $aPrice[array_search($server['tickrate'], $aTICK)]/30*$server['slots'];
+
+ // Цена аренды за остаток дней (с текущим тарифным планом)
+ $oldprice = ($server['time']-$start_point)/86400*($aPriceold[array_search($server['tickrate'], $aTICKold)]/30*$server['slots']);
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$oldprice/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ include(SEC.'servers/games/tarif/unit.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/csgo/web.php b/system/sections/servers/csgo/web.php
new file mode 100644
index 0000000..634e9b6
--- /dev/null
+++ b/system/sections/servers/csgo/web.php
@@ -0,0 +1,87 @@
+nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ include(DATA.'web.php');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub) AND in_array($url['action'], array_merge($aAction[$url['subsection']], array('install', 'manage'))))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_web_go_'.$id, 10);
+ else
+ $html->nav('Web', $cfg['http'].'servers/id/'.$id.'/section/web');
+
+ include(SEC.'web/'.$url['subsection'].'/free/'.$url['action'].'.php');
+ }else{
+ $html->nav('Web');
+
+ if($mcache->get('server_web_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_web_'.$id);
+ else{
+ // Услуги
+ foreach($aWeb[$server['game']] as $service => $active)
+ {
+ if($active)
+ {
+ if($service == 'hosting')
+ {
+ if(!$aWebVHTtype && !in_array($server['tarif'], $aWebVHT))
+ continue;
+
+ if($aWebVHTtype && in_array($server['tarif'], $aWebVHT))
+ continue;
+ }
+
+ // Проверка на установку
+ switch($aWebInstall[$server['game']][$service])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ }
+
+ if($sql->num())
+ $html->get('list_install', 'sections/servers/games/web');
+ else
+ $html->get('list', 'sections/servers/games/web');
+
+ $html->set('id', $id);
+ $html->set('service', $service);
+ $html->set('name', $aWebname[$service]);
+ $html->set('desc', $aWebDesc[$service]);
+
+ $html->pack($aWebType[$service]);
+ }
+ }
+
+ // Блоки услуг
+ foreach($aWebTypeInfo[$server['game']] as $type => $name)
+ {
+ if(!isset($html->arr[$type]))
+ continue;
+
+ $html->get('block', 'sections/servers/games/web');
+ $html->set('name', $name);
+ $html->set('list', $html->arr[$type]);
+ $html->pack('web');
+ }
+
+ $html->get('web', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('web', isset($html->arr['web']) ? $html->arr['web'] : 'Дополнительные услуги отсутствуют');
+ $html->pack('main');
+
+ $mcache->set('server_web_'.$id, $html->arr['main'], false, 4);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/console.php b/system/sections/servers/css/console.php
new file mode 100644
index 0000000..f31d841
--- /dev/null
+++ b/system/sections/servers/css/console.php
@@ -0,0 +1,67 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $command = isset($_POST['command']) ? sys::cmd($_POST['command']) : '';
+
+ if($server['status'] == 'off')
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('servers', 'off')));
+
+ sys::out(sys::text('servers', 'off'));
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ sys::out(sys::text('error', 'ssh'));
+ }
+
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ $filecmd = $dir.'console.log';
+
+ if($command)
+ {
+ if(strtolower($command) == 'clear')
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"Очистка консоли\n\" > '.$filecmd.'"');
+ else
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.$command.'"\015\';'
+ .'sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/copy.php b/system/sections/servers/css/copy.php
new file mode 100644
index 0000000..7a9c216
--- /dev/null
+++ b/system/sections/servers/css/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('server_copy_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_copy_'.$id);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `copy` WHERE `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_copy_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/graph.php b/system/sections/servers/css/graph.php
new file mode 100644
index 0000000..d8d8df8
--- /dev/null
+++ b/system/sections/servers/css/graph.php
@@ -0,0 +1,71 @@
+query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+ $graph = $sql->get();
+
+ $nmch = 'server_graph_full_'.$id;
+
+ $time = isset($url['time']) ? $url['time'] : 'day';
+
+ if(!in_array($time, array('day', 'week', 'month')))
+ $time = 'day';
+
+ // Выхлоп кеш графика
+ if($mcache->get($nmch) AND file_exists(TEMP.(md5($graph['key'].'full_'.$time)).'.png'))
+ {
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ include(LIB.'games/graph/pData.php');
+ include(LIB.'games/graph/pDraw.php');
+ include(LIB.'games/graph/pImage.php');
+
+ include(LIB.'games/graph.php');
+
+ graph::full($id, $server['slots_start'], $graph['key'], $time);
+
+ $mcache->set($nmch, true, false, 300);
+
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Графики');
+
+ if($mcache->get('server_graph_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_graph_'.$id);
+ else{
+ $sql->query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+
+ // Если отсутствует ключ, создать
+ if(!$sql->num())
+ {
+ // Генерация ключа
+ $key = md5($id.sys::key('graph'));
+
+ $sql->query('INSERT INTO `graph` set `server`="'.$id.'", `key`="'.$key.'", `time`="0"');
+ }else{
+ $graph = $sql->get();
+
+ $key = $graph['key'];
+ }
+
+ $html->get('graph', 'sections/servers/games');
+
+ $html->set('id', $id);
+
+ $html->set('key', $key);
+ $html->set('address', $server['address']);
+ $html->set('_img', '[img]');
+
+ $html->pack('main');
+
+ $mcache->set('server_graph_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/index.php b/system/sections/servers/css/index.php
new file mode 100644
index 0000000..098c6c5
--- /dev/null
+++ b/system/sections/servers/css/index.php
@@ -0,0 +1,44 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `tickrate`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address']);
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name'].' / '.$server['tickrate'].' TickRate');
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img'));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/maps.php b/system/sections/servers/css/maps.php
new file mode 100644
index 0000000..6e04160
--- /dev/null
+++ b/system/sections/servers/css/maps.php
@@ -0,0 +1,86 @@
+query('SELECT `unit`, `tarif` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'list', 'listing', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Карты', $cfg['http'].'servers/id/'.$id.'/section/maps');
+
+ $nmch = sys::rep_act('server_maps_go_'.$id, 10);
+
+ include(SEC.'servers/'.$server['game'].'/maps/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Карты');
+
+ // Построение списка установленных карт
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id);
+ }
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $ssh->set('cd '.$tarif['install'].$server['uid'].'/cstrike/maps/ && ls | grep -e "\.bsp$"');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace('.bsp', '', $maps));
+
+ // Сортировка карт
+ sort($aMaps);
+ reset($aMaps);
+
+ $mapsjs = '';
+ $i = 0;
+
+ foreach($aMaps as $index => $map)
+ {
+ if(!isset($map{3}))
+ continue;
+
+ $mapjs = str_replace('$', '-_-', $map);
+
+ $i+=1;
+ $mapsjs .= $i.' : "'.$mapjs.'",';
+
+ $html->get('map_server', 'sections/servers/games/maps');
+ $html->set('img', sys::img($map, $server['game']));
+ $html->set('map', $mapjs);
+ $html->set('name', $map);
+ $html->pack('maps');
+ }
+
+ // Если есть кеш
+ if($mcache->get('server_maps_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_maps_'.$id);
+ else{
+ $html->get('maps', 'sections/servers/games');
+ $html->set('id', $id);
+ $html->set('types', isset($html->arr['types']) ? $html->arr['types'] : '');
+ $html->set('maps', isset($html->arr['maps']) ? $html->arr['maps'] : '');
+ $html->set('mapsjs', $mapsjs);
+ $html->pack('main');
+
+ $mcache->set('server_maps_'.$id, $html->arr['main'], false, 3);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/maps/delete.php b/system/sections/servers/css/maps/delete.php
new file mode 100644
index 0000000..7b0173a
--- /dev/null
+++ b/system/sections/servers/css/maps/delete.php
@@ -0,0 +1,70 @@
+query('SELECT `unit`, `tarif`, `map_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ // Генерация списка карт
+ $ssh->set('cd '.$dir.'maps/ && ls | grep -iE "\.bsp$"');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace('.bsp', '', $maps));
+
+ // Массив переданных карт
+ $in_aMaps = isset($_POST['maps']) ? $_POST['maps'] : array();
+
+ // Обработка выборки
+ foreach($in_aMaps as $name => $sel)
+ if($sel)
+ {
+ $map = str_replace(array("\\", "'", "'", '-_-'), array('', '', '', '$'), $name);
+
+ // Проверка наличия карты
+ if(!in_array($map, $aMaps))
+ continue;
+
+ // Проверка: является ли карта стартовой
+ if($server['map_start'] == $map)
+ continue;
+
+ $ssh->set('cd /path/maps/'.$server['game'].'/'.sys::map($map).' && du -a | grep -iE "\.[a-z]{1,3}$" | awk \'{print $2}\'');
+
+ $aFiles = explode("\n", str_replace('./', '', $ssh->get()));
+
+ if(isset($aFiles[count($aFiles)-1]) AND $aFiles[count($aFiles)-1] == '')
+ unset($aFiles[count($aFiles)-1]);
+
+ $files = '';
+
+ foreach($aFiles as $file)
+ $files .= $dir.$file.' ';
+
+ $rm = '';
+ $aFlrm = explode(' ', $dir.'maps/'.$map.'.* '.trim($files));
+
+ foreach($aFlrm as $flrm)
+ $rm .= sys::map($flrm).' ';
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -dmS md'.$start_point.$id.' sh -c \'rm '.trim($rm).'\'');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
diff --git a/system/sections/servers/css/maps/install.php b/system/sections/servers/css/maps/install.php
new file mode 100644
index 0000000..b54e0fd
--- /dev/null
+++ b/system/sections/servers/css/maps/install.php
@@ -0,0 +1,56 @@
+query('SELECT `unit`, `tarif`, `hdd` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ // Проверить наличие свободного места
+ $ssh->set('cd '.$dir.' && du -ms');
+ $hdd = ceil(sys::int($ssh->get())/($server['hdd']/100));
+ $hdd = $hdd > 100 ? 100 : $hdd;
+
+ if($hdd == 100)
+ sys::outjs(array('e' => 'Невозможно выполнить установку, нет свободного места'), $nmch);
+
+ // Массив переданных карт
+ $in_aMaps = isset($_POST['maps']) ? $_POST['maps'] : array();
+
+ // Обработка выборки
+ foreach($in_aMaps as $mid => $sel)
+ if($sel)
+ {
+ $map = sys::int($mid);
+
+ // Проверка наличия карты
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `id`="'.$map.'" AND `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $map = $sql->get();
+
+ $cp = 'cp /path/maps/'.$server['game'].'/'.sys::map($map['name']).'.* '.$dir.'maps/;'
+ .'cd /path/maps/'.$server['game'].'/'.sys::map($map['name']).'/ && cp -r * '.$dir;
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -dmS mc'.$start_point.$id.' sh -c \''.$cp.'\'');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
diff --git a/system/sections/servers/css/maps/list.php b/system/sections/servers/css/maps/list.php
new file mode 100644
index 0000000..abb251a
--- /dev/null
+++ b/system/sections/servers/css/maps/list.php
@@ -0,0 +1,62 @@
+nav('Установка карт');
+
+ // Категории для быстрой сортировки
+ $html->get('types', 'sections/servers/'.$server['game'].'/maps');
+
+ $html->set('id', $id);
+
+ $html->pack('types');
+
+ $type = false;
+
+ if(isset($url['type']) AND in_array($url['type'], array('de', 'cs', 'aim', 'awp', 'bhop', 'csde', 'deathrun', 'jail')))
+ $type = '^'.$url['type'].'\_';
+
+ if($type)
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` REGEXP FROM_BASE64(\''.base64_encode($type).'\') ORDER BY `name` ASC LIMIT 72');
+ else{
+ $sql->query('SELECT `id` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'"');
+
+ // Массив для построения страниц
+ $aPage = sys::page($page, $sql->num(), 30);
+
+ // Генерация массива ($html->arr['pages']) страниц
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'servers/id/'.$id.'/section/maps/subsection/list');
+
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `name` ASC LIMIT '.$aPage['num'].', 30');
+ }
+
+ $mapsjs = '';
+ $i = 0;
+
+ while($map = $sql->get())
+ {
+ $i+=1;
+
+ $mapsjs .= $i.' : "'.$map['id'].'",';
+
+ $html->get('map_install', 'sections/servers/games/maps');
+
+ $html->set('id', $map['id']);
+ $html->set('img', sys::img($map['name'], $server['game']));
+ $html->set('name', $map['name']);
+
+ $html->pack('maps');
+ }
+
+ $html->get('install', 'sections/servers/games/maps');
+
+ $html->set('id', $id);
+
+ $html->set('types', isset($html->arr['types']) ? $html->arr['types'] : '');
+ $html->set('maps', isset($html->arr['maps']) ? $html->arr['maps'] : 'К сожалению карты не найдены в базе');
+ $html->set('amaps', $mapsjs);
+ $html->set('pages', isset($html->arr['pages']) ? $html->arr['pages'] : '');
+ $html->set('cdn', $cfg['cdn']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/maps/listing.php b/system/sections/servers/css/maps/listing.php
new file mode 100644
index 0000000..f47dcc0
--- /dev/null
+++ b/system/sections/servers/css/maps/listing.php
@@ -0,0 +1,94 @@
+nav('Списки карт');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/maps');
+ }
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Директория сервера
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ // Генерация списка
+ if($go AND isset($url['gen']))
+ {
+ $ssh->set('cd '.$dir.'maps/ && ls | grep -e "\.bsp$"');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace(array('./', '.bsp'), '', $maps));
+
+ sort($aMaps);
+ reset($aMaps);
+
+ $list = '';
+
+ foreach($aMaps as $index => $map)
+ {
+ $aMap = explode('/', $map);
+ $name = end($aMap);
+ if(strlen($name) < 4)
+ continue;
+
+ $list .= $map."\n";
+ }
+
+ sys::outjs(array('s' => $list), $nmch);
+ }
+
+ $aFiles = array(
+ 'mapcycle' => 'mapcycle.txt',
+ 'maps' => 'maplist.txt'
+ );
+
+ // Сохранение
+ if($go AND isset($url['file']))
+ {
+ if(!array_key_exists($url['file'], $aFiles))
+ exit;
+
+ $data = isset($_POST['data']) ? $_POST['data'] : '';
+
+ $temp = sys::temp($data);
+
+ // Отправление файла на сервер
+ $ssh->setfile($temp, $dir.$aFiles[$url['file']], 0644);
+
+ // Смена владельца/группы файла
+ $ssh->set('chown server'.$server['uid'].':servers '.$dir.$aFiles[$url['file']]);
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$dir.$aFiles['mapcycle'].'; cat '.$dir.$aFiles['mapcycle'].'"');
+ $mapcycle = $ssh->get();
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$dir.$aFiles['maps'].'; cat '.$dir.$aFiles['maps'].'"');
+ $maps = $ssh->get();
+
+ $html->get('listing', 'sections/servers/'.$server['game'].'/maps');
+
+ $html->set('id', $id);
+
+ $html->set('mapcycle', $mapcycle);
+ $html->set('maps', $maps);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/maps/search.php b/system/sections/servers/css/maps/search.php
new file mode 100644
index 0000000..866ca47
--- /dev/null
+++ b/system/sections/servers/css/maps/search.php
@@ -0,0 +1,64 @@
+get($mkey);
+
+ if(is_array($cache))
+ {
+ if($go)
+ sys::outjs($cache, $nmch);
+
+ sys::outjs($cache);
+ }
+
+ if(!isset($text{2}))
+ {
+ if($go)
+ sys::outjs(array('e' => 'Для выполнения поиска, необходимо больше данных'), $nmch);
+
+ sys::outjs(array('e' => ''));
+ }
+
+ // Поиск по картам
+ if($text{0} == '^')
+ {
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` REGEXP FROM_BASE64(\''.base64_encode(str_replace('_', '\_', $text).'').'\') ORDER BY `name` ASC LIMIT 12');
+ $text = substr($text, 1);
+ }else
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` LIKE FROM_BASE64(\''.base64_encode('%'.str_replace('_', '\_', $text).'%').'\') ORDER BY `name` ASC LIMIT 12');
+
+ if(!$sql->num())
+ {
+ if($go)
+ sys::outjs(array('e' => 'По вашему запросу ничего не найдено'), $nmch);
+
+ sys::outjs(array('e' => 'По вашему запросу ничего не найдено'));
+ }
+
+ $i = 0;
+ $mapsjs = '';
+
+ while($map = $sql->get())
+ {
+ $i+=1;
+
+ $mapsjs[$i] = 's'.$map['id'];
+
+ $html->get('map_search', 'sections/servers/games/maps');
+
+ $html->set('id', 's'.$map['id']);
+ $html->set('img', sys::img($map['name'], $server['game']));
+ $html->set('name', sys::find($map['name'], $text));
+
+ $html->pack('maps');
+ }
+
+ $mcache->set($mkey, array('maps' => $html->arr['maps'], 'mapsjs' => $mapsjs), false, 15);
+
+ sys::outjs(array('maps' => $html->arr['maps'], 'mapsjs' => $mapsjs));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/owners.php b/system/sections/servers/css/owners.php
new file mode 100644
index 0000000..71a5157
--- /dev/null
+++ b/system/sections/servers/css/owners.php
@@ -0,0 +1,172 @@
+ 'Включение',
+ 'stop' => 'Выключение',
+ 'restart' => 'Перезагрузка',
+ 'change' => 'Смена карты',
+ 'reinstall' => 'Переустановка',
+ 'update' => 'Обновление',
+ 'console' => 'Раздел "Консоль"',
+ 'settings' => 'Раздел "Настройки"',
+ 'plugins' => 'Раздел "Плагины"',
+ 'maps' => 'Раздел "Карты"'
+ );
+
+ $aAccess = array('start', 'stop', 'restart', 'change', 'reinstall', 'update', 'console', 'settings', 'plugins', 'maps');
+
+ // Проверка прав
+ if(isset($url['rights']) AND $url['rights'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['rights']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Совладелец не найден.'));
+
+ $owner = $sql->get();
+
+ $aRights = sys::b64djs($owner['rights']);
+
+ $rights = '';
+
+ foreach($aAccess as $access)
+ if($aRights[$access]) $rights .= $aAccessI[$access].', ';
+
+ sys::outjs(array('s' => substr($rights, 0, -2)));
+ }
+
+ // Удаление совладельца
+ if(isset($url['delete']) AND $url['delete'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if($sql->num())
+ $sql->query('DELETE FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/owners');
+ }
+
+ // Добавление совладельца
+ if($go)
+ {
+ $nmch = sys::rep_act('server_owners_go_'.$id, 5);
+
+ $aData = (isset($_POST['owner']) AND is_array($_POST['owner'])) ? $_POST['owner'] : array();
+
+ $aDate = isset($aData['\'time\'']) ? explode('.', $aData['\'time\'']) : explode('.', date('d.m.Y', $start_point));
+ $aTime = explode(':', date('H:i:s', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ sys::outjs(array('e' => 'Дата доступа указана неверно.'), $nmch);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2])+3600;
+
+ if($time < $start_point)
+ sys::outjs(array('e' => 'Время доступа не может быть меньше 60 минут.'), $nmch);
+
+ // Проверка пользователя
+ if(!isset($aData['\'user\'']))
+ sys::outjs(array('e' => 'Необходимо указать пользователя.'), $nmch);
+
+ if(is_numeric($aData['\'user\'']))
+ $sql->query('SELECT `id` FROM `users` WHERE `id`="'.$aData['\'user\''].'" LIMIT 1');
+ else{
+ if(sys::valid($aData['\'user\''], 'other', $aValid['login']))
+ sys::outjs(array('e' => sys::text('input', 'login_valid')), $nmch);
+
+ $sql->query('SELECT `id` FROM `users` WHERE `login`="'.$aData['\'user\''].'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Пользователь не найден в базе.'), $nmch);
+
+ $uowner = $sql->get();
+
+ $owner = $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+
+ // Если не обновление доступа совладельца, проверить кол-во
+ if(!$sql->num($owner))
+ {
+ $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" LIMIT 5');
+
+ if($sql->num() == 5)
+ sys::outjs(array('e' => 'Вы добавили максимально число совладельцев.'), $nmch);
+ }
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Владельца сервера нельзя добавить в совладельцы.'), $nmch);
+
+ $aRights = array();
+
+ $check = 0;
+
+ foreach($aAccess as $access)
+ {
+ $aRights[$access] = isset($aData['\''.$access.'\'']) ? 1 : 0;
+
+ $check += $aRights[$access];
+ }
+
+ if(!$check)
+ sys::outjs(array('e' => 'Необходимо включить минимум одно разрешение.'), $nmch);
+
+
+
+ if($sql->num($owner))
+ $sql->query('UPDATE `owners` set `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'" WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ else
+ $sql->query('INSERT INTO `owners` set `server`="'.$id.'", `user`="'.$uowner['id'].'", `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $html->nav('Друзья');
+
+ $cache = $mcache->get('server_owners_'.$id);
+
+ if($cache != '')
+ $html->arr['main'] = $cache;
+ else{
+ $owners = $sql->query('SELECT `id`, `user`, `rights`, `time` FROM `owners` WHERE `server`="'.$id.'" AND `time`>"'.$start_point.'" ORDER BY `id` ASC LIMIT 5');
+
+ if($sql->num())
+ include(LIB.'games/games.php');
+
+ while($owner = $sql->get($owners))
+ {
+ $sql->query('SELECT `login` FROM `users` WHERE `id`="'.$owner['user'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $uowner = $sql->get();
+
+ $rights = games::owners(sys::b64djs($owner['rights']));
+
+ $html->get('owners_list', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('oid', $owner['id']);
+ $html->set('user', $uowner['login']);
+ $html->set('rights', $rights);
+ $html->set('time', date('d.m.Y - H:i', $owner['time']));
+
+ $html->pack('owners');
+ }
+
+ $html->get('owners', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('time', date('d.m.Y', $start_point));
+
+ $html->set('owners', isset($html->arr['owners']) ? $html->arr['owners'] : 'Для данного сервера совладельцы отсутсвуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_owners_'.$id, $html->arr['main'], false, 1);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/plugins.php b/system/sections/servers/css/plugins.php
new file mode 100644
index 0000000..a116ca1
--- /dev/null
+++ b/system/sections/servers/css/plugins.php
@@ -0,0 +1,158 @@
+query('SELECT `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $nmch = sys::rep_act('server_plugins_go_'.$id, 10);
+
+ include(SEC.'servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('server_plugins_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_plugins_'.$id);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$id.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `plugins_install` WHERE `server`="'.$id.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_plugins_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/rcon.php b/system/sections/servers/css/rcon.php
new file mode 100644
index 0000000..1a0de70
--- /dev/null
+++ b/system/sections/servers/css/rcon.php
@@ -0,0 +1,54 @@
+ 'Необходимо выбрать игрока.'));
+
+ if($url['action'] == 'kick')
+ rcon::cmd(array_merge($server, array('id' => $id)), 'kickid "'.$player.'" "EGP Panel"');
+ else
+ rcon::cmd(array_merge($server, array('id' => $id)), 'sm_slay "'.$player.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ include(LIB.'geo.php');
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $aPlayers = rcon::players(rcon::cmd(array_merge($server, array('id' => $id))));
+
+ foreach($aPlayers as $i => $aPlayer)
+ {
+ $html->get('player', 'sections/servers/'.$server['game'].'/rcon');
+
+ $html->set('i', $i);
+ $html->set('userid', $aPlayer['userid']);
+ $html->set('name', $aPlayer['name']);
+ $html->set('steamid', $aPlayer['steamid']);
+ $html->set('time', $aPlayer['time']);
+ $html->set('ping', $aPlayer['ping']);
+ $html->set('ip', $aPlayer['ip']);
+ $html->set('ico', $aPlayer['ico']);
+ $html->set('country', $aPlayer['country']);
+
+ $html->pack('players');
+ }
+
+ sys::outjs(array('s' => isset($html->arr['players']) ? $html->arr['players'] : ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Rcon управление игроками');
+
+ $html->get('rcon', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/settings.php b/system/sections/servers/css/settings.php
new file mode 100644
index 0000000..b775147
--- /dev/null
+++ b/system/sections/servers/css/settings.php
@@ -0,0 +1,77 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $aSub = array('start', 'server', 'admins', 'bans', 'firewall', 'crontab', 'startlogs', 'debug', 'logs', 'smlogs', 'pack', 'file', 'antiddos', 'api');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aEditslist = 1;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $antiddos = 'Индивидуальная защита отключена '
+ .'Индивидуальная защита (Заблокировать всех кроме: RU, UA) '
+ .'Индивидуальная защита (Заблокировать всех кроме: AM, BY, UA, RU, KZ) ';
+
+ include(SEC.'servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('antiddos', str_replace($server['ddos'], $server['ddos'].'" selected="select', $antiddos));
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+
+ $sql->query('SELECT `key` FROM `api` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ {
+ $api = $sql->get();
+
+ $html->set('api', $api['key']);
+ $html->unit('api', 1, 1);
+ }else
+ $html->unit('api', 0, 1);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/settings/admins.php b/system/sections/servers/css/settings/admins.php
new file mode 100644
index 0000000..f8ad37e
--- /dev/null
+++ b/system/sections/servers/css/settings/admins.php
@@ -0,0 +1,114 @@
+nav('Управление администраторами');
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $aData = array();
+
+ $aData['active'] = isset($_POST['active']) ? $_POST['active'] : '';
+ $aData['value'] = isset($_POST['value']) ? $_POST['value'] : '';
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : '';
+ $aData['flags'] = isset($_POST['flags']) ? $_POST['flags'] : '';
+ $aData['immunity'] = isset($_POST['immunity']) ? sys::int($_POST['immunity']) : '';
+ $aData['time'] = isset($_POST['time']) ? $_POST['time'] : '';
+ $aData['info'] = isset($_POST['info']) ? $_POST['info'] : '';
+
+ // Удаление текущих записей
+ $sql->query('DELETE FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'"');
+
+ $usini = '';
+
+ foreach($aData['value'] as $index => $val)
+ {
+ if($val != '')
+ {
+ $aDate = isset($aData['time'][$index]) ? explode('.', $aData['time'][$index]) : explode('.', date('d.m.Y', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ $aDate = explode('.', date('d.m.Y', $start_point));
+
+ $time = mktime(0, 0, 0, $aDate[1], $aDate[0], $aDate[2]);
+
+ $aData['active'][$index] = isset($aData['active'][$index]) ? 1 : 0;
+ $aData['passwd'][$index] = isset($aData['passwd'][$index]) ? $aData['passwd'][$index] : '';
+ $aData['flags'][$index] = isset($aData['flags'][$index]) ? $aData['flags'][$index] : '';
+ $aData['info'][$index] = isset($aData['info'][$index]) ? $aData['info'][$index] : '';
+
+ $text = '"'.$val.'" "'.$aData['immunity'][$index].':'.$aData['flags'][$index].'" "'.$aData['passwd'][$index].'"';
+
+ $sql->query('INSERT INTO `admins_'.$server['game'].'` set'
+ .'`server`="'.$id.'",'
+ .'`value`="'.htmlspecialchars($val).'",'
+ .'`active`="'.$aData['active'][$index].'",'
+ .'`passwd`="'.htmlspecialchars($aData['passwd'][$index]).'",'
+ .'`flags`="'.htmlspecialchars($aData['flags'][$index]).'",'
+ .'`immunity`="'.$aData['immunity'][$index].'",'
+ .'`time`="'.$time.'",'
+ .'`text`="'.htmlspecialchars($text).'",'
+ .'`info`="'.htmlspecialchars($aData['info'][$index]).'"');
+
+ if($aData['active'][$index])
+ $usini .= $text.PHP_EOL;
+ }
+ }
+
+ $temp = sys::temp($usini);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/addons/sourcemod/configs/admins_simple.ini', 0644);
+
+ unlink($temp);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/addons/sourcemod/configs/admins_simple.ini');
+
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \" sm_reloadadmins\"\015'");
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Построение списка добавленных админов
+ $sql->query('SELECT `id`, `value`, `active`, `passwd`, `flags`, `immunity`, `time`, `info` FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'" ORDER BY `id` ASC');
+ while($admin = $sql->get())
+ {
+ $html->get('list', 'sections/servers/'.$server['game'].'/settings/admins');
+
+ if($admin['active'])
+ $html->unit('active', 1);
+ else
+ $html->unit('active');
+
+ $html->set('id', $admin['id']);
+ $html->set('value', $admin['value']);
+ $html->set('passwd', $admin['passwd']);
+ $html->set('flags', $admin['flags']);
+ $html->set('immunity', $admin['immunity']);
+ $html->set('time', date('d.m.y', $admin['time']));
+ $html->set('info', $admin['info']);
+
+ $html->pack('admins');
+ }
+
+ $sql->query('SELECT `id` FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'" ORDER BY `id` DESC LIMIT 1');
+ $max = $sql->get();
+
+ $html->get('admins', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('admins', isset($html->arr['admins']) ? $html->arr['admins'] : '');
+ $html->set('index', $max['id'] < 1 ? 0 : $max['id']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/settings/antiddos.php b/system/sections/servers/css/settings/antiddos.php
new file mode 100644
index 0000000..6451e5d
--- /dev/null
+++ b/system/sections/servers/css/settings/antiddos.php
@@ -0,0 +1,73 @@
+query('SELECT `id` FROM `units` WHERE `id`="'.$server['unit'].'" AND `ddos`="1" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'В данный момент нельзя изменить параметр, т.к. включена защита на всю локацию.'), $name_mcache);
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($url['type']) || !in_array($url['type'], array('0', '1', '2')))
+ sys::outjs(array('e' => 'Неправильно передан параметр.'), $name_mcache);
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh', $user['group'])), $name_mcache);
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $geo = $cfg['iptables'].'_geo';
+
+ if($url['type'] == 2)
+ {
+ if($server['ddos'] == 2)
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ $cmd = '';
+
+ if($server['ddos'])
+ $cmd = 'iptables -D INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country UA,RU -j DROP;'
+ .'sed "`nl '.$geo.' | grep \"#'.$id.'\" | awk \'{print $1","$1+1}\'`d" '.$geo.' > '.$geo.'_temp; cat '.$geo.'_temp > '.$geo.'; rm '.$geo.'_temp;';
+
+ $rule = 'iptables -I INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country AM,BY,UA,RU,KZ -j DROP;';
+
+ $ssh->set($cmd.$rule.' echo -e "#'.$id.';\n'.$rule.'" >> '.$geo);
+
+ $sql->query('UPDATE `servers` set `ddos`="2" WHERE `id`="'.$id.'" LIMIT 1');
+ }elseif($url['type'] == 1){
+ if($server['ddos'] == 1)
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ $cmd = '';
+
+ if($server['ddos'])
+ $cmd = 'iptables -D INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country AM,BY,UA,RU,KZ -j DROP;'
+ .'sed "`nl '.$geo.' | grep \"#'.$id.'\" | awk \'{print $1","$1+1}\'`d" '.$geo.' > '.$geo.'_temp; cat '.$geo.'_temp > '.$geo.'; rm '.$geo.'_temp;';
+
+ $rule = 'iptables -I INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country UA,RU -j DROP;';
+
+ $ssh->set($cmd.$rule.' echo -e "#'.$id.';\n'.$rule.'" >> '.$geo);
+
+ $sql->query('UPDATE `servers` set `ddos`="1" WHERE `id`="'.$id.'" LIMIT 1');
+ }else{
+ if(!$server['ddos'])
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ $country = $server['ddos'] == 2 ? 'AM,BY,UA,RU,KZ' : 'UA,RU';
+
+ $ssh->set('iptables -D INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country '.$country.' -j DROP;'
+ .'sed "`nl '.$geo.' | grep \"#'.$id.'\" | awk \'{print $1","$1+1}\'`d" '.$geo.' > '.$geo.'_temp; cat '.$geo.'_temp > '.$geo.'; rm '.$geo.'_temp;');
+
+ $sql->query('UPDATE `servers` set `ddos`="0" WHERE `id`="'.$id.'" LIMIT 1');
+ }
+
+ $mcache->delete('server_settings_'.$id);
+
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/settings/bans.php b/system/sections/servers/css/settings/bans.php
new file mode 100644
index 0000000..eeef061
--- /dev/null
+++ b/system/sections/servers/css/settings/bans.php
@@ -0,0 +1,160 @@
+nav('Бан листы');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к файлам (banned_user.cfg / banned_ip.cfg)
+ $folder = $tarif['install'].$server['uid'].'/cstrike';
+
+ // Если бан/разбан/проверка
+ if($go)
+ {
+ $aData = array();
+
+ $aData['value'] = isset($_POST['value']) ? trim($_POST['value']) : sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+ $aData['userid'] = isset($_POST['userid']) ? sys::int($_POST['userid']) : false;
+ $aData['amxbans'] = isset($_POST['amxbans']) ? true : false;
+
+ // Проверка входных данных
+ if(sys::valid($aData['value'], 'steamid') AND sys::valid($aData['value'], 'steamid3') AND sys::valid($aData['value'], 'ip'))
+ sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+
+ // Если указан steamid
+ if(sys::valid($aData['value'], 'ip'))
+ {
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'] AND $aData['userid'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['userid']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"banid 0.0 ".$aData['value']." kick\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"banid 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_user.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_user.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_user.cfg | grep -v '.$aData['value'].' > temp_banned.cfg; echo "" >> temp_banned.cfg && cat temp_banned.cfg > banned_user.cfg; rm temp_banned.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeid ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeid\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный SteamID найден в файле banned_user.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный SteamID не найден в файле banned_user.cfg'), $nmch);
+ }
+ }else{
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['value']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"addip 0.0 ".$aData['value']." EGP\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"addip 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_ip.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_ip.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_ip.cfg | grep -v '.$aData['value'].' > temp_listip.cfg; echo "" >> temp_listip.cfg && cat temp_listip.cfg > banned_ip.cfg; rm temp_listip.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeip ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeip\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный IP найден в файле banned_ip.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный IP не найден в файле banned_ip.cfg'), $nmch);
+ }
+ }
+ }
+
+ // Содержимое banned_user.cfg
+ $ssh->set('cd '.$folder.' && cat banned_user.cfg | awk \'{print $3}\' | egrep "^\[U:[01]:[0-9]{3,12}\]$"');
+ $aBanned = explode("\n", $ssh->get());
+
+ // Содержимое banned_ip.cfg
+ $ssh->set('cd '.$folder.' && cat banned_ip.cfg | awk \'{print $3}\' | egrep "(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}"');
+ $aListip = explode("\n", $ssh->get());
+
+ if(isset($aBanned[count($aBanned)-1]) AND $aBanned[count($aBanned)-1] == '')
+ unset($aBanned[count($aBanned)-1]);
+
+ if(isset($aListip[count($aListip)-1]) AND $aListip[count($aListip)-1] == '')
+ unset($aListip[count($aListip)-1]);
+
+ // Построение списка забаненых по steamid
+ foreach($aBanned as $line => $steam)
+ {
+ $html->get('bans_list', 'sections/servers/games/settings');
+
+ $html->set('value', trim($steam));
+
+ $html->pack('banned');
+ }
+
+ // Построение списка забаненых по ip
+ foreach($aListip as $line => $ip)
+ {
+ $html->get('bans_list', 'sections/servers/games/settings');
+
+ $html->set('value', trim($ip));
+
+ $html->pack('listip');
+ }
+
+ $html->get('bans', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('banned', isset($html->arr['banned']) ? $html->arr['banned'] : '');
+ $html->set('listip', isset($html->arr['listip']) ? $html->arr['listip'] : '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/settings/debug.php b/system/sections/servers/css/settings/debug.php
new file mode 100644
index 0000000..180f9d0
--- /dev/null
+++ b/system/sections/servers/css/settings/debug.php
@@ -0,0 +1,28 @@
+nav('Отладочный лог');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Чтение файла - oldstart.log
+ $file = $tarif['install'].$server['uid'].'/debug.log';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep "CRASH: " | grep -ve "^#\|^[[:space:]]*$"');
+
+ $html->get('debug', 'sections/servers/games/settings');
+
+ $html->set('log', htmlspecialchars($ssh->get()));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/settings/logs.php b/system/sections/servers/css/settings/logs.php
new file mode 100644
index 0000000..1aa0914
--- /dev/null
+++ b/system/sections/servers/css/settings/logs.php
@@ -0,0 +1,89 @@
+nav('Логи');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к логам
+ $folder = $tarif['install'].$server['uid'].'/cstrike/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['cslogs']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/logs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'logs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/logs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"\/"$1"\/"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('\/', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', end($name));
+ $html->set('uri', 'logs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('uri', '');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/settings/server.php b/system/sections/servers/css/settings/server.php
new file mode 100644
index 0000000..b6a4696
--- /dev/null
+++ b/system/sections/servers/css/settings/server.php
@@ -0,0 +1,117 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ $config_end = $servercfg['\'other\''];
+
+ unset($servercfg['\'other\'']);
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).' "'.$val.'"'."\n";
+
+ // Временый файл
+ $temp = sys::temp($config.$config_end);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg');
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Чтение файла - server.cfg
+ $file = $tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+ $other = '';
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode(' ', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.' ', "", $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ else
+ $other .= $line."\n";
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $html->get('servercfg', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('cfg', $html->arr['list']);
+ $html->set('other', $other);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/settings/smlogs.php b/system/sections/servers/css/settings/smlogs.php
new file mode 100644
index 0000000..b39aa81
--- /dev/null
+++ b/system/sections/servers/css/settings/smlogs.php
@@ -0,0 +1,89 @@
+nav('Логи SourceMod');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к логам
+ $folder = $tarif['install'].$server['uid'].'/cstrike/addons/sourcemod/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['csssmlogs']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/smlogs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'smlogs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/smlogs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"\/"$1"\/"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('\/', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', end($name));
+ $html->set('uri', 'smlogs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('uri', 'sm');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/settings/start.php b/system/sections/servers/css/settings/start.php
new file mode 100644
index 0000000..dc83025
--- /dev/null
+++ b/system/sections/servers/css/settings/start.php
@@ -0,0 +1,149 @@
+query('SELECT `uid`, `slots`, `slots_start`, `map_start`, `vac`, `fastdl`, `autorestart`, `fps`, `tickrate` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install`, `tickrate`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'games/games.php');
+ include(LIB.'games/tarifs.php');
+ include(LIB.'games/'.$server['game'].'/tarif.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/cstrike/maps', $server['map_start'], false);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/cstrike/maps', $map, true, $nmch);
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `servers` set `vac`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `servers` set `autorestart`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'tickrate':
+ if(!tarif::price($tarif['price']) AND in_array($value, explode(':', $tarif['tickrate'])))
+ $sql->query('UPDATE `servers` set `tickrate`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.sys::first(explode(':', $unit['address'])).':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/cfg/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/cfg/fastdl.cfg;'
+ .'ln -s '.$tarif['install'].$server['uid'].'/cstrike /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'echo "exec fastdl.cfg" >> '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'rm '.$tarif['install'].$server['uid'].'/cstrike/cfg/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `servers` set `fastdl`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $tickrate = ''.$server['tickrate'].' TickRate ';
+
+ if(!tarif::price($tarif['price']))
+ {
+ $aTick = explode(':', $tarif['tickrate']);
+
+ unset($aTick[array_search($server['tickrate'], $aTick)]);
+
+ if(count($aTick))
+ foreach($aTick as $value)
+ $tickrate .= ''.$value.' TickRate ';
+ }
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('map', $server['map_start']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ if(!tarif::price($tarif['price']))
+ {
+ $html->unit('tickrate', true);
+ $html->set('tickrate', $tickrate);
+ }else
+ $html->unit('tickrate');
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/tarif.php b/system/sections/servers/css/tarif.php
new file mode 100644
index 0000000..30cc896
--- /dev/null
+++ b/system/sections/servers/css/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `fps`, `tickrate`, `ram`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'plan', 'address', 'addextend', 'unit', 'slots');
+
+ include(SEC.'servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/tarif/extend.php b/system/sections/servers/css/tarif/extend.php
new file mode 100644
index 0000000..9d060bc
--- /dev/null
+++ b/system/sections/servers/css/tarif/extend.php
@@ -0,0 +1,51 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['tickrate'] = $server['tickrate'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ $aPrice = explode(':', $tarif['price']);
+
+ // Цена за 30 дней 1 слота
+ $price = $aPrice[array_search($server['tickrate'], explode(':', $tarif['tickrate']))];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/tarif/plan.php b/system/sections/servers/css/tarif/plan.php
new file mode 100644
index 0000000..3eed81c
--- /dev/null
+++ b/system/sections/servers/css/tarif/plan.php
@@ -0,0 +1,66 @@
+ 'Переданые не все данные'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ // Проверка плана
+ if(array_search($plan, $aTICK) === FALSE)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ if($plan == $server['tickrate'])
+ sys::outjs(array('e' => 'Смысла в этой операции нет'), $nmch);
+
+ if(!tarif::price($tarif['price']))
+ sys::outjs(array('e' => 'Чтобы изменить тариф, перейдите в настройки запуска'), $nmch);
+
+ if($server['time'] < $start_point+86400)
+ $time = $server['time'];
+ else{
+ // Цена за 1 день аренды (по новому тарифному плану)
+ $price = $aPrice[array_search($plan, $aTICK)]/30*$server['slots'];
+
+ // Цена за 1 день аренды (по старому тарифному плану)
+ $price_old = $aPrice[array_search($server['tickrate'], $aTICK)]/30*$server['slots'];
+
+ // Остаток дней аренды
+ $days = ($server['time']-$start_point)/86400;
+
+ $time = date('H:i:s', $server['time']);
+ $date = date('d.m.Y', round($start_point+$days*$price_old/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+ $aTime = explode(':', $time);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2]);
+ }
+
+ // Выполнение смена тарифного плана
+ if($go)
+ {
+ sys::benefitblock($id, $nmch);
+
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `tickrate`="'.$plan.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_plan').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/tarif/slots.php b/system/sections/servers/css/tarif/slots.php
new file mode 100644
index 0000000..e457286
--- /dev/null
+++ b/system/sections/servers/css/tarif/slots.php
@@ -0,0 +1,34 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $aPrice[array_search($server['tickrate'], $aTICK)]/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($aPrice[array_search($server['tickrate'], $aTICK)]/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/tarif/unit.php b/system/sections/servers/css/tarif/unit.php
new file mode 100644
index 0000000..b840bc7
--- /dev/null
+++ b/system/sections/servers/css/tarif/unit.php
@@ -0,0 +1,50 @@
+ 'Переданы не все данные.'), $nmch);
+
+ if(!$cfg['change_unit'][$server['game']] || $server['time'] < $start_point+86400 || $server['test'])
+ exit;
+
+ $sql->query('SELECT `id`, `unit`, `packs`, `tickrate`, `price` FROM `tarifs` WHERE `unit`="'.$uid.'" AND `game`="'.$server['game'].'" AND `name`="'.$tarif['name'].'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Не найден подходящий тариф.'), $nmch);
+
+ $oldTarif = $tarif;
+
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $oldUnit = $sql->get();
+
+ $aPriceold = explode(':', $oldTarif['price']);
+ $aTICKold = explode(':', $oldTarif['tickrate']);
+
+ $sql->query('SELECT `id` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная локация не доступна.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ if(!in_array($server['tickrate'], $aTICK))
+ sys::outjs(array('e' => 'Не найден подходящий тарифный план.'), $nmch);
+
+ // Цена за 1 день (при новом тарифном плане)
+ $price = $aPrice[array_search($server['tickrate'], $aTICK)]/30*$server['slots'];
+
+ // Цена аренды за остаток дней (с текущим тарифным планом)
+ $oldprice = ($server['time']-$start_point)/86400*($aPriceold[array_search($server['tickrate'], $aTICKold)]/30*$server['slots']);
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$oldprice/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ include(SEC.'servers/games/tarif/unit.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/css/web.php b/system/sections/servers/css/web.php
new file mode 100644
index 0000000..634e9b6
--- /dev/null
+++ b/system/sections/servers/css/web.php
@@ -0,0 +1,87 @@
+nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ include(DATA.'web.php');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub) AND in_array($url['action'], array_merge($aAction[$url['subsection']], array('install', 'manage'))))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_web_go_'.$id, 10);
+ else
+ $html->nav('Web', $cfg['http'].'servers/id/'.$id.'/section/web');
+
+ include(SEC.'web/'.$url['subsection'].'/free/'.$url['action'].'.php');
+ }else{
+ $html->nav('Web');
+
+ if($mcache->get('server_web_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_web_'.$id);
+ else{
+ // Услуги
+ foreach($aWeb[$server['game']] as $service => $active)
+ {
+ if($active)
+ {
+ if($service == 'hosting')
+ {
+ if(!$aWebVHTtype && !in_array($server['tarif'], $aWebVHT))
+ continue;
+
+ if($aWebVHTtype && in_array($server['tarif'], $aWebVHT))
+ continue;
+ }
+
+ // Проверка на установку
+ switch($aWebInstall[$server['game']][$service])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ }
+
+ if($sql->num())
+ $html->get('list_install', 'sections/servers/games/web');
+ else
+ $html->get('list', 'sections/servers/games/web');
+
+ $html->set('id', $id);
+ $html->set('service', $service);
+ $html->set('name', $aWebname[$service]);
+ $html->set('desc', $aWebDesc[$service]);
+
+ $html->pack($aWebType[$service]);
+ }
+ }
+
+ // Блоки услуг
+ foreach($aWebTypeInfo[$server['game']] as $type => $name)
+ {
+ if(!isset($html->arr[$type]))
+ continue;
+
+ $html->get('block', 'sections/servers/games/web');
+ $html->set('name', $name);
+ $html->set('list', $html->arr[$type]);
+ $html->pack('web');
+ }
+
+ $html->get('web', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('web', isset($html->arr['web']) ? $html->arr['web'] : 'Дополнительные услуги отсутствуют');
+ $html->pack('main');
+
+ $mcache->set('server_web_'.$id, $html->arr['main'], false, 4);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/console.php b/system/sections/servers/cssold/console.php
new file mode 100644
index 0000000..f31d841
--- /dev/null
+++ b/system/sections/servers/cssold/console.php
@@ -0,0 +1,67 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $command = isset($_POST['command']) ? sys::cmd($_POST['command']) : '';
+
+ if($server['status'] == 'off')
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('servers', 'off')));
+
+ sys::out(sys::text('servers', 'off'));
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ sys::out(sys::text('error', 'ssh'));
+ }
+
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ $filecmd = $dir.'console.log';
+
+ if($command)
+ {
+ if(strtolower($command) == 'clear')
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"Очистка консоли\n\" > '.$filecmd.'"');
+ else
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.$command.'"\015\';'
+ .'sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/copy.php b/system/sections/servers/cssold/copy.php
new file mode 100644
index 0000000..7a9c216
--- /dev/null
+++ b/system/sections/servers/cssold/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('server_copy_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_copy_'.$id);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `copy` WHERE `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_copy_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/graph.php b/system/sections/servers/cssold/graph.php
new file mode 100644
index 0000000..d8d8df8
--- /dev/null
+++ b/system/sections/servers/cssold/graph.php
@@ -0,0 +1,71 @@
+query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+ $graph = $sql->get();
+
+ $nmch = 'server_graph_full_'.$id;
+
+ $time = isset($url['time']) ? $url['time'] : 'day';
+
+ if(!in_array($time, array('day', 'week', 'month')))
+ $time = 'day';
+
+ // Выхлоп кеш графика
+ if($mcache->get($nmch) AND file_exists(TEMP.(md5($graph['key'].'full_'.$time)).'.png'))
+ {
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ include(LIB.'games/graph/pData.php');
+ include(LIB.'games/graph/pDraw.php');
+ include(LIB.'games/graph/pImage.php');
+
+ include(LIB.'games/graph.php');
+
+ graph::full($id, $server['slots_start'], $graph['key'], $time);
+
+ $mcache->set($nmch, true, false, 300);
+
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Графики');
+
+ if($mcache->get('server_graph_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_graph_'.$id);
+ else{
+ $sql->query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+
+ // Если отсутствует ключ, создать
+ if(!$sql->num())
+ {
+ // Генерация ключа
+ $key = md5($id.sys::key('graph'));
+
+ $sql->query('INSERT INTO `graph` set `server`="'.$id.'", `key`="'.$key.'", `time`="0"');
+ }else{
+ $graph = $sql->get();
+
+ $key = $graph['key'];
+ }
+
+ $html->get('graph', 'sections/servers/games');
+
+ $html->set('id', $id);
+
+ $html->set('key', $key);
+ $html->set('address', $server['address']);
+ $html->set('_img', '[img]');
+
+ $html->pack('main');
+
+ $mcache->set('server_graph_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/index.php b/system/sections/servers/cssold/index.php
new file mode 100644
index 0000000..89f90ad
--- /dev/null
+++ b/system/sections/servers/cssold/index.php
@@ -0,0 +1,44 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `fps`, `tickrate`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address']);
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name'].' / '.$server['fps'].' FPS / '.$server['tickrate'].' TickRate');
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img'));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/maps.php b/system/sections/servers/cssold/maps.php
new file mode 100644
index 0000000..6e04160
--- /dev/null
+++ b/system/sections/servers/cssold/maps.php
@@ -0,0 +1,86 @@
+query('SELECT `unit`, `tarif` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'list', 'listing', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Карты', $cfg['http'].'servers/id/'.$id.'/section/maps');
+
+ $nmch = sys::rep_act('server_maps_go_'.$id, 10);
+
+ include(SEC.'servers/'.$server['game'].'/maps/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Карты');
+
+ // Построение списка установленных карт
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id);
+ }
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $ssh->set('cd '.$tarif['install'].$server['uid'].'/cstrike/maps/ && ls | grep -e "\.bsp$"');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace('.bsp', '', $maps));
+
+ // Сортировка карт
+ sort($aMaps);
+ reset($aMaps);
+
+ $mapsjs = '';
+ $i = 0;
+
+ foreach($aMaps as $index => $map)
+ {
+ if(!isset($map{3}))
+ continue;
+
+ $mapjs = str_replace('$', '-_-', $map);
+
+ $i+=1;
+ $mapsjs .= $i.' : "'.$mapjs.'",';
+
+ $html->get('map_server', 'sections/servers/games/maps');
+ $html->set('img', sys::img($map, $server['game']));
+ $html->set('map', $mapjs);
+ $html->set('name', $map);
+ $html->pack('maps');
+ }
+
+ // Если есть кеш
+ if($mcache->get('server_maps_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_maps_'.$id);
+ else{
+ $html->get('maps', 'sections/servers/games');
+ $html->set('id', $id);
+ $html->set('types', isset($html->arr['types']) ? $html->arr['types'] : '');
+ $html->set('maps', isset($html->arr['maps']) ? $html->arr['maps'] : '');
+ $html->set('mapsjs', $mapsjs);
+ $html->pack('main');
+
+ $mcache->set('server_maps_'.$id, $html->arr['main'], false, 3);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/maps/delete.php b/system/sections/servers/cssold/maps/delete.php
new file mode 100644
index 0000000..7b0173a
--- /dev/null
+++ b/system/sections/servers/cssold/maps/delete.php
@@ -0,0 +1,70 @@
+query('SELECT `unit`, `tarif`, `map_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ // Генерация списка карт
+ $ssh->set('cd '.$dir.'maps/ && ls | grep -iE "\.bsp$"');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace('.bsp', '', $maps));
+
+ // Массив переданных карт
+ $in_aMaps = isset($_POST['maps']) ? $_POST['maps'] : array();
+
+ // Обработка выборки
+ foreach($in_aMaps as $name => $sel)
+ if($sel)
+ {
+ $map = str_replace(array("\\", "'", "'", '-_-'), array('', '', '', '$'), $name);
+
+ // Проверка наличия карты
+ if(!in_array($map, $aMaps))
+ continue;
+
+ // Проверка: является ли карта стартовой
+ if($server['map_start'] == $map)
+ continue;
+
+ $ssh->set('cd /path/maps/'.$server['game'].'/'.sys::map($map).' && du -a | grep -iE "\.[a-z]{1,3}$" | awk \'{print $2}\'');
+
+ $aFiles = explode("\n", str_replace('./', '', $ssh->get()));
+
+ if(isset($aFiles[count($aFiles)-1]) AND $aFiles[count($aFiles)-1] == '')
+ unset($aFiles[count($aFiles)-1]);
+
+ $files = '';
+
+ foreach($aFiles as $file)
+ $files .= $dir.$file.' ';
+
+ $rm = '';
+ $aFlrm = explode(' ', $dir.'maps/'.$map.'.* '.trim($files));
+
+ foreach($aFlrm as $flrm)
+ $rm .= sys::map($flrm).' ';
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -dmS md'.$start_point.$id.' sh -c \'rm '.trim($rm).'\'');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
diff --git a/system/sections/servers/cssold/maps/install.php b/system/sections/servers/cssold/maps/install.php
new file mode 100644
index 0000000..b54e0fd
--- /dev/null
+++ b/system/sections/servers/cssold/maps/install.php
@@ -0,0 +1,56 @@
+query('SELECT `unit`, `tarif`, `hdd` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ // Проверить наличие свободного места
+ $ssh->set('cd '.$dir.' && du -ms');
+ $hdd = ceil(sys::int($ssh->get())/($server['hdd']/100));
+ $hdd = $hdd > 100 ? 100 : $hdd;
+
+ if($hdd == 100)
+ sys::outjs(array('e' => 'Невозможно выполнить установку, нет свободного места'), $nmch);
+
+ // Массив переданных карт
+ $in_aMaps = isset($_POST['maps']) ? $_POST['maps'] : array();
+
+ // Обработка выборки
+ foreach($in_aMaps as $mid => $sel)
+ if($sel)
+ {
+ $map = sys::int($mid);
+
+ // Проверка наличия карты
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `id`="'.$map.'" AND `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $map = $sql->get();
+
+ $cp = 'cp /path/maps/'.$server['game'].'/'.sys::map($map['name']).'.* '.$dir.'maps/;'
+ .'cd /path/maps/'.$server['game'].'/'.sys::map($map['name']).'/ && cp -r * '.$dir;
+
+ $ssh->set('sudo -u server'.$server['uid'].' screen -dmS mc'.$start_point.$id.' sh -c \''.$cp.'\'');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
diff --git a/system/sections/servers/cssold/maps/list.php b/system/sections/servers/cssold/maps/list.php
new file mode 100644
index 0000000..abb251a
--- /dev/null
+++ b/system/sections/servers/cssold/maps/list.php
@@ -0,0 +1,62 @@
+nav('Установка карт');
+
+ // Категории для быстрой сортировки
+ $html->get('types', 'sections/servers/'.$server['game'].'/maps');
+
+ $html->set('id', $id);
+
+ $html->pack('types');
+
+ $type = false;
+
+ if(isset($url['type']) AND in_array($url['type'], array('de', 'cs', 'aim', 'awp', 'bhop', 'csde', 'deathrun', 'jail')))
+ $type = '^'.$url['type'].'\_';
+
+ if($type)
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` REGEXP FROM_BASE64(\''.base64_encode($type).'\') ORDER BY `name` ASC LIMIT 72');
+ else{
+ $sql->query('SELECT `id` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'"');
+
+ // Массив для построения страниц
+ $aPage = sys::page($page, $sql->num(), 30);
+
+ // Генерация массива ($html->arr['pages']) страниц
+ sys::page_gen($aPage['ceil'], $page, $aPage['page'], 'servers/id/'.$id.'/section/maps/subsection/list');
+
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `name` ASC LIMIT '.$aPage['num'].', 30');
+ }
+
+ $mapsjs = '';
+ $i = 0;
+
+ while($map = $sql->get())
+ {
+ $i+=1;
+
+ $mapsjs .= $i.' : "'.$map['id'].'",';
+
+ $html->get('map_install', 'sections/servers/games/maps');
+
+ $html->set('id', $map['id']);
+ $html->set('img', sys::img($map['name'], $server['game']));
+ $html->set('name', $map['name']);
+
+ $html->pack('maps');
+ }
+
+ $html->get('install', 'sections/servers/games/maps');
+
+ $html->set('id', $id);
+
+ $html->set('types', isset($html->arr['types']) ? $html->arr['types'] : '');
+ $html->set('maps', isset($html->arr['maps']) ? $html->arr['maps'] : 'К сожалению карты не найдены в базе');
+ $html->set('amaps', $mapsjs);
+ $html->set('pages', isset($html->arr['pages']) ? $html->arr['pages'] : '');
+ $html->set('cdn', $cfg['cdn']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/maps/listing.php b/system/sections/servers/cssold/maps/listing.php
new file mode 100644
index 0000000..f47dcc0
--- /dev/null
+++ b/system/sections/servers/cssold/maps/listing.php
@@ -0,0 +1,94 @@
+nav('Списки карт');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/maps');
+ }
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Директория сервера
+ $dir = $tarif['install'].$server['uid'].'/cstrike/';
+
+ // Генерация списка
+ if($go AND isset($url['gen']))
+ {
+ $ssh->set('cd '.$dir.'maps/ && ls | grep -e "\.bsp$"');
+
+ $maps = $ssh->get();
+
+ $aMaps = explode("\n", str_ireplace(array('./', '.bsp'), '', $maps));
+
+ sort($aMaps);
+ reset($aMaps);
+
+ $list = '';
+
+ foreach($aMaps as $index => $map)
+ {
+ $aMap = explode('/', $map);
+ $name = end($aMap);
+ if(strlen($name) < 4)
+ continue;
+
+ $list .= $map."\n";
+ }
+
+ sys::outjs(array('s' => $list), $nmch);
+ }
+
+ $aFiles = array(
+ 'mapcycle' => 'mapcycle.txt',
+ 'maps' => 'maplist.txt'
+ );
+
+ // Сохранение
+ if($go AND isset($url['file']))
+ {
+ if(!array_key_exists($url['file'], $aFiles))
+ exit;
+
+ $data = isset($_POST['data']) ? $_POST['data'] : '';
+
+ $temp = sys::temp($data);
+
+ // Отправление файла на сервер
+ $ssh->setfile($temp, $dir.$aFiles[$url['file']], 0644);
+
+ // Смена владельца/группы файла
+ $ssh->set('chown server'.$server['uid'].':servers '.$dir.$aFiles[$url['file']]);
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$dir.$aFiles['mapcycle'].'; cat '.$dir.$aFiles['mapcycle'].'"');
+ $mapcycle = $ssh->get();
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$dir.$aFiles['maps'].'; cat '.$dir.$aFiles['maps'].'"');
+ $maps = $ssh->get();
+
+ $html->get('listing', 'sections/servers/'.$server['game'].'/maps');
+
+ $html->set('id', $id);
+
+ $html->set('mapcycle', $mapcycle);
+ $html->set('maps', $maps);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/maps/search.php b/system/sections/servers/cssold/maps/search.php
new file mode 100644
index 0000000..866ca47
--- /dev/null
+++ b/system/sections/servers/cssold/maps/search.php
@@ -0,0 +1,64 @@
+get($mkey);
+
+ if(is_array($cache))
+ {
+ if($go)
+ sys::outjs($cache, $nmch);
+
+ sys::outjs($cache);
+ }
+
+ if(!isset($text{2}))
+ {
+ if($go)
+ sys::outjs(array('e' => 'Для выполнения поиска, необходимо больше данных'), $nmch);
+
+ sys::outjs(array('e' => ''));
+ }
+
+ // Поиск по картам
+ if($text{0} == '^')
+ {
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` REGEXP FROM_BASE64(\''.base64_encode(str_replace('_', '\_', $text).'').'\') ORDER BY `name` ASC LIMIT 12');
+ $text = substr($text, 1);
+ }else
+ $sql->query('SELECT `id`, `name` FROM `maps` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `name` LIKE FROM_BASE64(\''.base64_encode('%'.str_replace('_', '\_', $text).'%').'\') ORDER BY `name` ASC LIMIT 12');
+
+ if(!$sql->num())
+ {
+ if($go)
+ sys::outjs(array('e' => 'По вашему запросу ничего не найдено'), $nmch);
+
+ sys::outjs(array('e' => 'По вашему запросу ничего не найдено'));
+ }
+
+ $i = 0;
+ $mapsjs = '';
+
+ while($map = $sql->get())
+ {
+ $i+=1;
+
+ $mapsjs[$i] = 's'.$map['id'];
+
+ $html->get('map_search', 'sections/servers/games/maps');
+
+ $html->set('id', 's'.$map['id']);
+ $html->set('img', sys::img($map['name'], $server['game']));
+ $html->set('name', sys::find($map['name'], $text));
+
+ $html->pack('maps');
+ }
+
+ $mcache->set($mkey, array('maps' => $html->arr['maps'], 'mapsjs' => $mapsjs), false, 15);
+
+ sys::outjs(array('maps' => $html->arr['maps'], 'mapsjs' => $mapsjs));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/owners.php b/system/sections/servers/cssold/owners.php
new file mode 100644
index 0000000..71a5157
--- /dev/null
+++ b/system/sections/servers/cssold/owners.php
@@ -0,0 +1,172 @@
+ 'Включение',
+ 'stop' => 'Выключение',
+ 'restart' => 'Перезагрузка',
+ 'change' => 'Смена карты',
+ 'reinstall' => 'Переустановка',
+ 'update' => 'Обновление',
+ 'console' => 'Раздел "Консоль"',
+ 'settings' => 'Раздел "Настройки"',
+ 'plugins' => 'Раздел "Плагины"',
+ 'maps' => 'Раздел "Карты"'
+ );
+
+ $aAccess = array('start', 'stop', 'restart', 'change', 'reinstall', 'update', 'console', 'settings', 'plugins', 'maps');
+
+ // Проверка прав
+ if(isset($url['rights']) AND $url['rights'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['rights']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Совладелец не найден.'));
+
+ $owner = $sql->get();
+
+ $aRights = sys::b64djs($owner['rights']);
+
+ $rights = '';
+
+ foreach($aAccess as $access)
+ if($aRights[$access]) $rights .= $aAccessI[$access].', ';
+
+ sys::outjs(array('s' => substr($rights, 0, -2)));
+ }
+
+ // Удаление совладельца
+ if(isset($url['delete']) AND $url['delete'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if($sql->num())
+ $sql->query('DELETE FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/owners');
+ }
+
+ // Добавление совладельца
+ if($go)
+ {
+ $nmch = sys::rep_act('server_owners_go_'.$id, 5);
+
+ $aData = (isset($_POST['owner']) AND is_array($_POST['owner'])) ? $_POST['owner'] : array();
+
+ $aDate = isset($aData['\'time\'']) ? explode('.', $aData['\'time\'']) : explode('.', date('d.m.Y', $start_point));
+ $aTime = explode(':', date('H:i:s', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ sys::outjs(array('e' => 'Дата доступа указана неверно.'), $nmch);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2])+3600;
+
+ if($time < $start_point)
+ sys::outjs(array('e' => 'Время доступа не может быть меньше 60 минут.'), $nmch);
+
+ // Проверка пользователя
+ if(!isset($aData['\'user\'']))
+ sys::outjs(array('e' => 'Необходимо указать пользователя.'), $nmch);
+
+ if(is_numeric($aData['\'user\'']))
+ $sql->query('SELECT `id` FROM `users` WHERE `id`="'.$aData['\'user\''].'" LIMIT 1');
+ else{
+ if(sys::valid($aData['\'user\''], 'other', $aValid['login']))
+ sys::outjs(array('e' => sys::text('input', 'login_valid')), $nmch);
+
+ $sql->query('SELECT `id` FROM `users` WHERE `login`="'.$aData['\'user\''].'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Пользователь не найден в базе.'), $nmch);
+
+ $uowner = $sql->get();
+
+ $owner = $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+
+ // Если не обновление доступа совладельца, проверить кол-во
+ if(!$sql->num($owner))
+ {
+ $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" LIMIT 5');
+
+ if($sql->num() == 5)
+ sys::outjs(array('e' => 'Вы добавили максимально число совладельцев.'), $nmch);
+ }
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Владельца сервера нельзя добавить в совладельцы.'), $nmch);
+
+ $aRights = array();
+
+ $check = 0;
+
+ foreach($aAccess as $access)
+ {
+ $aRights[$access] = isset($aData['\''.$access.'\'']) ? 1 : 0;
+
+ $check += $aRights[$access];
+ }
+
+ if(!$check)
+ sys::outjs(array('e' => 'Необходимо включить минимум одно разрешение.'), $nmch);
+
+
+
+ if($sql->num($owner))
+ $sql->query('UPDATE `owners` set `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'" WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ else
+ $sql->query('INSERT INTO `owners` set `server`="'.$id.'", `user`="'.$uowner['id'].'", `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $html->nav('Друзья');
+
+ $cache = $mcache->get('server_owners_'.$id);
+
+ if($cache != '')
+ $html->arr['main'] = $cache;
+ else{
+ $owners = $sql->query('SELECT `id`, `user`, `rights`, `time` FROM `owners` WHERE `server`="'.$id.'" AND `time`>"'.$start_point.'" ORDER BY `id` ASC LIMIT 5');
+
+ if($sql->num())
+ include(LIB.'games/games.php');
+
+ while($owner = $sql->get($owners))
+ {
+ $sql->query('SELECT `login` FROM `users` WHERE `id`="'.$owner['user'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $uowner = $sql->get();
+
+ $rights = games::owners(sys::b64djs($owner['rights']));
+
+ $html->get('owners_list', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('oid', $owner['id']);
+ $html->set('user', $uowner['login']);
+ $html->set('rights', $rights);
+ $html->set('time', date('d.m.Y - H:i', $owner['time']));
+
+ $html->pack('owners');
+ }
+
+ $html->get('owners', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('time', date('d.m.Y', $start_point));
+
+ $html->set('owners', isset($html->arr['owners']) ? $html->arr['owners'] : 'Для данного сервера совладельцы отсутсвуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_owners_'.$id, $html->arr['main'], false, 1);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/plugins.php b/system/sections/servers/cssold/plugins.php
new file mode 100644
index 0000000..a116ca1
--- /dev/null
+++ b/system/sections/servers/cssold/plugins.php
@@ -0,0 +1,158 @@
+query('SELECT `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $nmch = sys::rep_act('server_plugins_go_'.$id, 10);
+
+ include(SEC.'servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('server_plugins_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_plugins_'.$id);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$id.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `plugins_install` WHERE `server`="'.$id.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_plugins_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/rcon.php b/system/sections/servers/cssold/rcon.php
new file mode 100644
index 0000000..1a0de70
--- /dev/null
+++ b/system/sections/servers/cssold/rcon.php
@@ -0,0 +1,54 @@
+ 'Необходимо выбрать игрока.'));
+
+ if($url['action'] == 'kick')
+ rcon::cmd(array_merge($server, array('id' => $id)), 'kickid "'.$player.'" "EGP Panel"');
+ else
+ rcon::cmd(array_merge($server, array('id' => $id)), 'sm_slay "'.$player.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ include(LIB.'geo.php');
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $aPlayers = rcon::players(rcon::cmd(array_merge($server, array('id' => $id))));
+
+ foreach($aPlayers as $i => $aPlayer)
+ {
+ $html->get('player', 'sections/servers/'.$server['game'].'/rcon');
+
+ $html->set('i', $i);
+ $html->set('userid', $aPlayer['userid']);
+ $html->set('name', $aPlayer['name']);
+ $html->set('steamid', $aPlayer['steamid']);
+ $html->set('time', $aPlayer['time']);
+ $html->set('ping', $aPlayer['ping']);
+ $html->set('ip', $aPlayer['ip']);
+ $html->set('ico', $aPlayer['ico']);
+ $html->set('country', $aPlayer['country']);
+
+ $html->pack('players');
+ }
+
+ sys::outjs(array('s' => isset($html->arr['players']) ? $html->arr['players'] : ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Rcon управление игроками');
+
+ $html->get('rcon', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/settings.php b/system/sections/servers/cssold/settings.php
new file mode 100644
index 0000000..6ffcbee
--- /dev/null
+++ b/system/sections/servers/cssold/settings.php
@@ -0,0 +1,77 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack`, `ddos` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $aSub = array('start', 'server', 'admins', 'bans', 'firewall', 'crontab', 'startlogs', 'debug', 'logs', 'smlogs', 'pack', 'file', 'antiddos', 'api');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aEditslist = 1;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ $antiddos = 'Индивидуальная защита отключена '
+ .'Индивидуальная защита (Заблокировать всех кроме: RU, UA) '
+ .'Индивидуальная защита (Заблокировать всех кроме: AM, BY, UA, RU, KZ) ';
+
+ include(SEC.'servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('antiddos', str_replace($server['ddos'], $server['ddos'].'" selected="select', $antiddos));
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+
+ $sql->query('SELECT `key` FROM `api` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ {
+ $api = $sql->get();
+
+ $html->set('api', $api['key']);
+ $html->unit('api', 1, 1);
+ }else
+ $html->unit('api', 0, 1);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/settings/admins.php b/system/sections/servers/cssold/settings/admins.php
new file mode 100644
index 0000000..f8ad37e
--- /dev/null
+++ b/system/sections/servers/cssold/settings/admins.php
@@ -0,0 +1,114 @@
+nav('Управление администраторами');
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $aData = array();
+
+ $aData['active'] = isset($_POST['active']) ? $_POST['active'] : '';
+ $aData['value'] = isset($_POST['value']) ? $_POST['value'] : '';
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : '';
+ $aData['flags'] = isset($_POST['flags']) ? $_POST['flags'] : '';
+ $aData['immunity'] = isset($_POST['immunity']) ? sys::int($_POST['immunity']) : '';
+ $aData['time'] = isset($_POST['time']) ? $_POST['time'] : '';
+ $aData['info'] = isset($_POST['info']) ? $_POST['info'] : '';
+
+ // Удаление текущих записей
+ $sql->query('DELETE FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'"');
+
+ $usini = '';
+
+ foreach($aData['value'] as $index => $val)
+ {
+ if($val != '')
+ {
+ $aDate = isset($aData['time'][$index]) ? explode('.', $aData['time'][$index]) : explode('.', date('d.m.Y', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ $aDate = explode('.', date('d.m.Y', $start_point));
+
+ $time = mktime(0, 0, 0, $aDate[1], $aDate[0], $aDate[2]);
+
+ $aData['active'][$index] = isset($aData['active'][$index]) ? 1 : 0;
+ $aData['passwd'][$index] = isset($aData['passwd'][$index]) ? $aData['passwd'][$index] : '';
+ $aData['flags'][$index] = isset($aData['flags'][$index]) ? $aData['flags'][$index] : '';
+ $aData['info'][$index] = isset($aData['info'][$index]) ? $aData['info'][$index] : '';
+
+ $text = '"'.$val.'" "'.$aData['immunity'][$index].':'.$aData['flags'][$index].'" "'.$aData['passwd'][$index].'"';
+
+ $sql->query('INSERT INTO `admins_'.$server['game'].'` set'
+ .'`server`="'.$id.'",'
+ .'`value`="'.htmlspecialchars($val).'",'
+ .'`active`="'.$aData['active'][$index].'",'
+ .'`passwd`="'.htmlspecialchars($aData['passwd'][$index]).'",'
+ .'`flags`="'.htmlspecialchars($aData['flags'][$index]).'",'
+ .'`immunity`="'.$aData['immunity'][$index].'",'
+ .'`time`="'.$time.'",'
+ .'`text`="'.htmlspecialchars($text).'",'
+ .'`info`="'.htmlspecialchars($aData['info'][$index]).'"');
+
+ if($aData['active'][$index])
+ $usini .= $text.PHP_EOL;
+ }
+ }
+
+ $temp = sys::temp($usini);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/addons/sourcemod/configs/admins_simple.ini', 0644);
+
+ unlink($temp);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/addons/sourcemod/configs/admins_simple.ini');
+
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \" sm_reloadadmins\"\015'");
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Построение списка добавленных админов
+ $sql->query('SELECT `id`, `value`, `active`, `passwd`, `flags`, `immunity`, `time`, `info` FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'" ORDER BY `id` ASC');
+ while($admin = $sql->get())
+ {
+ $html->get('list', 'sections/servers/'.$server['game'].'/settings/admins');
+
+ if($admin['active'])
+ $html->unit('active', 1);
+ else
+ $html->unit('active');
+
+ $html->set('id', $admin['id']);
+ $html->set('value', $admin['value']);
+ $html->set('passwd', $admin['passwd']);
+ $html->set('flags', $admin['flags']);
+ $html->set('immunity', $admin['immunity']);
+ $html->set('time', date('d.m.y', $admin['time']));
+ $html->set('info', $admin['info']);
+
+ $html->pack('admins');
+ }
+
+ $sql->query('SELECT `id` FROM `admins_'.$server['game'].'` WHERE `server`="'.$id.'" ORDER BY `id` DESC LIMIT 1');
+ $max = $sql->get();
+
+ $html->get('admins', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('admins', isset($html->arr['admins']) ? $html->arr['admins'] : '');
+ $html->set('index', $max['id'] < 1 ? 0 : $max['id']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/settings/antiddos.php b/system/sections/servers/cssold/settings/antiddos.php
new file mode 100644
index 0000000..6451e5d
--- /dev/null
+++ b/system/sections/servers/cssold/settings/antiddos.php
@@ -0,0 +1,73 @@
+query('SELECT `id` FROM `units` WHERE `id`="'.$server['unit'].'" AND `ddos`="1" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'В данный момент нельзя изменить параметр, т.к. включена защита на всю локацию.'), $name_mcache);
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($url['type']) || !in_array($url['type'], array('0', '1', '2')))
+ sys::outjs(array('e' => 'Неправильно передан параметр.'), $name_mcache);
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh', $user['group'])), $name_mcache);
+
+ list($ip, $port) = explode(':', $server['address']);
+
+ $geo = $cfg['iptables'].'_geo';
+
+ if($url['type'] == 2)
+ {
+ if($server['ddos'] == 2)
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ $cmd = '';
+
+ if($server['ddos'])
+ $cmd = 'iptables -D INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country UA,RU -j DROP;'
+ .'sed "`nl '.$geo.' | grep \"#'.$id.'\" | awk \'{print $1","$1+1}\'`d" '.$geo.' > '.$geo.'_temp; cat '.$geo.'_temp > '.$geo.'; rm '.$geo.'_temp;';
+
+ $rule = 'iptables -I INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country AM,BY,UA,RU,KZ -j DROP;';
+
+ $ssh->set($cmd.$rule.' echo -e "#'.$id.';\n'.$rule.'" >> '.$geo);
+
+ $sql->query('UPDATE `servers` set `ddos`="2" WHERE `id`="'.$id.'" LIMIT 1');
+ }elseif($url['type'] == 1){
+ if($server['ddos'] == 1)
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ $cmd = '';
+
+ if($server['ddos'])
+ $cmd = 'iptables -D INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country AM,BY,UA,RU,KZ -j DROP;'
+ .'sed "`nl '.$geo.' | grep \"#'.$id.'\" | awk \'{print $1","$1+1}\'`d" '.$geo.' > '.$geo.'_temp; cat '.$geo.'_temp > '.$geo.'; rm '.$geo.'_temp;';
+
+ $rule = 'iptables -I INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country UA,RU -j DROP;';
+
+ $ssh->set($cmd.$rule.' echo -e "#'.$id.';\n'.$rule.'" >> '.$geo);
+
+ $sql->query('UPDATE `servers` set `ddos`="1" WHERE `id`="'.$id.'" LIMIT 1');
+ }else{
+ if(!$server['ddos'])
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ $country = $server['ddos'] == 2 ? 'AM,BY,UA,RU,KZ' : 'UA,RU';
+
+ $ssh->set('iptables -D INPUT -p udp -d '.$ip.' --dport '.$port.' -m geoip ! --source-country '.$country.' -j DROP;'
+ .'sed "`nl '.$geo.' | grep \"#'.$id.'\" | awk \'{print $1","$1+1}\'`d" '.$geo.' > '.$geo.'_temp; cat '.$geo.'_temp > '.$geo.'; rm '.$geo.'_temp;');
+
+ $sql->query('UPDATE `servers` set `ddos`="0" WHERE `id`="'.$id.'" LIMIT 1');
+ }
+
+ $mcache->delete('server_settings_'.$id);
+
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/settings/bans.php b/system/sections/servers/cssold/settings/bans.php
new file mode 100644
index 0000000..71a11ca
--- /dev/null
+++ b/system/sections/servers/cssold/settings/bans.php
@@ -0,0 +1,160 @@
+nav('Бан листы');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к файлам (banned_user.cfg / banned_ip.cfg)
+ $folder = $tarif['install'].$server['uid'].'/cstrike';
+
+ // Если бан/разбан/проверка
+ if($go)
+ {
+ $aData = array();
+
+ $aData['value'] = isset($_POST['value']) ? trim($_POST['value']) : sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+ $aData['userid'] = isset($_POST['userid']) ? sys::int($_POST['userid']) : false;
+ $aData['amxbans'] = isset($_POST['amxbans']) ? true : false;
+
+ // Проверка входных данных
+ if(sys::valid($aData['value'], 'steamid') AND sys::valid($aData['value'], 'ip'))
+ sys::outjs(array('e' => sys::text('servers', 'bans')), $nmch);
+
+ // Если указан steamid
+ if(sys::valid($aData['value'], 'ip'))
+ {
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'] AND $aData['userid'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['userid']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"banid 0.0 ".$aData['value']." kick\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"banid 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_user.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_user.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_user.cfg | grep -v '.$aData['value'].' > temp_banned.cfg; echo "" >> temp_banned.cfg && cat temp_banned.cfg > banned_user.cfg; rm temp_banned.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeid ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeid\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_user.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный SteamID найден в файле banned_user.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный SteamID не найден в файле banned_user.cfg'), $nmch);
+ }
+ }else{
+ // бан
+ if(isset($url['action']) AND $url['action'] == 'ban')
+ {
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"sm_ban 0 ".$aData['value']." EGP\"\015'");
+ else
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"addip 0.0 ".$aData['value']." EGP\"\015'");
+
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] != trim($ssh->get()))
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"addip 0.0 '.$aData['value'].'\" >> '.$folder.'/banned_ip.cfg"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ // разбан
+ }elseif(isset($url['action']) AND $url['action'] == 'unban'){
+ // Убираем запись из banned_ip.cfg
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' sh -c "cat banned_ip.cfg | grep -v '.$aData['value'].' > temp_listip.cfg; echo "" >> temp_listip.cfg && cat temp_listip.cfg > banned_ip.cfg; rm temp_listip.cfg"');
+
+ // Если включен sourcebans
+ if($aData['amxbans'])
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"amx_unban ".$aData['value']."\"\015'");
+ else{
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"removeip ".$aData['value']."\"\015'");
+ $ssh->set("sudo -u server".$server['uid']." screen -p 0 -S s_".$server['uid']." -X eval 'stuff \"writeip\"\015'");
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ // проверка
+ }else{
+ $ssh->set('cd '.$folder.' && sudo -u server'.$server['uid'].' fgrep '.$aData['value'].' banned_ip.cfg | awk \'{print $3}\'');
+
+ if($aData['value'] == trim($ssh->get()))
+ sys::outjs(array('ban' => 'Данный IP найден в файле banned_ip.cfg'), $nmch);
+
+ sys::outjs(array('unban' => 'Данный IP не найден в файле banned_ip.cfg'), $nmch);
+ }
+ }
+ }
+
+ // Содержимое banned_user.cfg
+ $ssh->set('cd '.$folder.' && cat banned_user.cfg | awk \'{print $3}\' | grep STEAM_');
+ $aBanned = explode("\n", $ssh->get());
+
+ // Содержимое banned_ip.cfg
+ $ssh->set('cd '.$folder.' && cat banned_ip.cfg | awk \'{print $3}\' | egrep "(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])(\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[0-9]{2}|[0-9])){3}"');
+ $aListip = explode("\n", $ssh->get());
+
+ if(isset($aBanned[count($aBanned)-1]) AND $aBanned[count($aBanned)-1] == '')
+ unset($aBanned[count($aBanned)-1]);
+
+ if(isset($aListip[count($aListip)-1]) AND $aListip[count($aListip)-1] == '')
+ unset($aListip[count($aListip)-1]);
+
+ // Построение списка забаненых по steamid
+ foreach($aBanned as $line => $steam)
+ {
+ $html->get('bans_list', 'sections/servers/games/settings');
+
+ $html->set('value', trim($steam));
+
+ $html->pack('banned');
+ }
+
+ // Построение списка забаненых по ip
+ foreach($aListip as $line => $ip)
+ {
+ $html->get('bans_list', 'sections/servers/games/settings');
+
+ $html->set('value', trim($ip));
+
+ $html->pack('listip');
+ }
+
+ $html->get('bans', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('banned', isset($html->arr['banned']) ? $html->arr['banned'] : '');
+ $html->set('listip', isset($html->arr['listip']) ? $html->arr['listip'] : '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/settings/debug.php b/system/sections/servers/cssold/settings/debug.php
new file mode 100644
index 0000000..180f9d0
--- /dev/null
+++ b/system/sections/servers/cssold/settings/debug.php
@@ -0,0 +1,28 @@
+nav('Отладочный лог');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Чтение файла - oldstart.log
+ $file = $tarif['install'].$server['uid'].'/debug.log';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep "CRASH: " | grep -ve "^#\|^[[:space:]]*$"');
+
+ $html->get('debug', 'sections/servers/games/settings');
+
+ $html->set('log', htmlspecialchars($ssh->get()));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/settings/logs.php b/system/sections/servers/cssold/settings/logs.php
new file mode 100644
index 0000000..1aa0914
--- /dev/null
+++ b/system/sections/servers/cssold/settings/logs.php
@@ -0,0 +1,89 @@
+nav('Логи');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к логам
+ $folder = $tarif['install'].$server['uid'].'/cstrike/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['cslogs']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/logs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'logs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/logs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"\/"$1"\/"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('\/', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', end($name));
+ $html->set('uri', 'logs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('uri', '');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/settings/server.php b/system/sections/servers/cssold/settings/server.php
new file mode 100644
index 0000000..b6a4696
--- /dev/null
+++ b/system/sections/servers/cssold/settings/server.php
@@ -0,0 +1,117 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ $config_end = $servercfg['\'other\''];
+
+ unset($servercfg['\'other\'']);
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).' "'.$val.'"'."\n";
+
+ // Временый файл
+ $temp = sys::temp($config.$config_end);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg');
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Чтение файла - server.cfg
+ $file = $tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+ $other = '';
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode(' ', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.' ', "", $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ else
+ $other .= $line."\n";
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $html->get('servercfg', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('cfg', $html->arr['list']);
+ $html->set('other', $other);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/settings/smlogs.php b/system/sections/servers/cssold/settings/smlogs.php
new file mode 100644
index 0000000..b39aa81
--- /dev/null
+++ b/system/sections/servers/cssold/settings/smlogs.php
@@ -0,0 +1,89 @@
+nav('Логи SourceMod');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к логам
+ $folder = $tarif['install'].$server['uid'].'/cstrike/addons/sourcemod/logs';
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['csssmlogs']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/smlogs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get()));
+ $html->set('uri', 'smlogs');
+
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/smlogs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"\/"$1"\/"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('\/', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/servers/games/settings/logs');
+
+ $html->set('id', $id);
+ $html->set('name', end($name));
+ $html->set('uri', 'smlogs/log/'.end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('uri', 'sm');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/settings/start.php b/system/sections/servers/cssold/settings/start.php
new file mode 100644
index 0000000..3715413
--- /dev/null
+++ b/system/sections/servers/cssold/settings/start.php
@@ -0,0 +1,170 @@
+query('SELECT `uid`, `slots`, `slots_start`, `map_start`, `vac`, `fastdl`, `autorestart`, `fps`, `tickrate` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install`, `fps`, `tickrate`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'games/games.php');
+ include(LIB.'games/tarifs.php');
+ include(LIB.'games/'.$server['game'].'/tarif.php');
+
+ // Вывод списка карт
+ if(isset($url['maps']))
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/cstrike/maps', $server['map_start'], false);
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'map':
+ $map = isset($url['value']) ? trim($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ if($map != $server['map_start'])
+ games::maplist($id, $unit, $tarif['install'].$server['uid'].'/cstrike/maps', $map, true, $nmch);
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'vac':
+ if($value != $server['vac'])
+ $sql->query('UPDATE `servers` set `vac`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `servers` set `autorestart`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fps':
+ if(!tarif::price($tarif['price']) AND in_array($value, explode(':', $tarif['fps'])))
+ $sql->query('UPDATE `servers` set `fps`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'tickrate':
+ if(!tarif::price($tarif['price']) AND in_array($value, explode(':', $tarif['tickrate'])))
+ $sql->query('UPDATE `servers` set `tickrate`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'fastdl':
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ if($value)
+ {
+ $fastdl = 'sv_downloadurl "http://'.sys::first(explode(':', $unit['address'])).':8080/fast_'.$server['uid'].'"'.PHP_EOL
+ .'sv_consistency 1'.PHP_EOL
+ .'sv_allowupload 1'.PHP_EOL
+ .'sv_allowdownload 1';
+
+ // Временый файл
+ $temp = sys::temp($fastdl);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/cstrike/cfg/fastdl.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/cstrike/cfg/fastdl.cfg;'
+ .'ln -s '.$tarif['install'].$server['uid'].'/cstrike /var/nginx/fast_'.$server['uid'].';'
+ .'sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'echo "exec fastdl.cfg" >> '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg');
+
+ unlink($temp);
+ }else
+ $ssh->set('sed -i '."'s/exec fastdl.cfg//g'".' '.$tarif['install'].$server['uid'].'/cstrike/cfg/server.cfg;'
+ .'rm '.$tarif['install'].$server['uid'].'/cstrike/cfg/fastdl.cfg; rm /var/nginx/fast_'.$server['uid']);
+
+ $sql->query('UPDATE `servers` set `fastdl`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Античит VAC
+ $vac = $server['vac'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Быстрая скачака
+ $fastdl = $server['fastdl'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $fps = ''.$server['fps'].' FPS ';
+ $tickrate = ''.$server['tickrate'].' TickRate ';
+
+ if(!tarif::price($tarif['price']))
+ {
+ $aFps = explode(':', $tarif['fps']);
+
+ unset($aFps[array_search($server['fps'], $aFps)]);
+
+ if(count($aFps))
+ foreach($aFps as $value)
+ $fps .= ''.$value.' FPS ';
+
+ $aTick = explode(':', $tarif['tickrate']);
+
+ unset($aTick[array_search($server['tickrate'], $aTick)]);
+
+ if(count($aTick))
+ foreach($aTick as $value)
+ $tickrate .= ''.$value.' TickRate ';
+ }
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('map', $server['map_start']);
+ $html->set('vac', $vac);
+ $html->set('fastdl', $fastdl);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ if(!tarif::price($tarif['price']))
+ {
+ $html->unit('fps', true);
+ $html->set('fps', $fps);
+
+ $html->unit('tickrate', true);
+ $html->set('tickrate', $tickrate);
+ }else{
+ $html->unit('fps');
+ $html->unit('tickrate');
+ }
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/tarif.php b/system/sections/servers/cssold/tarif.php
new file mode 100644
index 0000000..30cc896
--- /dev/null
+++ b/system/sections/servers/cssold/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `fps`, `tickrate`, `ram`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'plan', 'address', 'addextend', 'unit', 'slots');
+
+ include(SEC.'servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/tarif/extend.php b/system/sections/servers/cssold/tarif/extend.php
new file mode 100644
index 0000000..5125e5f
--- /dev/null
+++ b/system/sections/servers/cssold/tarif/extend.php
@@ -0,0 +1,52 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['fps'] = $server['fps'];
+ $aData['tickrate'] = $server['tickrate'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ $aPrice = sys::b64djs($tarif['price']);
+
+ // Цена за 30 дней 1 слота
+ $price = $aPrice[$server['tickrate'].'_'.$server['fps']];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/tarif/plan.php b/system/sections/servers/cssold/tarif/plan.php
new file mode 100644
index 0000000..a5173f7
--- /dev/null
+++ b/system/sections/servers/cssold/tarif/plan.php
@@ -0,0 +1,67 @@
+ 'Переданые не все данные'), $nmch);
+
+ $aPrice = sys::b64djs($tarif['price']);
+
+ // Проверка плана
+ if(!array_key_exists($plan, $aPrice))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ list($tickrate, $fps) = explode('_', $plan);
+
+ if($tickrate == $server['tickrate'] AND $fps == $server['fps'])
+ sys::outjs(array('e' => 'Смысла в этой операции нет'), $nmch);
+
+ if(!tarif::price($tarif['price']))
+ sys::outjs(array('e' => 'Чтобы изменить тариф, перейдите в настройки запуска'), $nmch);
+
+ if($server['time'] < $start_point+86400)
+ $time = $server['time'];
+ else{
+ // Цена за 1 день (по новому тарифному плану)
+ $price = $aPrice[$plan]/30*$server['slots'];
+
+ // Цена аренды за остаток дней
+ $price_old = $aPrice[$server['tickrate'].'_'.$server['fps']]/30*$server['slots'];
+
+ // Остаток дней аренды
+ $days = ($server['time']-$start_point)/86400;
+
+ $time = date('H:i:s', $server['time']);
+ $date = date('d.m.Y', round($start_point+$days*$price_old/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+ $aTime = explode(':', $time);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2]);
+ }
+
+ // Выполнение смена тарифного плана
+ if($go)
+ {
+ sys::benefitblock($id, $nmch);
+
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `fps`="'.$fps.'", `tickrate`="'.$tickrate.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')))
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_plan').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/tarif/slots.php b/system/sections/servers/cssold/tarif/slots.php
new file mode 100644
index 0000000..fae705c
--- /dev/null
+++ b/system/sections/servers/cssold/tarif/slots.php
@@ -0,0 +1,33 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $aPrice = sys::b64djs($tarif['price']);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $aPrice[$server['tickrate'].'_'.$server['fps']]/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($aPrice[$server['tickrate'].'_'.$server['fps']]/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/tarif/unit.php b/system/sections/servers/cssold/tarif/unit.php
new file mode 100644
index 0000000..bc023d1
--- /dev/null
+++ b/system/sections/servers/cssold/tarif/unit.php
@@ -0,0 +1,48 @@
+ 'Переданы не все данные.'), $nmch);
+
+ if(!$cfg['change_unit'][$server['game']] || $server['time'] < $start_point+86400 || $server['test'])
+ exit;
+
+ $sql->query('SELECT `id`, `unit`, `packs`, `fps`, `tickrate`, `price` FROM `tarifs` WHERE `unit`="'.$uid.'" AND `game`="'.$server['game'].'" AND `name`="'.$tarif['name'].'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Не найден подходящий тариф.'), $nmch);
+
+ $oldTarif = $tarif;
+
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $oldUnit = $sql->get();
+
+ $aPriceold = sys::b64djs($oldTarif['price']);
+
+ $sql->query('SELECT `id` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная локация не доступна.'), $nmch);
+
+ $aPrice = sys::b64djs($tarif['price']);
+
+ if(!array_key_exists($server['tickrate'].'_'.$server['fps'], $aPrice))
+ sys::outjs(array('e' => 'Не найден подходящий тарифный план.'), $nmch);
+
+ // Цена за 1 день (при новом тарифном плане)
+ $price = $aPrice[$server['tickrate'].'_'.$server['fps']]/30*$server['slots'];
+
+ // Цена аренды за остаток дней (с текущим тарифным планом)
+ $oldprice = ($server['time']-$start_point)/86400*($aPriceold[$server['tickrate'].'_'.$server['fps']]/30*$server['slots']);
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$oldprice/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ include(SEC.'servers/games/tarif/unit.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/cssold/web.php b/system/sections/servers/cssold/web.php
new file mode 100644
index 0000000..634e9b6
--- /dev/null
+++ b/system/sections/servers/cssold/web.php
@@ -0,0 +1,87 @@
+nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ include(DATA.'web.php');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub) AND in_array($url['action'], array_merge($aAction[$url['subsection']], array('install', 'manage'))))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_web_go_'.$id, 10);
+ else
+ $html->nav('Web', $cfg['http'].'servers/id/'.$id.'/section/web');
+
+ include(SEC.'web/'.$url['subsection'].'/free/'.$url['action'].'.php');
+ }else{
+ $html->nav('Web');
+
+ if($mcache->get('server_web_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_web_'.$id);
+ else{
+ // Услуги
+ foreach($aWeb[$server['game']] as $service => $active)
+ {
+ if($active)
+ {
+ if($service == 'hosting')
+ {
+ if(!$aWebVHTtype && !in_array($server['tarif'], $aWebVHT))
+ continue;
+
+ if($aWebVHTtype && in_array($server['tarif'], $aWebVHT))
+ continue;
+ }
+
+ // Проверка на установку
+ switch($aWebInstall[$server['game']][$service])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ }
+
+ if($sql->num())
+ $html->get('list_install', 'sections/servers/games/web');
+ else
+ $html->get('list', 'sections/servers/games/web');
+
+ $html->set('id', $id);
+ $html->set('service', $service);
+ $html->set('name', $aWebname[$service]);
+ $html->set('desc', $aWebDesc[$service]);
+
+ $html->pack($aWebType[$service]);
+ }
+ }
+
+ // Блоки услуг
+ foreach($aWebTypeInfo[$server['game']] as $type => $name)
+ {
+ if(!isset($html->arr[$type]))
+ continue;
+
+ $html->get('block', 'sections/servers/games/web');
+ $html->set('name', $name);
+ $html->set('list', $html->arr[$type]);
+ $html->pack('web');
+ }
+
+ $html->get('web', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('web', isset($html->arr['web']) ? $html->arr['web'] : 'Дополнительные услуги отсутствуют');
+ $html->pack('main');
+
+ $mcache->set('server_web_'.$id, $html->arr['main'], false, 4);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/filetp.php b/system/sections/servers/filetp.php
new file mode 100644
index 0000000..feb39c9
--- /dev/null
+++ b/system/sections/servers/filetp.php
@@ -0,0 +1,223 @@
+query('SELECT `uid`, `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ if(!$server['ftp_use'])
+ sys::back($cfg['http'].'servers/id/'.$id);
+
+ sys::nav($server, $id, 'filetp');
+
+ $frouter = explode('/', sys::route($server, 'filetp', $go));
+
+ if(end($frouter) == 'noaccess.php')
+ include(SEC.'servers/noaccess.php');
+ else{
+ $sql->query('SELECT `uid`, `unit`, `tarif`, `ftp`, `ftp_root`, `ftp_passwd`, `ftp_on`, `hdd` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+ $ip = sys::first(explode(':', $unit['address']));
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('FileTP');
+
+ // Корневой каталог сервера
+ if($cfg['ftp']['root'][$server['game']] || $server['ftp_root'])
+ {
+ // Путь для Proftpd
+ $homedir = $tarif['install'].$server['uid'];
+
+ // Путь для файлового менеджера
+ $dir = $cfg['ftp']['dir'][$server['game']];
+ }else{
+ // Путь для Proftpd
+ $homedir = $tarif['install'].$server['uid'].$cfg['ftp']['home'][$server['game']];
+
+ // Путь для файлового менеджера
+ $dir = '/';
+ }
+
+ $aData = array(
+ 'root' => $dir,
+ 'host' => $ip,
+ 'login' => $server['uid'],
+ 'passwd' => $server['ftp_passwd']
+ );
+
+ if($go)
+ {
+ if(isset($url['action']) AND in_array($url['action'], array('on', 'off', 'change', 'logs')))
+ {
+ $sql->query('SELECT `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = array_merge($unit, $sql->get());
+
+ include(LIB.'ssh.php');
+
+ // Проверка соединения с ssh сервером
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/filetp');
+ }else{
+ include(LIB.'ftp.php');
+
+ $ftp = new ftp;
+
+ // Проверка соединения с ftp сервером
+ if(!$ftp->auth($aData['host'], $aData['login'], $aData['passwd']))
+ {
+ if(isset($url['action']))
+ {
+ if($url['action'] == 'search')
+ sys::out('Не удалось соединиться с ftp-сервером.');
+
+ sys::outjs(array('e' => 'Не удалось соединиться с ftp-сервером.'));
+ }
+
+ sys::out();
+ }
+ }
+
+ // Выполнение операций
+ if(isset($url['action']))
+ switch($url['action'])
+ {
+ case 'on':
+ if($server['ftp'])
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/filetp');
+
+ $used = sys::int($ssh->get('cd '.$tarif['install'].$server['uid'].' && du -b | tail -1'));
+
+ if($used < 1)
+ sys::back($cfg['http'].'help/action/create');
+
+ $bytes = $server['hdd']*1048576;
+
+ $server['ftp_passwd'] = isset($server['ftp_passwd']{1}) ? $server['ftp_passwd'] : sys::passwd(8);
+
+ $qSql = 'DELETE FROM users WHERE username=\''.$server['uid'].'\';'
+ .'DELETE FROM quotalimits WHERE name=\''.$server['uid'].'\';'
+ .'DELETE FROM quotatallies WHERE name=\''.$server['uid'].'\';'
+ .'INSERT INTO users set username=\''.$server['uid'].'\', password=\''.$server['ftp_passwd'].'\', uid=\''.$server['uid'].'\', gid=\'1000\', homedir=\''.$homedir.'\', shell=\'/bin/false\';'
+ .'INSERT INTO quotalimits set name=\''.$server['uid'].'\', quota_type=\'user\', per_session=\'false\', limit_type=\'hard\', bytes_in_avail=\''.$bytes.'\';'
+ .'INSERT INTO quotatallies set name=\''.$server['uid'].'\', quota_type=\'user\', bytes_in_used=\''.$used.'\'';
+
+ $ssh->set('screen -dmS ftp'.$server['uid'].' mysql -P '.$unit['sql_port'].' -u'.$unit['sql_login'].' -p'.$unit['sql_passwd'].' --database '.$unit['sql_ftp'].' -e "'.$qSql.'"');
+
+ $sql->query('UPDATE `servers` SET `ftp`="1", `ftp_on`="1", `ftp_passwd`="'.$server['ftp_passwd'].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_filetp_'.$id);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/filetp');
+
+ case 'change':
+ if(!$server['ftp'])
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/filetp');
+
+ $passwd = sys::passwd(8);
+
+ $qSql = "UPDATE users set password='".$passwd."' WHERE username='".$server['uid']."' LIMIT 1";
+
+ $ssh->set('screen -dmS ftp'.$server['uid'].' mysql -P '.$unit['sql_port'].' -u'.$unit['sql_login'].' -p'.$unit['sql_passwd'].' --database '.$unit['sql_ftp'].' -e '.'"'.$qSql.'"');
+
+ $sql->query('UPDATE `servers` SET `ftp_passwd`="'.$passwd.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_filetp_'.$id);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/filetp');
+
+ case 'off':
+ if(!$server['ftp'])
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/filetp');
+
+ $qSql = 'DELETE FROM users WHERE username=\''.$server['uid'].'\';'
+ .'DELETE FROM quotalimits WHERE name=\''.$server['uid'].'\';'
+ .'DELETE FROM quotatallies WHERE name=\''.$server['uid'].'\'';
+
+ $ssh->set('screen -dmS ftp'.$server['uid'].' mysql -P '.$unit['sql_port'].' -u'.$unit['sql_login'].' -p'.$unit['sql_passwd'].' --database '.$unit['sql_ftp'].' -e "'.$qSql.'"');
+
+ $sql->query('UPDATE `servers` SET `ftp`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_filetp_'.$id);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/filetp');
+
+ case 'rename':
+ $ftp->rename(json_decode($_POST['path']), json_decode($_POST['name']), json_decode($_POST['newname']));
+
+ case 'edit':
+ $ftp->edit_file(json_decode($_POST['path']), json_decode($_POST['name']));
+
+ case 'create':
+ if(isset($url['folder']))
+ $ftp->mkdir(json_decode($_POST['path']), json_decode($_POST['name']));
+
+ $ftp->touch(json_decode($_POST['path']), json_decode($_POST['name']), json_decode($_POST['text']));
+
+ case 'delete':
+ if(isset($url['folder']))
+ $ftp->rmdir(json_decode($_POST['path']), json_decode($_POST['name']));
+
+ $ftp->rmfile(json_decode($_POST['path']).'/'.json_decode($_POST['name']));
+
+ case 'chmod':
+ $ftp->chmod(json_decode($_POST['path']), json_decode($_POST['name']), sys::int($_POST['chmod']));
+
+ case 'search':
+ $text = isset($_POST['find']) ? sys::first(explode('.', json_decode($_POST['find']))) : sys::out();
+
+ if(!isset($text{2}))
+ sys::out('Для выполнения поиска, необходимо больше данных');
+
+ $ftp->search($text, $id);
+
+ case 'logs':
+ $logs = $mcache->get('filetp_logs_'.$id);
+
+ if(!$logs)
+ {
+ include(LIB.'ftp.php');
+
+ $ftp = new ftp;
+
+ $logs = $ftp->logs($ssh->get('cat /var/log/proftpd/xferlog | grep "/'.$server['uid'].'/" | awk \'{print $2"\\\"$3"\\\"$4"\\\"$5"\\\"$7"\\\"$8"\\\"$9"\\\"$12}\' | tail -50'), $server['uid']);
+
+ $mcache->set('filetp_logs_'.$id, $logs, false, 300);
+ }
+
+ sys::out($logs);
+ }
+
+ if(!isset($_POST['path'])) $_POST['path'] = json_encode($aData['root']);
+
+ sys::out($ftp->view($ftp->read(json_decode($_POST['path'])), $id));
+ }
+
+ if($mcache->get('server_filetp_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_filetp_'.$id);
+ else{
+ if($server['ftp'])
+ {
+ $html->get('filetp_on', 'sections/servers/games/filetp');
+
+ $html->set('address', 'ftp://'.$aData['login'].':'.$aData['passwd'].'@'.$aData['host']);
+ $html->set('server', $aData['host']);
+ $html->set('login', $aData['login']);
+ $html->set('passwd', $aData['passwd']);
+ $html->set('path', $aData['root']);
+ }else
+ $html->get('filetp_off', 'sections/servers/games/filetp');
+
+ $html->set('id', $id);
+
+ $html->pack('main');
+
+ $mcache->set('server_filetp_'.$id, $html->arr['main'], false, 10);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/copy/check.php b/system/sections/servers/games/copy/check.php
new file mode 100644
index 0000000..c50e8c4
--- /dev/null
+++ b/system/sections/servers/games/copy/check.php
@@ -0,0 +1,26 @@
+get($nmch))
+ sys::outjs(array('e' => sys::text('other', 'mcache')));
+
+ $mcache->set($nmch, true, false, 10);
+
+ $copys = $sql->query('SELECT `id` FROM `copy` WHERE `user`="'.$server['user'].'_'.$server['unit'].'" AND `status`="0"');
+ if(!$sql->num($copys))
+ sys::outjs(array('e' => 'no find'), $nmch);
+
+ while($copy = $sql->get($copys))
+ {
+ if(!sys::int($ssh->get('ps aux | grep copy_'.$server['uid'].' | grep -v grep | awk \'{print $2}\'')))
+ $sql->query('UPDATE `copy` set `status`="1" WHERE `id`="'.$copy['id'].'" LIMIT 1');
+ }
+
+ // Очистка кеша
+ $mcache->delete('server_copy_'.$id);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/copy/create.php b/system/sections/servers/games/copy/create.php
new file mode 100644
index 0000000..e27d8b7
--- /dev/null
+++ b/system/sections/servers/games/copy/create.php
@@ -0,0 +1,54 @@
+query('SELECT `id` FROM `copy` WHERE `server`="'.$id.'" ORDER BY `id` DESC LIMIT 3');
+ if($sql->num() > 2)
+ sys::outjs(array('e' => 'Для создания новой копии необходимо удалить старые.'), $nmch);
+
+ $sql->query('SELECT `id` FROM `copy` WHERE `server`="'.$id.'" AND `status`="0" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Для создания новой копии дождитесь создания предыдущей.'), $nmch);
+
+ $aSel = array();
+
+ $aData = isset($_POST['copy']) ? $_POST['copy'] : sys::outjs(array('e' => 'Для создания копии необходимо выбрать директории/файлы.'), $nmch);
+
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ if(!isset($aData['\''.$name.'\'']))
+ continue;
+
+ $aSel[] = $name;
+ }
+
+ if(!count($aSel))
+ sys::outjs(array('e' => 'Для создания копии необходимо выбрать директории/файлы.'), $nmch);
+
+ $copy = '';
+ $info = '';
+ $plugins = '';
+
+ foreach($aSel as $name)
+ {
+ $copy .= isset(params::$section_copy[$server['game']]['aCopyDir'][$name]) ? params::$section_copy[$server['game']]['aCopyDir'][$name].' ' : '';
+ $copy .= isset(params::$section_copy[$server['game']]['aCopyFile'][$name]) ? params::$section_copy[$server['game']]['aCopyFile'][$name].' ' : '';
+
+ $info .= $name.', ';
+ }
+
+ $name_copy = md5($start_point.$id.$server['game']);
+
+ $ssh->set('cd '.$tarif['install'].$server['uid'].' && screen -dmS copy_'.$server['uid'].' sh -c "tar -cf '.$name_copy.'.tar '.$copy.'; mv '.$name_copy.'.tar /copy"');
+
+ $sql->query('SELECT `plugin`, `upd` FROM `plugins_install` WHERE `server`="'.$id.'"');
+ while($plugin = $sql->get())
+ $plugins .= $plugin['plugin'].'.'.$plugin['upd'].',';
+
+ $sql->query('INSERT INTO `copy` set `user`="'.$server['user'].'_'.$server['unit'].'", `game`="'.$server['game'].'", `server`="'.$id.'", `pack`="'.$server['pack'].'", `name`="'.$name_copy.'", `info`="'.substr($info, 0, -2).'", `plugins`="'.substr($plugins, 0, -1).'", `date`="'.$start_point.'", `status`="0"');
+
+ // Очистка кеша
+ $mcache->delete('server_copy_'.$id);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/copy/fullcopy.php b/system/sections/servers/games/copy/fullcopy.php
new file mode 100644
index 0000000..23bc872
--- /dev/null
+++ b/system/sections/servers/games/copy/fullcopy.php
@@ -0,0 +1,25 @@
+query('SELECT `id` FROM `copy` WHERE `server`="'.$id.'" AND `info`="'.params::$section_copy[$server['game']]['CopyFull'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Для создания новой копии необходимо удалить старую.'), $nmch);
+
+ $name_copy = md5($start_point.$id.$server['game']);
+
+ $ssh->set('cd '.$tarif['install'].$server['uid'].' && screen -dmS copy_'.$server['uid'].' sh -c "tar -cf '.$name_copy.'.tar '.params::$section_copy[$server['game']]['CopyFull'].'; mv '.$name_copy.'.tar /copy"');
+
+ $plugins = '';
+
+ $sql->query('SELECT `plugin`, `upd` FROM `plugins_install` WHERE `server`="'.$id.'"');
+ while($plugin = $sql->get())
+ $plugins .= $plugin['plugin'].'.'.$plugin['upd'].',';
+
+ $sql->query('INSERT INTO `copy` set `user`="'.$server['user'].'_'.$server['unit'].'", `game`="'.$server['game'].'", `server`="'.$id.'", `pack`="'.$server['pack'].'", `name`="'.$name_copy.'", `info`="'.params::$section_copy[$server['game']]['CopyFull'].'", `plugins`="'.substr($plugins, 0, -1).'", `date`="'.$start_point.'", `status`="0"');
+
+ // Очистка кеша
+ $mcache->delete('server_copy_'.$id);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/copy/recfull.php b/system/sections/servers/games/copy/recfull.php
new file mode 100644
index 0000000..76b0f26
--- /dev/null
+++ b/system/sections/servers/games/copy/recfull.php
@@ -0,0 +1,79 @@
+ 'Выбранная копия не найдена.'), $nmch);
+
+ $sql->query('SELECT `id`, `pack`, `name`, `info`, `plugins`, `date`, `status` FROM `copy` WHERE `id`="'.$cid.'" AND `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная копия не найдена.'), $nmch);
+
+ $copy = $sql->get();
+
+ if(!$copy['status'])
+ sys::outjs(array('e' => 'Дождитесь создания резервной копии.'), $nmch);
+
+ if($copy['pack'] != $server['pack'])
+ {
+ $sql->query('SELECT `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = array_merge($tarif, $sql->get());
+
+ $aPack = sys::b64djs($tarif['packs'], true);
+
+ sys::outjs(array('e' => 'Для восстановления необходимо установить сборку: '.$aPack[$copy['pack']].'.'), $nmch);
+ }
+
+ if(params::$section_copy[$server['game']]['CopyFull'] == $copy['info'])
+ $rm = 'rm -r '.$copy['info'];
+ else{
+ $rm = '';
+
+ $aInfo = explode(', ', $copy['info']);
+
+ foreach($aInfo as $name)
+ {
+ $rm .= isset(params::$section_copy[$server['game']]['aCopyDir'][$name]) ? 'rm -r '.params::$section_copy[$server['game']]['aCopyDir'][$name].' ' : '';
+ $rm .= isset(params::$section_copy[$server['game']]['aCopyFile'][$name]) ? 'rm '.params::$section_copy[$server['game']]['aCopyFile'][$name].' ' : '';
+ }
+
+ }
+
+ $ssh->set('cd '.$tarif['install'].$server['uid'].' && screen -dmS rec_'.$server['uid'].' sh -c "'
+ .$rm.'; cp /copy/'.$copy['name'].'.tar . && tar -xf '.$copy['name'].'.tar; rm '.$copy['name'].'.tar;'
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].';'
+ .'chown -R servers'.$server['uid'].':servers ."');
+
+ // Удаление плагинов
+ $sql->query('DELETE FROM `plugins_install` WHERE `server`="'.$id.'"');
+
+ // Установка плагинов (имитирование)
+ $aPlugins = explode(',', $copy['plugins']);
+
+ foreach($aPlugins as $plugin)
+ {
+ $aPlugin = explode('.', $plugin);
+
+ if(!count($aPlugin != 2))
+ continue;
+
+ if(!$aPlugin[0])
+ continue;
+
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `plugin`="'.$aPlugin[0].'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$aPlugin[1])
+ $aPlugin[1] = 0;
+
+ if(!$sql->num())
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$aPlugin[0].'", `upd`="'.$aPlugin[1].'", `time`="'.$copy['date'].'"');
+ }
+
+ // Очистка кеша
+ $mcache->delete('server_plugins_'.$id);
+
+ $sql->query('UPDATE `servers` set `status`="recovery" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/copy/recpart.php b/system/sections/servers/games/copy/recpart.php
new file mode 100644
index 0000000..da74d73
--- /dev/null
+++ b/system/sections/servers/games/copy/recpart.php
@@ -0,0 +1,53 @@
+ 'Выбранная копия не найдена.'), $nmch);
+
+ $sql->query('SELECT `id`, `pack`, `name`, `plugins`, `date`, `status` FROM `copy` WHERE `id`="'.$cid.'" AND `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная копия не найдена.'), $nmch);
+
+ $copy = $sql->get();
+
+ if(!$copy['status'])
+ sys::outjs(array('e' => 'Дождитесь создания резервной копии.'), $nmch);
+
+ if($copy['pack'] != $server['pack'])
+ {
+ $sql->query('SELECT `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = array_merge($tarif, $sql->get());
+
+ $aPack = sys::b64djs($tarif['packs'], true);
+
+ sys::outjs(array('e' => 'Для восстановления необходимо установить сборку: '.$aPack[$copy['pack']].'.'), $nmch);
+ }
+
+ $ssh->set('cd '.$tarif['install'].$server['uid'].' && screen -dmS rec_'.$server['uid'].' sh -c "'
+ .'cp /copy/'.$copy['name'].'.tar . && tar -xf '.$copy['name'].'.tar; rm '.$copy['name'].'.tar;'
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].';'
+ .'chown -R servers'.$server['uid'].':servers ."');
+
+ // Установка плагинов (имитирование)
+ $aPlugin = explode(',', $copy['plugins']);
+
+ foreach($aPlugin as $plugin)
+ {
+ if(!$plugin)
+ continue;
+
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `plugin`="'.$plugin.'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$plugin.'", `time`="'.$copy['date'].'"');
+ }
+
+ // Очистка кеша
+ $mcache->delete('server_plugins_'.$id);
+
+ $sql->query('UPDATE `servers` set `status`="recovery" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/copy/remove.php b/system/sections/servers/games/copy/remove.php
new file mode 100644
index 0000000..67eff02
--- /dev/null
+++ b/system/sections/servers/games/copy/remove.php
@@ -0,0 +1,24 @@
+ 'Выбранная копия не найдена.'), $nmch);
+
+ $sql->query('SELECT `name`, `status` FROM `copy` WHERE `id`="'.$cid.'" AND `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная копия не найдена.'), $nmch);
+
+ $copy = $sql->get();
+
+ if(!$copy['status'])
+ sys::outjs(array('e' => 'Дождитесь создания резервной копии.'), $nmch);
+
+ $ssh->set('screen -dmS rem_copy_'.$cid.' rm /copy/'.$copy['name'].'.tar');
+
+ $sql->query('DELETE FROM `copy` WHERE `id`="'.$cid.'" LIMIT 1');
+
+ // Очистка кеша
+ $mcache->delete('server_copy_'.$id);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/owners.php b/system/sections/servers/games/owners.php
new file mode 100644
index 0000000..ed9e83d
--- /dev/null
+++ b/system/sections/servers/games/owners.php
@@ -0,0 +1,153 @@
+query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['rights']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Совладелец не найден.'));
+
+ $owner = $sql->get();
+
+ $aRights = sys::b64djs($owner['rights']);
+
+ $rights = '';
+
+ foreach($aOwnersI as $access => $info)
+ if($aRights[$access]) $rights .= $info.', ';
+
+ sys::outjs(array('s' => substr($rights, 0, -2)));
+ }
+
+ // Удаление совладельца
+ if(isset($url['delete']) && $url['delete'])
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if($sql->num())
+ $sql->query('DELETE FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/owners');
+ }
+
+ // Добавление совладельца
+ if($go)
+ {
+ $nmch = sys::rep_act('server_owners_go_'.$id, 5);
+
+ $aData = (isset($_POST['owner']) && is_array($_POST['owner'])) ? $_POST['owner'] : array();
+
+ $aDate = isset($aData['\'time\'']) ? explode('.', $aData['\'time\'']) : explode('.', date('d.m.Y', $start_point));
+ $aTime = explode(':', date('H:i:s', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ sys::outjs(array('e' => 'Дата доступа указана неверно.'), $nmch);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2])+3600;
+
+ if($time < $start_point)
+ sys::outjs(array('e' => 'Время доступа не может быть меньше 60 минут.'), $nmch);
+
+ // Проверка пользователя
+ if(!isset($aData['\'user\'']))
+ sys::outjs(array('e' => 'Необходимо указать пользователя.'), $nmch);
+
+ if(is_numeric($aData['\'user\'']))
+ $sql->query('SELECT `id` FROM `users` WHERE `id`="'.$aData['\'user\''].'" LIMIT 1');
+ else{
+ if(sys::valid($aData['\'user\''], 'other', $aValid['login']))
+ sys::outjs(array('e' => sys::text('input', 'login_valid')), $nmch);
+
+ $sql->query('SELECT `id` FROM `users` WHERE `login`="'.$aData['\'user\''].'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Пользователь не найден в базе.'), $nmch);
+
+ $uowner = $sql->get();
+
+ if($server['user'] == $uowner['id'])
+ sys::outjs(array('e' => 'Владельца сервера нельзя добавить в совладельцы.'), $nmch);
+
+ $owner = $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+
+ $upd = $sql->num($owner);
+
+ // Если не обновление доступа совладельца, проверить кол-во
+ if(!$upd)
+ {
+ $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" AND `time`>"'.$start_point.'" LIMIT 5');
+
+ if($sql->num() == 5)
+ sys::outjs(array('e' => 'Вы добавили максимально кол-во совладельцев.'), $nmch);
+ }
+
+ $aRights = array();
+
+ $check = 0;
+
+ foreach($aOwners[$server['game']] as $access)
+ {
+ $aRights[$access] = isset($aData['\''.$access.'\'']) ? 1 : 0;
+
+ $check += $aRights[$access];
+ }
+
+ if(!$check)
+ sys::outjs(array('e' => 'Необходимо включить минимум одно разрешение.'), $nmch);
+
+ if($upd)
+ $sql->query('UPDATE `owners` set `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'" WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ else
+ $sql->query('INSERT INTO `owners` set `server`="'.$id.'", `user`="'.$uowner['id'].'", `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'"');
+
+ $sql->query('DELETE FROM `owners` WHERE `server`="'.$id.'" AND `time`<"'.$start_point.'" LIMIT 5');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Друзья');
+
+ $owners = $sql->query('SELECT `id`, `user`, `rights`, `time` FROM `owners` WHERE `server`="'.$id.'" AND `time`>"'.$start_point.'" ORDER BY `id` ASC LIMIT 5');
+
+ if($sql->num())
+ include(LIB.'games/games.php');
+
+ while($owner = $sql->get($owners))
+ {
+ $sql->query('SELECT `login` FROM `users` WHERE `id`="'.$owner['user'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $uowner = $sql->get();
+
+ $rights = games::owners(sys::b64djs($owner['rights']));
+
+ $html->get('owners', 'sections/servers/games/owners');
+ $html->set('id', $id);
+ $html->set('oid', $owner['id']);
+ $html->set('user', $uowner['login']);
+ $html->set('rights', $rights);
+ $html->set('time', date('d.m.Y - H:i', $owner['time']));
+ $html->pack('owners');
+ }
+
+ foreach($aOwnersI as $access => $info)
+ {
+ $html->get('access', 'sections/servers/games/owners');
+ $html->set('access', $access);
+ $html->set('info', $info);
+ $html->pack('access');
+ }
+
+ $html->get('index', 'sections/servers/games/owners');
+ $html->set('id', $id);
+ $html->set('time', date('d.m.Y', $start_point));
+ $html->set('access', $html->arr['access']);
+ $html->set('owners', isset($html->arr['owners']) ? $html->arr['owners'] : 'Для данного сервера совладельцы отсутсвуют.');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/plugins/config.php b/system/sections/servers/games/plugins/config.php
new file mode 100644
index 0000000..cc8971e
--- /dev/null
+++ b/system/sections/servers/games/plugins/config.php
@@ -0,0 +1,83 @@
+query('SELECT `plugin`, `update`, `file` FROM `plugins_config` WHERE `id`="'.$fid.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $config = $sql->get();
+
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$config['plugin'].'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ // Если обновленный плагин
+ if($config['update'])
+ $sql->query('SELECT `name` FROM `plugins_update` WHERE `id`="'.$config['update'].'" LIMIT 1');
+ else
+ $sql->query('SELECT `name` FROM `plugins` WHERE `id`="'.$config['plugin'].'" LIMIT 1');
+
+ $plugin = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+ }
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Данные файла
+ $file = explode('/', $config['file']);
+
+ // Полный путь файла
+ $path = $tarif['install'].$server['uid'].'/'.$config['file'];
+
+ // Сохранение
+ if($go)
+ {
+ $data = isset($_POST['data']) ? $_POST['data'] : '';
+
+ $temp = sys::temp($data);
+
+ // Отправление файла на сервер
+ $ssh->setfile($temp, $path, 0644);
+
+ // Смена владельца/группы файла
+ $ssh->set('chown server'.$server['uid'].':servers '.$path);
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$path.'; cat '.$path.'"');
+
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+ $html->nav($plugin['name'], $cfg['http'].'servers/id/'.$id.'/section/plugins/subsection/plugin/plugin/'.$config['plugin']);
+
+ $html->get('config', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('file', $fid);
+ $html->set('plugin', $config['plugin']);
+ $html->set('name', end($file));
+ $html->set('data', htmlspecialchars($ssh->get()));
+
+ $html->pack('main');
+
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/plugins/delete.php b/system/sections/servers/games/plugins/delete.php
new file mode 100644
index 0000000..d97111b
--- /dev/null
+++ b/system/sections/servers/games/plugins/delete.php
@@ -0,0 +1,87 @@
+query('SELECT `id`, `upd` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$pid.'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Данный плагин не установлен'));
+
+ $plugin = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Директория игр. сервера
+ $dir = $tarif['install'].$server['uid'].'/';
+
+ // Имя исполняемого файла
+ if($plugin['upd'])
+ {
+ $qsql = 'WHERE `update`="'.$plugin['upd'].'" ORDER BY `id` ASC';
+ $frm = 'u'.$plugin['upd'];
+ }else{
+ $qsql = 'WHERE `plugin`="'.$pid.'" AND `update`="0" ORDER BY `id` ASC';
+ $frm = $pid;
+ }
+
+ // Удаление и установка файлов
+ $ssh->set('cd '.$dir.' && screen -dmS delete_upd_'.$start_point.' '
+ .'sudo -u server'.$server['uid'].' sh -c "'
+ .'wget --no-check-certificate '.$cfg['plugins'].'delete/'.$frm.'.rm && '
+ .'chmod 755 '.$frm.'.rm; ./'.$frm.'.rm; rm '.$frm.'.rm"');
+
+ include(LIB.'games/plugins.php');
+
+ // Удаление добавленного при установке текста в файлах
+ $sql->query('SELECT `text`, `file` FROM `plugins_write` '.$qsql);
+ while($clear = $sql->get())
+ plugins::clear($clear, $server['uid'], $dir);
+
+ unset($clear);
+
+ // Добавление текста при удалении в файлы
+ $sql->query('SELECT `text`, `file`, `top` FROM `plugins_write_del` '.$qsql);
+ while($write = $sql->get())
+ plugins::write($write, $server['uid'], $dir);
+
+ // Удаление записи установленного плагина в базе
+ $sql->query('DELETE FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$pid.'"');
+
+ // Очистка кеша
+ $mcache->delete('server_plugins_'.$id);
+
+ if($plugin['upd'])
+ $sql->query('SELECT `install` FROM `plugins_delete_ins` WHERE `update`="'.$plugin['upd'].'" LIMIT 1');
+ else
+ $sql->query('SELECT `install` FROM `plugins_delete_ins` WHERE `plugin`="'.$pid.'" AND `update`="0" LIMIT 1');
+
+ if($sql->num())
+ {
+ $ins = $sql->get();
+
+ $sql->query('SELECT `name` FROM `plugins` WHERE `id`="'.$ins['install'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ sys::outjs(array('i' => $ins['install'], 'pname' => $plugin['name']), $nmch);
+ }
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/plugins/install.php b/system/sections/servers/games/plugins/install.php
new file mode 100644
index 0000000..e52dbb1
--- /dev/null
+++ b/system/sections/servers/games/plugins/install.php
@@ -0,0 +1,136 @@
+query('SELECT `name`, `cfg`, `upd`, `incompatible`, `choice`, `required`, `packs`, `price` FROM `plugins` WHERE `id`="'.$pid.'" AND `game`="'.$server['game'].'" LIMIT 1');
+
+ if(!$sql->num())
+ exit;
+
+ $plugin = $sql->get();
+
+ // Проверка установки плагина
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$pid.'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный плагин уже установлен'));
+
+ $upd = false;
+
+ // Если есть более поздняя версия плагина
+ if($plugin['upd'])
+ {
+ $sql->query('SELECT `name`, `id`, `cfg`, `incompatible`, `choice`, `required`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$pid.'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = array_merge($plugin, $sql->get());
+
+ $upd = true;
+ }
+ }
+
+ $buy = false;
+
+ // Если платный плагин
+ if($plugin['price'])
+ {
+ // Проверка покупки
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$pid.'" AND `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ $buy = true;
+ else{
+ // Проверка баланса
+ if($user['balance'] < $plugin['price'])
+ sys::outjs(array('e' => 'У вас не хватает '.(round($plugin['price']-$user['balance'], 2)).' '.$cfg['currency']), $nmch);
+ }
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ exit;
+
+ include(LIB.'games/plugins.php');
+
+ // Проверка на наличие несовместимости с уже установленными плагинами
+ plugins::incompatible($id, $plugin['incompatible'], $nmch);
+
+ // Проверка на наличие необходимых установленых плагинов для устанавливаемого дополнения
+ plugins::required($id, $plugin['required'], $plugin['choice'], $nmch);
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ if($upd)
+ {
+ $qsql = 'WHERE `update`="'.$plugin['id'].'" ORDER BY `id` ASC';
+ $fzip = 'u'.$plugin['id'];
+ $upd = $plugin['id'];
+ }else{
+ $qsql = 'WHERE `plugin`="'.$pid.'" AND `update`="0" ORDER BY `id` ASC';
+ $fzip = $pid;
+ $upd = 0;
+ }
+
+ // Директория игр. сервера
+ $dir = $tarif['install'].$server['uid'].'/';
+
+ // Установка файлов на сервер
+ $ssh->set('cd '.$dir.' && screen -dmS install_'.$start_point.' sudo -u server'.$server['uid'].' sh -c "'
+ .'wget --no-check-certificate '.$cfg['plugins'].'install/'.$fzip.'.zip && unzip -o '.$fzip.'.zip; rm '.$fzip.'.zip;'
+ .'find . -type d -exec chmod 700 {} \;;'
+ .'find . -type f -exec chmod 600 {} \;;'
+ .'chmod 500 '.params::$aFileGame[$server['game']].'"');
+
+ // Удаление файлов
+ $sql->query('SELECT `file` FROM `plugins_delete` '.$qsql);
+ while($delete = $sql->get())
+ $ssh->set('sudo -u server'.$server['uid'].' rm '.$dir.$delete['file']);
+
+ // Удаление текста из файлов
+ $sql->query('SELECT `text`, `file`, `regex` FROM `plugins_clear` '.$qsql);
+ while($clear = $sql->get())
+ plugins::clear($clear, $server['uid'], $dir);
+
+ // Добавление текста в файлы
+ $sql->query('SELECT `text`, `file`, `top` FROM `plugins_write` '.$qsql);
+ while($write = $sql->get())
+ plugins::write($write, $server['uid'], $dir);
+
+ // Если платный плагин
+ if(!$buy AND $plugin['price'])
+ {
+ $sql->query('UPDATE `users` set `balance`=`balance`-"'.$plugin['price'].'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ $sql->query('INSERT INTO `plugins_buy` set `plugin`="'.$pid.'", `key`="'.md5(strip_tags($plugin['name'])).'", `server`="'.$id.'", `price`="'.$plugin['price'].'", `time`="'.$start_point.'"');
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_plugin'),
+ array('plugin' => strip_tags($plugin['name']), 'money' => $plugin['price'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$plugin['price'].'"');
+ }
+
+ // Запись данных в базу
+ $sql->query('INSERT INTO `plugins_install` set `server`="'.$id.'", `plugin`="'.$pid.'", `upd`="'.$upd.'", `time`="'.$start_point.'"');
+
+ // Очистка кеша
+ $mcache->delete('server_plugins_'.$id);
+
+ if($plugin['cfg'])
+ sys::outjs(array('s' => 'cfg'), $nmch);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/plugins/plugin.php b/system/sections/servers/games/plugins/plugin.php
new file mode 100644
index 0000000..1655e23
--- /dev/null
+++ b/system/sections/servers/games/plugins/plugin.php
@@ -0,0 +1,87 @@
+query('SELECT `id`, `upd` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$pid.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $install = $sql->get();
+
+ // Если установленно обновление
+ if($install['upd'])
+ $sql->query('SELECT `name`, `info`, `images`, `upd` FROM `plugins_update` WHERE `id`="'.$install['upd'].'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `info`, `images`, `upd` FROM `plugins` WHERE `id`="'.$pid.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $plugin = $sql->get();
+
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+ $html->nav($plugin['name']);
+
+ // Если есть кеш
+ if($mcache->get('server_plugin_'.$pid.$id) != '')
+ $html->arr['main'] = $mcache->get('server_plugin_'.$pid.$id);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Построение списка редактируемых файлов
+ $aConf = array();
+
+ $sql->query('SELECT `id`, `file` FROM `plugins_config` WHERE (`plugin`="'.$pid.'" AND `update`="0") OR (`plugin`="'.$pid.'" AND `update`="'.$install['upd'].'") ORDER BY `sort`, `id` ASC');
+ while($config = $sql->get())
+ {
+ // Исключить дублирование, путем проверки массива файлов
+ if(in_array($config['file'], $aConf))
+ continue;
+
+ $aConf[] = $config['file'];
+
+ // Данные файла
+ $file = explode('/', $config['file']);
+
+ $html->get('config_list', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('fid', $config['id']);
+ $html->set('name', end($file));
+ $html->set('file', $config['file']);
+
+ $html->pack('configs');
+ }
+
+ $images = plugins::images($plugin['images'], $pid);
+
+ $html->get('configs', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('name', $plugin['name']);
+ $html->set('info', htmlspecialchars_decode($plugin['info']));
+
+ // Картинки
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ // Редактируемые файлы
+ if(isset($html->arr['configs']))
+ {
+ $html->set('configs', $html->arr['configs']);
+ $html->unit('configs', 1);
+ }else
+ $html->unit('configs');
+
+ $html->pack('main');
+
+ $mcache->set('server_plugin_'.$pid.$id, $html->arr['main'], false, 60);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/plugins/search.php b/system/sections/servers/games/plugins/search.php
new file mode 100644
index 0000000..3572d2b
--- /dev/null
+++ b/system/sections/servers/games/plugins/search.php
@@ -0,0 +1,200 @@
+ ''));
+
+ $mkey = md5($text.$id);
+
+ if($mcache->get($mkey) != '')
+ sys::outjs(array('s' => $mcache->get($mkey)));
+
+ if(!isset($text{2}))
+ sys::outjs(array('s' => 'Для выполнения поиска, необходимо больше данных', $nmch));
+
+ $sPlugins = array();
+ $sUpdate = array();
+
+ // Поиск по плагинам
+ $plugins = $sql->query('SELECT `id`, `packs` FROM `plugins` WHERE `game`="'.$server['game'].'" AND `name` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') OR `desc` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') LIMIT 5');
+
+ // Поиск по обновлениям
+ $update = false;
+
+ if(!$sql->num($plugins))
+ {
+ $plugins = $sql->query('SELECT `id`, `plugin`, `packs` FROM `plugins_update` WHERE `game`="'.$server['game'].'" AND (`name` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') OR `desc` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\')) AND `upd`="0" LIMIT 5');
+ $update = true;
+ }
+
+ // Если нет ниодного совпадения по вводимому тексту
+ if(!$sql->num($plugins))
+ {
+ // Поиск по словам
+ if(strpos($text, ' '))
+ {
+ // Массив слов
+ $aText = explode(' ', $text);
+
+ // Метка, которая изменится в процессе, если будет найдено хоть одно совпадение
+ $sWord = false;
+
+ foreach($aText as $word)
+ {
+ if($word == '' || !isset($word{2}))
+ continue;
+
+ // Поиск по плагинам
+ $plugins = $sql->query('SELECT `id`, `packs` FROM `plugins` WHERE `name` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') OR `desc` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') LIMIT 5');
+
+ // Поиск по обновлениям
+ $update = false;
+
+ if(!$sql->num($plugins))
+ {
+ $plugins = $sql->query('SELECT `id`, `plugin`, `packs` FROM `plugins_update` WHERE (`name` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') OR `desc` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\')) AND `upd`="0" LIMIT 5');
+ $update = true;
+ }
+
+ if($sql->num($plugins))
+ {
+ if(!$sWord) $sWord = true;
+
+ $sPlugins[] = $plugins;
+ $sUpdate[] = $update;
+ }
+ }
+
+ // Если нет ниодного совпадения
+ if(!$sWord)
+ {
+ $mcache->set($mkey, 'По вашему запросу ничего не найдено', false, 15);
+
+ sys::outjs(array('s' => 'По вашему запросу ничего не найдено'));
+ }
+ }else{
+ $mcache->set($mkey, 'По вашему запросу ничего не найдено', false, 15);
+
+ sys::outjs(array('s' => 'По вашему запросу ничего не найдено'));
+ }
+ }else{
+ $sPlugins[] = $plugins;
+ $sUpdate[] = $update;
+ }
+
+ // Массив для исклуючения дублирования
+ $aPlugins = array();
+
+ foreach($sPlugins as $index => $plugins)
+ {
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка дублирования
+ if(($sUpdate[$index] AND in_array($plugin['plugin'], $aPlugins)) || !$sUpdate[$index] AND in_array($plugin['id'], $aPlugins))
+ continue;
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':', $plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $install = false; // не установлен плагин
+ $upd = false; // не обновлен плагин
+
+ if($sUpdate[$index])
+ {
+ $sql->query('SELECT `id`, `upd`, `time` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $aPlugins[] = $plugin['plugin'];
+ }else{
+ $sql->query('SELECT `id`, `upd`, `time` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+
+ $aPlugins[] = $plugin['id'];
+ }
+
+ // Проверка на установку
+ if($sql->num())
+ {
+ $install = $sql->get();
+
+ $upd = $install['upd'];
+ $time = sys::today($install['time']);
+
+ $install = true;
+ }
+
+ // Если установлен обновленный плагин
+ if($upd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$upd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['id'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ $html->get('search', 'sections/servers/games/plugins');
+
+ // Если установлен
+ if($install)
+ {
+ // Если есть обновление
+ if($plugin['upd'] > $upd) $html->unit('update', 1); else $html->unit('update');
+
+ // Если есть редактируемые файлы
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ $html->unit('install', 1);
+ $html->unit('!install');
+ }else{
+ // Обновление данных на более позднею версию плагина
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" AND `upd`="0" LIMIT 1');
+ if($sql->num())
+ {
+ $upd = $sql->get();
+
+ $plugin['name'] = $upd['name'];
+ $plugin['desc'] = $upd['desc'];
+ $plugin['status'] = $upd['status'];
+ $plugin['cfg'] = $upd['cfg'];
+ }
+
+ $html->unit('install');
+ $html->unit('!install', 1);
+ }
+
+ if(!$plugin['status'])
+ {
+ $html->unit('unstable');
+ $html->unit('stable', 1);
+ $html->unit('testing');
+ }elseif($plugin['status'] == 2){
+ $html->unit('unstable');
+ $html->unit('stable');
+ $html->unit('testing', 1);
+ }else{
+ $html->unit('unstable', 1);
+ $html->unit('stable');
+ $html->unit('testing');
+ }
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['id']);
+
+ if($install)
+ $html->set('time', $time);
+
+ $html->set('name', sys::find(htmlspecialchars_decode($plugin['name']), $text));
+ $html->set('desc', sys::find(htmlspecialchars_decode($plugin['desc']), $text));
+
+ $html->pack('plugins');
+ }
+ }
+
+ $html->arr['plugins'] = isset($html->arr['plugins']) ? $html->arr['plugins'] : '';
+
+ $mcache->set($mkey, $html->arr['plugins'], false, 15);
+
+ sys::outjs(array('s' => $html->arr['plugins']), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/plugins/update.php b/system/sections/servers/games/plugins/update.php
new file mode 100644
index 0000000..ddecd7c
--- /dev/null
+++ b/system/sections/servers/games/plugins/update.php
@@ -0,0 +1,88 @@
+query('SELECT `id` FROM `plugins_update` WHERE `plugin`="'.$pid.'" ORDER BY `id` DESC LIMIT 1');
+
+ if(!$sql->num())
+ exit();
+
+ $plugin = $sql->get();
+
+ // Проверка установки плагина
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$pid.'" LIMIT 1');
+ if(!$sql->num())
+ exit();
+
+ // Проверка установки обновления плагина
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$pid.'" AND `upd`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный плагин уже обновлен'));
+
+ // Данные обновления
+ $sql->query('SELECT `id`, `cfg`, `incompatible`, `required` FROM `plugins_update` WHERE `id`="'.$plugin['id'].'" LIMIT 1');
+
+ $plugin = $sql->get();
+
+ include(LIB.'games/plugins.php');
+
+ // Проверка на наличие несовместимости с уже установленными плагинами
+ plugins::incompatible($id, $plugin['incompatible'], $nmch);
+
+ // Проверка на наличие необходимых установленых плагинов для устанавливаемого плагина
+ plugins::required($id, $plugin['required'], $nmch);
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ if(!isset($ssh))
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Директория игр. сервера
+ $dir = $tarif['install'].$server['uid'].'/';
+
+ // Установка файлов на сервер
+ $ssh->set('cd '.$dir.' && screen -dmS update_'.$start_point.' sudo -u server'.$server['uid'].' sh -c "wget --no-check-certificate '.$cfg['plugins'].'update/'.$plugin['id'].'.zip && unzip -o '.$plugin['id'].'.zip; rm '.$plugin['id'].'.zip"');
+
+ // Удаление файлов
+ $sql->query('SELECT `file` FROM `plugins_delete` WHERE `update`="'.$plugin['id'].'"');
+ while($delete = $sql->get())
+ $ssh->set('sudo -u server'.$server['uid'].' rm '.$dir.$delete['file']);
+
+ unset($delete);
+
+ // Удаление текста из файлов
+ $sql->query('SELECT `text`, `file`, `regex` FROM `plugins_clear` WHERE `update`="'.$plugin['id'].'"');
+ while($clear = $sql->get())
+ plugins::clear($clear, $server['uid'], $dir);
+
+ unset($clear);
+
+ // Добавление текста в файлы
+ $sql->query('SELECT `text`, `file`, `top` FROM `plugins_write` WHERE `update`="'.$plugin['id'].'" ORDER BY `id` ASC');
+ while($write = $sql->get())
+ plugins::write($write, $server['uid'], $dir);
+
+ // Обновление данных в базе
+ $sql->query('UPDATE `plugins_install` set `upd`="'.$plugin['id'].'", `time`="'.$start_point.'" WHERE `server`="'.$id.'" AND `plugin`="'.$pid.'" LIMIT 1');
+
+ // Очистка кеша
+ $mcache->delete('server_plugins_'.$id);
+
+ if($plugin['cfg'])
+ sys::outjs(array('s' => 'cfg'), $nmch);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/settings/api.php b/system/sections/servers/games/settings/api.php
new file mode 100644
index 0000000..fa951bd
--- /dev/null
+++ b/system/sections/servers/games/settings/api.php
@@ -0,0 +1,14 @@
+query('SELECT `key` FROM `api` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ $sql->query('DELETE FROM `api` WHERE `server`="'.$id.'" LIMIT 1');
+ else
+ $sql->query('INSERT INTO `api` set `server`="'.$id.'", `key`="'.md5(sys::passwd(10)).'"');
+
+ $mcache->delete('server_settings_'.$id);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/settings/crontab.php b/system/sections/servers/games/settings/crontab.php
new file mode 100644
index 0000000..928c2cf
--- /dev/null
+++ b/system/sections/servers/games/settings/crontab.php
@@ -0,0 +1,117 @@
+nav('Планировщик задач');
+
+ $sql->query('SELECT `autostop` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `panel` LIMIT 1');
+ $panel = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($panel['passwd'], $panel['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ // Удаление задания
+ if(isset($url['action']) AND $url['action'] == 'delete')
+ {
+ $task = isset($_POST['task']) ? sys::int($_POST['task']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ $sql->query('SELECT `cron` FROM `crontab` WHERE `id`="'.$task.'" AND `server`="'.$id.'" LIMIT 1');
+ if(!$sql->num())
+ $sys->outjs(array('s' => 'ok'), $nmch);
+
+ $cron = $sql->get();
+
+ $ssh->set('touch /etc/crontab; cat /etc/crontab');
+ $crontab = str_replace($cron['cron'], '', $ssh->get());
+
+ // Временный файл
+ $temp = sys::temp($crontab);
+
+ $ssh->setfile($temp, '/etc/crontab', 0644);
+
+ $ssh->set("sed -i '/^$/d' /etc/crontab;"
+ .'crontab -u root /etc/crontab');
+
+ unlink($temp);
+
+ $sql->query('DELETE FROM `crontab` WHERE `id`="'.$task.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Добавление задания
+ $sql->query('SELECT `id` FROM `crontab` WHERE `server`="'.$id.'" LIMIT 5');
+ if($sql->num() == $cfg['crontabs'])
+ sys::outjs(array('e' => sys::text('servers', 'crontab')), $nmch);
+
+ $data = array();
+
+ $data['task'] = isset($_POST['task']) ? $_POST['task'] : 'start';
+
+ $task = in_array($server['game'], array('samp', 'crmp')) ? array('start', 'restart', 'stop') : array('start', 'restart', 'stop', 'console');
+
+ if(!in_array($data['task'], $task))
+ $data['task'] = 'start';
+
+ $data['commands'] = isset($_POST['commands']) ? base64_encode(htmlspecialchars($_POST['commands'])) : '';
+ $data['allhour'] = isset($_POST['allhour']) ? true : false;
+ $data['hour'] = isset($_POST['hour']) ? $_POST['hour'] : '00';
+ $data['minute'] = isset($_POST['minute']) ? $_POST['minute'] : '00';
+ $data['week'] = (isset($_POST['week']) AND is_array($_POST['week'])) ? $_POST['week'] : array();
+
+ $sql->query('INSERT INTO `crontab` set `server`="'.$id.'"');
+ $cid = $sql->id();
+
+ include(LIB.'games/games.php');
+
+ $cron_rule = games::crontab($data, $id, $cid);
+
+ $ssh->set('echo "'.$cron_rule.'" >> /etc/crontab;'
+ ."sed -i '/^$/d' /etc/crontab;"
+ .'crontab -u root /etc/crontab');
+
+ $time = games::crontab_time($data['allhour'], $data['hour'], $data['minute']);
+ $week = games::crontab_week($data['week']);
+
+ $sql->query('UPDATE `crontab` set `server`="'.$id.'", `task`="'.$data['task'].'", `cron`="'.$cron_rule.'", `week`="'.$week.'", `time`="'.$time.'", `commands`="'.$data['commands'].'" WHERE `id`="'.$cid.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $aTask = array(
+ 'start' => 'Включение сервера',
+ 'stop' => 'Выключение сервера',
+ 'restart' => 'Перезагрузка сервера',
+ 'console' => 'Отправка команд на сервер'
+ );
+
+ $sql->query('SELECT `id`, `task`, `week`, `time` FROM `crontab` WHERE `server`="'.$id.'" ORDER BY `id` ASC');
+ while($crontab = $sql->get())
+ {
+ $html->get('crontab_list', 'sections/servers/games/settings');
+ $html->set('id', $crontab['id']);
+ $html->set('task', $aTask[$crontab['task']]);
+ $html->set('week', $crontab['week']);
+ $html->set('time', $crontab['time']);
+ $html->pack('crontab');
+ }
+
+ $html->get('crontab', 'sections/servers/'.$server['game'].'/settings');
+ $html->set('id', $id);
+ $html->set('time', date('H:i:s', $start_point));
+
+ if($server['autostop'])
+ $html->unit('!autostop');
+ else
+ $html->unit('!autostop', 1);
+
+ $html->set('crontab', isset($html->arr['crontab']) ? $html->arr['crontab'] : '');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/settings/file.php b/system/sections/servers/games/settings/file.php
new file mode 100644
index 0000000..db980a1
--- /dev/null
+++ b/system/sections/servers/games/settings/file.php
@@ -0,0 +1,60 @@
+nav('Редактирование файла: '.$file);
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+ }
+
+ // Полный путь файла
+ $path = $tarif['install'].$server['uid'].'/'.$aEdits[$server['game']]['all']['path'][$file].$file;
+ if($go)
+ {
+ $data = isset($_POST['data']) ? $_POST['data'] : '';
+
+ $temp = sys::temp($data);
+
+ // Отправление файла на сервер
+ $ssh->setfile($temp, $path, 0644);
+
+ // Смена владельца/группы файла
+ $ssh->set('chown server'.$server['uid'].':servers '.$path);
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "touch '.$path.'; cat '.$path.'"');
+
+ $html->get('file', 'sections/servers/games/settings');
+
+ $html->set('id', $id);
+ $html->set('file', $file);
+ $html->set('data', htmlspecialchars($ssh->get()));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/settings/firewall.php b/system/sections/servers/games/settings/firewall.php
new file mode 100644
index 0000000..ae2c194
--- /dev/null
+++ b/system/sections/servers/games/settings/firewall.php
@@ -0,0 +1,39 @@
+nav('Блокировка на оборудовании');
+
+ if(isset($url['action']))
+ {
+ include(LIB.'games/games.php');
+
+ // Получение информации адреса
+ if($url['action'] == 'info')
+ games::iptables_whois($nmch);
+
+ // Добавление / удаление правил
+ if($go && in_array($url['action'], array('block', 'unblock')))
+ {
+ $address = isset($_POST['address']) ? trim($_POST['address']) : sys::outjs(array('e' => sys::text('servers', 'firewall')), $nmch);
+ $snw = isset($_POST['subnetwork']) ? true : false;
+
+ sys::outjs(games::iptables($id, $url['action'], $address, explode(':', $server['address']), $server['unit'], $snw), $nmch);
+ }
+ }
+
+ $sql->query('SELECT `id`, `sip` FROM `firewall` WHERE `server`="'.$id.'" ORDER BY `id` ASC');
+
+ while($firewall = $sql->get())
+ {
+ $html->get('list', 'sections/servers/games/settings/firewall');
+ $html->set('id', $firewall['id']);
+ $html->set('address', $firewall['sip']);
+ $html->pack('firewall');
+ }
+
+ $html->get('firewall', 'sections/servers/games/settings');
+ $html->set('id', $id);
+ $html->set('firewall', isset($html->arr['firewall']) ? $html->arr['firewall'] : '');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/settings/pack.php b/system/sections/servers/games/settings/pack.php
new file mode 100644
index 0000000..93955f9
--- /dev/null
+++ b/system/sections/servers/games/settings/pack.php
@@ -0,0 +1,22 @@
+query('SELECT `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aPacks = sys::b64djs($tarif['packs'], true);
+
+ $pack = isset($url['pack']) ? $url['pack'] : exit;
+
+ if($pack == $server['pack'])
+ sys::outjs(array('s' => 'ok'));
+
+ // Проверка сборки
+ if(!array_key_exists($pack, $aPacks))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ $sql->query('UPDATE `servers` set `pack`="'.$pack.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), 'server_settings_'.$id);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/settings/startlogs.php b/system/sections/servers/games/settings/startlogs.php
new file mode 100644
index 0000000..b23c6ce
--- /dev/null
+++ b/system/sections/servers/games/settings/startlogs.php
@@ -0,0 +1,98 @@
+nav('Снимки консоли');
+
+ $sql->query('SELECT `ftp`, `ftp_root`, `ftp_passwd` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ // Путь к логам
+ $folder = $tarif['install'].$server['uid'].'/'.$aSLdir[$server['game']];
+
+ // Если выбран лог
+ if(isset($url['log']))
+ {
+ if(sys::valid($url['log'], 'other', $aValid['startlogs']))
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/startlogs');
+
+ $ssh->set('sudo -u server'.$server['uid'].' cat '.$folder.'/'.$url['log']);
+
+ $html->get('view', 'sections/servers/games/settings/logs');
+ $html->set('id', $id);
+ $html->set('name', $url['log']);
+ $html->set('log', htmlspecialchars($ssh->get(), NULL, ''));
+ $html->set('uri', 'startlogs');
+ $html->pack('main');
+ }else{
+ if(isset($url['delall']))
+ {
+ $ssh->set('cd '.$folder.' && rm *.log');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings/subsection/startlogs');
+ }
+
+ $ssh->set('cd '.$folder.' && du -ab --time | grep -e .log$ | awk \'{print $2" "$3"@"$1"@"$4}\' | sort -Mr');
+
+ // Массив данных
+ $aData = explode("\n", $ssh->get());
+
+ if(isset($aData[count($aData)-1]))
+ unset($aData[count($aData)-1]);
+
+ $olds = $aSLdirFtp[$server['game']];
+
+ if($server['ftp_root'] || $cfg['ftp']['root'][$server['game']])
+ $olds = $aSLdir[$server['game']];
+
+ // Построение списка
+ foreach($aData as $line => $log)
+ {
+ $aLog = explode('@', $log);
+
+ // Название
+ $name = explode('/', $aLog[2]);
+
+ if(count($name) > 2)
+ continue;
+
+ // Дата
+ $date = sys::unidate($aLog[0]);
+
+ // Вес
+ $size = sys::size($aLog[1]);
+
+ $html->get('list', 'sections/servers/games/settings/startlogs');
+ $html->set('id', $id);
+ $html->set('name', end($name));
+ $html->set('date', $date);
+ $html->set('size', $size);
+
+ if($server['ftp'])
+ {
+ $html->unit('download', true, true);
+
+ $html->set('url', 'ftp://'.$server['uid'].':'.$server['ftp_passwd'].'@'.sys::first(explode(':', $unit['address'])).'/'.$olds.'/'.end($name));
+ }else
+ $html->unit('download', false, true);
+ $html->pack('logs');
+ }
+
+ $html->get('startlogs', 'sections/servers/games/settings');
+ $html->set('id', $id);
+ $html->set('uri', 'start');
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : '');
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/tarif.php b/system/sections/servers/games/tarif.php
new file mode 100644
index 0000000..3c5de6e
--- /dev/null
+++ b/system/sections/servers/games/tarif.php
@@ -0,0 +1,61 @@
+nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Тариф');
+
+ // Общий шаблон раздела
+ $html->get('tarif', 'sections/servers/games');
+
+ $html->set('id', $id);
+
+ $html->pack('main');
+
+ // Шаблон продления
+ if($cfg['settlement_period'])
+ tarif::extend_sp($server, $tarif, $id);
+ else{
+ $options = games::parse_time(explode(':', $tarif['timext']), $tarif['discount'], $server['tarif'], 'extend');
+
+ tarif::extend($options, $server, $tarif['name'], $id);
+ }
+
+ // Если не тестовый период
+ if(!$server['test'])
+ {
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Шаблон смены тарифа (если аренда не менее 1 дня и цены планов различны)
+ if($server['time'] > $start_point+86400 AND tarif::price($tarif['price']))
+ tarif::plan($server, $tarif['name'], $id);
+
+ // Шаблон изменения кол-ва слот
+ if($tarif['slots_min'] != $tarif['slots_max'])
+ tarif::slots($server, array('min' => $tarif['slots_min'], 'max' => $tarif['slots_max']), $id);
+
+ // Шаблон изменения локации (если аренда не менее 1 дня)
+ if($server['time'] > $start_point+86400)
+ tarif::unit($server, $unit['name'], $tarif['name'], $id);
+
+ // Шаблон покупки/аренды выделенного адреса
+ if($server['port'] != 27015)
+ tarif::address($server, $id);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/tarif/addextend.php b/system/sections/servers/games/tarif/addextend.php
new file mode 100644
index 0000000..5daa0e4
--- /dev/null
+++ b/system/sections/servers/games/tarif/addextend.php
@@ -0,0 +1,39 @@
+
+query('SELECT `id`, `aid`, `time` FROM `address_buy` WHERE `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ $add = $sql->get();
+
+ $sql->query('SELECT `price` FROM `address` WHERE `id`="'.$add['aid'].'" LIMIT 1');
+
+ $add = array_merge($add, $sql->get());
+
+ // Проверка баланса
+ if($user['balance'] < $add['price'])
+ sys::outjs(array('e' => 'У вас не хватает '.(round($add['price']-$user['balance'], 2)).' '.$cfg['currency']), $nmch);
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$add['price']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Реф. система
+ games::part($user['id'], $add['price']);
+
+ // Обновление информации
+ $sql->query('UPDATE `address_buy` set `time`="'.($add['time']+2592000).'" WHERE `id`="'.$add['id'].'" LIMIT 1');
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'extend_address'),
+ array('money' => $add['price'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="extend", `money`="'.$add['price'].'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/tarif/address.php b/system/sections/servers/games/tarif/address.php
new file mode 100644
index 0000000..0b4eeea
--- /dev/null
+++ b/system/sections/servers/games/tarif/address.php
@@ -0,0 +1,64 @@
+query('SELECT `id` FROM `address_buy` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num() AND $go)
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ $aid = isset($url['aid']) ? sys::int($url['aid']) : sys::outjs(array('e' => 'Переданы не все данные'), $nmch);
+
+ $sql->query('SELECT `ip`, `price` FROM `address` WHERE `id`="'.$aid.'" AND `unit`="'.$server['unit'].'" AND `buy`="0" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выделенный адрес не найден.'), $nmch);
+
+ $add = $sql->get();
+
+ // Выполнение операции
+ if($go)
+ {
+ // Проверка баланса
+ if($user['balance'] < $add['price'])
+ sys::outjs(array('e' => 'У вас не хватает '.(round($add['price']-$user['balance'], 2)).' '.$cfg['currency']), $nmch);
+
+ include(LIB.'ssh.php');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Проверка ssh соединения с локацией
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$add['price']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Реф. система
+ games::part($user['id'], $add['price']);
+
+ // Обновление информации
+ $sql->query('UPDATE `address` set `buy`="1" WHERE `id`="'.$aid.'" LIMIT 1');
+ $sql->query('UPDATE `servers` set `address`="'.$add['ip'].':'.params::$aDefPort[$server['game']].'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $sql->query('INSERT INTO `address_buy` set `aid`="'.$aid.'", `server`="'.$id.'", `time`="'.($start_point+2592000).'"');
+
+ // Порт игрового сервера
+ $port = explode(':', $server['address']);
+
+ // Очистка правил FireWall
+ games::iptables($server['id'], 'remove', NULL, NULL, NULL, false, $ssh);
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_address'),
+ array('money' => $add['price'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$add['price'].'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ sys::outjs(array('s' => $add['price']));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/tarif/extend.php b/system/sections/servers/games/tarif/extend.php
new file mode 100644
index 0000000..8c17ebe
--- /dev/null
+++ b/system/sections/servers/games/tarif/extend.php
@@ -0,0 +1,71 @@
+ с учетом промо-кода)
+ if(isset($url['promo']) || $aData['promo'] != '')
+ $promo = games::define_promo(
+ $aData['promo'],
+ $aData,
+ $tarif['discount'],
+ $sum,
+ 'extend'
+ );
+
+ // Использование промо-кода
+ if(is_array($promo))
+ {
+ if(array_key_exists('sum', $promo))
+ $sum = $promo['sum'];
+ else
+ $aData['time'] += $promo['days']*86400; // Кол-во дней аренды с учетом подарочных (промо-код)
+ }
+
+ // Выполнение продления
+ if($go)
+ {
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']), $nmch);
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Реф. система
+ games::part($user['id'], $sum);
+
+ $status = $server['status'] == 'overdue' ? '`status`="off",' : '';
+
+ // Время аренды
+ $time = $server['time'] < $start_point ? $start_point+$aData['time']*86400 : $server['time']+$aData['time']*86400;
+
+ // Обновление информации
+ $sql->query('UPDATE `servers` set '.$status.' `time`="'.$time.'", `test`="0" WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Продление адреса на 30 дней
+ if($add_sum)
+ $sql->query('UPDATE `address_buy` set `time`=`time`+"2592000" WHERE `server`="'.$id.'" LIMIT 1');
+
+ // Запись логов
+ if(!is_array($promo))
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'extend_server'),
+ array('days' => $days,
+ 'money' => $sum,
+ 'id' => $id)).'", `date`="'.$start_point.'", `type`="extend", `money`="'.$sum.'"');
+ else{
+ $sql->query('UPDATE `servers` set `benefit`="'.$time.'" WHERE `id`="'.$id.'" LIMIT 1');
+ $sql->query('INSERT INTO `promo_use` set `promo`="'.$promo['id'].'", `user`="'.$user['id'].'", `time`="'.$start_point.'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'extend_server_promo'),
+ array('days' => $days,
+ 'money' => $sum,
+ 'promo' => $promo['cod'], 'id' => $id)).'", `date`="'.$start_point.'", `type`="extend", `money`="'.$sum.'"');
+ }
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп цены
+ sys::outjs(array('s' => $sum));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/tarif/slots.php b/system/sections/servers/games/tarif/slots.php
new file mode 100644
index 0000000..b298439
--- /dev/null
+++ b/system/sections/servers/games/tarif/slots.php
@@ -0,0 +1,128 @@
+ ''));
+
+ if($go)
+ {
+ $start = $server['slots_start'] > $slots ? ', `slots_start`="'.$slots.'"' : '';
+
+ $sql->query('UPDATE `servers` set `slots`="'.$slots.'" '.$start.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_slots').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // При возможности уменьшить
+ if($cfg['change_slots'][$server['game']]['down'] || $overdue)
+ {
+ // Проверка кол-ва слот
+ if($slots < $tarif['slots_min'] || $slots > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданые неверные данные.'), $nmch);
+
+ if($server['slots'] == $slots)
+ {
+ if($go)
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ sys::outjs(array('s' => 'Сервер будет арендован до: '.date('d.m.Y - H:i', $server['time']).' ('.sys::date('min', $server['time']).')'), $nmch);
+ }
+ }else{
+ // Установлено макс. значение
+ if($server['slots'] == $tarif['slots_max'] AND !$overdue)
+ sys::outjs(array('e' => 'На игровом сервере установлено максимальное значение.'), $nmch);
+
+ if($slots < 1 || $slots > $max)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ $slots += $server['slots'];
+ }
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$price_old/($price*$slots)*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ // При уменьшении кол-ва слот не добавлять дни
+ if($slots < $server['slots'] AND ($cfg['change_slots'][$server['game']]['days'] AND $cfg['change_slots'][$server['game']]['down'] AND !$cfg['change_slots'][$server['game']]['add']))
+ $time = $server['time'];
+
+ // Выполнение операции
+ if($go)
+ {
+ sys::benefitblock($id, $nmch);
+
+ $start = $server['slots_start'] > $slots ? ', `slots_start`="'.$slots.'"' : '';
+
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `slots`="'.$slots.'" '.$start.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')) AND $slots < $server['slots_start'])
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_slots').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => 'Сервер будет арендован до: '.$arenda.' '.date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'));
+ }
+
+ if($slots < 1 || $slots > $max)
+ sys::outjs(array('e' => 'Переданые неверные данные'), $nmch);
+
+ // Выполнение операции
+ if($go)
+ {
+ sys::benefitblock($id, $nmch);
+
+ $slots_new = $server['slots']+$slots;
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']), $nmch);
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Реф. система
+ games::part($user['id'], $sum);
+
+ $start = $server['slots_start'] == $server['slots'] ? ', `slots_start`="'.$slots_new.'"' : '';
+
+ // Обновление информации
+ $sql->query('UPDATE `servers` set `slots`="'.$slots_new.'" '.$start.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart', 'change')) AND $slots_new != $server['slots_start'])
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_slots'),
+ array('slots' => $slots, 'money' => $sum, 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$sum.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => 'Цена за дополнительные слоты: '.$sum.' '.$cfg['currency']));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/games/tarif/unit.php b/system/sections/servers/games/tarif/unit.php
new file mode 100644
index 0000000..c5e4e81
--- /dev/null
+++ b/system/sections/servers/games/tarif/unit.php
@@ -0,0 +1,119 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $pack = isset($url['pack']) ? $url['pack'] : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ // Проверка сборки
+ if(!array_key_exists($pack, sys::b64djs($tarif['packs'], true)))
+ sys::outjs(array('e' => 'Сборка не найдена.'));
+
+ $sql->query('SELECT `id`, `unit`, `port_min`, `port_max`, `hostname`, `path`, `install`, `map`, `plugins_install`, `hdd`, `autostop`, `core_fix`, `ip` FROM `tarifs` WHERE `id`="'.$tarif['id'].'" LIMIT 1');
+ $tarif = array_merge(array('pack' => $pack), $sql->get());
+
+ $sql->query('SELECT `name`, `address`, `passwd` FROM `units` WHERE `id`="'.$tarif['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ // Выделенный адрес игрового сервера
+ if(!empty($tarif['ip']))
+ {
+ $aIp = explode(':', $tarif['ip']);
+
+ $ip = false;
+ $port = params::$aDefPort[$server['game']];
+
+ // Проверка наличия свободного адреса
+ foreach($aIp as $adr)
+ {
+ $adr = trim($adr);
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$tarif['unit'].'" AND `address` LIKE "'.$adr.':%" LIMIT 1');
+ if(!$sql->num())
+ {
+ $ip = $adr;
+
+ break;
+ }
+ }
+ }else{
+ $ip = sys::first(explode(':', $unit['address']));
+ $port = false;
+
+ // Проверка наличия свободного порта
+ for($tarif['port_min']; $tarif['port_min'] <= $tarif['port_max']; $tarif['port_min']+=1)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$tarif['unit'].'" AND `port`="'.$tarif['port_min'].'" LIMIT 1');
+ if(!$sql->num())
+ {
+ $port = $tarif['port_min'];
+
+ break;
+ }
+ }
+ }
+
+ $core = 0;
+
+ if($tarif['core_fix'] != '')
+ {
+ $aCore = explode(',', $tarif['core_fix']);
+
+ foreach($aCore as $cpu)
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `unit`="'.$tarif['unit'].'" AND `tarif`="'.$tarif['id'].'" AND `core_fix`="'.$cpu.'" AND `core_fix_one`="1" LIMIT 1');
+
+ if($sql->num())
+ continue;
+
+ $core = $cpu;
+ $tarif['core_fix'] = $cpu;
+
+ break;
+ }
+ }
+
+ if(!$ip || !$port || !$core)
+ sys::outjs(array('e' => 'К сожалению нет доступных мест, обратитесь в тех.поддержку.'));
+
+ $server['id'] = $id;
+
+ // Време аренды
+ $tarif['time'] = $time;
+
+ include(LIB.'ssh.php');
+
+ // Удаление данных с текущей локации
+ tarif::unit_old($oldTarif, $oldUnit, $server, $nmch);
+
+ $mcache->delete('server_filetp_'.$id);
+
+ $adUnit = explode(':', $unit['address']);
+
+ $server['address'] = $ip.':'.$port;
+
+ // Создание сервера на новой локации
+ tarif::unit_new($tarif, $unit, $server, $nmch);
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_unit').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Генерация списка сборок
+ $packs = '';
+ $aPack = sys::b64djs($tarif['packs'], true);
+
+ if(is_array($aPack))
+ foreach($aPack as $index => $name)
+ $packs .= ''.$name.' ';
+
+ // Выхлоп информации
+ sys::outjs(array('s' => date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')', 'p' => $packs));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/graph.php b/system/sections/servers/graph.php
new file mode 100644
index 0000000..2ca88cb
--- /dev/null
+++ b/system/sections/servers/graph.php
@@ -0,0 +1,11 @@
+query('SELECT `uid`, `unit`, `user`, `address`, `game`, `status`, `slots_start`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'graph');
+
+ include(sys::route($server, 'graph', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/index.php b/system/sections/servers/index.php
new file mode 100644
index 0000000..497a567
--- /dev/null
+++ b/system/sections/servers/index.php
@@ -0,0 +1,11 @@
+query('SELECT `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'index');
+
+ include(SEC.'servers/'.$server['game'].'/index.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/list.php b/system/sections/servers/list.php
new file mode 100644
index 0000000..c145ee1
--- /dev/null
+++ b/system/sections/servers/list.php
@@ -0,0 +1,113 @@
+query('SELECT `unit`, `tarif` FROM `servers` WHERE `user`="'.$user['id'].'" ORDER BY `id` ASC');
+
+ $n = $sql->num($q_Servers);
+
+ $aUnits = array();
+ $aTarifs = array();
+
+ // Проверка массивов в кеше
+ if(is_array($mcache->get('aut_'.$user['id'])) AND $mcache->get('nser_'.$user['id']) == $n)
+ {
+ $aUT = $mcache->get('aut_'.$user['id']);
+ $aUnits = $aUT[0];
+ $aTarifs = $aUT[1];
+ }else{
+ while($server = $sql->get($q_Servers))
+ {
+ if(!array_key_exists($server['unit'], $aUnits))
+ {
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $aUnits[$server['unit']] = array(
+ 'name' => $unit['name']
+ );
+ }
+
+ if(!array_key_exists($server['tarif'], $aTarifs))
+ {
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aTarifs[$server['tarif']] = array(
+ 'name' => $tarif['name'],
+ 'packs' => sys::b64djs($tarif['packs'])
+ );
+ }
+ }
+
+ // Запись массивов в кеш
+ $mcache->set('aut_'.$user['id'], array($aUnits, $aTarifs), false, 60);
+
+ // Запись кол-во серверов в кеш
+ $mcache->set('nser_'.$user['id'], $n, false, 60);
+ }
+
+ include(LIB.'games/games.php');
+
+ $sql->query('SELECT '
+ .'`id`,'
+ .'`unit`,'
+ .'`tarif`,'
+ .'`address`,'
+ .'`game`,'
+ .'`slots_start`,'
+ .'`online`,'
+ .'`status`,'
+ .'`name`,'
+ .'`pack`,'
+ .'`fps`,'
+ .'`tickrate`,'
+ .'`ram`,'
+ .'`map`,'
+ .'`time`,'
+ .'`date`,'
+ .'`overdue`'
+ .' FROM `servers` WHERE `user`="'.$user['id'].'" ORDER BY `id` ASC');
+
+ $wait_servers = '';
+ $updates_servers = '';
+
+ while($server = $sql->get())
+ {
+ $btn = sys::buttons($server['id'], $server['status'], $server['game']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('list', 'sections/servers');
+
+ $html->set('id', $server['id']);
+ $html->set('unit', $aUnits[$server['unit']]['name']);
+ $html->set('tarif',
+ games::info_tarif(
+ $server['game'],
+ $aTarifs[$server['tarif']]['name'],
+ array('fps' => $server['fps'], 'tickrate' => $server['tickrate'], 'ram' => $server['ram'])
+ )
+ );
+
+ $html->set('pack', $aTarifs[$server['tarif']]['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('name', $server['name']);
+ $html->set('fps', $server['fps']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img', $server['game']));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('list');
+
+ $wait_servers .= $server['id'].':false,';
+ $updates_servers .= 'setTimeout(function() {update_info(\''.$server['id'].'\', true)}, 5000); setTimeout(function() {update_status(\''.$server['id'].'\', true)}, 10000);';
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/maps.php b/system/sections/servers/maps.php
new file mode 100644
index 0000000..2bc0c1a
--- /dev/null
+++ b/system/sections/servers/maps.php
@@ -0,0 +1,11 @@
+query('SELECT `uid`, `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'maps');
+
+ include(sys::route($server, 'maps', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/console.php b/system/sections/servers/mc/console.php
new file mode 100644
index 0000000..3e7765f
--- /dev/null
+++ b/system/sections/servers/mc/console.php
@@ -0,0 +1,67 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $command = isset($_POST['command']) ? sys::cmd($_POST['command']) : '';
+
+ if($server['status'] == 'off')
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('servers', 'off')));
+
+ sys::out(sys::text('servers', 'off'));
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ sys::out(sys::text('error', 'ssh'));
+ }
+
+ $dir = $tarif['install'].$server['uid'].'/';
+
+ $filecmd = $dir.'console.log';
+
+ if($command)
+ {
+ if(strtolower($command) == 'clear')
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"Очистка консоли\n\" > '.$filecmd.'"');
+ else
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.$command.'"\015\';'
+ .'sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/copy.php b/system/sections/servers/mc/copy.php
new file mode 100644
index 0000000..7a9c216
--- /dev/null
+++ b/system/sections/servers/mc/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('server_copy_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_copy_'.$id);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `copy` WHERE `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_copy_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/graph.php b/system/sections/servers/mc/graph.php
new file mode 100644
index 0000000..d8d8df8
--- /dev/null
+++ b/system/sections/servers/mc/graph.php
@@ -0,0 +1,71 @@
+query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+ $graph = $sql->get();
+
+ $nmch = 'server_graph_full_'.$id;
+
+ $time = isset($url['time']) ? $url['time'] : 'day';
+
+ if(!in_array($time, array('day', 'week', 'month')))
+ $time = 'day';
+
+ // Выхлоп кеш графика
+ if($mcache->get($nmch) AND file_exists(TEMP.(md5($graph['key'].'full_'.$time)).'.png'))
+ {
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ include(LIB.'games/graph/pData.php');
+ include(LIB.'games/graph/pDraw.php');
+ include(LIB.'games/graph/pImage.php');
+
+ include(LIB.'games/graph.php');
+
+ graph::full($id, $server['slots_start'], $graph['key'], $time);
+
+ $mcache->set($nmch, true, false, 300);
+
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Графики');
+
+ if($mcache->get('server_graph_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_graph_'.$id);
+ else{
+ $sql->query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+
+ // Если отсутствует ключ, создать
+ if(!$sql->num())
+ {
+ // Генерация ключа
+ $key = md5($id.sys::key('graph'));
+
+ $sql->query('INSERT INTO `graph` set `server`="'.$id.'", `key`="'.$key.'", `time`="0"');
+ }else{
+ $graph = $sql->get();
+
+ $key = $graph['key'];
+ }
+
+ $html->get('graph', 'sections/servers/games');
+
+ $html->set('id', $id);
+
+ $html->set('key', $key);
+ $html->set('address', $server['address']);
+ $html->set('_img', '[img]');
+
+ $html->pack('main');
+
+ $mcache->set('server_graph_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/index.php b/system/sections/servers/mc/index.php
new file mode 100644
index 0000000..f8fe3bd
--- /dev/null
+++ b/system/sections/servers/mc/index.php
@@ -0,0 +1,44 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `map`, `time`, `date`, `overdue`, `ram` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address']);
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status'], $server['game']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name'].' / '.$server['ram'].' RAM');
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], 'mc', 'img'));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/owners.php b/system/sections/servers/mc/owners.php
new file mode 100644
index 0000000..db63c5e
--- /dev/null
+++ b/system/sections/servers/mc/owners.php
@@ -0,0 +1,169 @@
+ 'Включение',
+ 'stop' => 'Выключение',
+ 'restart' => 'Перезагрузка',
+ 'reinstall' => 'Переустановка',
+ 'console' => 'Раздел "Консоль"',
+ 'settings' => 'Раздел "Настройки"',
+ 'plugins' => 'Раздел "Плагины"'
+ );
+
+ $aAccess = array('start', 'stop', 'restart', 'reinstall', 'console', 'settings', 'plugins');
+
+ // Проверка прав
+ if(isset($url['rights']) AND $url['rights'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['rights']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Совладелец не найден.'));
+
+ $owner = $sql->get();
+
+ $aRights = sys::b64djs($owner['rights']);
+
+ $rights = '';
+
+ foreach($aAccess as $access)
+ if($aRights[$access]) $rights .= $aAccessI[$access].', ';
+
+ sys::outjs(array('s' => substr($rights, 0, -2)));
+ }
+
+ // Удаление совладельца
+ if(isset($url['delete']) AND $url['delete'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if($sql->num())
+ $sql->query('DELETE FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/owners');
+ }
+
+ // Добавление совладельца
+ if($go)
+ {
+ $nmch = sys::rep_act('server_owners_go_'.$id, 5);
+
+ $aData = (isset($_POST['owner']) AND is_array($_POST['owner'])) ? $_POST['owner'] : array();
+
+ $aDate = isset($aData['\'time\'']) ? explode('.', $aData['\'time\'']) : explode('.', date('d.m.Y', $start_point));
+ $aTime = explode(':', date('H:i:s', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ sys::outjs(array('e' => 'Дата доступа указана неверно.'), $nmch);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2])+3600;
+
+ if($time < $start_point)
+ sys::outjs(array('e' => 'Время доступа не может быть меньше 60 минут.'), $nmch);
+
+ // Проверка пользователя
+ if(!isset($aData['\'user\'']))
+ sys::outjs(array('e' => 'Необходимо указать пользователя.'), $nmch);
+
+ if(is_numeric($aData['\'user\'']))
+ $sql->query('SELECT `id` FROM `users` WHERE `id`="'.$aData['\'user\''].'" LIMIT 1');
+ else{
+ if(sys::valid($aData['\'user\''], 'other', $aValid['login']))
+ sys::outjs(array('e' => sys::text('input', 'login_valid')), $nmch);
+
+ $sql->query('SELECT `id` FROM `users` WHERE `login`="'.$aData['\'user\''].'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Пользователь не найден в базе.'), $nmch);
+
+ $uowner = $sql->get();
+
+ $owner = $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+
+ // Если не обновление доступа совладельца, проверить кол-во
+ if(!$sql->num($owner))
+ {
+ $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" LIMIT 5');
+
+ if($sql->num() == 5)
+ sys::outjs(array('e' => 'Вы добавили максимально число совладельцев.'), $nmch);
+ }
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Владельца сервера нельзя добавить в совладельцы.'), $nmch);
+
+ $aRights = array();
+
+ $check = 0;
+
+ foreach($aAccess as $access)
+ {
+ $aRights[$access] = isset($aData['\''.$access.'\'']) ? 1 : 0;
+
+ $check += $aRights[$access];
+ }
+
+ if(!$check)
+ sys::outjs(array('e' => 'Необходимо включить минимум одно разрешение.'), $nmch);
+
+
+
+ if($sql->num($owner))
+ $sql->query('UPDATE `owners` set `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'" WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ else
+ $sql->query('INSERT INTO `owners` set `server`="'.$id.'", `user`="'.$uowner['id'].'", `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $html->nav('Друзья');
+
+ $cache = $mcache->get('server_owners_'.$id);
+
+ if($cache != '')
+ $html->arr['main'] = $cache;
+ else{
+ $owners = $sql->query('SELECT `id`, `user`, `rights`, `time` FROM `owners` WHERE `server`="'.$id.'" AND `time`>"'.$start_point.'" ORDER BY `id` ASC LIMIT 5');
+
+ if($sql->num())
+ include(LIB.'games/games.php');
+
+ while($owner = $sql->get($owners))
+ {
+ $sql->query('SELECT `login` FROM `users` WHERE `id`="'.$owner['user'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $uowner = $sql->get();
+
+ $rights = games::owners(sys::b64djs($owner['rights']));
+
+ $html->get('owners_list', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('oid', $owner['id']);
+ $html->set('user', $uowner['login']);
+ $html->set('rights', $rights);
+ $html->set('time', date('d.m.Y - H:i', $owner['time']));
+
+ $html->pack('owners');
+ }
+
+ $html->get('owners', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('time', date('d.m.Y', $start_point));
+
+ $html->set('owners', isset($html->arr['owners']) ? $html->arr['owners'] : 'Для данного сервера совладельцы отсутсвуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_owners_'.$id, $html->arr['main'], false, 1);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/plugins.php b/system/sections/servers/mc/plugins.php
new file mode 100644
index 0000000..a116ca1
--- /dev/null
+++ b/system/sections/servers/mc/plugins.php
@@ -0,0 +1,158 @@
+query('SELECT `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $nmch = sys::rep_act('server_plugins_go_'.$id, 10);
+
+ include(SEC.'servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('server_plugins_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_plugins_'.$id);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$id.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `plugins_install` WHERE `server`="'.$id.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_plugins_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/settings.php b/system/sections/servers/mc/settings.php
new file mode 100644
index 0000000..132d29b
--- /dev/null
+++ b/system/sections/servers/mc/settings.php
@@ -0,0 +1,72 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $aSub = array('start', 'server', 'firewall', 'crontab', 'startlogs', 'pack', 'file', 'api');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aEditslist = 1;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ include(SEC.'servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+
+ $sql->query('SELECT `key` FROM `api` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ {
+ $api = $sql->get();
+
+ $html->set('api', $api['key']);
+ $html->unit('api', 1, 1);
+ }else
+ $html->unit('api', 0, 1);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/settings/server.php b/system/sections/servers/mc/settings/server.php
new file mode 100644
index 0000000..1d9c622
--- /dev/null
+++ b/system/sections/servers/mc/settings/server.php
@@ -0,0 +1,109 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).'='.$val.PHP_EOL;
+
+ // Временый файл
+ $temp = sys::temp($config);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/server.properties', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/server.properties');
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Чтение файла - server.properties
+ $file = $tarif['install'].$server['uid'].'/server.properties';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode('=', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.'=', '', $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $html->get('servercfg', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('cfg', $html->arr['list']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/settings/start.php b/system/sections/servers/mc/settings/start.php
new file mode 100644
index 0000000..6e1d332
--- /dev/null
+++ b/system/sections/servers/mc/settings/start.php
@@ -0,0 +1,47 @@
+nav('Параметры запуска');
+
+ $sql->query('SELECT `uid`, `slots`, `slots_start`, `autorestart` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ include(LIB.'games/games.php');
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/tarif.php b/system/sections/servers/mc/tarif.php
new file mode 100644
index 0000000..c0e19f0
--- /dev/null
+++ b/system/sections/servers/mc/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `timext`, `discount`, `price`, `ram` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'plan', 'address', 'addextend', 'unit', 'slots');
+
+ include(SEC.'servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/tarif/extend.php b/system/sections/servers/mc/tarif/extend.php
new file mode 100644
index 0000000..1c37b4a
--- /dev/null
+++ b/system/sections/servers/mc/tarif/extend.php
@@ -0,0 +1,53 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $ram = $server['slots_fix'] ? $server['ram'] : $server['ram']/$server['slots'];
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['ram'] = $ram;
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ $aPrice = explode(':', $tarif['price']);
+
+ // Цена за 30 дней 1 слота
+ $price = $aPrice[array_search($ram, explode(':', $tarif['ram']))];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/tarif/plan.php b/system/sections/servers/mc/tarif/plan.php
new file mode 100644
index 0000000..36b0445
--- /dev/null
+++ b/system/sections/servers/mc/tarif/plan.php
@@ -0,0 +1,70 @@
+ 'Переданые не все данные'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aRAM = explode(':', $tarif['ram']);
+
+ // Проверка плана
+ if(array_search($plan, $aRAM) === FALSE)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ $ram = $server['slots_fix'] ? $server['ram'] : $server['ram']/$server['slots'];
+
+ if($plan == $ram)
+ sys::outjs(array('e' => 'Смысла в этой операции нет'), $nmch);
+
+ if(!tarif::price($tarif['price']))
+ sys::outjs(array('e' => 'Чтобы изменить тариф, перейдите в настройки запуска'), $nmch);
+
+ if($server['time'] < $start_point+86400)
+ $time = $server['time'];
+ else{
+ // Цена за 1 день аренды (по новому тарифному плану)
+ $price = $aPrice[array_search($plan, $aRAM)]/30*$server['slots'];
+
+ // Цена за 1 день аренды (по старому тарифному плану)
+ $price_old = $aPrice[array_search($ram, $aRAM)]/30*$server['slots'];
+
+ // Остаток дней аренды
+ $days = ($server['time']-$start_point)/86400;
+
+ $time = date('H:i:s', $server['time']);
+ $date = date('d.m.Y', round($start_point+$days*$price_old/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+ $aTime = explode(':', $time);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2]);
+ }
+
+ $plan = $server['slots_fix'] ? $plan : $plan*$server['slots'];
+
+ // Выполнение смена тарифного плана
+ if($go)
+ {
+ sys::benefitblock($id, $nmch);
+
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `ram`="'.$plan.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart')))
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_plan').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/tarif/slots.php b/system/sections/servers/mc/tarif/slots.php
new file mode 100644
index 0000000..efaccf8
--- /dev/null
+++ b/system/sections/servers/mc/tarif/slots.php
@@ -0,0 +1,162 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aRAM = explode(':', $tarif['ram']);
+
+ $ram = $server['ram_fix'] ? $server['ram'] : $server['ram']/$server['slots'];
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $aPrice[array_search($ram, $aRAM)]/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($aPrice[array_search($ram, $aRAM)]/30)*$slots, 2);
+
+ // Изменение кол-ва слот за счет пересчета дней аренды или закончился срок аренды (иначе аренда дополнительных слот)
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ $ram = $server['ram_fix'] ? $server['ram'] : $server['ram']/$server['slots']*$slots;
+
+ // Если просрочен
+ if($overdue)
+ {
+ sys::outjs(array('i' => ''));
+
+ if($go)
+ {
+ $start = $server['slots_start'] > $slots ? ', `slots_start`="'.$slots.'"' : '';
+
+ $sql->query('UPDATE `servers` set `slots`="'.$slots.'" '.$start.', `ram`='.$ram.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_slots').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // При возможности уменьшить
+ if($cfg['change_slots'][$server['game']]['down'] || $overdue)
+ {
+ // Проверка кол-ва слот
+ if($slots < $tarif['slots_min'] || $slots > $tarif['slots_max'])
+ sys::outjs(array('e' => 'Переданые неверные данные.'), $nmch);
+
+ if($server['slots'] == $slots)
+ {
+ if($go)
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ sys::outjs(array('s' => 'Сервер будет арендован до: '.date('d.m.Y - H:i', $server['time']).' ('.sys::date('min', $server['time']).')'), $nmch);
+ }
+ }else{
+ // Установлено макс. значение
+ if($server['slots'] == $tarif['slots_max'] AND !$overdue)
+ sys::outjs(array('e' => 'На игровом сервере установлено максимальное значение.'), $nmch);
+
+ if($slots < 1 || $slots > $max)
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ $slots += $server['slots'];
+ }
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$price_old/($price*$slots)*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ // При уменьшении кол-ва слот не добавлять дни
+ if($slots < $server['slots'] AND ($cfg['change_slots'][$server['game']]['days'] AND $cfg['change_slots'][$server['game']]['down'] AND !$cfg['change_slots'][$server['game']]['add']))
+ $time = $server['time'];
+
+ // Выполнение операции
+ if($go)
+ {
+ sys::benefitblock($id, $nmch);
+
+ $start = $server['slots_start'] > $slots ? ', `slots_start`="'.$slots.'"' : '';
+
+ $sql->query('UPDATE `servers` set `time`="'.$time.'", `slots`="'.$slots.'" '.$start.', `ram`='.$ram.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart')) AND $slots < $server['slots_start'])
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs_sys` set `user`="'.$user['id'].'", `server`="'.$id.'", `text`="'.sys::text('syslogs', 'change_slots').'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => 'Сервер будет арендован до: '.$arenda.' '.date('d.m.Y - H:i', $time).' ('.sys::date('min', $time).')'));
+ }
+
+ if($slots < 1 || $slots > $max)
+ sys::outjs(array('e' => 'Переданые неверные данные'), $nmch);
+
+ // Выполнение операции
+ if($go)
+ {
+ sys::benefitblock($id, $nmch);
+
+ $slots_new = $server['slots']+$slots;
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']), $nmch);
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Реф. система
+ games::part($user['id'], $sum);
+
+ $start = $server['slots_start'] == $server['slots'] ? ', `slots_start`="'.$slots_new.'"' : '';
+
+ $ram = $server['ram_fix'] ? $server['ram'] : $server['ram']/$server['slots']*$slots_new;
+
+ // Обновление информации
+ $sql->query('UPDATE `servers` set `slots`="'.$slots_new.'" '.$start.', `ram`='.$ram.' WHERE `id`="'.$id.'" LIMIT 1');
+
+ if(in_array($server['status'], array('working', 'start', 'restart')) AND $slots_new != $server['slots_start'])
+ {
+ include(LIB.'games/'.$server['game'].'/action.php');
+
+ action::start($id, 'restart');
+ }
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_slots'),
+ array('slots' => $slots, 'money' => $sum, 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$sum.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Выхлоп информации
+ sys::outjs(array('s' => 'Цена за дополнительные слоты: '.$sum.' '.$cfg['currency']));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/tarif/unit.php b/system/sections/servers/mc/tarif/unit.php
new file mode 100644
index 0000000..ddc5efc
--- /dev/null
+++ b/system/sections/servers/mc/tarif/unit.php
@@ -0,0 +1,52 @@
+ 'Переданы не все данные.'), $nmch);
+
+ if(!$cfg['change_unit'][$server['game']] || $server['time'] < $start_point+86400 || $server['test'])
+ exit;
+
+ $sql->query('SELECT `id`, `unit`, `packs`, `ram`, `price` FROM `tarifs` WHERE `unit`="'.$uid.'" AND `game`="'.$server['game'].'" AND `name`="'.$tarif['name'].'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Не найден подходящий тариф.'), $nmch);
+
+ $oldTarif = $tarif;
+
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $oldUnit = $sql->get();
+
+ $aPriceold = explode(':', $oldTarif['price']);
+ $aRAMold = explode(':', $oldTarif['ram']);
+
+ $sql->query('SELECT `id` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная локация не доступна.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aRAM = explode(':', $tarif['ram']);
+
+ $ram = $server['slots_fix'] ? $server['ram'] : $server['ram']/$server['slots'];
+
+ if(!in_array($ram, $aRAM))
+ sys::outjs(array('e' => 'Не найден подходящий тарифный план.'), $nmch);
+
+ // Цена за 1 день (при новом тарифном плане)
+ $price = $aPrice[array_search($ram, $aRAM)]/30*$server['slots'];
+
+ // Цена аренды за остаток дней (с текущим тарифным планом)
+ $oldprice = ($server['time']-$start_point)/86400*($aPriceold[array_search($ram, $aRAMold)]/30*$server['slots']);
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$oldprice/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ include(SEC.'servers/games/tarif/unit.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mc/web.php b/system/sections/servers/mc/web.php
new file mode 100644
index 0000000..634e9b6
--- /dev/null
+++ b/system/sections/servers/mc/web.php
@@ -0,0 +1,87 @@
+nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ include(DATA.'web.php');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub) AND in_array($url['action'], array_merge($aAction[$url['subsection']], array('install', 'manage'))))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_web_go_'.$id, 10);
+ else
+ $html->nav('Web', $cfg['http'].'servers/id/'.$id.'/section/web');
+
+ include(SEC.'web/'.$url['subsection'].'/free/'.$url['action'].'.php');
+ }else{
+ $html->nav('Web');
+
+ if($mcache->get('server_web_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_web_'.$id);
+ else{
+ // Услуги
+ foreach($aWeb[$server['game']] as $service => $active)
+ {
+ if($active)
+ {
+ if($service == 'hosting')
+ {
+ if(!$aWebVHTtype && !in_array($server['tarif'], $aWebVHT))
+ continue;
+
+ if($aWebVHTtype && in_array($server['tarif'], $aWebVHT))
+ continue;
+ }
+
+ // Проверка на установку
+ switch($aWebInstall[$server['game']][$service])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ }
+
+ if($sql->num())
+ $html->get('list_install', 'sections/servers/games/web');
+ else
+ $html->get('list', 'sections/servers/games/web');
+
+ $html->set('id', $id);
+ $html->set('service', $service);
+ $html->set('name', $aWebname[$service]);
+ $html->set('desc', $aWebDesc[$service]);
+
+ $html->pack($aWebType[$service]);
+ }
+ }
+
+ // Блоки услуг
+ foreach($aWebTypeInfo[$server['game']] as $type => $name)
+ {
+ if(!isset($html->arr[$type]))
+ continue;
+
+ $html->get('block', 'sections/servers/games/web');
+ $html->set('name', $name);
+ $html->set('list', $html->arr[$type]);
+ $html->pack('web');
+ }
+
+ $html->get('web', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('web', isset($html->arr['web']) ? $html->arr['web'] : 'Дополнительные услуги отсутствуют');
+ $html->pack('main');
+
+ $mcache->set('server_web_'.$id, $html->arr['main'], false, 4);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/console.php b/system/sections/servers/mta/console.php
new file mode 100644
index 0000000..7cd0730
--- /dev/null
+++ b/system/sections/servers/mta/console.php
@@ -0,0 +1,67 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ $command = isset($_POST['command']) ? sys::cmd($_POST['command']) : '';
+
+ if($server['status'] == 'off')
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('servers', 'off')));
+
+ sys::out(sys::text('servers', 'off'));
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($command)
+ sys::outjs(array('e' => sys::text('error', 'ssh')));
+
+ sys::out(sys::text('error', 'ssh'));
+ }
+
+ $dir = $tarif['install'].$server['uid'].'/mods/deathmatch/';
+
+ $filecmd = $dir.'logs/server.log';
+
+ if($command)
+ {
+ if(strtolower($command) == 'clear')
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "echo \"Очистка консоли\n\" > '.$filecmd.'"');
+ else
+ $ssh->set('sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff "'.$command.'"\015\';'
+ .'sudo -u server'.$server['uid'].' screen -p 0 -S s_'.$server['uid'].' -X eval \'stuff \015\'');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/copy.php b/system/sections/servers/mta/copy.php
new file mode 100644
index 0000000..7a9c216
--- /dev/null
+++ b/system/sections/servers/mta/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('server_copy_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_copy_'.$id);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `copy` WHERE `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_copy_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/graph.php b/system/sections/servers/mta/graph.php
new file mode 100644
index 0000000..d8d8df8
--- /dev/null
+++ b/system/sections/servers/mta/graph.php
@@ -0,0 +1,71 @@
+query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+ $graph = $sql->get();
+
+ $nmch = 'server_graph_full_'.$id;
+
+ $time = isset($url['time']) ? $url['time'] : 'day';
+
+ if(!in_array($time, array('day', 'week', 'month')))
+ $time = 'day';
+
+ // Выхлоп кеш графика
+ if($mcache->get($nmch) AND file_exists(TEMP.(md5($graph['key'].'full_'.$time)).'.png'))
+ {
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ include(LIB.'games/graph/pData.php');
+ include(LIB.'games/graph/pDraw.php');
+ include(LIB.'games/graph/pImage.php');
+
+ include(LIB.'games/graph.php');
+
+ graph::full($id, $server['slots_start'], $graph['key'], $time);
+
+ $mcache->set($nmch, true, false, 300);
+
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Графики');
+
+ if($mcache->get('server_graph_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_graph_'.$id);
+ else{
+ $sql->query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+
+ // Если отсутствует ключ, создать
+ if(!$sql->num())
+ {
+ // Генерация ключа
+ $key = md5($id.sys::key('graph'));
+
+ $sql->query('INSERT INTO `graph` set `server`="'.$id.'", `key`="'.$key.'", `time`="0"');
+ }else{
+ $graph = $sql->get();
+
+ $key = $graph['key'];
+ }
+
+ $html->get('graph', 'sections/servers/games');
+
+ $html->set('id', $id);
+
+ $html->set('key', $key);
+ $html->set('address', $server['address']);
+ $html->set('_img', '[img]');
+
+ $html->pack('main');
+
+ $mcache->set('server_graph_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/index.php b/system/sections/servers/mta/index.php
new file mode 100644
index 0000000..9ca3443
--- /dev/null
+++ b/system/sections/servers/mta/index.php
@@ -0,0 +1,44 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address']);
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status'], $server['game']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name']);
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], 'samp', 'img'));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/owners.php b/system/sections/servers/mta/owners.php
new file mode 100644
index 0000000..db63c5e
--- /dev/null
+++ b/system/sections/servers/mta/owners.php
@@ -0,0 +1,169 @@
+ 'Включение',
+ 'stop' => 'Выключение',
+ 'restart' => 'Перезагрузка',
+ 'reinstall' => 'Переустановка',
+ 'console' => 'Раздел "Консоль"',
+ 'settings' => 'Раздел "Настройки"',
+ 'plugins' => 'Раздел "Плагины"'
+ );
+
+ $aAccess = array('start', 'stop', 'restart', 'reinstall', 'console', 'settings', 'plugins');
+
+ // Проверка прав
+ if(isset($url['rights']) AND $url['rights'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['rights']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Совладелец не найден.'));
+
+ $owner = $sql->get();
+
+ $aRights = sys::b64djs($owner['rights']);
+
+ $rights = '';
+
+ foreach($aAccess as $access)
+ if($aRights[$access]) $rights .= $aAccessI[$access].', ';
+
+ sys::outjs(array('s' => substr($rights, 0, -2)));
+ }
+
+ // Удаление совладельца
+ if(isset($url['delete']) AND $url['delete'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if($sql->num())
+ $sql->query('DELETE FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/owners');
+ }
+
+ // Добавление совладельца
+ if($go)
+ {
+ $nmch = sys::rep_act('server_owners_go_'.$id, 5);
+
+ $aData = (isset($_POST['owner']) AND is_array($_POST['owner'])) ? $_POST['owner'] : array();
+
+ $aDate = isset($aData['\'time\'']) ? explode('.', $aData['\'time\'']) : explode('.', date('d.m.Y', $start_point));
+ $aTime = explode(':', date('H:i:s', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ sys::outjs(array('e' => 'Дата доступа указана неверно.'), $nmch);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2])+3600;
+
+ if($time < $start_point)
+ sys::outjs(array('e' => 'Время доступа не может быть меньше 60 минут.'), $nmch);
+
+ // Проверка пользователя
+ if(!isset($aData['\'user\'']))
+ sys::outjs(array('e' => 'Необходимо указать пользователя.'), $nmch);
+
+ if(is_numeric($aData['\'user\'']))
+ $sql->query('SELECT `id` FROM `users` WHERE `id`="'.$aData['\'user\''].'" LIMIT 1');
+ else{
+ if(sys::valid($aData['\'user\''], 'other', $aValid['login']))
+ sys::outjs(array('e' => sys::text('input', 'login_valid')), $nmch);
+
+ $sql->query('SELECT `id` FROM `users` WHERE `login`="'.$aData['\'user\''].'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Пользователь не найден в базе.'), $nmch);
+
+ $uowner = $sql->get();
+
+ $owner = $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+
+ // Если не обновление доступа совладельца, проверить кол-во
+ if(!$sql->num($owner))
+ {
+ $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" LIMIT 5');
+
+ if($sql->num() == 5)
+ sys::outjs(array('e' => 'Вы добавили максимально число совладельцев.'), $nmch);
+ }
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Владельца сервера нельзя добавить в совладельцы.'), $nmch);
+
+ $aRights = array();
+
+ $check = 0;
+
+ foreach($aAccess as $access)
+ {
+ $aRights[$access] = isset($aData['\''.$access.'\'']) ? 1 : 0;
+
+ $check += $aRights[$access];
+ }
+
+ if(!$check)
+ sys::outjs(array('e' => 'Необходимо включить минимум одно разрешение.'), $nmch);
+
+
+
+ if($sql->num($owner))
+ $sql->query('UPDATE `owners` set `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'" WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ else
+ $sql->query('INSERT INTO `owners` set `server`="'.$id.'", `user`="'.$uowner['id'].'", `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $html->nav('Друзья');
+
+ $cache = $mcache->get('server_owners_'.$id);
+
+ if($cache != '')
+ $html->arr['main'] = $cache;
+ else{
+ $owners = $sql->query('SELECT `id`, `user`, `rights`, `time` FROM `owners` WHERE `server`="'.$id.'" AND `time`>"'.$start_point.'" ORDER BY `id` ASC LIMIT 5');
+
+ if($sql->num())
+ include(LIB.'games/games.php');
+
+ while($owner = $sql->get($owners))
+ {
+ $sql->query('SELECT `login` FROM `users` WHERE `id`="'.$owner['user'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $uowner = $sql->get();
+
+ $rights = games::owners(sys::b64djs($owner['rights']));
+
+ $html->get('owners_list', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('oid', $owner['id']);
+ $html->set('user', $uowner['login']);
+ $html->set('rights', $rights);
+ $html->set('time', date('d.m.Y - H:i', $owner['time']));
+
+ $html->pack('owners');
+ }
+
+ $html->get('owners', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('time', date('d.m.Y', $start_point));
+
+ $html->set('owners', isset($html->arr['owners']) ? $html->arr['owners'] : 'Для данного сервера совладельцы отсутсвуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_owners_'.$id, $html->arr['main'], false, 1);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/plugins.php b/system/sections/servers/mta/plugins.php
new file mode 100644
index 0000000..a116ca1
--- /dev/null
+++ b/system/sections/servers/mta/plugins.php
@@ -0,0 +1,158 @@
+query('SELECT `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $nmch = sys::rep_act('server_plugins_go_'.$id, 10);
+
+ include(SEC.'servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('server_plugins_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_plugins_'.$id);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$id.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `plugins_install` WHERE `server`="'.$id.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_plugins_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/settings.php b/system/sections/servers/mta/settings.php
new file mode 100644
index 0000000..fb529e4
--- /dev/null
+++ b/system/sections/servers/mta/settings.php
@@ -0,0 +1,72 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $aSub = array('start', 'file', 'firewall', 'crontab', 'startlogs', 'pack', 'api');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aEditslist = 1;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ include(SEC.'servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+
+ $sql->query('SELECT `key` FROM `api` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ {
+ $api = $sql->get();
+
+ $html->set('api', $api['key']);
+ $html->unit('api', 1, 1);
+ }else
+ $html->unit('api', 0, 1);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 20);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/settings/start.php b/system/sections/servers/mta/settings/start.php
new file mode 100644
index 0000000..11b9d0a
--- /dev/null
+++ b/system/sections/servers/mta/settings/start.php
@@ -0,0 +1,52 @@
+query('SELECT `uid`, `slots`, `slots_start`, `autorestart` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ include(LIB.'games/games.php');
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ case 'autorestart':
+ if($value != $server['autorestart'])
+ $sql->query('UPDATE `servers` set `autorestart`="'.$value.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/tarif.php b/system/sections/servers/mta/tarif.php
new file mode 100644
index 0000000..c321da7
--- /dev/null
+++ b/system/sections/servers/mta/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'address', 'addextend', 'unit', 'slots');
+
+ include(SEC.'servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/tarif/extend.php b/system/sections/servers/mta/tarif/extend.php
new file mode 100644
index 0000000..cf31b1e
--- /dev/null
+++ b/system/sections/servers/mta/tarif/extend.php
@@ -0,0 +1,48 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ // Цена за 30 дней 1 слота
+ $price = $tarif['price'];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/tarif/slots.php b/system/sections/servers/mta/tarif/slots.php
new file mode 100644
index 0000000..3a60d63
--- /dev/null
+++ b/system/sections/servers/mta/tarif/slots.php
@@ -0,0 +1,31 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $tarif['price']/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($tarif['price']/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/tarif/unit.php b/system/sections/servers/mta/tarif/unit.php
new file mode 100644
index 0000000..b840bc7
--- /dev/null
+++ b/system/sections/servers/mta/tarif/unit.php
@@ -0,0 +1,50 @@
+ 'Переданы не все данные.'), $nmch);
+
+ if(!$cfg['change_unit'][$server['game']] || $server['time'] < $start_point+86400 || $server['test'])
+ exit;
+
+ $sql->query('SELECT `id`, `unit`, `packs`, `tickrate`, `price` FROM `tarifs` WHERE `unit`="'.$uid.'" AND `game`="'.$server['game'].'" AND `name`="'.$tarif['name'].'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Не найден подходящий тариф.'), $nmch);
+
+ $oldTarif = $tarif;
+
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $oldUnit = $sql->get();
+
+ $aPriceold = explode(':', $oldTarif['price']);
+ $aTICKold = explode(':', $oldTarif['tickrate']);
+
+ $sql->query('SELECT `id` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная локация не доступна.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ if(!in_array($server['tickrate'], $aTICK))
+ sys::outjs(array('e' => 'Не найден подходящий тарифный план.'), $nmch);
+
+ // Цена за 1 день (при новом тарифном плане)
+ $price = $aPrice[array_search($server['tickrate'], $aTICK)]/30*$server['slots'];
+
+ // Цена аренды за остаток дней (с текущим тарифным планом)
+ $oldprice = ($server['time']-$start_point)/86400*($aPriceold[array_search($server['tickrate'], $aTICKold)]/30*$server['slots']);
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$oldprice/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ include(SEC.'servers/games/tarif/unit.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/mta/web.php b/system/sections/servers/mta/web.php
new file mode 100644
index 0000000..634e9b6
--- /dev/null
+++ b/system/sections/servers/mta/web.php
@@ -0,0 +1,87 @@
+nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ include(DATA.'web.php');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub) AND in_array($url['action'], array_merge($aAction[$url['subsection']], array('install', 'manage'))))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_web_go_'.$id, 10);
+ else
+ $html->nav('Web', $cfg['http'].'servers/id/'.$id.'/section/web');
+
+ include(SEC.'web/'.$url['subsection'].'/free/'.$url['action'].'.php');
+ }else{
+ $html->nav('Web');
+
+ if($mcache->get('server_web_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_web_'.$id);
+ else{
+ // Услуги
+ foreach($aWeb[$server['game']] as $service => $active)
+ {
+ if($active)
+ {
+ if($service == 'hosting')
+ {
+ if(!$aWebVHTtype && !in_array($server['tarif'], $aWebVHT))
+ continue;
+
+ if($aWebVHTtype && in_array($server['tarif'], $aWebVHT))
+ continue;
+ }
+
+ // Проверка на установку
+ switch($aWebInstall[$server['game']][$service])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ }
+
+ if($sql->num())
+ $html->get('list_install', 'sections/servers/games/web');
+ else
+ $html->get('list', 'sections/servers/games/web');
+
+ $html->set('id', $id);
+ $html->set('service', $service);
+ $html->set('name', $aWebname[$service]);
+ $html->set('desc', $aWebDesc[$service]);
+
+ $html->pack($aWebType[$service]);
+ }
+ }
+
+ // Блоки услуг
+ foreach($aWebTypeInfo[$server['game']] as $type => $name)
+ {
+ if(!isset($html->arr[$type]))
+ continue;
+
+ $html->get('block', 'sections/servers/games/web');
+ $html->set('name', $name);
+ $html->set('list', $html->arr[$type]);
+ $html->pack('web');
+ }
+
+ $html->get('web', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('web', isset($html->arr['web']) ? $html->arr['web'] : 'Дополнительные услуги отсутствуют');
+ $html->pack('main');
+
+ $mcache->set('server_web_'.$id, $html->arr['main'], false, 4);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/noaccess.php b/system/sections/servers/noaccess.php
new file mode 100644
index 0000000..db9cd53
--- /dev/null
+++ b/system/sections/servers/noaccess.php
@@ -0,0 +1,24 @@
+nav('Раздел недоступен');
+
+ if($server['time'] < $start_point)
+ $html->get('overdue');
+ else{
+ $status = array(
+ 'install' => 'установки',
+ 'reinstall' => 'переустановки',
+ 'update' => 'обновления',
+ 'recovery' => 'восстановления',
+ 'blocked' => 'блокировки'
+ );
+
+ $html->get('noaccess');
+
+ $html->set('status', $status[$server['status']]);
+ }
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/owners.php b/system/sections/servers/owners.php
new file mode 100644
index 0000000..e639405
--- /dev/null
+++ b/system/sections/servers/owners.php
@@ -0,0 +1,11 @@
+query('SELECT `user`, `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'owners');
+
+ include(sys::route($server, 'owners', $go, true));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/owners_list.php b/system/sections/servers/owners_list.php
new file mode 100644
index 0000000..384f478
--- /dev/null
+++ b/system/sections/servers/owners_list.php
@@ -0,0 +1,125 @@
+query('SELECT `server` FROM `owners` WHERE `user`="'.$user['id'].'" AND `time`>"'.$start_point.'" ORDER BY `id` ASC');
+
+ $n = $sql->num($owners);
+
+ $aUnits = array();
+ $aTarifs = array();
+
+ // Проверка массивов в кеше
+ if(is_array($mcache->get('owners_aut_'.$user['id'])) AND $mcache->get('owners_nser_'.$user['id']) == $n)
+ {
+ $aUT = $mcache->get('owners_aut_'.$user['id']);
+ $aUnits = $aUT[0];
+ $aTarifs = $aUT[1];
+ }else{
+ while($owner = $sql->get($owners))
+ {
+ $server_sql = $sql->query('SELECT `unit`, `tarif` FROM `servers` WHERE `id`="'.$owner['server'].'"');
+
+ while($server = $sql->get($server_sql))
+ {
+ if(!array_key_exists($server['unit'], $aUnits))
+ {
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $aUnits[$server['unit']] = array(
+ 'name' => $unit['name']
+ );
+ }
+
+ if(!array_key_exists($server['tarif'], $aTarifs))
+ {
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aTarifs[$server['tarif']] = array(
+ 'name' => $tarif['name'],
+ 'packs' => sys::b64djs($tarif['packs'])
+ );
+ }
+ }
+ }
+
+ // Запись массивов в кеш
+ $mcache->set('owners_aut_'.$user['id'], array($aUnits, $aTarifs), false, 60);
+
+ // Запись кол-во серверов в кеш
+ $mcache->set('owners_nser_'.$user['id'], $n, false, 60);
+ }
+
+ $owners = $sql->query('SELECT `id`, `server`, `time` FROM `owners` WHERE `user`="'.$user['id'].'" ORDER BY `id` ASC');
+
+ while($owner = $sql->get($owners))
+ {
+ if($owner['time'] < $start_point)
+ {
+ $sql->query('DELETE FROM `owners` WHERE `id`="'.$owner['id'].'" LIMIT 1');
+
+ continue;
+ }
+
+ $sql->query('SELECT '
+ .'`id`,'
+ .'`unit`,'
+ .'`tarif`,'
+ .'`address`,'
+ .'`game`,'
+ .'`slots_start`,'
+ .'`online`,'
+ .'`status`,'
+ .'`name`,'
+ .'`pack`,'
+ .'`fps`,'
+ .'`tickrate`,'
+ .'`ram`,'
+ .'`map`,'
+ .'`time`,'
+ .'`date`,'
+ .'`overdue`'
+ .' FROM `servers` WHERE `id`="'.$owner['server'].'" LIMIT 1');
+
+ while($server = $sql->get())
+ {
+ $btn = sys::buttons($server['id'], $server['status'], $server['game']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('list', 'sections/servers');
+
+ $html->set('id', $server['id']);
+ $html->set('unit', $aUnits[$server['unit']]['name']);
+ $html->set('tarif',
+ games::info_tarif(
+ $server['game'],
+ $aTarifs[$server['tarif']]['name'],
+ array('fps' => $server['fps'], 'tickrate' => $server['tickrate'], 'ram' => $server['ram'])
+ )
+ );
+
+ $html->set('pack', $aTarifs[$server['tarif']]['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('name', $server['name']);
+ $html->set('fps', $server['fps']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], $server['map'], 'img'));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('list');
+
+ $wait_servers .= $server['id'].':false,';
+ $updates_servers .= 'setTimeout(function() {update_info(\''.$server['id'].'\', true)}, 5000); setTimeout(function() {update_status(\''.$server['id'].'\', true)}, 10000);';
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/plugins.php b/system/sections/servers/plugins.php
new file mode 100644
index 0000000..a7f1ec2
--- /dev/null
+++ b/system/sections/servers/plugins.php
@@ -0,0 +1,11 @@
+query('SELECT `uid`, `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'plugins');
+
+ include(sys::route($server, 'plugins', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/rcon.php b/system/sections/servers/rcon.php
new file mode 100644
index 0000000..9694d70
--- /dev/null
+++ b/system/sections/servers/rcon.php
@@ -0,0 +1,11 @@
+query('SELECT `uid`, `unit`, `tarif`, `user`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'rcon');
+
+ include(sys::route($server, 'rcon', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/console.php b/system/sections/servers/samp/console.php
new file mode 100644
index 0000000..3c42b22
--- /dev/null
+++ b/system/sections/servers/samp/console.php
@@ -0,0 +1,44 @@
+query('SELECT `uid`, `unit`, `tarif`, `time_start` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ if($go)
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if($server['status'] == 'off')
+ sys::out(sys::text('servers', 'off'));
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::out(sys::text('error', 'ssh'));
+
+ $dir = $tarif['install'].$server['uid'].'/';
+
+ $filecmd = $dir.'server_log.txt';
+
+ $filecmd_copy = $dir.'oldstart/'.date('d.m.Y_H:i:s', $server['time_start']).'.log';
+
+ $weight = sys::int($ssh->get('du --block-size=1 '.$filecmd.' | awk \'{print $1}\''));
+
+ if($weight > 524288)
+ $ssh->set('sudo -u server'.$server['uid'].' sh -c "mkdir -p '.$dir.'oldstart; cat '.$filecmd.' >> '.$filecmd_copy.'; echo \"Выполнена очистка консоли, слишком большой объем данных\n\" > '.$filecmd.'"');
+
+ sys::out(htmlspecialchars($ssh->get('cat '.$filecmd), NULL, ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Консоль');
+
+ $html->get('console', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/copy.php b/system/sections/servers/samp/copy.php
new file mode 100644
index 0000000..7a9c216
--- /dev/null
+++ b/system/sections/servers/samp/copy.php
@@ -0,0 +1,85 @@
+ 'Игровой сервер должен быть выключен'), $nmch);
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ include(SEC.'servers/games/copy/'.$url['subsection'].'.php');
+ }
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Резервные копии');
+
+ if($mcache->get('server_copy_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_copy_'.$id);
+ else{
+ // Построение списка создания копии
+ foreach(params::$section_copy[$server['game']]['aCopy'] as $name => $info)
+ {
+ $html->get('list', 'sections/servers/games/copy');
+
+ $html->set('name', $name);
+ $html->set('info', $info);
+
+ $html->pack('list');
+ }
+
+ // Построение списка созданных копий
+ $sql->query('SELECT `id`, `server`, `info`, `date`, `status` FROM `copy` WHERE `user`="'.$server['user'].'_'.$server['unit'].'" AND `game`="'.$server['game'].'" ORDER BY `id` ASC');
+ while($copy = $sql->get())
+ {
+ $html->get('copy', 'sections/servers/games/copy');
+
+ $html->set('id', $copy['id']);
+ $html->set('info', $copy['info']);
+ $html->set('server', $copy['server']);
+ $html->set('date', sys::today($copy['date']));
+
+ if($copy['status'])
+ {
+ $html->unit('created', 1);
+ $html->unit('!created');
+ }else{
+ $html->unit('created');
+ $html->unit('!created', 1);
+ }
+
+ $html->pack('copy');
+ }
+
+ $html->get('copy', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->set('list', isset($html->arr['list']) ? $html->arr['list'] : '');
+ $html->set('copy', isset($html->arr['copy']) ? $html->arr['copy'] : 'Резервные копии отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_copy_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/graph.php b/system/sections/servers/samp/graph.php
new file mode 100644
index 0000000..d8d8df8
--- /dev/null
+++ b/system/sections/servers/samp/graph.php
@@ -0,0 +1,71 @@
+query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+ $graph = $sql->get();
+
+ $nmch = 'server_graph_full_'.$id;
+
+ $time = isset($url['time']) ? $url['time'] : 'day';
+
+ if(!in_array($time, array('day', 'week', 'month')))
+ $time = 'day';
+
+ // Выхлоп кеш графика
+ if($mcache->get($nmch) AND file_exists(TEMP.(md5($graph['key'].'full_'.$time)).'.png'))
+ {
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ include(LIB.'games/graph/pData.php');
+ include(LIB.'games/graph/pDraw.php');
+ include(LIB.'games/graph/pImage.php');
+
+ include(LIB.'games/graph.php');
+
+ graph::full($id, $server['slots_start'], $graph['key'], $time);
+
+ $mcache->set($nmch, true, false, 300);
+
+ header('Content-type: image/png');
+ exit(file_get_contents(TEMP.(md5($graph['key'].'full_'.$time)).'.png'));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Графики');
+
+ if($mcache->get('server_graph_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_graph_'.$id);
+ else{
+ $sql->query('SELECT `key` FROM `graph` WHERE `server`="'.$id.'" LIMIT 1');
+
+ // Если отсутствует ключ, создать
+ if(!$sql->num())
+ {
+ // Генерация ключа
+ $key = md5($id.sys::key('graph'));
+
+ $sql->query('INSERT INTO `graph` set `server`="'.$id.'", `key`="'.$key.'", `time`="0"');
+ }else{
+ $graph = $sql->get();
+
+ $key = $graph['key'];
+ }
+
+ $html->get('graph', 'sections/servers/games');
+
+ $html->set('id', $id);
+
+ $html->set('key', $key);
+ $html->set('address', $server['address']);
+ $html->set('_img', '[img]');
+
+ $html->pack('main');
+
+ $mcache->set('server_graph_'.$id, $html->arr['main'], false, 4);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/index.php b/system/sections/servers/samp/index.php
new file mode 100644
index 0000000..9ca3443
--- /dev/null
+++ b/system/sections/servers/samp/index.php
@@ -0,0 +1,44 @@
+query('SELECT `unit`, `tarif`, `slots_start`, `online`, `players`, `name`, `pack`, `map`, `time`, `date`, `overdue` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address']);
+
+ $sql->query('SELECT `name` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $btn = sys::buttons($id, $server['status'], $server['game']);
+
+ $time_end = $server['status'] == 'overdue' ? 'Удаление через: '.sys::date('min', $server['overdue']+$cfg['server_delete']*86400) : 'Осталось: '.sys::date('min', $server['time']);
+
+ $html->get('index', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('unit', $unit['name']);
+ $html->set('tarif', $tarif['name']);
+
+ $tarif['packs'] = sys::b64djs($tarif['packs']);
+
+ $html->set('pack', $tarif['packs'][$server['pack']]);
+ $html->set('address', $server['address']);
+ $html->set('game', $aGname[$server['game']]);
+ $html->set('slots', $server['slots_start']);
+ $html->set('online', $server['online']);
+ $html->set('players', base64_decode($server['players']));
+ $html->set('name', $server['name']);
+ $html->set('status', sys::status($server['status'], $server['game'], $server['map']));
+ $html->set('img', sys::status($server['status'], $server['game'], 'samp', 'img'));
+ $html->set('time_end', $time_end);
+ $html->set('time', sys::today($server['time']));
+ $html->set('date', sys::today($server['date']));
+
+ $html->set('btn', $btn);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/owners.php b/system/sections/servers/samp/owners.php
new file mode 100644
index 0000000..db63c5e
--- /dev/null
+++ b/system/sections/servers/samp/owners.php
@@ -0,0 +1,169 @@
+ 'Включение',
+ 'stop' => 'Выключение',
+ 'restart' => 'Перезагрузка',
+ 'reinstall' => 'Переустановка',
+ 'console' => 'Раздел "Консоль"',
+ 'settings' => 'Раздел "Настройки"',
+ 'plugins' => 'Раздел "Плагины"'
+ );
+
+ $aAccess = array('start', 'stop', 'restart', 'reinstall', 'console', 'settings', 'plugins');
+
+ // Проверка прав
+ if(isset($url['rights']) AND $url['rights'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['rights']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Совладелец не найден.'));
+
+ $owner = $sql->get();
+
+ $aRights = sys::b64djs($owner['rights']);
+
+ $rights = '';
+
+ foreach($aAccess as $access)
+ if($aRights[$access]) $rights .= $aAccessI[$access].', ';
+
+ sys::outjs(array('s' => substr($rights, 0, -2)));
+ }
+
+ // Удаление совладельца
+ if(isset($url['delete']) AND $url['delete'] > 0)
+ {
+ $sql->query('SELECT `rights` FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ if($sql->num())
+ $sql->query('DELETE FROM `owners` WHERE `id`="'.sys::int($url['delete']).'" AND `server`="'.$id.'" LIMIT 1');
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/owners');
+ }
+
+ // Добавление совладельца
+ if($go)
+ {
+ $nmch = sys::rep_act('server_owners_go_'.$id, 5);
+
+ $aData = (isset($_POST['owner']) AND is_array($_POST['owner'])) ? $_POST['owner'] : array();
+
+ $aDate = isset($aData['\'time\'']) ? explode('.', $aData['\'time\'']) : explode('.', date('d.m.Y', $start_point));
+ $aTime = explode(':', date('H:i:s', $start_point));
+
+ if(!isset($aDate[1], $aDate[0], $aDate[2]) || !checkdate($aDate[1], $aDate[0], $aDate[2]))
+ sys::outjs(array('e' => 'Дата доступа указана неверно.'), $nmch);
+
+ $time = mktime($aTime[0], $aTime[1], $aTime[2], $aDate[1], $aDate[0], $aDate[2])+3600;
+
+ if($time < $start_point)
+ sys::outjs(array('e' => 'Время доступа не может быть меньше 60 минут.'), $nmch);
+
+ // Проверка пользователя
+ if(!isset($aData['\'user\'']))
+ sys::outjs(array('e' => 'Необходимо указать пользователя.'), $nmch);
+
+ if(is_numeric($aData['\'user\'']))
+ $sql->query('SELECT `id` FROM `users` WHERE `id`="'.$aData['\'user\''].'" LIMIT 1');
+ else{
+ if(sys::valid($aData['\'user\''], 'other', $aValid['login']))
+ sys::outjs(array('e' => sys::text('input', 'login_valid')), $nmch);
+
+ $sql->query('SELECT `id` FROM `users` WHERE `login`="'.$aData['\'user\''].'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Пользователь не найден в базе.'), $nmch);
+
+ $uowner = $sql->get();
+
+ $owner = $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+
+ // Если не обновление доступа совладельца, проверить кол-во
+ if(!$sql->num($owner))
+ {
+ $sql->query('SELECT `id` FROM `owners` WHERE `server`="'.$id.'" LIMIT 5');
+
+ if($sql->num() == 5)
+ sys::outjs(array('e' => 'Вы добавили максимально число совладельцев.'), $nmch);
+ }
+
+ $sql->query('SELECT `id` FROM `servers` WHERE `id`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Владельца сервера нельзя добавить в совладельцы.'), $nmch);
+
+ $aRights = array();
+
+ $check = 0;
+
+ foreach($aAccess as $access)
+ {
+ $aRights[$access] = isset($aData['\''.$access.'\'']) ? 1 : 0;
+
+ $check += $aRights[$access];
+ }
+
+ if(!$check)
+ sys::outjs(array('e' => 'Необходимо включить минимум одно разрешение.'), $nmch);
+
+
+
+ if($sql->num($owner))
+ $sql->query('UPDATE `owners` set `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'" WHERE `server`="'.$id.'" AND `user`="'.$uowner['id'].'" LIMIT 1');
+ else
+ $sql->query('INSERT INTO `owners` set `server`="'.$id.'", `user`="'.$uowner['id'].'", `rights`="'.sys::b64js($aRights).'", `time`="'.$time.'"');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $html->nav('Друзья');
+
+ $cache = $mcache->get('server_owners_'.$id);
+
+ if($cache != '')
+ $html->arr['main'] = $cache;
+ else{
+ $owners = $sql->query('SELECT `id`, `user`, `rights`, `time` FROM `owners` WHERE `server`="'.$id.'" AND `time`>"'.$start_point.'" ORDER BY `id` ASC LIMIT 5');
+
+ if($sql->num())
+ include(LIB.'games/games.php');
+
+ while($owner = $sql->get($owners))
+ {
+ $sql->query('SELECT `login` FROM `users` WHERE `id`="'.$owner['user'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $uowner = $sql->get();
+
+ $rights = games::owners(sys::b64djs($owner['rights']));
+
+ $html->get('owners_list', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('oid', $owner['id']);
+ $html->set('user', $uowner['login']);
+ $html->set('rights', $rights);
+ $html->set('time', date('d.m.Y - H:i', $owner['time']));
+
+ $html->pack('owners');
+ }
+
+ $html->get('owners', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+ $html->set('time', date('d.m.Y', $start_point));
+
+ $html->set('owners', isset($html->arr['owners']) ? $html->arr['owners'] : 'Для данного сервера совладельцы отсутсвуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_owners_'.$id, $html->arr['main'], false, 1);
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/plugins.php b/system/sections/servers/samp/plugins.php
new file mode 100644
index 0000000..a116ca1
--- /dev/null
+++ b/system/sections/servers/samp/plugins.php
@@ -0,0 +1,158 @@
+query('SELECT `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ // Подразделы
+ $aSub = array('install', 'delete', 'update', 'plugin', 'config', 'search');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Плагины', $cfg['http'].'servers/id/'.$id.'/section/plugins');
+
+ $nmch = sys::rep_act('server_plugins_go_'.$id, 10);
+
+ include(SEC.'servers/games/plugins/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Плагины');
+
+ // Если есть кеш
+ if($mcache->get('server_plugins_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_plugins_'.$id);
+ else{
+ include(LIB.'games/plugins.php');
+
+ // Категории
+ $cats = $sql->query('SELECT `id`, `name` FROM `plugins_category` WHERE `game`="'.$server['game'].'" ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ // Плагины
+ $plugins = $sql->query('SELECT `id`, `name`, `desc`, `images`, `status`, `upd`, `packs`, `price` FROM `plugins` WHERE `cat`="'.$cat['id'].'" ORDER BY `sort`, `id` ASC');
+ while($plugin = $sql->get($plugins))
+ {
+ // Проверка, установлен ли плагин на сервер
+ $sql->query('SELECT `id` FROM `plugins_install` WHERE `server`="'.$id.'" AND `plugin`="'.$plugin['id'].'" LIMIT 1');
+ if($sql->num())
+ continue;
+
+ // Проверка наличия обновленной версии плагина
+ if($plugin['upd'])
+ {
+ $idp = $plugin['id'];
+
+ $sql->query('SELECT `name`, `desc`, `images`, `status`, `packs`, `price` FROM `plugins_update` WHERE `plugin`="'.$plugin['id'].'" ORDER BY `id` DESC LIMIT 1');
+ if($sql->num())
+ {
+ $plugin = $sql->get();
+
+ $plugin['id'] = $idp;
+ }else
+ $plugin['upd'] = 0;
+ }
+
+ // Проверка на доступность плагина к установленной на сервере сборке
+ $packs = strpos($plugin['packs'], ':') ? explode(':',$plugin['packs']) : array($plugin['packs']);
+ if(!in_array($server['pack'], $packs) AND $plugin['packs'] != 'all')
+ continue;
+
+ $images = plugins::images($plugin['images'], $plugin['id']);
+
+ if($plugin['price'])
+ {
+ $sql->query('SELECT `id` FROM `plugins_buy` WHERE `plugin`="'.$plugin['id'].'" AND `server`="'.$id.'" LIMIT 1');
+ $buy = $sql->num();
+ }
+
+ // Шаблон плагина
+ $html->get('plugin', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['id']);
+
+ plugins::status($plugin['status']);
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ if(!empty($images))
+ {
+ $html->unit('images', 1);
+ $html->set('images', $images);
+ }else
+ $html->unit('images');
+
+ if(!$buy AND $plugin['price'])
+ {
+ $html->unit('price', true, true);
+ $html->set('price', $plugin['price']);
+ }else
+ $html->unit('price', false, true);
+
+ $html->pack('plugins');
+ }
+
+ // Шаблон блока плагинов
+ $html->get('category', 'sections/servers/games/plugins');
+
+ $html->set('name', $cat['name']);
+ $html->set('plugins', isset($html->arr['plugins']) ? $html->arr['plugins'] : 'Доступных для установки плагинов нет.', 1);
+
+ $html->pack('addons');
+ }
+
+ unset($cats, $cat, $plugins, $plugin);
+
+ // Список установленных плагинов на сервер (отдельный блок)
+ $pl_ins = $sql->query('SELECT `plugin`, `upd`, `time` FROM `plugins_install` WHERE `server`="'.$id.'" ORDER BY `plugin`');
+ while($plugin = $sql->get($pl_ins))
+ {
+ $sql->query('SELECT `id` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $isUpd = $plugin['upd'];
+
+ // Если установлен обновленный плагин
+ if($isUpd)
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins_update` WHERE `id`="'.$isUpd.'" LIMIT 1');
+ else
+ $sql->query('SELECT `name`, `desc`, `status`, `cfg`, `upd` FROM `plugins` WHERE `id`="'.$plugin['plugin'].'" LIMIT 1');
+
+ $plugin = array_merge($plugin, $sql->get());
+
+ // Шаблон плагина
+ $html->get('plugin_install', 'sections/servers/games/plugins');
+
+ $html->set('id', $id);
+ $html->set('plugin', $plugin['plugin']);
+
+ plugins::status($plugin['status']);
+
+ if($plugin['cfg']) $html->unit('config', 1); else $html->unit('config');
+
+ if($plugin['upd']) $html->unit('update', 1); else $html->unit('update');
+
+ $html->set('name', htmlspecialchars_decode($plugin['name']));
+ $html->set('time', sys::today($plugin['time']));
+ $html->set('desc', htmlspecialchars_decode($plugin['desc']));
+
+ $html->pack('install');
+ }
+
+ $html->get('plugins', 'sections/servers/games');
+
+ $html->set('id', $id);
+ $html->set('addons', isset($html->arr['addons']) ? $html->arr['addons'] : '');
+ $html->set('install', isset($html->arr['install']) ? $html->arr['install'] : 'Установленные плагины отсутствуют.');
+
+ $html->pack('main');
+
+ $mcache->set('server_plugins_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/rcon.php b/system/sections/servers/samp/rcon.php
new file mode 100644
index 0000000..1a0de70
--- /dev/null
+++ b/system/sections/servers/samp/rcon.php
@@ -0,0 +1,54 @@
+ 'Необходимо выбрать игрока.'));
+
+ if($url['action'] == 'kick')
+ rcon::cmd(array_merge($server, array('id' => $id)), 'kickid "'.$player.'" "EGP Panel"');
+ else
+ rcon::cmd(array_merge($server, array('id' => $id)), 'sm_slay "'.$player.'"');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+
+ include(LIB.'geo.php');
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $aPlayers = rcon::players(rcon::cmd(array_merge($server, array('id' => $id))));
+
+ foreach($aPlayers as $i => $aPlayer)
+ {
+ $html->get('player', 'sections/servers/'.$server['game'].'/rcon');
+
+ $html->set('i', $i);
+ $html->set('userid', $aPlayer['userid']);
+ $html->set('name', $aPlayer['name']);
+ $html->set('steamid', $aPlayer['steamid']);
+ $html->set('time', $aPlayer['time']);
+ $html->set('ping', $aPlayer['ping']);
+ $html->set('ip', $aPlayer['ip']);
+ $html->set('ico', $aPlayer['ico']);
+ $html->set('country', $aPlayer['country']);
+
+ $html->pack('players');
+ }
+
+ sys::outjs(array('s' => isset($html->arr['players']) ? $html->arr['players'] : ''));
+ }
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+ $html->nav('Rcon управление игроками');
+
+ $html->get('rcon', 'sections/servers/'.$server['game']);
+
+ $html->set('id', $id);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/settings.php b/system/sections/servers/samp/settings.php
new file mode 100644
index 0000000..b4de528
--- /dev/null
+++ b/system/sections/servers/samp/settings.php
@@ -0,0 +1,72 @@
+query('SELECT `uid`, `unit`, `tarif`, `pack` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ $html->nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ $aSub = array('start', 'server', 'firewall', 'crontab', 'startlogs', 'pack', 'file', 'api');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub))
+ {
+ $html->nav('Настройки', $cfg['http'].'servers/id/'.$id.'/section/settings');
+
+ if($go)
+ $nmch = sys::rep_act('server_settings_go_'.$id, 10);
+
+ if(in_array($url['subsection'], $aRouteSub['settings']))
+ include(SEC.'servers/games/settings/'.$url['subsection'].'.php');
+ else
+ include(SEC.'servers/'.$server['game'].'/settings/'.$url['subsection'].'.php');
+ }else{
+ $html->nav('Настройки');
+
+ if($mcache->get('server_settings_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_settings_'.$id);
+ else{
+ $sql->query('SELECT `name`, `packs` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aEditslist = 1;
+ include(DATA.'filedits.php');
+
+ // Построение списка доступных сборок
+ $aPacks = sys::b64djs($tarif['packs']);
+
+ $packs = ''.$aPacks[$server['pack']].' ';
+ unset($aPacks[$server['pack']]);
+
+ foreach($aPacks as $pack => $desc)
+ $packs .= ''.$desc.' ';
+
+ include(SEC.'servers/'.$server['game'].'/settings/start.php');
+
+ $html->get('settings', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('packs', $packs);
+ $html->set('start', $html->arr['start']);
+ if(isset($html->arr['edits']))
+ {
+ $html->set('edits', $html->arr['edits']);
+ $html->unit('edits', 1);
+ }else
+ $html->unit('edits');
+
+ $sql->query('SELECT `key` FROM `api` WHERE `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ {
+ $api = $sql->get();
+
+ $html->set('api', $api['key']);
+ $html->unit('api', 1, 1);
+ }else
+ $html->unit('api', 0, 1);
+ $html->pack('main');
+
+ $mcache->set('server_settings_'.$id, $html->arr['main'], false, 60);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/settings/server.php b/system/sections/servers/samp/settings/server.php
new file mode 100644
index 0000000..c9b07a5
--- /dev/null
+++ b/system/sections/servers/samp/settings/server.php
@@ -0,0 +1,119 @@
+nav('Параметры server.cfg');
+
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $sql->query('SELECT `install` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ {
+ if($go)
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/settings');
+ }
+
+ include(DATA.'scfg/'.$server['game'].'.php');
+
+ // Сохранение изменений
+ if($go)
+ {
+ $servercfg = isset($_POST['config']) ? $_POST['config'] : '';
+
+ $config = '';
+
+ $config_end = $servercfg['\'other\''];
+
+ unset($servercfg['\'other\'']);
+
+ foreach($servercfg as $cvar => $val)
+ if($val != '')
+ $config .= str_replace("'", '', $cvar).' '.$val."\n";
+
+ // Временый файл
+ $temp = sys::temp($config.$config_end);
+
+ $ssh->setfile($temp, $tarif['install'].$server['uid'].'/server.cfg', 0644);
+
+ $ssh->set('chown server'.$server['uid'].':servers '.$tarif['install'].$server['uid'].'/server.cfg');
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ // Чтение файла - server.cfg
+ $file = $tarif['install'].$server['uid'].'/server.cfg';
+
+ $ssh->set('echo "" >> '.$file.' && cat '.$file.' | grep -ve "^#\|^[[:space:]]*$"');
+
+ $fScfg = explode("\n", strip_tags($ssh->get()));
+
+ $servercfg = array();
+ $other = '';
+
+ // Убираем пробелы и генерируем массив
+ foreach($fScfg as $line)
+ {
+ // имя квара
+ $cvar = sys::first(explode(' ', $line));
+
+ if($cvar == '')
+ continue;
+
+ // убираем имя квара и оставляем только значение
+ $value = str_replace($cvar.' ', "", $line);
+
+ // выбираем только то, что нам нужно
+ preg_match_all('~([^"]+)~', $value, $cvar_value, PREG_SET_ORDER);
+
+ // Исключаем комментарии
+ if($cvar == '//')
+ continue;
+
+ $val = sys::first(explode(' //', $cvar_value[0][1]));
+
+ // Добавляем данные в массив
+ if(array_key_exists($cvar, $aScfg))
+ $servercfg[$cvar] = trim($val);
+ else
+ $other .= $line."\n";
+ }
+
+ foreach($aScfg as $name => $desc)
+ {
+ if(!isset($servercfg[$name]))
+ $servercfg[$name] = '';
+
+ // Формирование формы
+ if(strpos($aScfg_form[$name], 'select'))
+ $form = str_replace('value="'.$servercfg[$name].'"', 'value="'.$servercfg[$name].'" selected="select"', $aScfg_form[$name]);
+ else
+ $form = str_replace('['.$name.']', $servercfg[$name], $aScfg_form[$name]);
+
+ $html->get('servercfg_list', 'sections/servers/games/settings');
+
+ $html->set('name', $name);
+ $html->set('desc', $desc);
+ $html->set('form', $form);
+
+ $html->pack('list');
+ }
+
+ $sql->query('UPDATE `servers` set `map_start`="'.htmlspecialchars($servercfg['rcon_password']).'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $html->get('servercfg', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('cfg', $html->arr['list']);
+ $html->set('other', $other);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/settings/start.php b/system/sections/servers/samp/settings/start.php
new file mode 100644
index 0000000..862c835
--- /dev/null
+++ b/system/sections/servers/samp/settings/start.php
@@ -0,0 +1,45 @@
+query('SELECT `uid`, `slots`, `slots_start`, `autorestart` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = array_merge($server, $sql->get());
+
+ include(LIB.'games/games.php');
+
+ // Сохранение
+ if($go AND $url['save'])
+ {
+ $value = isset($url['value']) ? sys::int($url['value']) : sys::outjs(array('s' => 'ok'), $nmch);
+
+ switch($url['save'])
+ {
+ case 'slots':
+ $slots = $value > $server['slots'] ? $server['slots'] : $value;
+ $slots = $value < 2 ? 2 : $slots;
+
+ if($slots != $server['slots_start'])
+ $sql->query('UPDATE `servers` set `slots_start`="'.$slots.'" WHERE `id`="'.$id.'" LIMIT 1');
+
+ $mcache->delete('server_settings_'.$id);
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+ }
+
+ // Генерация списка слот
+ $slots = '';
+
+ for($slot = 2; $slot <= $server['slots']; $slot+=1)
+ $slots .= ''.$slot.' шт. ';
+
+ // Авторестарт при зависании
+ $autorestart = $server['autorestart'] ? 'Включен Выключен ' : 'Выключен Включен ';
+
+ $html->get('start', 'sections/servers/'.$server['game'].'/settings');
+
+ $html->set('id', $id);
+ $html->set('autorestart', $autorestart);
+ $html->set('slots', str_replace('"'.$server['slots_start'].'"', '"'.$server['slots_start'].'" selected="select"', $slots));
+
+ $html->pack('start');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/tarif.php b/system/sections/servers/samp/tarif.php
new file mode 100644
index 0000000..c321da7
--- /dev/null
+++ b/system/sections/servers/samp/tarif.php
@@ -0,0 +1,12 @@
+query('SELECT `name`, `slots_min`, `slots_max`, `install`, `timext`, `discount`, `price` FROM `tarifs` WHERE `id`="'.$server['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Подразделы
+ $aSub = array('extend', 'address', 'addextend', 'unit', 'slots');
+
+ include(SEC.'servers/games/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/tarif/extend.php b/system/sections/servers/samp/tarif/extend.php
new file mode 100644
index 0000000..cf31b1e
--- /dev/null
+++ b/system/sections/servers/samp/tarif/extend.php
@@ -0,0 +1,48 @@
+ 'Переданы не все данные'), $nmch);
+
+ // Проверка периода
+ if(!in_array($aData['time'], explode(':', $tarif['timext'])))
+ sys::outjs(array('e' => 'Переданы неверные данные'), $nmch);
+
+ }
+
+ $aData['promo'] = isset($_POST['promo']) ? $_POST['promo'] : '';
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : false;
+ $aData['server'] = $id;
+ $aData['user'] = $server['user'];
+ $aData['tarif'] = $server['tarif'];
+ $aData['slots'] = $server['slots'];
+
+ // Цена за выделенный адрес
+ $add_sum = tarifs::address_add_sum($aData['address'], $server);
+
+ // Цена за 30 дней 1 слота
+ $price = $tarif['price'];
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = $server['time'];
+
+ // Цена аренды
+ $sum = games::define_sum($tarif['discount'], $price, $server['slots'], $aData['time'], 'extend')+$add_sum;
+
+ // Если расчетный период
+ if($cfg['settlement_period'])
+ $aData['time'] = games::define_period('extend', params::$aDayMonth, $server['time']);
+
+ $days = params::$aDayMonth[date('n', $server['time'])] == $aData['time'] ? 'месяц' : games::parse_day($aData['time'], true);
+
+ include(SEC.'servers/games/tarif/extend.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/tarif/slots.php b/system/sections/servers/samp/tarif/slots.php
new file mode 100644
index 0000000..3a60d63
--- /dev/null
+++ b/system/sections/servers/samp/tarif/slots.php
@@ -0,0 +1,31 @@
+ 'На данном тарифе нельзя изменить количество слот.'), $nmch);
+
+ $slots = isset($url['slots']) ? sys::int($url['slots']) : sys::outjs(array('e' => 'Переданы не все данные.'), $nmch);
+
+ $overdue = $server['time'] < $start_point;
+
+ if($cfg['change_slots'][$server['game']]['days'] || $overdue)
+ {
+ // Цена за 1 день
+ $price = $tarif['price']/30;
+
+ // Цена аренды за остаток дней (с текущим кол-вом слот)
+ $price_old = ($server['time']-$start_point)/86400*$price*$server['slots'];
+ }
+
+ $max = $tarif['slots_max']-$server['slots'];
+
+ // Сумма за добавляемые слоты
+ $sum = round(($server['time']-$start_point)/86400*($tarif['price']/30)*$slots, 2);
+
+ include(SEC.'servers/games/tarif/slots.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/tarif/unit.php b/system/sections/servers/samp/tarif/unit.php
new file mode 100644
index 0000000..b840bc7
--- /dev/null
+++ b/system/sections/servers/samp/tarif/unit.php
@@ -0,0 +1,50 @@
+ 'Переданы не все данные.'), $nmch);
+
+ if(!$cfg['change_unit'][$server['game']] || $server['time'] < $start_point+86400 || $server['test'])
+ exit;
+
+ $sql->query('SELECT `id`, `unit`, `packs`, `tickrate`, `price` FROM `tarifs` WHERE `unit`="'.$uid.'" AND `game`="'.$server['game'].'" AND `name`="'.$tarif['name'].'" AND `id`!="'.$server['tarif'].'" AND `show`="1" ORDER BY `unit`');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Не найден подходящий тариф.'), $nmch);
+
+ $oldTarif = $tarif;
+
+ $tarif = $sql->get();
+
+ $sql->query('SELECT `address`, `passwd`, `sql_login`, `sql_passwd`, `sql_port`, `sql_ftp` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $oldUnit = $sql->get();
+
+ $aPriceold = explode(':', $oldTarif['price']);
+ $aTICKold = explode(':', $oldTarif['tickrate']);
+
+ $sql->query('SELECT `id` FROM `units` WHERE `id`="'.$tarif['unit'].'" AND `show`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Выбранная локация не доступна.'), $nmch);
+
+ $aPrice = explode(':', $tarif['price']);
+ $aTICK = explode(':', $tarif['tickrate']);
+
+ if(!in_array($server['tickrate'], $aTICK))
+ sys::outjs(array('e' => 'Не найден подходящий тарифный план.'), $nmch);
+
+ // Цена за 1 день (при новом тарифном плане)
+ $price = $aPrice[array_search($server['tickrate'], $aTICK)]/30*$server['slots'];
+
+ // Цена аренды за остаток дней (с текущим тарифным планом)
+ $oldprice = ($server['time']-$start_point)/86400*($aPriceold[array_search($server['tickrate'], $aTICKold)]/30*$server['slots']);
+
+ $date = date('H.i.s.d.m.Y', round($start_point+$oldprice/$price*86400-86400));
+
+ $aDate = explode('.', $date);
+
+ $time = mktime($aDate[0], $aDate[1], $aDate[2], $aDate[4], $aDate[3], $aDate[5]);
+
+ include(SEC.'servers/games/tarif/unit.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/samp/web.php b/system/sections/servers/samp/web.php
new file mode 100644
index 0000000..634e9b6
--- /dev/null
+++ b/system/sections/servers/samp/web.php
@@ -0,0 +1,87 @@
+nav($server['address'], $cfg['http'].'servers/id/'.$id);
+
+ include(DATA.'web.php');
+
+ // Если выбран подраздел
+ if(isset($url['subsection']) AND in_array($url['subsection'], $aSub) AND in_array($url['action'], array_merge($aAction[$url['subsection']], array('install', 'manage'))))
+ {
+ if($go)
+ $nmch = sys::rep_act('server_web_go_'.$id, 10);
+ else
+ $html->nav('Web', $cfg['http'].'servers/id/'.$id.'/section/web');
+
+ include(SEC.'web/'.$url['subsection'].'/free/'.$url['action'].'.php');
+ }else{
+ $html->nav('Web');
+
+ if($mcache->get('server_web_'.$id) != '')
+ $html->arr['main'] = $mcache->get('server_web_'.$id);
+ else{
+ // Услуги
+ foreach($aWeb[$server['game']] as $service => $active)
+ {
+ if($active)
+ {
+ if($service == 'hosting')
+ {
+ if(!$aWebVHTtype && !in_array($server['tarif'], $aWebVHT))
+ continue;
+
+ if($aWebVHTtype && in_array($server['tarif'], $aWebVHT))
+ continue;
+ }
+
+ // Проверка на установку
+ switch($aWebInstall[$server['game']][$service])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$service.'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ }
+
+ if($sql->num())
+ $html->get('list_install', 'sections/servers/games/web');
+ else
+ $html->get('list', 'sections/servers/games/web');
+
+ $html->set('id', $id);
+ $html->set('service', $service);
+ $html->set('name', $aWebname[$service]);
+ $html->set('desc', $aWebDesc[$service]);
+
+ $html->pack($aWebType[$service]);
+ }
+ }
+
+ // Блоки услуг
+ foreach($aWebTypeInfo[$server['game']] as $type => $name)
+ {
+ if(!isset($html->arr[$type]))
+ continue;
+
+ $html->get('block', 'sections/servers/games/web');
+ $html->set('name', $name);
+ $html->set('list', $html->arr[$type]);
+ $html->pack('web');
+ }
+
+ $html->get('web', 'sections/servers/'.$server['game']);
+ $html->set('id', $id);
+ $html->set('web', isset($html->arr['web']) ? $html->arr['web'] : 'Дополнительные услуги отсутствуют');
+ $html->pack('main');
+
+ $mcache->set('server_web_'.$id, $html->arr['main'], false, 4);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/servers/scan.php b/system/sections/servers/scan.php
new file mode 100644
index 0000000..f2e2f23
--- /dev/null
+++ b/system/sections/servers/scan.php
@@ -0,0 +1,27 @@
+query('SELECT `game` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ include(LIB.'games/'.$server['game'].'/scan.php');
+
+ // Запрошена информация (статус, онлайн, название)
+ if(isset($url['mon']))
+ sys::outjs(scan::mon($id));
+
+ // Запрошена информация (статус, онлайн, название, игроки)
+ if(isset($url['fmon']))
+ sys::outjs(scan::mon($id, true));
+
+ // Запрошена информация (cpu, ram, hdd)
+ if(isset($url['resources']))
+ sys::outjs(scan::resources($id));
+
+ // Запрошена информация (работает, меняется карта, переустанавливается)
+ if(isset($url['status']))
+ sys::outjs(scan::status($id));
+
+ exit;
+?>
\ No newline at end of file
diff --git a/system/sections/servers/settings.php b/system/sections/servers/settings.php
new file mode 100644
index 0000000..d8dd817
--- /dev/null
+++ b/system/sections/servers/settings.php
@@ -0,0 +1,11 @@
+query('SELECT `unit`, `address`, `game`, `status`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'settings');
+
+ include(sys::route($server, 'settings', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/servers/tarif.php b/system/sections/servers/tarif.php
new file mode 100644
index 0000000..0d5bbfe
--- /dev/null
+++ b/system/sections/servers/tarif.php
@@ -0,0 +1,18 @@
+query('SELECT `uid`, `unit`, `user`, `tarif`, `address`, `port`, `game`, `status`, `slots`, `slots_start`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time`, `test`, `fps`, `tickrate`, `ram`, `ram_fix` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'tarif');
+
+ if($server['status'] == 'blocked')
+ {
+ if($go)
+ sys::out('Раздел недоступен');
+
+ include(SEC.'servers/noaccess.php');
+ }else
+ include(SEC.'servers/'.$server['game'].'/tarif.php');
+?>
\ No newline at end of file
diff --git a/system/sections/servers/web.php b/system/sections/servers/web.php
new file mode 100644
index 0000000..ea2e3e5
--- /dev/null
+++ b/system/sections/servers/web.php
@@ -0,0 +1,11 @@
+query('SELECT `uid`, `unit`, `tarif`, `user`, `address`, `game`, `status`, `name`, `slots_start`, `plugins_use`, `ftp_use`, `console_use`, `stats_use`, `copy_use`, `web_use`, `time` FROM `servers` WHERE `id`="'.$id.'" LIMIT 1');
+ $server = $sql->get();
+
+ sys::nav($server, $id, 'web');
+
+ include(sys::route($server, 'web', $go));
+?>
\ No newline at end of file
diff --git a/system/sections/services/control.php b/system/sections/services/control.php
new file mode 100644
index 0000000..23a3362
--- /dev/null
+++ b/system/sections/services/control.php
@@ -0,0 +1,114 @@
+get('buy_server'))
+ sleep(1.5);
+
+ $mcache->set('buy_server', true, false, 3);
+
+ $aData = array(
+ 'address' => isset($_POST['address']) ? trim($_POST['address']) : 0,
+ 'passwd' => isset($_POST['passwd']) ? $_POST['passwd'] : 0,
+ 'time' => isset($_POST['time']) ? sys::int($_POST['time']) : 30,
+ 'limit' => isset($_POST['limit']) ? sys::int($_POST['limit']) : key(array_shift($cfg['control_limit']))
+ );
+
+ if(sys::valid($aData['address'], 'ip'))
+ sys::outjs(array('e' => 'Указанный адрес имеет неправильный формат'));
+
+ $sql->query('SELECT `id` FROM `control` WHERE `address`="'.$aData['address'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный сервер уже подключен'));
+
+ if(sys::strlen($aData['passwd']) > 32)
+ sys::outjs(array('e' => 'Указанный пароль слишком длинный'));
+
+ if(sys::valid($aData['passwd'], 'other', $aValid['passwd']))
+ sys::outjs(array('e' => 'Пожалуйста, поменяйте пароль используя только латинские буквы и цифры'));
+
+ if(!array_key_exists($aData['limit'], $cfg['control_limit']))
+ $aData['limit'] = key(array_shift($cfg['control_limit']));
+
+ if(!in_array($aData['time'], $cfg['control_time']))
+ $aData['time'] = array_shift($cfg['control_time']);
+
+ $sum = games::define_sum(false, $cfg['control_limit'][$aData['limit']], 1, $aData['time']);
+
+ // Проверка баланса
+ if($user['balance'] < $sum)
+ sys::outjs(array('e' => 'У вас не хватает '.(round($sum-$user['balance'], 2)).' '.$cfg['currency']));
+
+ include(LIB.'ssh.php');
+
+ // Проверка ssh соединения с физ. сервером
+ if(!$ssh->auth($aData['passwd'], $aData['address']))
+ sys::outjs(array('e' => 'Не удалось создать связь с физическим сервером, проверьте адрес и пароль'));
+
+ // Списание средств с баланса пользователя
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']-$sum).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Реф. система
+ games::part($user['id'], $sum);
+
+ $days = $cfg['settlement_period'] ? games::define_period('buy', params::$aDayMonth) : $aData['time'];
+
+ $sql->query('INSERT INTO `control` set '
+ .'`user`="'.$user['id'].'",'
+ .'`address`="'.$aData['address'].'",'
+ .'`passwd`="'.$aData['passwd'].'",'
+ .'`time`="'.($start_point+$days*86400).'",'
+ .'`date`="'.$start_point.'",'
+ .'`limit`="'.$aData['limit'].'",'
+ .'`price`="'.$sum.'",'
+ .'`status`="install"');
+
+ $id = $sql->id();
+
+ // Запись логов
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'buy_control'), array('days' => games::parse_day($days, true), 'money' => $sum, 'id' => $id)).'", `date`="'.$start_point.'", `type`="buy", `money`="'.$sum.'"');
+
+ sys::outjs(array('s' => 'ok', 'id' => $id));
+ }
+
+ if(isset($url['get']))
+ {
+ if(!isset($url['time']) || !in_array($url['time'], $cfg['control_time']))
+ $url['time'] = array_shift($cfg['control_time']);
+
+ if(!isset($url['limit']) || !array_key_exists($url['limit'], $cfg['control_limit']))
+ $url['limit'] = key(array_shift($cfg['control_limit']));
+
+ sys::out(games::define_sum(false, $cfg['control_limit'][$url['limit']], 1, $url['time']));
+ }
+
+ $options = '';
+
+ foreach($cfg['control_time'] as $time)
+ $options .= ''.games::parse_day($time, true).' ';
+
+ $limits = '';
+
+ foreach($cfg['control_limit'] as $limit => $price)
+ $limits .= 'Серверов: '.$limit.' шт. / '.$price.' '.$cfg['currency'].' ';
+
+ $html->get('index', 'sections/services/control');
+ $html->set('time', $options);
+ $html->set('limit', $limits);
+ $html->set('cur', $cfg['currency']);
+ if($cfg['settlement_period'])
+ {
+ $html->set('date', date('d.m.Y', $start_point));
+ $html->unit('settlement_period', true, true);
+ }else
+ $html->unit('settlement_period', false, true);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/services/crmp.php b/system/sections/services/crmp.php
new file mode 100644
index 0000000..b76d999
--- /dev/null
+++ b/system/sections/services/crmp.php
@@ -0,0 +1,144 @@
+get('buy_server'))
+ sleep(1.5);
+
+ $mcache->set('buy_server', true, false, 3);
+
+ include(LIB.'games/'.$section.'/service.php');
+
+ // Входные данные
+ $aData = array(
+ 'unit' => isset($_POST['unit']) ? sys::int($_POST['unit']) : 0,
+ 'tarif' => isset($_POST['tarif']) ? sys::int($_POST['tarif']) : 0,
+ 'pack' => isset($_POST['pack']) ? $_POST['pack'] : '',
+ 'slots' => isset($_POST['slots']) ? sys::int($_POST['slots']) : 0,
+ 'time' => isset($_POST['time']) ? sys::int($_POST['time']) : 30,
+ 'test' => (isset($_POST['time']) AND $_POST['time'] == 'test') ? true : false,
+ 'promo' => isset($_POST['promo']) ? $_POST['promo'] : false,
+ );
+
+ // Массвив данных
+ $aSDATA = service::buy($aData);
+
+ // Процесс выдачи игрового сервера
+ $id = service::install($aSDATA);
+
+ sys::outjs(array('s' => 'ok', 'id' => $id));
+ }
+
+ include(LIB.'games/services.php');
+
+ $check = false;
+
+ // Проверка наличия доступной локации
+ $sql->query(services::unit($section));
+ if($sql->num())
+ {
+ // Выбранная локация
+ if(isset($url['get']) AND in_array($url['get'], array('tarifs', 'data')))
+ $sql->query('SELECT `id`, `test` FROM `units` WHERE `id`="'.$id.'" LIMIT 1');
+
+ $select_unit = $sql->get();
+
+ // Генерация списка локаций
+ $units = services::units($section);
+
+ // Генерация списка тарифов
+ $tarifs = services::tarifs($section, $select_unit['id']);
+
+ if(isset($url['get']) AND in_array($url['get'], array('price', 'promo')))
+ {
+ $aGet = array(
+ 'tarif' => sys::int($url['tarif']),
+ 'slots' => sys::int($url['slots']),
+ 'time' => sys::int($url['time']),
+ 'user' => $user['id']
+ );
+
+ $sql->query('SELECT `price`, `discount` FROM `tarifs` WHERE `id`="'.$aGet['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Выхлоп цены за выбранные параметры
+ if($url['get'] == 'price')
+ {
+ // Если выбран тестовый период
+ if($url['time'] == 'test')
+ sys::outjs(array('sum' => 0));
+
+ sys::outjs(array(
+ 'sum' => games::define_sum($tarif['discount'], $tarif['price'], $aGet['slots'], $aGet['time'])
+ ));
+ }
+
+ // Выхлоп цены с учетом промо-кода
+ if($url['get'] == 'promo')
+ games::define_promo(
+ $url['cod'],
+ $aGet,
+ $tarif['discount'],
+ games::define_sum($tarif['discount'], $tarif['price'], $aGet['slots'], $aGet['time'])
+ );
+ }
+
+ // Генерация сборок/слот/периодов
+ if(isset($url['get']) AND $url['get'] == 'data')
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `time`, `test`, `discount` FROM `tarifs` WHERE `id`="'.sys::int($url['tarif']).'" LIMIT 1');
+ else
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `time`, `test`, `discount` FROM `tarifs` WHERE `game`="'.$section.'" AND `unit`="'.$select_unit['id'].'" AND `show`="1" ORDER BY `sort` ASC LIMIT 1');
+
+ if($sql->num())
+ {
+ $select_tarif = $sql->get();
+
+ $aTarif = games::parse_tarif($select_tarif, $select_unit);
+
+ if(isset($url['get']))
+ {
+ // Выхлоп при выборе локации
+ if($url['get'] == 'tarifs')
+ sys::outjs(array_merge(array('tarifs' => $tarifs), $aTarif));
+
+ // Выхлоп при выборе тарифа
+ if($url['get'] == 'data')
+ sys::outjs($aTarif);
+ }
+
+ $html->get($section, 'sections/services/games');
+ $html->set('units', $units);
+ $html->set('tarifs', $tarifs);
+ $html->set('packs', $aTarif['packs']);
+ $html->set('slots', $aTarif['slots']);
+ $html->set('time', $aTarif['time']);
+ $html->set('cur', $cfg['currency']);
+ $html->set('date', date('d.m.Y', $start_point));
+
+ if($cfg['settlement_period'])
+ $html->unit('settlement_period', true, true);
+ else
+ $html->unit('settlement_period', false, true);
+
+ $html->unit('informer', false, true);
+ $html->pack('main');
+
+ $check = true;
+ }
+ }
+
+ if(!$check)
+ {
+ $html->get($section, 'sections/services/games');
+ $html->unit('informer', true, true);
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/services/cs.php b/system/sections/services/cs.php
new file mode 100644
index 0000000..ac7004f
--- /dev/null
+++ b/system/sections/services/cs.php
@@ -0,0 +1,152 @@
+get('buy_server'))
+ sleep(1.5);
+
+ $mcache->set('buy_server', true, false, 3);
+
+ include(LIB.'games/'.$section.'/service.php');
+
+ // Входные данные
+ $aData = array(
+ 'unit' => isset($_POST['unit']) ? sys::int($_POST['unit']) : 0,
+ 'tarif' => isset($_POST['tarif']) ? sys::int($_POST['tarif']) : 0,
+ 'pack' => isset($_POST['pack']) ? $_POST['pack'] : '',
+ 'fps' => isset($_POST['fps']) ? sys::int($_POST['fps']) : 0,
+ 'slots' => isset($_POST['slots']) ? sys::int($_POST['slots']) : 0,
+ 'time' => isset($_POST['time']) ? sys::int($_POST['time']) : 30,
+ 'test' => (isset($_POST['time']) AND $_POST['time'] == 'test') ? true : false,
+ 'promo' => isset($_POST['promo']) ? $_POST['promo'] : false,
+ );
+
+ // Массвив данных
+ $aSDATA = service::buy($aData);
+
+ // Процесс выдачи игрового сервера
+ $id = service::install($aSDATA);
+
+ sys::outjs(array('s' => 'ok', 'id' => $id));
+ }
+
+ include(LIB.'games/services.php');
+
+ $check = false;
+
+ // Проверка наличия доступной локации
+ $sql->query(services::unit($section));
+ if($sql->num())
+ {
+ // Выбранная локация
+ if(isset($url['get']) AND in_array($url['get'], array('tarifs', 'data')))
+ $sql->query('SELECT `id`, `test` FROM `units` WHERE `id`="'.$id.'" LIMIT 1');
+
+ $select_unit = $sql->get();
+
+ // Генерация списка локаций
+ $units = services::units($section);
+
+ // Генерация списка тарифов
+ $tarifs = services::tarifs($section, $select_unit['id']);
+
+ if(isset($url['get']) AND in_array($url['get'], array('price', 'promo')))
+ {
+ $aGet = array(
+ 'tarif' => sys::int($url['tarif']),
+ 'fps' => sys::int($url['fps']),
+ 'slots' => sys::int($url['slots']),
+ 'time' => sys::int($url['time']),
+ 'user' => $user['id']
+ );
+
+ $sql->query('SELECT `price`, `fps`, `discount` FROM `tarifs` WHERE `id`="'.$aGet['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aPrice = explode(':', $tarif['price']);
+
+ // Определение цены
+ $price = $aPrice[array_search($aGet['fps'], explode(':', $tarif['fps']))];
+
+ // Выхлоп цены за выбранные параметры
+ if($url['get'] == 'price')
+ {
+ // Если выбран тестовый период
+ if($url['time'] == 'test')
+ sys::outjs(array('sum' => 0));
+
+ sys::outjs(array(
+ 'sum' => games::define_sum($tarif['discount'], $price, $aGet['slots'], $aGet['time'])
+ ));
+ }
+
+ // Выхлоп цены с учетом промо-кода
+ if($url['get'] == 'promo')
+ games::define_promo(
+ $url['cod'],
+ $aGet,
+ $tarif['discount'],
+ games::define_sum($tarif['discount'], $price, $aGet['slots'], $aGet['time'])
+ );
+ }
+
+ // Генерация сборок/слот/периодов
+ if(isset($url['get']) AND $url['get'] == 'data')
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `fps`, `time`, `test`, `discount` FROM `tarifs` WHERE `id`="'.sys::int($url['tarif']).'" LIMIT 1');
+ else
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `fps`, `time`, `test`, `discount` FROM `tarifs` WHERE `game`="'.$section.'" AND `unit`="'.$select_unit['id'].'" AND `show`="1" ORDER BY `sort` ASC LIMIT 1');
+
+ if($sql->num())
+ {
+ $select_tarif = $sql->get();
+
+ $aTarif = games::parse_tarif($select_tarif, $select_unit);
+
+ if(isset($url['get']))
+ {
+ // Выхлоп при выборе локации
+ if($url['get'] == 'tarifs')
+ sys::outjs(array_merge(array('tarifs' => $tarifs), $aTarif));
+
+ // Выхлоп при выборе тарифа
+ if($url['get'] == 'data')
+ sys::outjs($aTarif);
+ }
+
+ $html->get($section, 'sections/services/games');
+ $html->set('units', $units);
+ $html->set('tarifs', $tarifs);
+ $html->set('packs', $aTarif['packs']);
+ $html->set('fps', $aTarif['fps']);
+ $html->set('slots', $aTarif['slots']);
+ $html->set('time', $aTarif['time']);
+ $html->set('cur', $cfg['currency']);
+ $html->set('date', date('d.m.Y', $start_point));
+
+ if($cfg['settlement_period'])
+ $html->unit('settlement_period', true, true);
+ else
+ $html->unit('settlement_period', false, true);
+
+ $html->unit('informer', false, true);
+ $html->pack('main');
+
+ $check = true;
+ }
+ }
+
+ if(!$check)
+ {
+ $html->get($section, 'sections/services/games');
+ $html->unit('informer', true, true);
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/services/csgo.php b/system/sections/services/csgo.php
new file mode 100644
index 0000000..1e3c431
--- /dev/null
+++ b/system/sections/services/csgo.php
@@ -0,0 +1,152 @@
+get('buy_server'))
+ sleep(1.5);
+
+ $mcache->set('buy_server', true, false, 3);
+
+ include(LIB.'games/'.$section.'/service.php');
+
+ // Входные данные
+ $aData = array(
+ 'unit' => isset($_POST['unit']) ? sys::int($_POST['unit']) : 0,
+ 'tarif' => isset($_POST['tarif']) ? sys::int($_POST['tarif']) : 0,
+ 'pack' => isset($_POST['pack']) ? $_POST['pack'] : '',
+ 'tickrate' => isset($_POST['tickrate']) ? sys::int($_POST['tickrate']) : 0,
+ 'slots' => isset($_POST['slots']) ? sys::int($_POST['slots']) : 0,
+ 'time' => isset($_POST['time']) ? sys::int($_POST['time']) : 30,
+ 'test' => (isset($_POST['time']) AND $_POST['time'] == 'test') ? true : false,
+ 'promo' => isset($_POST['promo']) ? $_POST['promo'] : false,
+ );
+
+ // Массвив данных
+ $aSDATA = service::buy($aData);
+
+ // Процесс выдачи игрового сервера
+ $id = service::install($aSDATA);
+
+ sys::outjs(array('s' => 'ok', 'id' => $id));
+ }
+
+ include(LIB.'games/services.php');
+
+ $check = false;
+
+ // Проверка наличия доступной локации
+ $sql->query(services::unit($section));
+ if($sql->num())
+ {
+ // Выбранная локация
+ if(isset($url['get']) AND in_array($url['get'], array('tarifs', 'data')))
+ $sql->query('SELECT `id`, `test` FROM `units` WHERE `id`="'.$id.'" LIMIT 1');
+
+ $select_unit = $sql->get();
+
+ // Генерация списка локаций
+ $units = services::units($section);
+
+ // Генерация списка тарифов
+ $tarifs = services::tarifs($section, $select_unit['id']);
+
+ if(isset($url['get']) AND in_array($url['get'], array('price', 'promo')))
+ {
+ $aGet = array(
+ 'tarif' => sys::int($url['tarif']),
+ 'tickrate' => sys::int($url['tickrate']),
+ 'slots' => sys::int($url['slots']),
+ 'time' => sys::int($url['time']),
+ 'user' => $user['id']
+ );
+
+ $sql->query('SELECT `price`, `tickrate`, `discount` FROM `tarifs` WHERE `id`="'.$aGet['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aPrice = explode(':', $tarif['price']);
+
+ // Определение цены
+ $price = $aPrice[array_search($aGet['tickrate'], explode(':', $tarif['tickrate']))];
+
+ // Выхлоп цены за выбранные параметры
+ if($url['get'] == 'price')
+ {
+ // Если выбран тестовый период
+ if($url['time'] == 'test')
+ sys::outjs(array('sum' => 0));
+
+ sys::outjs(array(
+ 'sum' => games::define_sum($tarif['discount'], $price, $aGet['slots'], $aGet['time'])
+ ));
+ }
+
+ // Выхлоп цены с учетом промо-кода
+ if($url['get'] == 'promo')
+ games::define_promo(
+ $url['cod'],
+ $aGet,
+ $tarif['discount'],
+ games::define_sum($tarif['discount'], $price, $aGet['slots'], $aGet['time'])
+ );
+ }
+
+ // Генерация сборок/слот/периодов
+ if(isset($url['get']) AND $url['get'] == 'data')
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `tickrate`, `time`, `test`, `discount` FROM `tarifs` WHERE `id`="'.sys::int($url['tarif']).'" LIMIT 1');
+ else
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `tickrate`, `time`, `test`, `discount` FROM `tarifs` WHERE `game`="'.$section.'" AND `unit`="'.$select_unit['id'].'" AND `show`="1" ORDER BY `sort` ASC LIMIT 1');
+
+ if($sql->num())
+ {
+ $select_tarif = $sql->get();
+
+ $aTarif = games::parse_tarif($select_tarif, $select_unit);
+
+ if(isset($url['get']))
+ {
+ // Выхлоп при выборе локации
+ if($url['get'] == 'tarifs')
+ sys::outjs(array_merge(array('tarifs' => $tarifs), $aTarif));
+
+ // Выхлоп при выборе тарифа
+ if($url['get'] == 'data')
+ sys::outjs($aTarif);
+ }
+
+ $html->get($section, 'sections/services/games');
+ $html->set('units', $units);
+ $html->set('tarifs', $tarifs);
+ $html->set('packs', $aTarif['packs']);
+ $html->set('tickrate', $aTarif['tickrate']);
+ $html->set('slots', $aTarif['slots']);
+ $html->set('time', $aTarif['time']);
+ $html->set('cur', $cfg['currency']);
+ $html->set('date', date('d.m.Y', $start_point));
+
+ if($cfg['settlement_period'])
+ $html->unit('settlement_period', true, true);
+ else
+ $html->unit('settlement_period', false, true);
+
+ $html->unit('informer', false, true);
+ $html->pack('main');
+
+ $check = true;
+ }
+ }
+
+ if(!$check)
+ {
+ $html->get($section, 'sections/services/games');
+ $html->unit('informer', true, true);
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/services/css.php b/system/sections/services/css.php
new file mode 100644
index 0000000..1e3c431
--- /dev/null
+++ b/system/sections/services/css.php
@@ -0,0 +1,152 @@
+get('buy_server'))
+ sleep(1.5);
+
+ $mcache->set('buy_server', true, false, 3);
+
+ include(LIB.'games/'.$section.'/service.php');
+
+ // Входные данные
+ $aData = array(
+ 'unit' => isset($_POST['unit']) ? sys::int($_POST['unit']) : 0,
+ 'tarif' => isset($_POST['tarif']) ? sys::int($_POST['tarif']) : 0,
+ 'pack' => isset($_POST['pack']) ? $_POST['pack'] : '',
+ 'tickrate' => isset($_POST['tickrate']) ? sys::int($_POST['tickrate']) : 0,
+ 'slots' => isset($_POST['slots']) ? sys::int($_POST['slots']) : 0,
+ 'time' => isset($_POST['time']) ? sys::int($_POST['time']) : 30,
+ 'test' => (isset($_POST['time']) AND $_POST['time'] == 'test') ? true : false,
+ 'promo' => isset($_POST['promo']) ? $_POST['promo'] : false,
+ );
+
+ // Массвив данных
+ $aSDATA = service::buy($aData);
+
+ // Процесс выдачи игрового сервера
+ $id = service::install($aSDATA);
+
+ sys::outjs(array('s' => 'ok', 'id' => $id));
+ }
+
+ include(LIB.'games/services.php');
+
+ $check = false;
+
+ // Проверка наличия доступной локации
+ $sql->query(services::unit($section));
+ if($sql->num())
+ {
+ // Выбранная локация
+ if(isset($url['get']) AND in_array($url['get'], array('tarifs', 'data')))
+ $sql->query('SELECT `id`, `test` FROM `units` WHERE `id`="'.$id.'" LIMIT 1');
+
+ $select_unit = $sql->get();
+
+ // Генерация списка локаций
+ $units = services::units($section);
+
+ // Генерация списка тарифов
+ $tarifs = services::tarifs($section, $select_unit['id']);
+
+ if(isset($url['get']) AND in_array($url['get'], array('price', 'promo')))
+ {
+ $aGet = array(
+ 'tarif' => sys::int($url['tarif']),
+ 'tickrate' => sys::int($url['tickrate']),
+ 'slots' => sys::int($url['slots']),
+ 'time' => sys::int($url['time']),
+ 'user' => $user['id']
+ );
+
+ $sql->query('SELECT `price`, `tickrate`, `discount` FROM `tarifs` WHERE `id`="'.$aGet['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aPrice = explode(':', $tarif['price']);
+
+ // Определение цены
+ $price = $aPrice[array_search($aGet['tickrate'], explode(':', $tarif['tickrate']))];
+
+ // Выхлоп цены за выбранные параметры
+ if($url['get'] == 'price')
+ {
+ // Если выбран тестовый период
+ if($url['time'] == 'test')
+ sys::outjs(array('sum' => 0));
+
+ sys::outjs(array(
+ 'sum' => games::define_sum($tarif['discount'], $price, $aGet['slots'], $aGet['time'])
+ ));
+ }
+
+ // Выхлоп цены с учетом промо-кода
+ if($url['get'] == 'promo')
+ games::define_promo(
+ $url['cod'],
+ $aGet,
+ $tarif['discount'],
+ games::define_sum($tarif['discount'], $price, $aGet['slots'], $aGet['time'])
+ );
+ }
+
+ // Генерация сборок/слот/периодов
+ if(isset($url['get']) AND $url['get'] == 'data')
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `tickrate`, `time`, `test`, `discount` FROM `tarifs` WHERE `id`="'.sys::int($url['tarif']).'" LIMIT 1');
+ else
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `tickrate`, `time`, `test`, `discount` FROM `tarifs` WHERE `game`="'.$section.'" AND `unit`="'.$select_unit['id'].'" AND `show`="1" ORDER BY `sort` ASC LIMIT 1');
+
+ if($sql->num())
+ {
+ $select_tarif = $sql->get();
+
+ $aTarif = games::parse_tarif($select_tarif, $select_unit);
+
+ if(isset($url['get']))
+ {
+ // Выхлоп при выборе локации
+ if($url['get'] == 'tarifs')
+ sys::outjs(array_merge(array('tarifs' => $tarifs), $aTarif));
+
+ // Выхлоп при выборе тарифа
+ if($url['get'] == 'data')
+ sys::outjs($aTarif);
+ }
+
+ $html->get($section, 'sections/services/games');
+ $html->set('units', $units);
+ $html->set('tarifs', $tarifs);
+ $html->set('packs', $aTarif['packs']);
+ $html->set('tickrate', $aTarif['tickrate']);
+ $html->set('slots', $aTarif['slots']);
+ $html->set('time', $aTarif['time']);
+ $html->set('cur', $cfg['currency']);
+ $html->set('date', date('d.m.Y', $start_point));
+
+ if($cfg['settlement_period'])
+ $html->unit('settlement_period', true, true);
+ else
+ $html->unit('settlement_period', false, true);
+
+ $html->unit('informer', false, true);
+ $html->pack('main');
+
+ $check = true;
+ }
+ }
+
+ if(!$check)
+ {
+ $html->get($section, 'sections/services/games');
+ $html->unit('informer', true, true);
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/services/cssold.php b/system/sections/services/cssold.php
new file mode 100644
index 0000000..586446b
--- /dev/null
+++ b/system/sections/services/cssold.php
@@ -0,0 +1,155 @@
+get('buy_server'))
+ sleep(1.5);
+
+ $mcache->set('buy_server', true, false, 3);
+
+ include(LIB.'games/'.$section.'/service.php');
+
+ // Входные данные
+ $aData = array(
+ 'unit' => isset($_POST['unit']) ? sys::int($_POST['unit']) : 0,
+ 'tarif' => isset($_POST['tarif']) ? sys::int($_POST['tarif']) : 0,
+ 'pack' => isset($_POST['pack']) ? $_POST['pack'] : '',
+ 'tickrate' => isset($_POST['tickrate']) ? sys::int($_POST['tickrate']) : 0,
+ 'fps' => isset($_POST['fps']) ? sys::int($_POST['fps']) : 0,
+ 'slots' => isset($_POST['slots']) ? sys::int($_POST['slots']) : 0,
+ 'time' => isset($_POST['time']) ? sys::int($_POST['time']) : 30,
+ 'test' => (isset($_POST['time']) AND $_POST['time'] == 'test') ? true : false,
+ 'promo' => isset($_POST['promo']) ? $_POST['promo'] : false,
+ );
+
+ // Массвив данных
+ $aSDATA = service::buy($aData);
+
+ // Процесс выдачи игрового сервера
+ $id = service::install($aSDATA);
+
+ sys::outjs(array('s' => 'ok', 'id' => $id));
+ }
+
+ include(LIB.'games/services.php');
+
+ $check = false;
+
+ // Проверка наличия доступной локации
+ $sql->query(services::unit($section));
+ if($sql->num())
+ {
+ // Выбранная локация
+ if(isset($url['get']) AND in_array($url['get'], array('tarifs', 'data')))
+ $sql->query('SELECT `id`, `test` FROM `units` WHERE `id`="'.$id.'" LIMIT 1');
+
+ $select_unit = $sql->get();
+
+ // Генерация списка локаций
+ $units = services::units($section);
+
+ // Генерация списка тарифов
+ $tarifs = services::tarifs($section, $select_unit['id']);
+
+ if(isset($url['get']) AND in_array($url['get'], array('price', 'promo')))
+ {
+ $aGet = array(
+ 'tarif' => sys::int($url['tarif']),
+ 'tickrate' => sys::int($url['tickrate']),
+ 'fps' => sys::int($url['fps']),
+ 'slots' => sys::int($url['slots']),
+ 'time' => sys::int($url['time']),
+ 'user' => $user['id']
+ );
+
+ $sql->query('SELECT `price`, `fps`, `tickrate`, `discount` FROM `tarifs` WHERE `id`="'.$aGet['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aPrice = sys::b64djs($tarif['price'], true);
+
+ // Определение цены
+ $price = $aPrice[$aGet['tickrate'].'_'.$aGet['fps']];
+
+ // Выхлоп цены за выбранные параметры
+ if($url['get'] == 'price')
+ {
+ // Если выбран тестовый период
+ if($url['time'] == 'test')
+ sys::outjs(array('sum' => 0));
+
+ sys::outjs(array(
+ 'sum' => games::define_sum($tarif['discount'], $price, $aGet['slots'], $aGet['time'])
+ ));
+ }
+
+ // Выхлоп цены с учетом промо-кода
+ if($url['get'] == 'promo')
+ games::define_promo(
+ $url['cod'],
+ $aGet,
+ $tarif['discount'],
+ games::define_sum($tarif['discount'], $price, $aGet['slots'], $aGet['time'])
+ );
+ }
+
+ // Генерация сборок/слот/периодов
+ if(isset($url['get']) AND $url['get'] == 'data')
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `tickrate`, `fps`, `time`, `test`, `discount` FROM `tarifs` WHERE `id`="'.sys::int($url['tarif']).'" LIMIT 1');
+ else
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `tickrate`, `fps`, `time`, `test`, `discount` FROM `tarifs` WHERE `game`="'.$section.'" AND `unit`="'.$select_unit['id'].'" AND `show`="1" ORDER BY `sort` ASC LIMIT 1');
+
+ if($sql->num())
+ {
+ $select_tarif = $sql->get();
+
+ $aTarif = games::parse_tarif($select_tarif, $select_unit);
+
+ if(isset($url['get']))
+ {
+ // Выхлоп при выборе локации
+ if($url['get'] == 'tarifs')
+ sys::outjs(array_merge(array('tarifs' => $tarifs), $aTarif));
+
+ // Выхлоп при выборе тарифа
+ if($url['get'] == 'data')
+ sys::outjs($aTarif);
+ }
+
+ $html->get($section, 'sections/services/games');
+ $html->set('units', $units);
+ $html->set('tarifs', $tarifs);
+ $html->set('packs', $aTarif['packs']);
+ $html->set('tickrate', $aTarif['tickrate']);
+ $html->set('fps', $aTarif['fps']);
+ $html->set('slots', $aTarif['slots']);
+ $html->set('time', $aTarif['time']);
+ $html->set('cur', $cfg['currency']);
+ $html->set('date', date('d.m.Y', $start_point));
+
+ if($cfg['settlement_period'])
+ $html->unit('settlement_period', true, true);
+ else
+ $html->unit('settlement_period', false, true);
+
+ $html->unit('informer', false, true);
+ $html->pack('main');
+
+ $check = true;
+ }
+ }
+
+ if(!$check)
+ {
+ $html->get($section, 'sections/services/games');
+ $html->unit('informer', true, true);
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/services/mc.php b/system/sections/services/mc.php
new file mode 100644
index 0000000..69efcde
--- /dev/null
+++ b/system/sections/services/mc.php
@@ -0,0 +1,152 @@
+get('buy_server'))
+ sleep(1.5);
+
+ $mcache->set('buy_server', true, false, 3);
+
+ include(LIB.'games/'.$section.'/service.php');
+
+ // Входные данные
+ $aData = array(
+ 'unit' => isset($_POST['unit']) ? sys::int($_POST['unit']) : 0,
+ 'tarif' => isset($_POST['tarif']) ? sys::int($_POST['tarif']) : 0,
+ 'pack' => isset($_POST['pack']) ? $_POST['pack'] : '',
+ 'ram' => isset($_POST['ram']) ? sys::int($_POST['ram']) : 0,
+ 'slots' => isset($_POST['slots']) ? sys::int($_POST['slots']) : 0,
+ 'time' => isset($_POST['time']) ? sys::int($_POST['time']) : 30,
+ 'test' => (isset($_POST['time']) AND $_POST['time'] == 'test') ? true : false,
+ 'promo' => isset($_POST['promo']) ? $_POST['promo'] : false,
+ );
+
+ // Массвив данных
+ $aSDATA = service::buy($aData);
+
+ // Процесс выдачи игрового сервера
+ $id = service::install($aSDATA);
+
+ sys::outjs(array('s' => 'ok', 'id' => $id));
+ }
+
+ include(LIB.'games/services.php');
+
+ $check = false;
+
+ // Проверка наличия доступной локации
+ $sql->query(services::unit($section));
+ if($sql->num())
+ {
+ // Выбранная локация
+ if(isset($url['get']) AND in_array($url['get'], array('tarifs', 'data')))
+ $sql->query('SELECT `id`, `test` FROM `units` WHERE `id`="'.$id.'" LIMIT 1');
+
+ $select_unit = $sql->get();
+
+ // Генерация списка локаций
+ $units = services::units($section);
+
+ // Генерация списка тарифов
+ $tarifs = services::tarifs($section, $select_unit['id']);
+
+ if(isset($url['get']) AND in_array($url['get'], array('price', 'promo')))
+ {
+ $aGet = array(
+ 'tarif' => sys::int($url['tarif']),
+ 'ram' => sys::int($url['ram']),
+ 'slots' => sys::int($url['slots']),
+ 'time' => sys::int($url['time']),
+ 'user' => $user['id']
+ );
+
+ $sql->query('SELECT `price`, `ram`, `discount` FROM `tarifs` WHERE `id`="'.$aGet['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ $aPrice = explode(':', $tarif['price']);
+
+ // Определение цены
+ $price = $aPrice[array_search($aGet['ram'], explode(':', $tarif['ram']))];
+
+ // Выхлоп цены за выбранные параметры
+ if($url['get'] == 'price')
+ {
+ // Если выбран тестовый период
+ if($url['time'] == 'test')
+ sys::outjs(array('sum' => 0));
+
+ sys::outjs(array(
+ 'sum' => games::define_sum($tarif['discount'], $price, $aGet['slots'], $aGet['time'])
+ ));
+ }
+
+ // Выхлоп цены с учетом промо-кода
+ if($url['get'] == 'promo')
+ games::define_promo(
+ $url['cod'],
+ $aGet,
+ $tarif['discount'],
+ games::define_sum($tarif['discount'], $price, $aGet['slots'], $aGet['time'])
+ );
+ }
+
+ // Генерация сборок/слот/периодов
+ if(isset($url['get']) AND $url['get'] == 'data')
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `ram`, `param_fix`, `time`, `test`, `discount` FROM `tarifs` WHERE `id`="'.sys::int($url['tarif']).'" LIMIT 1');
+ else
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `ram`, `param_fix`, `time`, `test`, `discount` FROM `tarifs` WHERE `game`="'.$section.'" AND `unit`="'.$select_unit['id'].'" AND `show`="1" ORDER BY `sort` ASC LIMIT 1');
+
+ if($sql->num())
+ {
+ $select_tarif = $sql->get();
+
+ $aTarif = games::parse_tarif($select_tarif, $select_unit);
+
+ if(isset($url['get']))
+ {
+ // Выхлоп при выборе локации
+ if($url['get'] == 'tarifs')
+ sys::outjs(array_merge(array('tarifs' => $tarifs), $aTarif));
+
+ // Выхлоп при выборе тарифа
+ if($url['get'] == 'data')
+ sys::outjs($aTarif);
+ }
+
+ $html->get($section, 'sections/services/games');
+ $html->set('units', $units);
+ $html->set('tarifs', $tarifs);
+ $html->set('packs', $aTarif['packs']);
+ $html->set('ram', $aTarif['ram']);
+ $html->set('slots', $aTarif['slots']);
+ $html->set('time', $aTarif['time']);
+ $html->set('cur', $cfg['currency']);
+ $html->set('date', date('d.m.Y', $start_point));
+
+ if($cfg['settlement_period'])
+ $html->unit('settlement_period', true, true);
+ else
+ $html->unit('settlement_period', false, true);
+
+ $html->unit('informer', false, true);
+ $html->pack('main');
+
+ $check = true;
+ }
+ }
+
+ if(!$check)
+ {
+ $html->get($section, 'sections/services/games');
+ $html->unit('informer', true, true);
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/services/mta.php b/system/sections/services/mta.php
new file mode 100644
index 0000000..b76d999
--- /dev/null
+++ b/system/sections/services/mta.php
@@ -0,0 +1,144 @@
+get('buy_server'))
+ sleep(1.5);
+
+ $mcache->set('buy_server', true, false, 3);
+
+ include(LIB.'games/'.$section.'/service.php');
+
+ // Входные данные
+ $aData = array(
+ 'unit' => isset($_POST['unit']) ? sys::int($_POST['unit']) : 0,
+ 'tarif' => isset($_POST['tarif']) ? sys::int($_POST['tarif']) : 0,
+ 'pack' => isset($_POST['pack']) ? $_POST['pack'] : '',
+ 'slots' => isset($_POST['slots']) ? sys::int($_POST['slots']) : 0,
+ 'time' => isset($_POST['time']) ? sys::int($_POST['time']) : 30,
+ 'test' => (isset($_POST['time']) AND $_POST['time'] == 'test') ? true : false,
+ 'promo' => isset($_POST['promo']) ? $_POST['promo'] : false,
+ );
+
+ // Массвив данных
+ $aSDATA = service::buy($aData);
+
+ // Процесс выдачи игрового сервера
+ $id = service::install($aSDATA);
+
+ sys::outjs(array('s' => 'ok', 'id' => $id));
+ }
+
+ include(LIB.'games/services.php');
+
+ $check = false;
+
+ // Проверка наличия доступной локации
+ $sql->query(services::unit($section));
+ if($sql->num())
+ {
+ // Выбранная локация
+ if(isset($url['get']) AND in_array($url['get'], array('tarifs', 'data')))
+ $sql->query('SELECT `id`, `test` FROM `units` WHERE `id`="'.$id.'" LIMIT 1');
+
+ $select_unit = $sql->get();
+
+ // Генерация списка локаций
+ $units = services::units($section);
+
+ // Генерация списка тарифов
+ $tarifs = services::tarifs($section, $select_unit['id']);
+
+ if(isset($url['get']) AND in_array($url['get'], array('price', 'promo')))
+ {
+ $aGet = array(
+ 'tarif' => sys::int($url['tarif']),
+ 'slots' => sys::int($url['slots']),
+ 'time' => sys::int($url['time']),
+ 'user' => $user['id']
+ );
+
+ $sql->query('SELECT `price`, `discount` FROM `tarifs` WHERE `id`="'.$aGet['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Выхлоп цены за выбранные параметры
+ if($url['get'] == 'price')
+ {
+ // Если выбран тестовый период
+ if($url['time'] == 'test')
+ sys::outjs(array('sum' => 0));
+
+ sys::outjs(array(
+ 'sum' => games::define_sum($tarif['discount'], $tarif['price'], $aGet['slots'], $aGet['time'])
+ ));
+ }
+
+ // Выхлоп цены с учетом промо-кода
+ if($url['get'] == 'promo')
+ games::define_promo(
+ $url['cod'],
+ $aGet,
+ $tarif['discount'],
+ games::define_sum($tarif['discount'], $tarif['price'], $aGet['slots'], $aGet['time'])
+ );
+ }
+
+ // Генерация сборок/слот/периодов
+ if(isset($url['get']) AND $url['get'] == 'data')
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `time`, `test`, `discount` FROM `tarifs` WHERE `id`="'.sys::int($url['tarif']).'" LIMIT 1');
+ else
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `time`, `test`, `discount` FROM `tarifs` WHERE `game`="'.$section.'" AND `unit`="'.$select_unit['id'].'" AND `show`="1" ORDER BY `sort` ASC LIMIT 1');
+
+ if($sql->num())
+ {
+ $select_tarif = $sql->get();
+
+ $aTarif = games::parse_tarif($select_tarif, $select_unit);
+
+ if(isset($url['get']))
+ {
+ // Выхлоп при выборе локации
+ if($url['get'] == 'tarifs')
+ sys::outjs(array_merge(array('tarifs' => $tarifs), $aTarif));
+
+ // Выхлоп при выборе тарифа
+ if($url['get'] == 'data')
+ sys::outjs($aTarif);
+ }
+
+ $html->get($section, 'sections/services/games');
+ $html->set('units', $units);
+ $html->set('tarifs', $tarifs);
+ $html->set('packs', $aTarif['packs']);
+ $html->set('slots', $aTarif['slots']);
+ $html->set('time', $aTarif['time']);
+ $html->set('cur', $cfg['currency']);
+ $html->set('date', date('d.m.Y', $start_point));
+
+ if($cfg['settlement_period'])
+ $html->unit('settlement_period', true, true);
+ else
+ $html->unit('settlement_period', false, true);
+
+ $html->unit('informer', false, true);
+ $html->pack('main');
+
+ $check = true;
+ }
+ }
+
+ if(!$check)
+ {
+ $html->get($section, 'sections/services/games');
+ $html->unit('informer', true, true);
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/services/privileges.php b/system/sections/services/privileges.php
new file mode 100644
index 0000000..2075f8c
--- /dev/null
+++ b/system/sections/services/privileges.php
@@ -0,0 +1,225 @@
+get($nmch))
+ sys::outjs(array('e' => sys::text('other', 'mcache')), $nmch);
+
+ $mcache->set($nmch, 1, false, 10);
+
+ $aData = array();
+
+ $aData['address'] = isset($_POST['address']) ? $_POST['address'] : sys::outjs(array('e' => 'Необходимо указать адрес сервера'), $nmch);
+ $aData['type'] = isset($_POST['type']) ? $_POST['type'] : sys::outjs(array('e' => 'Необходимо указать тип авторизации на сервере'), $nmch);
+ $aData['data'] = isset($_POST['data']) ? str_replace('"', '', $_POST['data']) : sys::outjs(array('e' => 'Необходимо указать данные авторизации'), $nmch);
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : '';
+ $aData['service'] = isset($_POST['service']) ? sys::int($_POST['service']) : sys::outjs(array('e' => 'Необходимо указать услугу'), $nmch);
+ $aData['time'] = isset($_POST['time']) ? sys::int($_POST['time']) : sys::outjs(array('e' => 'Необходимо указать период'), $nmch);
+ $aData['mail'] = isset($_POST['mail']) ? $_POST['mail'] : sys::outjs(array('e' => 'Необходимо указать почту'), $nmch);
+
+ if(!in_array($aData['type'], array('a', 'ca', 'de')))
+ sys::outjs(array('e' => 'Неправильно передан тип авторизации на сервере'), $nmch);
+
+ switch($aData['type'])
+ {
+ case 'a':
+ if($aData['data'] == '')
+ sys::outjs(array('e' => 'Необходимо указать ник'), $nmch);
+ break;
+ case 'ca':
+ if(sys::valid($aData['data'], 'steamid') || sys::valid($aData['data'], 'steamid3'))
+ sys::outjs(array('e' => 'Неправильный формат SteamID'), $nmch);
+ break;
+ default:
+ if(sys::valid($aData['data'], 'ip'))
+ sys::outjs(array('e' => 'Неправильный формат IP'), $nmch);
+ }
+
+ if(sys::valid($aData['address'], 'other', $aValid['address']))
+ sys::outjs(array('e' => 'Адрес игрового сервера имеет неверный формат'), $nmch);
+
+ $sql->query('SELECT `id`, `name`, `game` FROM `servers` WHERE `address`="'.$aData['address'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Игровой сервер не найден в базе'), $nmch);
+
+ $server = $sql->get();
+
+ $sql->query('SELECT `id` FROM `admins_'.$server['game'].'` WHERE `server`="'.$server['id'].'" AND `value`="'.htmlspecialchars($aData['data']).'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Привилегия для данного игрока уже установлена, дождитесь её завершения.'), $nmch);
+
+ if($aData['type'] != 'de' AND sys::valid($aData['passwd'], 'other', $aValid['passwd']))
+ sys::outjs(array('e' => 'Неправильный формат пароля, используйте латинские буквы и цифры от 6 до 20 символов'), $nmch);
+
+ if(sys::valid($aData['mail'], 'other', $aValid['mail']))
+ sys::outjs(array('e' => 'Неправильный формат почты'), $nmch);
+
+ $sql->query('SELECT `flags`, `immunity`, `data` FROM `privileges_list` WHERE `id`="'.$aData['service'].'" AND `server`="'.$server['id'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Указанная услуга не найдена'), $nmch);
+
+ $privilege = $sql->get();
+
+ $data = sys::b64djs($privilege['data']);
+
+ if(!array_key_exists($aData['time'], $data))
+ sys::outjs(array('e' => 'Неправильно указан период'), $nmch);
+
+ $price = $data[$aData['time']];
+
+ $time = !$aData['time'] ? $start_point+172800000 : $start_point+$aData['time']*86400;
+
+ if($server['game'] == 'cs')
+ {
+ $text = '"'.$aData['data'].'" "'.$aData['passwd'].'" "'.$privilege['flags'].'" "'.$aData['type'].'"';
+ $sqlq = 'INSERT INTO `admins_'.$server['game'].'` set'
+ .'`server`="'.$server['id'].'",'
+ .'`value`="'.htmlspecialchars($aData['data']).'",'
+ .'`active`="1",'
+ .'`passwd`="'.$aData['passwd'].'",'
+ .'`flags`="'.$privilege['flags'].'",'
+ .'`type`="'.$aData['type'].'",'
+ .'`time`="'.$time.'",'
+ .'`text`="'.htmlspecialchars($text).'",'
+ .'`info`="Онлайн покупка"';
+ }else{
+ $text = '"'.$aData['data'].'" "'.$aData['immunity'].':'.$privilege['flags'].'" "'.$aData['passwd'].'"';
+ $sqlq = 'INSERT INTO `admins_'.$server['game'].'` set'
+ .'`server`="'.$server['id'].'",'
+ .'`value`="'.$aData['data'].'",'
+ .'`active`="1",'
+ .'`passwd`="'.htmlspecialchars($aData['passwd']).'",'
+ .'`flags`="'.$aData['flags'].'",'
+ .'`immunity`="'.$privilege['immunity'].'",'
+ .'`time`="'.$time.'",'
+ .'`text`="'.htmlspecialchars($text).'",'
+ .'`info`="Онлайн покупка"';
+ }
+
+ $sql->query('SELECT `key` FROM `privileges_buy` WHERE '
+ .'`server`="'.$server['id'].'" AND'
+ .'`text`="'.base64_encode($text).'" AND'
+ .'`price`="'.$price.'" AND'
+ .'`mail`="'.$aData['mail'].'" AND'
+ .'`status`="0" LIMIT 1');
+
+ if(!$sql->num())
+ {
+ $key = sys::key();
+
+ $sql->query('INSERT INTO `privileges_buy` set '
+ .'`server`="'.$server['id'].'",'
+ .'`text`="'.base64_encode($text).'",'
+ .'`sql`="'.base64_encode($sqlq).'",'
+ .'`price`="'.$price.'",'
+ .'`key`="'.$key.'",'
+ .'`date`="'.$start_point.'",'
+ .'`mail`="'.$aData['mail'].'",'
+ .'`status`="0"');
+ }else{
+ $pay = $sql->get();
+ $key = $pay['key'];
+ }
+
+ $html->get('pay', 'sections/services/privileges');
+
+ $html->set('cur', $cfg['currency']);
+ $html->set('wmr', $cfg['webmoney_wmr']);
+ $html->set('key', $key);
+ $html->set('sum', $price);
+
+ $html->pack('pay');
+
+ sys::outjs(array('s' => $html->arr['pay']), $nmch);
+ }
+
+ if(isset($url['select']))
+ {
+ if($url['select'] == 'time')
+ {
+ $service = isset($url['service']) ? sys::int($url['service']) : sys::out();
+
+ $sql->query('SELECT `data` FROM `privileges_list` WHERE `id`="'.$service.'" LIMIT 1');
+ $list = $sql->get();
+
+ $time = '';
+
+ $data = sys::b64djs($list['data']);
+
+ if(isset($data[0]))
+ {
+ $time = 'Навсегда / '.$data[0].' '.$cfg['currency'].' ';
+
+ unset($data[0]);
+ }
+
+ foreach($data as $days => $price)
+ $time .= ''.$days.' '.sys::day($time).' / '.$price.' '.$cfg['currency'].' ';
+
+ sys::out($time);
+ }
+
+ $address = isset($_POST['address']) ? trim($_POST['address']) : sys::outjs(array('e' => 'Необходимо указать адрес игрового сервера'));
+
+ if(sys::valid($address, 'other', $aValid['address']))
+ sys::outjs(array('e' => 'Указанный адрес имеет неверный формат'));
+
+ $sql->query('SELECT `id`, `name` FROM `servers` WHERE `address`="'.$address.'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Игровой сервер не найден в базе'));
+
+ $server = $sql->get();
+
+ $sql->query('SELECT `active` FROM `privileges` WHERE `server`="'.$server['id'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Игровой сервер не предоставляет услуги'));
+
+ $privilege = $sql->get();
+
+ if(!$privilege['active'])
+ sys::outjs(array('e' => 'Игровой сервер времено не предоставляет услуги'));
+
+ $name = '';
+
+ $sql->query('SELECT `id`, `name` FROM `privileges_list` WHERE `server`="'.$server['id'].'" ORDER BY `id` ASC LIMIT 5');
+ while($list = $sql->get())
+ $name .= ''.$list['name'].' ';
+
+ $sql->query('SELECT `data` FROM `privileges_list` WHERE `server`="'.$server['id'].'" ORDER BY `id` ASC LIMIT 1');
+ $list = $sql->get();
+
+ $time = '';
+
+ $data = sys::b64djs($list['data']);
+
+ if(isset($data[0]))
+ {
+ $time = 'Навсегда / '.$data[0].' '.$cfg['currency'].' ';
+
+ unset($data[0]);
+ }
+
+ foreach($data as $days => $price)
+ $time .= ''.$days.' '.sys::day($time).' / '.$price.' '.$cfg['currency'].' ';
+
+ $html->get('form', 'sections/services/privileges');
+
+ $html->set('home', $cfg['http']);
+ $html->set('name', $server['name']);
+ $html->set('address', $address);
+ $html->set('services', $name);
+ $html->set('time', $time);
+
+ $html->pack('form');
+
+ sys::outjs(array('s' => $html->arr['form']));
+ }
+
+ $html->get('index', 'sections/services/privileges');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/services/samp.php b/system/sections/services/samp.php
new file mode 100644
index 0000000..b76d999
--- /dev/null
+++ b/system/sections/services/samp.php
@@ -0,0 +1,144 @@
+get('buy_server'))
+ sleep(1.5);
+
+ $mcache->set('buy_server', true, false, 3);
+
+ include(LIB.'games/'.$section.'/service.php');
+
+ // Входные данные
+ $aData = array(
+ 'unit' => isset($_POST['unit']) ? sys::int($_POST['unit']) : 0,
+ 'tarif' => isset($_POST['tarif']) ? sys::int($_POST['tarif']) : 0,
+ 'pack' => isset($_POST['pack']) ? $_POST['pack'] : '',
+ 'slots' => isset($_POST['slots']) ? sys::int($_POST['slots']) : 0,
+ 'time' => isset($_POST['time']) ? sys::int($_POST['time']) : 30,
+ 'test' => (isset($_POST['time']) AND $_POST['time'] == 'test') ? true : false,
+ 'promo' => isset($_POST['promo']) ? $_POST['promo'] : false,
+ );
+
+ // Массвив данных
+ $aSDATA = service::buy($aData);
+
+ // Процесс выдачи игрового сервера
+ $id = service::install($aSDATA);
+
+ sys::outjs(array('s' => 'ok', 'id' => $id));
+ }
+
+ include(LIB.'games/services.php');
+
+ $check = false;
+
+ // Проверка наличия доступной локации
+ $sql->query(services::unit($section));
+ if($sql->num())
+ {
+ // Выбранная локация
+ if(isset($url['get']) AND in_array($url['get'], array('tarifs', 'data')))
+ $sql->query('SELECT `id`, `test` FROM `units` WHERE `id`="'.$id.'" LIMIT 1');
+
+ $select_unit = $sql->get();
+
+ // Генерация списка локаций
+ $units = services::units($section);
+
+ // Генерация списка тарифов
+ $tarifs = services::tarifs($section, $select_unit['id']);
+
+ if(isset($url['get']) AND in_array($url['get'], array('price', 'promo')))
+ {
+ $aGet = array(
+ 'tarif' => sys::int($url['tarif']),
+ 'slots' => sys::int($url['slots']),
+ 'time' => sys::int($url['time']),
+ 'user' => $user['id']
+ );
+
+ $sql->query('SELECT `price`, `discount` FROM `tarifs` WHERE `id`="'.$aGet['tarif'].'" LIMIT 1');
+ $tarif = $sql->get();
+
+ // Выхлоп цены за выбранные параметры
+ if($url['get'] == 'price')
+ {
+ // Если выбран тестовый период
+ if($url['time'] == 'test')
+ sys::outjs(array('sum' => 0));
+
+ sys::outjs(array(
+ 'sum' => games::define_sum($tarif['discount'], $tarif['price'], $aGet['slots'], $aGet['time'])
+ ));
+ }
+
+ // Выхлоп цены с учетом промо-кода
+ if($url['get'] == 'promo')
+ games::define_promo(
+ $url['cod'],
+ $aGet,
+ $tarif['discount'],
+ games::define_sum($tarif['discount'], $tarif['price'], $aGet['slots'], $aGet['time'])
+ );
+ }
+
+ // Генерация сборок/слот/периодов
+ if(isset($url['get']) AND $url['get'] == 'data')
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `time`, `test`, `discount` FROM `tarifs` WHERE `id`="'.sys::int($url['tarif']).'" LIMIT 1');
+ else
+ $sql->query('SELECT `id`, `name`, `price`, `slots_min`, `slots_max`, `packs`, `time`, `test`, `discount` FROM `tarifs` WHERE `game`="'.$section.'" AND `unit`="'.$select_unit['id'].'" AND `show`="1" ORDER BY `sort` ASC LIMIT 1');
+
+ if($sql->num())
+ {
+ $select_tarif = $sql->get();
+
+ $aTarif = games::parse_tarif($select_tarif, $select_unit);
+
+ if(isset($url['get']))
+ {
+ // Выхлоп при выборе локации
+ if($url['get'] == 'tarifs')
+ sys::outjs(array_merge(array('tarifs' => $tarifs), $aTarif));
+
+ // Выхлоп при выборе тарифа
+ if($url['get'] == 'data')
+ sys::outjs($aTarif);
+ }
+
+ $html->get($section, 'sections/services/games');
+ $html->set('units', $units);
+ $html->set('tarifs', $tarifs);
+ $html->set('packs', $aTarif['packs']);
+ $html->set('slots', $aTarif['slots']);
+ $html->set('time', $aTarif['time']);
+ $html->set('cur', $cfg['currency']);
+ $html->set('date', date('d.m.Y', $start_point));
+
+ if($cfg['settlement_period'])
+ $html->unit('settlement_period', true, true);
+ else
+ $html->unit('settlement_period', false, true);
+
+ $html->unit('informer', false, true);
+ $html->pack('main');
+
+ $check = true;
+ }
+ }
+
+ if(!$check)
+ {
+ $html->get($section, 'sections/services/games');
+ $html->unit('informer', true, true);
+ $html->pack('main');
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/user/auth.php b/system/sections/user/auth.php
new file mode 100644
index 0000000..cb486a5
--- /dev/null
+++ b/system/sections/user/auth.php
@@ -0,0 +1,129 @@
+get($nmch))
+ sys::outjs(array('e' => sys::text('other', 'mcache')), $nmch);
+
+ $mcache->set($nmch, 1, false, 15);
+
+ // Проверка капчи
+ if(!isset($_POST['captcha']) || sys::captcha_check('auth', $uip, $_POST['captcha']))
+ sys::outjs(array('e' => sys::text('other', 'captcha')), $nmch);
+
+ $aData = array();
+
+ $aData['login'] = isset($_POST['login']) ? $_POST['login'] : '';
+ $aData['passwd'] = isset($_POST['passwd']) ? sys::passwdkey($_POST['passwd']) : '';
+
+ // Проверка входных данных
+ foreach($aData as $val)
+ if($val == '')
+ sys::outjs(array('e' => sys::text('input', 'all')), $nmch);
+
+ // Проверка логина/почты на валидность
+ if(sys::valid($aData['login'], 'other', $aValid['mail']) AND sys::valid($aData['login'], 'other', $aValid['login']))
+ {
+ $out = 'login';
+
+ // Если в логине указана почта
+ if(sys::ismail($aData['login']))
+ $out = 'mail';
+
+ sys::outjs(array('e' => sys::text('input', $out.'_valid')), $nmch);
+ }
+
+ $sql_q = '`login`';
+
+ // Если в логине указана почта
+ if(sys::ismail($aData['login']))
+ $sql_q = '`mail`';
+
+ // Проверка существования пользователя
+ $sql->query('SELECT `id`, `login`, `mail`, `security_ip`, `security_code` FROM `users` WHERE '.$sql_q.'="'.$aData['login'].'" AND `passwd`="'.$aData['passwd'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => sys::text('input', 'auth')), $nmch);
+
+ $user = $sql->get();
+
+ $subnetwork = sys::whois($uip);
+
+ // Если включена защита по ip
+ if($user['security_ip'])
+ {
+ $sql->query('SELECT `id` FROM `security` WHERE `user`="'.$user['id'].'" AND `address`="'.$uip.'" LIMIT 1');
+
+ if(!$sql->num())
+ {
+ if($subnetwork != 'не определена')
+ {
+ $sql->query('SELECT `id` FROM `security` WHERE `user`="'.$user['id'].'" AND `address`="'.$subnetwork.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Ваш ip адрес не найден в числе указаных адресов для авторизации.'), $nmch);
+ }else
+ sys::outjs(array('e' => 'Ваш ip адрес не найден в числе указаных адресов для авторизации.'), $nmch);
+ }
+ }
+
+ // Если включена защита по коду
+ if($user['security_code'])
+ {
+ $code = isset($_POST['code']) ? $_POST['code'] : '';
+
+ if($code == '' || $code != $mcache->get('auth_code_security_'.$user['id']))
+ {
+ $ncod = sys::code();
+
+ // Отправка сообщения на почту
+ if(sys::mail('Авторизация', sys::updtext(sys::text('mail', 'security_code'), array('site' => $cfg['name'], 'code' => $ncod)), $user['mail']))
+ {
+ $mcache->set('auth_code_security_'.$user['id'], $ncod, false, 180);
+
+ if($code == '')
+ sys::outjs(array('i' => 'На вашу почту отправлено письмо с кодом подтверждения.', 'mail' => sys::mail_domain($user['mail'])), $nmch);
+
+ sys::outjs(array('i' => 'На вашу почту отправлено письмо с кодом подтверждения снова.', 'mail' => sys::mail_domain($user['mail'])), $nmch);
+ }
+
+ // Выхлоп: не удалось отправить письмо
+ sys::outjs(array('e' => sys::text('error', 'mail')), $nmch);
+ }
+ }
+
+ $_SERVER['HTTP_USER_AGENT'] = mb_substr($_SERVER['HTTP_USER_AGENT'], 0, 200);
+
+ // Обновление информации о пользователе
+ $sql->query('UPDATE `users` set `ip`="'.$uip.'", `browser`="'.sys::browser($_SERVER['HTTP_USER_AGENT']).'", `time`="'.$start_point.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Логирование ip
+ $sql->query('INSERT INTO `auth` set `user`="'.$user['id'].'", `ip`="'.$uip.'", `date`="'.$start_point.'", `browser`="'.sys::hb64($_SERVER['HTTP_USER_AGENT']).'"');
+
+ // Запись cookie пользователю
+ sys::cookie('egp_login', $user['login'], 14);
+ sys::cookie('egp_passwd', $aData['passwd'], 14);
+ sys::cookie('egp_authkeycheck', md5($user['login'].$uip.$aData['passwd']), 14);
+
+ // Выхлоп удачной авторизации
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $html->get('auth', 'sections/user');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/user/lk.php b/system/sections/user/lk.php
new file mode 100644
index 0000000..7d37e69
--- /dev/null
+++ b/system/sections/user/lk.php
@@ -0,0 +1,35 @@
+ 'Профиль',
+ 'settings' => 'Настройки',
+ 'auth' => 'Логи авторизаций',
+ 'logs' => 'История операций',
+ 'security' => 'Безопасность'
+ );
+
+ $url['subsection'] = isset($url['subsection']) ? $url['subsection'] : 'index';
+
+ // Подключение раздела
+ if(in_array($url['subsection'], array('index', 'settings', 'auth', 'logs', 'security', 'action', 'cashback')))
+ {
+ $title = isset($aTitle[$url['subsection']]) ? $aTitle[$url['subsection']] : '';
+ $html->nav($title);
+
+ include(LIB.'users.php');
+
+ users::nav($url['subsection']);
+
+ include(SEC.'user/lk/'.$url['subsection'].'.php');
+ }else
+ include(ENG.'404.php');
+?>
\ No newline at end of file
diff --git a/system/sections/user/lk/action.php b/system/sections/user/lk/action.php
new file mode 100644
index 0000000..217cdbc
--- /dev/null
+++ b/system/sections/user/lk/action.php
@@ -0,0 +1,189 @@
+query('SELECT `mail`, `new_mail`, `confirm_mail`, `wmr`, `phone`, `confirm_phone`, `contacts` FROM `users` WHERE `id`="'.$user['id'].'" LIMIT 1');
+ $user = array_merge($user, $sql->get());
+
+ if($go)
+ {
+ $name_mcache = 'lk_'.$user['id'];
+
+ // Проверка сессии
+ if($mcache->get($name_mcache))
+ sys::outjs(array('e' => $text['mcache']), $name_mcache);
+
+ // Создание сессии
+ $mcache->set($name_mcache, 1, false, 10);
+
+ if(!isset($url['type']))
+ exit;
+
+ switch($url['type'])
+ {
+ case 'contacts':
+ $contacts = isset($_POST['contacts']) ? $_POST['contacts'] : '';
+
+ if($contacts != '')
+ {
+ if(sys::valid($contacts, 'other', $aValid['contacts']))
+ sys::outjs(array('e' => sys::text('input', 'contacts_valid')), $name_mcache);
+ }
+
+ // Запись контактов в базу, если не совпадает с текущими данными
+ if($contacts != $user['contacts'])
+ $sql->query('UPDATE `users` set `contacts`="'.$contacts.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Выхлоп удачного выполнения операции
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ case 'passwd':
+ $passwd = isset($_POST['passwd']) ? $_POST['passwd'] : '';
+
+ if(sys::valid($passwd, 'other', $aValid['passwd']))
+ sys::outjs(array('e' => sys::text('input', 'passwd_valid')), $name_mcache);
+
+ $passwd = sys::passwdkey($passwd);
+
+ // Обновление пароля в базе, если он не совпадает с текущим
+ if($auth_data['passwd'] != $passwd)
+ {
+ $sql->query('UPDATE `users` set `passwd`="'.$passwd.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Обновление cookie
+ sys::cookie('login', $user['login'], 14);
+ sys::cookie('passwd', $passwd, 14);
+ sys::cookie('authkeycheck', md5($user['login'].$_SERVER['REMOTE_ADDR'].$passwd), 14);
+ }
+
+ // Выхлоп удачного выполнения операции
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ case 'mail':
+ $mail = isset($_POST['mail']) ? $_POST['mail'] : '';
+
+ // Проверка введенной почты
+ if(sys::valid($mail, 'other', $aValid['mail']))
+ sys::outjs(array('e' => sys::text('input', 'mail_valid')), $name_mcache);
+
+ if($mail == $user['mail'])
+ sys::outjs(array('e' => sys::text('input', 'similar')), $name_mcache);
+
+ // Проверка почты на занятость
+ $sql->query('SELECT `id` FROM `users` WHERE `mail`="'.$mail.'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => sys::text('input', 'mail_use')), $name_mcache);
+
+ // Генерация кода
+ $key = sys::key('mail_change'.$user['id']);
+
+ // Отправка письма на старую почту
+ if(sys::mail('Смена почты', sys::updtext(sys::text('mail', 'change'), array('site' => $cfg['name'], 'url' => $cfg['http'].'user/section/lk/subsection/action/type/confirm_mail/confirm/'.$key.'/go/1')), $user['mail']))
+ {
+ $sql->query('UPDATE `users` set `new_mail`="'.$mail.'", `confirm_mail`="'.$key.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => sys::text('output', 'oldmail'), 'mail' => sys::mail_domain($user['mail'])), $name_mcache);
+ }
+
+ // Выхлоп: неудалось отправить письмо
+ sys::outjs(array('e' => sys::text('error', 'mail')), $name_mcache);
+
+ case 'confirm_mail':
+ $key = isset($url['confirm']) ? $url['confirm'] : '';
+
+ if($key != $user['confirm_mail'])
+ sys::outhtml(sys::text('output', 'confirm_key_error'), 4, $cfg['http'].'user/section/lk', $name_mcache);
+
+ // Проверка почты на занятость
+ $sql->query('SELECT `id` FROM `users` WHERE `mail`="'.$user['confirm_mail'].'" LIMIT 1');
+ if($sql->num())
+ sys::outhtml(sys::text('input', 'mail_use'), 4, $cfg['http'].'user/section/lk', $name_mcache);
+
+ $sql->query('UPDATE `users` set `mail`="'.$user['new_mail'].'", `new_mail`="", `confirm_mail`="" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Выхлоп удачного выполнения операции
+ sys::outhtml(sys::text('output', 'confirm_mail_done'), 4, $cfg['http'].'user/section/lk', $name_mcache);
+
+ case 'phone':
+ // Проверка, подтвержден ли номер
+ if($user['confirm_phone'] == '1')
+ sys::outjs(array('e' => sys::text('output', 'confirm_phone')), $name_mcache);
+
+ $phone = isset($_POST['phone']) ? str_replace('+', '', trim($_POST['phone'])) : '';
+
+ // Проверка введенного номера
+ if($phone != '')
+ {
+ if(sys::valid($phone, 'other', $aValid['phone']))
+ sys::outjs(array('e' => sys::text('input', 'phone_valid')), $name_mcache);
+ }
+
+ // Запись номера, если не совпадает с текущим
+ if($phone != $user['phone'])
+ $sql->query('UPDATE `users` set `phone`="'.$phone.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Выхлоп удачного выполнения операции
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ case 'confirm_phone':
+ // Проверка, подтвержден ли номер
+ if($user['confirm_phone'] == '1')
+ sys::outjs(array('e' => sys::text('output', 'confirm_phone_done')), $name_mcache);
+
+ if($user['phone'] == '')
+ sys::outjs(array('e' => sys::text('input', 'phone')), $name_mcache);
+
+ // Проверка, отправлялось ли сообщение
+ if(strlen($user['confirm_phone']) == 6)
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ // Генерация кода подтверждения
+ $code = sys::smscode();
+
+ // Отправка кода подтверждения на номер
+ if(sys::sms('code: '.$code, $user['phone']))
+ {
+ $sql->query('UPDATE `users` set `confirm_phone`="'.$code.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+ }
+
+ // Выхлоп: неудалось отправить сообщение
+ sys::outjs(array('e' => sys::text('output', 'confirm_phone_error')), $name_mcache);
+
+ case 'confirm_phone_end':
+ // Проверка, подтвержден ли номер
+ if($user['confirm_phone'] == '1')
+ sys::outjs(array('e' => sys::text('output', 'confirm_phone_done')), $name_mcache);
+
+ if($user['phone'] == '')
+ sys::outjs(array('e' => sys::text('input', 'phone')), $name_mcache);
+
+ $code = isset($_POST['smscode']) ? sys::int($_POST['smscode']) : '';
+
+ if($code != $user['confirm_phone'])
+ sys::outjs(array('e' => sys::text('output', 'confirm_key_error')), $name_mcache);
+
+ $sql->query('UPDATE `users` set `confirm_phone`="1" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Выхлоп удачного выполнения операции
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+
+ case 'wmr':
+ $wmr = isset($_POST['wmr']) ? $_POST['wmr'] : '';
+
+ // Проверка наличия указанного кошелька
+ if(isset($user['wmr']{0}) AND in_array($user['wmr']{0}, array('R', 'Z', 'U')))
+ sys::outjs(array('e' => sys::text('input', 'wmr_confirm')), $name_mcache);
+
+ if(sys::valid($wmr, 'wm'))
+ sys::outjs(array('e' => sys::text('input', 'wmr_valid')), $name_mcache);
+
+ // Обновление кошелька в базе
+ $sql->query('UPDATE `users` set `wmr`="'.$wmr.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ // Выхлоп удачного выполнения операции
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+ }
+ }
+?>
\ No newline at end of file
diff --git a/system/sections/user/lk/auth.php b/system/sections/user/lk/auth.php
new file mode 100644
index 0000000..fde5edb
--- /dev/null
+++ b/system/sections/user/lk/auth.php
@@ -0,0 +1,34 @@
+query('SELECT `ip`, `date`, `browser` FROM `auth` WHERE `user`="'.$user['id'].'" ORDER BY `id` DESC LIMIT 20');
+ while($aBro = $sql->get($qBro))
+ {
+ $browser = base64_decode($aBro['browser']);
+
+ $cData = $SxGeo->getCityFull($aBro['ip']);
+ $ico = sys::country($cData['country']['iso']);
+
+ $html->get('list', 'sections/user/lk/auth');
+
+ $html->set('ip', $aBro['ip']);
+ $html->set('date', sys::today($aBro['date'], true));
+ $html->set('browser', sys::browser($browser));
+ $html->set('more', $browser);
+ $html->set('flag', $ico);
+ $html->set('country', empty($cData['country']['name_ru']) ? 'Не определена' : $cData['country']['name_ru']);
+
+ $html->pack('auth');
+ }
+
+ $html->get('auth', 'sections/user/lk');
+
+ $html->set('auth', isset($html->arr['auth']) ? $html->arr['auth'] : '', true);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/user/lk/cashback.php b/system/sections/user/lk/cashback.php
new file mode 100644
index 0000000..16ceed7
--- /dev/null
+++ b/system/sections/user/lk/cashback.php
@@ -0,0 +1,125 @@
+get($name_mcache))
+ sys::outjs(array('e' => $text['mcache']), $name_mcache);
+
+ // Создание сессии
+ $mcache->set($name_mcache, 1, false, 10);
+
+ if(!$cfg['part_money'])
+ sys::outjs(array('e' => 'Вывод средств невозможен'), $name_mcache);
+
+ $aData = array();
+
+ $aData['purse'] = isset($url['purse']) ? strtolower(trim($url['purse'])) : sys::outjs(array('e' => 'Необходимо указать кошелек'), $name_mcache);
+ $aData['sum'] = isset($url['sum']) ? round(floatval($url['sum']), 2) : sys::outjs(array('e' => 'Необходимо указать сумму'), $name_mcache);
+
+ $sql->query('SELECT `part_money` FROM `users` WHERE `id`="'.$user['id'].'" LIMIT 1');
+ $user = array_merge($user, $sql->get());
+
+ // Проверка доступной суммы
+ if($aData['sum'] > $user['part_money'])
+ sys::outjs(array('e' => 'У вас нет указанной суммы'), $name_mcache);
+
+ if(!in_array($aData['purse'], array('phone', 'wmr', 'lk')))
+ sys::outjs(array('e' => 'Неверно указан кошелек'), $name_mcache);
+
+ // Вывод на баланс сайта
+ if($aData['purse'] == 'lk')
+ {
+ if($aData['sum'] < 1)
+ sys::outjs(array('e' => 'Сумма не должна быть меньше 1 '.$cfg['currency']), $name_mcache);
+
+ $sql->query('UPDATE `users` set `balance`="'.($user['balance']+$aData['sum']).'", `part_money`="'.($user['part_money']-$aData['sum']).'" WHERE `id`="'.$user['id'].'"');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'cashback'),
+ array('purse' => $cfg['part_log'], 'money' => $aData['sum'])).'", `date`="'.$start_point.'", `type`="cashback", `money`="'.$aData['sum'].'"');
+
+ sys::outjs(array('s' => 'Перевод средств был успешно произведен'), $name_mcache);
+ }
+
+ // Проверка лимита на мин. сумму за перевод
+ if($aData['sum'] < $cfg['part_limit_min'])
+ sys::outjs(array('e' => 'Миниммальная сумма вывода '.$cfg['part_limit_min'].' '.$cfg['currency']), $name_mcache);
+
+ // Проверка кошелька
+ if($aData['purse'] == 'wmr')
+ {
+ $sql->query('SELECT `wmr` FROM `users` WHERE `id`="'.$user['id'].'" AND `wmr`!="" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Чтобы вывести деньги на WMR-кошелек, необходимо его указать в профиле'), $name_mcache);
+ }else{
+ $sql->query('SELECT `phone` FROM `users` WHERE `id`="'.$user['id'].'" AND `confirm_phone`="1" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Чтобы вывести деньги на QIWI, необходим подтвержденный номер в профиле'), $name_mcache);
+ }
+
+ $purse = $sql->get();
+
+ // Вывод без одобрения
+ if($cfg['part_output'])
+ {
+ // Проверка лимита на макс. сумму за 24 часа
+ $sql->query('SELECT SUM(`money`) FROM `cashback` WHERE `user`="'.$user['id'].'" AND `time`<"'.($start_point-86400).'" AND `status`="0"');
+ $sum = $sql->get();
+
+ if(($aData['sum']+$sum['SUM(`money`)']) > $cfg['part_limit_max'])
+ sys::outjs(array('e' => 'Максимальная сумма вывода за 24 часа '.$cfg['part_limit_max'].' '.$cfg['currency']), $name_mcache);
+
+ // Проверка общего лимита за 24 часа
+ $sql->query('SELECT SUM(`money`) FROM `cashback` WHERE `time`<"'.($start_point-86400).'" AND `status`="0"');
+ $sum = $sql->get();
+
+ if(($aData['sum']+$sum['SUM(`money`)']) > $cfg['part_limit_day'])
+ sys::outjs(array('e' => 'Общий лимит на вывод за 24 часа достигнут, попробуйте вывести завтра'), $name_mcache);
+
+ // Запрос на шлюз
+ if($cfg['part_gateway'] == 'unitpay')
+ {
+ $aType = array('phone' => 'qiwi', 'wmr' => 'webmoney');
+
+ $sql->query('INSERT INTO `cashback` set `user`="'.$user['id'].'", `purse`="'.$purse[$aData['purse']].'", `money`="'.$aData['sum'].'", `date`="'.$start_point.'", `status`="0"');
+ $id = $sql->id();
+
+ $sum = $aData['sum']-($aData['sum']/100*$cfg['part_output_proc']);
+
+ $json = file_get_contents('https://unitpay.ru/api?method=massPayment¶ms[sum]='.$sum.'¶ms[purse]='.$purse[$aData['purse']].'¶ms[login]='.$cfg['unitpay_mail'].'¶ms[transactionId]='.$id.' ¶ms[secretKey]='.$cfg['unitpay_api'].'¶ms[paymentType]='.$aType[$aData['purse']]);
+
+ $array = json_decode($json, true);
+
+ // Упешный вывод средств
+ if(is_array($array) AND isset($array['result']) AND in_array($array['result']['status'], array('success', 'not_completed ')))
+ {
+ $sql->query('UPDATE `users` set `part_money`="'.($user['part_money']-$aData['sum']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+ $sql->query('INSERT INTO `logs` set `user`="'.$user['id'].'", `text`="'.sys::updtext(sys::text('logs', 'cashback'),
+ array('purse' => $aType[$aData['purse']], 'money' => $aData['sum'])).'", `date`="'.$start_point.'", `type`="cashback", `money`="'.$aData['sum'].'"');
+
+ sys::outjs(array('s' => 'Запрос на вывод средств был успешно выполнен'), $name_mcache);
+ }
+
+ if(!is_array($array))
+ sys::outjs(array('e' => 'Неудалось выполнить запрос'), $name_mcache);
+
+ switch($array['error']['code'])
+ {
+ case '103':
+ sys::outjs(array('e' => 'На данный момент вы не можете вывести средства, обратитесь к администратору'), $name_mcache);
+ case '104':
+ sys::outjs(array('e' => 'Номер телефона не входит в список доступных для выплат стран'), $name_mcache);
+ case '1053':
+ sys::outjs(array('e' => 'Платежная система не смогла получить информацию о номере телефона'), $name_mcache);
+ }
+ }
+
+ sys::outjs(array('e' => 'Технические проблемы, обратитесь в службу поддержки'.$array['error']['code']), $name_mcache);
+ }
+
+ $sql->query('UPDATE `users` set `part_money`="'.($user['part_money']-$aData['sum']).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+ $sql->query('INSERT INTO `cashback` set `user`="'.$user['id'].'", `purse`="'.$purse[$aData['purse']].'", `money`="'.$aData['sum'].'", `date`="'.$start_point.'", `status`="1"');
+
+ sys::outjs(array('s' => 'Заявка на вывод средств была успешно создана'), $name_mcache);
+?>
\ No newline at end of file
diff --git a/system/sections/user/lk/index.php b/system/sections/user/lk/index.php
new file mode 100644
index 0000000..91ca6aa
--- /dev/null
+++ b/system/sections/user/lk/index.php
@@ -0,0 +1,81 @@
+query('SELECT `name`, `lastname`, `patronymic`, `mail`, `wmr`, `phone`, `contacts`, `date`, `part_money`, `rental`, `extend` FROM `users` WHERE `id`="'.$user['id'].'" LIMIT 1');
+ $user = array_merge($user, $sql->get());
+
+ // Подсчет рефералов
+ $nmch = 'part_'.$user['id'];
+
+ if($mcache->get($nmch) != '')
+ $part_users = $mcache->get($nmch);
+ else{
+ $sql->query('SELECT `id` FROM `users` WHERE `part`="'.$user['id'].'"');
+ $part_users = $sql->num();
+
+ $mcache->set($nmch, $part_users, false, 300);
+ }
+
+ if($user['rental'])
+ $rental = strpos($user['rental'], '%') ? $user['rental'] : $user['rental'].' '.$cfg['currency'];
+ else
+ $rental = 'отсутствует';
+
+ if($user['extend'])
+ $extend = strpos($user['extend'], '%') ? $user['extend'] : $user['extend'].' '.$cfg['currency'];
+ else
+ $extend = 'отсутствует';
+
+ $i = 1;
+
+ $part_user = '';
+
+ $part_inf =$sql->query('SELECT `id`, `login`, `date` FROM `users` WHERE `part`="'.$user['id'].'" ORDER BY `date` ASC');
+ while($part_info = $sql->get($part_inf))
+ {
+ $sql->query('SELECT `id` FROM `servers` WHERE `user`="'.$part_info['id'].'" LIMIT 10');
+ $servers = $sql->num();
+
+ $part_user .= '
';
+ $part_user .= ''.$i++.' ';
+ $part_user .= ''.$part_info['login'].' ';
+ $part_user .= ''.sys::today($part_info['date'], true).' ';
+ $part_user .= ''.$servers.' ';
+ $part_user .= ' ';
+ }
+
+ $html->get('index', 'sections/user/lk');
+
+ $html->set('id', $user['id']);
+ $html->set('name', $user['name']);
+ $html->set('lastname', $user['lastname']);
+ $html->set('patronymic', $user['patronymic']);
+ $html->set('login', $user['login']);
+ $html->set('mail', $user['mail']);
+ $html->set('phone', $user['phone']);
+ $html->set('contacts', $user['contacts']);
+ $html->set('cur', $cfg['currency']);
+ $html->set('wmr', $user['wmr']);
+ $html->set('rental', $rental);
+ $html->set('extend', $extend);
+ $html->set('date', sys::today($user['date'], true));
+ $html->set('balance', round($user['balance'], 2));
+
+ $html->set('part_users', $part_users);
+ $html->set('part_money', $user['part_money']);
+ $html->set('part_user', $part_user);
+
+ if($user['name']) $html->unit('name', true); else $html->unit('name');
+ if($user['lastname']) $html->unit('lastname', true); else $html->unit('lastname');
+ if($user['patronymic']) $html->unit('patronymic', true); else $html->unit('patronymic');
+
+ if($user['name'] || $user['lastname'] || $user['patronymic']) $html->unit('nlp', true); else $html->unit('nlp');
+
+ if(isset($user['wmr']{0}) AND in_array($user['wmr']{0}, array('R', 'Z', 'U')))
+ $html->unit('wmr', true, true);
+ else
+ $html->unit('wmr', false, true);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/user/lk/logs.php b/system/sections/user/lk/logs.php
new file mode 100644
index 0000000..d2f3818
--- /dev/null
+++ b/system/sections/user/lk/logs.php
@@ -0,0 +1,20 @@
+query('SELECT `text`, `date` FROM `logs` WHERE `user`="'.$user['id'].'" ORDER BY `id` DESC LIMIT 40');
+ while($aLog = $sql->get($qLog))
+ {
+ $html->get('list', 'sections/user/lk/logs');
+ $html->set('text', $aLog['text']);
+ $html->set('date', sys::today($aLog['date'], true));
+ $html->pack('logs');
+ }
+
+ $html->get('logs', 'sections/user/lk');
+
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : 'Нет логов операций', true);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/user/lk/security.php b/system/sections/user/lk/security.php
new file mode 100644
index 0000000..24ed831
--- /dev/null
+++ b/system/sections/user/lk/security.php
@@ -0,0 +1,160 @@
+query('SELECT `security_ip`, `security_code` FROM `users` WHERE `id`="'.$user['id'].'" LIMIT 1');
+ $user = array_merge($user, $sql->get());
+
+ // Выполнений действий
+ if(isset($url['action']) AND in_array($url['action'], array('on', 'off', 'on_code', 'off_code', 'add', 'del', 'info')))
+ {
+ $snw = isset($_POST['subnetwork']) ? true : false;
+
+ switch($url['action'])
+ {
+ case 'on':
+ $sql->query('UPDATE `users` set `security_ip`="1" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+
+ case 'off':
+ $sql->query('UPDATE `users` set `security_ip`="0" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+
+ case 'on_code':
+ $sql->query('UPDATE `users` set `security_code`="1" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+
+ case 'off_code':
+ $sql->query('UPDATE `users` set `security_code`="0" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+
+ case 'add':
+ $address = isset($_POST['address']) ? trim($_POST['address']) : exit();
+
+ if(sys::valid($address, 'ip'))
+ sys::outjs(array('e' => 'Указанный адрес имеет неверный формат.'));
+
+ // Если подсеть
+ if($snw)
+ {
+ $address = sys::whois($address);
+
+ if($address == 'не определена')
+ sys::outjs(array('e' => 'Не удалось определить подсеть для указанного адреса.'));
+ }
+
+ $sql->query('SELECT `id` FROM `security` WHERE `user`="'.$user['id'].'" AND `address`="'.$address.'" LIMIT 1');
+
+ // Если такой адрес уже добавлен
+ if($sql->num())
+ sys::outjs(array('s' => 'ok'));
+
+ $sql->query('INSERT INTO `security` set `user`="'.$user['id'].'", `address`="'.$address.'", `time`="'.$start_point.'"');
+
+ sys::outjs(array('s' => 'ok'));
+
+ case 'del':
+ $address = isset($_POST['address']) ? trim($_POST['address']) : exit();
+
+ if(!is_numeric($address) AND sys::valid($address, 'ip'))
+ sys::outjs(array('e' => sys::outjs(array('e' => 'Указанный адрес имеет неверный формат.'))));
+
+ if(is_numeric($address))
+ {
+ $sql->query('SELECT `id` FROM `security` WHERE `user`="'.$user['id'].'" AND `id`="'.$address.'" LIMIT 1');
+
+ // Если такое правило отсутствует
+ if(!$sql->num())
+ sys::outjs(array('s' => 'ok'));
+ }else{
+ $sql->query('SELECT `id` FROM `security` WHERE `user`="'.$user['id'].'" AND `address`="'.$address.'" LIMIT 1');
+
+ // Если одиночный адрес не найден, проверить на разрешенную подсеть
+ if(!$sql->num())
+ {
+ $address = sys::whois($address);
+
+ $sql->query('SELECT `id` FROM `security` WHERE `user`="'.$user['id'].'" AND `address`="'.$address.'" LIMIT 1');
+
+ if($sql->num())
+ {
+ $security = $sql->get();
+
+ sys::outjs(array('i' => 'Указанный адрес входит в разрешенную подсеть, удалить подсеть?', 'id' => $security['id']));
+ }
+
+ sys::outjs(array('s' => 'ok'));
+ }
+ }
+
+ $security = $sql->get();
+
+ $sql->query('DELETE FROM `security` WHERE `id`="'.$security['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+
+ case 'info':
+ $address = isset($_POST['address']) ? trim($_POST['address']) : sys::outjs(array('info' => 'Не удалось получить информацию.'));
+
+ if(sys::valid($address, 'ip'))
+ sys::outjs(array('e' => 'Указанный адрес имеет неверный формат.'));
+
+ include(LIB.'geo.php');
+
+ $SxGeo = new SxGeo(DATA.'SxGeoCity.dat');
+
+ $data = $SxGeo->getCityFull($address);
+
+ $info = 'Информация об IP адресе:';
+
+ if($data['country']['name_ru'] != '')
+ {
+ $info .= 'Страна: '.$data['country']['name_ru'];
+
+ if($data['city']['name_ru'] != '')
+ $info .= '
Город: '.$data['city']['name_ru'];
+
+ $info .= '
Подсеть: '.sys::whois($address);
+
+ }else
+ $info = 'Не удалось получить информацию.';
+
+ sys::outjs(array('info' => $info));
+ }
+ }
+
+ // Построение списка добавленных адресов
+ $sql->query('SELECT `id`, `address` FROM `security` WHERE `user`="'.$user['id'].'" ORDER BY `id` ASC');
+ while($security = $sql->get())
+ {
+ $html->get('list', 'sections/user/lk/security');
+
+ $html->set('id', $security['id']);
+ $html->set('address', $security['address']);
+
+ $html->pack('security');
+ }
+
+ $html->get('security', 'sections/user/lk');
+
+ $html->set('ip', $uip);
+ $html->set('subnetwork', sys::whois($uip));
+
+ $html->set('security', isset($html->arr['security']) ? $html->arr['security'] : '', true);
+
+ if($user['security_ip'])
+ $html->unit('security_ip', true, true);
+ else
+ $html->unit('security_ip', false, true);
+
+ if($user['security_code'])
+ $html->unit('security_code', true, true);
+ else
+ $html->unit('security_code', false, true);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/user/lk/settings.php b/system/sections/user/lk/settings.php
new file mode 100644
index 0000000..27c2e97
--- /dev/null
+++ b/system/sections/user/lk/settings.php
@@ -0,0 +1,61 @@
+query('SELECT `notice_news`, `notice_help` FROM `users` WHERE `id`="'.$user['id'].'" LIMIT 1');
+ $user = array_merge($user, $sql->get());
+
+ if(isset($url['action']) AND in_array($url['action'], array('upload', 'news', 'help', 'important')))
+ {
+ switch($url['action'])
+ {
+ case 'upload':
+ $file = isset($_POST['value']) ? $_POST['value'] : exit;
+ $name = isset($_POST['name']) ? $_POST['name'] : exit;
+
+ $pname = explode('.', $name);
+ $type = strtolower(end($pname));
+
+ if(!in_array($type, array('png', 'gif', 'jpg', 'bmp')))
+ exit('Допустимый формат изображений: png, gif, jpg, bmp.');
+
+ $aData = explode(',', $file);
+
+ if(file_put_contents(ROOT.'upload/avatars/'.$user['id'].'.'.$type, base64_decode(str_replace(' ','+', $aData[1]))))
+ exit($user['id'].':ok');
+
+ exit('Ошибка загрузки: убедитесь, что изображение не повреждено и имеет правильный формат.');
+
+ case 'news':
+ $notice = $user['notice_news'] ? 0 : 1;
+
+ $sql->query('UPDATE `users` set `notice_news`="'.$notice.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+
+ case 'help':
+ $notice = $user['notice_help'] ? 0 : 1;
+
+ $sql->query('UPDATE `users` set `notice_help`="'.$notice.'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'));
+ }
+ }
+
+ $html->get('settings', 'sections/user/lk');
+
+ $html->set('id', $user['id']);
+ $html->set('ava', users::ava($user['id']));
+
+ if($user['notice_news'])
+ $html->unit('notice_news', true);
+ else
+ $html->unit('notice_news');
+
+ if($user['notice_help'])
+ $html->unit('notice_help', true);
+ else
+ $html->unit('notice_help');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/user/quit.php b/system/sections/user/quit.php
new file mode 100644
index 0000000..ea3e4aa
--- /dev/null
+++ b/system/sections/user/quit.php
@@ -0,0 +1,17 @@
+query('UPDATE `users` set `time`="'.($start_point-10).'" WHERE `id`="'.$user['id'].'" LIMIT 1');
+
+ sys::users($users, $user, $authkey, true);
+ sys::back($cfg['http']);
+?>
\ No newline at end of file
diff --git a/system/sections/user/recovery.php b/system/sections/user/recovery.php
new file mode 100644
index 0000000..52a0924
--- /dev/null
+++ b/system/sections/user/recovery.php
@@ -0,0 +1,121 @@
+get($nmch))
+ sys::outjs(array('e' => sys::text('all', 'mcache')), $nmch);
+
+ $mcache->set($nmch, 1, false, 15);
+
+ // Проверка капчи
+ if(!isset($_POST['captcha']) || sys::captcha_check('recovery', $uip, $_POST['captcha']))
+ sys::outjs(array('e' => sys::text('other', 'captcha')), $nmch);
+
+ $aData = array();
+
+ $aData['login'] = isset($_POST['login']) ? $_POST['login'] : '';
+
+ // Проверка логина/почты на валидность
+ if(sys::valid($aData['login'], 'other', $aValid['mail']) && sys::valid($aData['login'], 'other', $aValid['login']))
+ {
+ $out = 'login';
+
+ // Если в логине указана почта
+ if(sys::ismail($aData['login']))
+ $out = 'mail';
+
+ sys::outjs(array('e' => sys::text('input', $out.'_valid')), $nmch);
+ }
+
+ $sql_q = '`login`';
+
+ // Если в логине указана почта
+ if(sys::ismail($aData['login']))
+ $sql_q = '`mail`';
+
+ // Проверка существования пользователя
+ $sql->query('SELECT `id`, `mail` FROM `users` WHERE '.$sql_q.'="'.$aData['login'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => sys::text('input', 'recovery')), $nmch);
+
+ $user = $sql->get();
+
+ $link = $device == '!mobile' ? 'user/section/recovery/confirm/' : 'recovery/confirm/';
+
+ // Проверка подачи запроса на восстановление
+ $sql->query('SELECT `id`, `key` FROM `recovery` WHERE `user`="'.$user['id'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $recovery = $sql->get();
+ $sql->query('UPDATE `recovery` set `date`="'.$start_point.'" WHERE `id`="'.$recovery['id'].'" LIMIT 1');
+
+ // Повторная отправка письма на почту
+ if(sys::mail('Восстановление доступа', sys::updtext(sys::text('mail', 'recovery'), array('site' => $cfg['name'], 'url' => $cfg['http'].$link.$recovery['key'])), $user['mail']))
+ sys::outjs(array('s' => sys::text('output', 'remail'), 'mail' => sys::mail_domain($user['mail'])), $nmch);
+
+ // Выхлоп: не удалось отправить письмо
+ sys::outjs(array('e' => sys::text('error', 'mail')), $nmch);
+ }
+
+ // Генерация ключа
+ $key = sys::key('recovery_'.$uip);
+
+ // Запись данных в базу
+ $sql->query('INSERT INTO `recovery` set `user`="'.$user['id'].'", `mail`="'.$user['mail'].'", `key`="'.$key.'", `date`="'.$start_point.'"');
+
+ // Отправка письма на почту
+ if(sys::mail('Восстановление доступа', sys::updtext(sys::text('mail', 'recovery'), array('site' => $cfg['name'], 'url' => $cfg['http'].$link.$key)), $user['mail']))
+ sys::outjs(array('s' => sys::text('output', 'mail'), 'mail' => sys::mail_domain($user['mail'])), $nmch);
+
+ // Выхлоп: не удалось отправить письмо
+ sys::outjs(array('e' => sys::text('error', 'mail')), $nmch);
+ }
+
+ // Завершение восстановления
+ if(isset($url['confirm']) && !sys::valid($url['confirm'], 'md5'))
+ {
+ $sql->query('SELECT `id`, `user`, `mail` FROM `recovery` WHERE `key`="'.$url['confirm'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $data = $sql->get();
+ $passwd = sys::passwd(10);
+
+ $sql->query('SELECT `security_ip` FROM `users` WHERE `id`="'.$data['user'].'" LIMIT 1');
+ $user = $sql->get();
+
+ // Если включена защита по ip
+ if($user['security_ip'])
+ {
+ $sql->query('SELECT `id` FROM `security` WHERE `user`="'.$data['user'].'" AND `address`="'.$uip.'" LIMIT 1');
+
+ if(!$sql->num())
+ $sql->query('INSERT INTO `security` set `user`="'.$data['user'].'", `address`="'.$uip.'", `time`="'.$start_point.'"');
+ }
+
+ $sql->query('UPDATE `users` set `passwd`="'.sys::passwdkey($passwd).'" WHERE `id`="'.$data['user'].'" LIMIT 1');
+ $sql->query('DELETE FROM `recovery` WHERE `id`="'.$data['id'].'" LIMIT 1');
+
+ if(sys::mail('Восстановление доступа', sys::updtext(sys::text('mail', 'recovery_end'), array('site' => $cfg['name'], 'passwd' => $passwd)), $data['mail']))
+ sys::outhtml('Операция по восстановлению успешно выполнена, на вашу почту отправлен новый пароль.', 5, 'http://'.sys::mail_domain($data['mail']));
+
+ sys::outhtml(sys::text('error', 'mail'), 5);
+ }
+
+ sys::outhtml(sys::text('error', 'recovery'), 5);
+ }
+
+ $html->get('recovery', 'sections/user');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/user/replenish.php b/system/sections/user/replenish.php
new file mode 100644
index 0000000..fccbef5
--- /dev/null
+++ b/system/sections/user/replenish.php
@@ -0,0 +1,29 @@
+query('SELECT `text`, `date` FROM `logs` WHERE `user`="'.$user['id'].'" AND `type`="replenish" ORDER BY `id` DESC LIMIT 10');
+ while($aLog = $sql->get($qLog))
+ {
+ $html->get('replenish', 'sections/user/lk/logs');
+ $html->set('text', $aLog['text']);
+ $html->set('date', sys::today($aLog['date'], true));
+ $html->pack('logs');
+ }
+
+ $html->get('replenish', 'sections/user');
+ $html->set('id', $user['id']);
+ $html->set('wmr', $cfg['webmoney_wmr']);
+ $html->set('freekassa', $cfg['freekassa_id']);
+ $html->set('balance', round($user['balance'], 2));
+ $html->set('cur', $cfg['currency']);
+ $html->set('logs', isset($html->arr['logs']) ? $html->arr['logs'] : 'Нет логов операций', true);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/user/signup.php b/system/sections/user/signup.php
new file mode 100644
index 0000000..ad6a4a4
--- /dev/null
+++ b/system/sections/user/signup.php
@@ -0,0 +1,217 @@
+ $add)
+ {
+ if(!$add)
+ continue;
+
+ $aData[$name] = isset($_POST[$name]) ? trim($_POST[$name]) : '';
+ }
+ }
+
+ // Регистрация
+ if($go)
+ {
+ $nmch = 'go_signup_'.$uip;
+
+ if($mcache->get($nmch))
+ sys::outjs(array('e' => sys::text('other', 'mcache')), $nmch);
+
+ $mcache->set($nmch, 1, false, 15);
+
+ // Проверка капчи
+ if(!isset($_POST['captcha']) || sys::captcha_check('signup', $uip, $_POST['captcha']))
+ sys::outjs(array('e' => sys::text('other', 'captcha')), $nmch);
+
+ // Проверка входных данных
+ foreach($aData as $input => $val)
+ {
+ // Если не заполнено поле
+ if($val == '')
+ sys::outjs(array('e' => sys::text('input', 'all')), $nmch);
+
+ // Проверка данных на валидность
+ if(sys::valid($val, 'other', $aValid[$input]))
+ sys::outjs(array('e' => sys::text('input', $input.'_valid')), $nmch);
+ }
+
+ // Проверка логина на занятость
+ if(isset($aData['login']))
+ {
+ $sql->query('SELECT `id` FROM `users` WHERE `login`="'.$aData['login'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => sys::text('input', 'login_use')), $nmch);
+ }
+
+ if(!isset($aData['mail']))
+ sys::outjs(array('e' => sys::text('input', 'mail_valid')), $nmch);
+
+ // Проверка почты на занятость
+ $sql->query('SELECT `id` FROM `users` WHERE `mail`="'.$aData['mail'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => sys::text('input', 'mail_use')), $nmch);
+
+ // Проверка телефона на занятость
+ if(isset($aData['phone']))
+ {
+ $sql->query('SELECT `id` FROM `users` WHERE `phone`="'.$aData['phone'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => sys::text('input', 'phone_use')), $nmch);
+ }
+
+ // Проверка контактов на занятость
+ if(isset($aData['contacts']))
+ {
+ $sql->query('SELECT `id` FROM `users` WHERE `contacts`="'.$aData['contacts'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => sys::text('input', 'use_contacts')), $nmch);
+ }
+
+ // Проверка почты на подачу регистрации
+ $sql->query('SELECT `id`, `key` FROM `signup` WHERE `mail`="'.$aData['mail'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $signup = $sql->get();
+ $sql->query('UPDATE `signup` set `date`="'.$start_point.'" WHERE `id`="'.$signup['id'].'" LIMIT 1');
+
+ // Повторная отправка письма на почту
+ sys::mail(
+ 'Регистрация',
+ sys::updtext(
+ sys::text('mail', 'signup'),
+ array(
+ 'site' => $cfg['name'],
+ 'url' => $cfg['http'].'user/section/signup/confirm/'.$signup['key']
+ )
+ ),
+ $aData['mail']
+ );
+ sys::outjs(array('s' => sys::text('output', 'remail'), 'mail' => sys::mail_domain($aData['mail'])), $nmch);
+ }
+
+ // Генерация ключа
+ $key = sys::key('signup_'.$uip);
+
+ $data = sys::b64js($aData);
+
+ // Запись данных в базу
+ $sql->query('INSERT INTO `signup` set `mail`="'.$aData['mail'].'", `key`="'.$key.'", `data`="'.$data.'", `date`="'.$start_point.'"');
+
+ // Отправка сообщения на почту
+ if(sys::mail('Регистрация', sys::updtext(sys::text('mail', 'signup'), array('site' => $cfg['name'], 'url' => $cfg['http'].'user/section/signup/confirm/'.$key)), $aData['mail']))
+ sys::outjs(array('s' => sys::text('output', 'mail'), 'mail' => sys::mail_domain($aData['mail'])), $nmch);
+
+ // Выхлоп: не удалось отправить письмо
+ sys::outjs(array('e' => sys::text('error', 'mail')), $nmch);
+ }
+
+ // Завершение регистрации
+ if(isset($url['confirm']) && !sys::valid($url['confirm'], 'md5'))
+ {
+ $sql->query('SELECT `id`, `data` FROM `signup` WHERE `key`="'.$url['confirm'].'" LIMIT 1');
+ if($sql->num())
+ {
+ $signup = $sql->get();
+
+ $aData = sys::b64djs($signup['data']);
+
+ foreach($aSignup['input'] as $name => $add)
+ $aNData[$name] = isset($aData[$name]) ? $aData[$name] : '';
+
+ unset($aData);
+
+ // Если регистрация без указания логина
+ if(empty($aNData['login']))
+ {
+ $lchar = false;
+
+ while(1)
+ {
+ $aNData['login'] = sys::login($aNData['mail'], $lchar);
+
+ $sql->query('SELECT `id` FROM `users` WHERE `login`="'.$aNData['login'].'" LIMIT 1');
+ if(!$sql->num())
+ break;
+
+ $lchar = true;
+ }
+ }
+
+ // Если регистрация без указания пароля
+ if(empty($aNData['passwd']))
+ $aNData['passwd'] = sys::passwd(10);
+
+ // Реферал
+ if(isset($_COOKIE['part']))
+ $part = ', `part`="'.sys::int($_COOKIE['part']).'"';
+
+ // Запись данных в базу
+ $sql->query('INSERT INTO `users` set '
+ .'`login`="'.$aNData['login'].'",'
+ .'`passwd`="'.sys::passwdkey($aNData['passwd']).'",'
+ .'`mail`="'.$aNData['mail'].'",'
+ .'`name`="'.$aNData['name'].'",'
+ .'`lastname`="'.$aNData['lastname'].'",'
+ .'`patronymic`="'.$aNData['patronymic'].'",'
+ .'`phone`="'.$aNData['phone'].'",'
+ .'`contacts`="'.$aNData['contacts'].'",'
+ .'`balance`="0", `group`="user", `date`="'.$start_point.'"'.$part);
+
+ $sql->query('DELETE FROM `signup` WHERE `id`="'.$signup['id'].'" LIMIT 1');
+
+ // Отправка сообщения на почту
+ if(sys::mail('Завершение регистрации', sys::updtext(sys::text('mail', 'signup_end'), array('site' => $cfg['name'], 'login' => $aNData['login'], 'passwd' => $aNData['passwd'])), $aNData['mail']))
+ sys::outhtml(sys::text('output', 'signup'), 5, 'http://'.sys::mail_domain($aNData['mail']));
+
+ // Выхлоп: не удалось отправить письмо
+ sys::outjs(array('e' => sys::text('error', 'mail')), $nmch);
+ }
+
+ sys::outhtml(sys::text('error', 'signup'), 5);
+ }
+
+ // Генерация формы
+ foreach($aSignup['input'] as $name => $add)
+ {
+ if(!$add)
+ continue;
+
+ $html->get('signup', 'sections/user/inputs');
+ $html->set('name', $name);
+ $html->set('info', $aSignup['info'][$name]);
+ $html->set('type', $aSignup['type'][$name]);
+ $html->set('placeholder', $aSignup['placeholder'][$name]);
+ $html->pack('inputs');
+ }
+
+ $html->get('signup', 'sections/user');
+
+ $inputsjs = '';
+
+ foreach($aSignup['input'] as $name => $add)
+ {
+ if(!$add)
+ continue;
+
+ $inputsjs .= '"'.$name.'",';
+ }
+
+ $html->set('inputs', $html->arr['inputs'], true);
+ $html->set('inputsjs', $inputsjs);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/aes/free/connect.php b/system/sections/web/aes/free/connect.php
new file mode 100644
index 0000000..3f5090b
--- /dev/null
+++ b/system/sections/web/aes/free/connect.php
@@ -0,0 +1,30 @@
+ 'Длина ключа не должна превышать 32 символа.'), $nmch);
+
+ include(LIB.'web/free.php');
+
+ $aData = array();
+
+ $aData['server'] = sys::int($url['server']);
+ $aData['type'] = $url['subsection'];
+ $aData['user'] = $server['user'];
+ $aData['file'] = 'cstrike/addons/amxmodx/configs/csstats_mysql.cfg';
+ $aData['cfg'] = 'cstrike/server.cfg';
+
+ $aData['orcfg'] = array(
+ 'key' => $key
+ );
+
+ $aData['orsql'] = array();
+
+ web::connect($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/aes/free/delete.php b/system/sections/web/aes/free/delete.php
new file mode 100644
index 0000000..0f47665
--- /dev/null
+++ b/system/sections/web/aes/free/delete.php
@@ -0,0 +1,19 @@
+ 'Чтобы удалить услугу, создайте вопрос выбрав свой сервер с причиной удаления.'), $nmch);
+
+ include(LIB.'web/free.php');
+
+ $aData = array(
+ 'type' => $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::delete($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/aes/free/install.php b/system/sections/web/aes/free/install.php
new file mode 100644
index 0000000..c631da5
--- /dev/null
+++ b/system/sections/web/aes/free/install.php
@@ -0,0 +1,75 @@
+ 'Необходимо указать адрес.'), $nmch);
+ $aData['domain'] = isset($_POST['domain']) ? strtolower($_POST['domain']) : sys::outjs(array('e' => 'Необходимо выбрать домен.'), $nmch);
+ $aData['desing'] = isset($_POST['desing']) ? strtolower($_POST['desing']) : sys::outjs(array('e' => 'Необходимо выбрать шаблон.'), $nmch);
+
+ $aData['type'] = $url['subsection'];
+ $aData['server'] = array_merge($server, array('id' => $id));
+
+ $aData['config_sql'] = 'csstats_key "[key]"'.PHP_EOL
+ .'csstats_url "[domain]"'.PHP_EOL
+ .'csstats_sort "-2"'.PHP_EOL
+ .'csstats_double "0"'.PHP_EOL
+ .'csstats_host "[host]"'.PHP_EOL
+ .'csstats_user "[login]"'.PHP_EOL
+ .'csstats_pass "[passwd]"'.PHP_EOL
+ .'csstats_db "[login]"'.PHP_EOL
+ .'csstats_table_players "csstats_players"'.PHP_EOL
+ .'csstats_table_settings "csstats_settings"'.PHP_EOL
+ .'csstats_delete_time "15 120 2"'.PHP_EOL
+ .'csstats_insert_time "60 1"'.PHP_EOL
+ .'csstats_update_time "30 0"'.PHP_EOL
+ .'csstats_slow "0"';
+
+ $aData['config_php'] = '';
+
+ web::install($aData, $nmch);
+ }
+
+ $html->nav('Установка '.$aWebname[$url['subsection']]);
+
+ $desing = '';
+
+ // Генерация списка шаблонов
+ foreach($aWebParam[$url['subsection']]['desing'] as $name => $desc)
+ $desing .= ''.$desc.' ';
+
+ $domains = '';
+
+ // Генерация списка доменов
+ foreach($aWebUnit['domains'] as $domain)
+ $domains .= '.'.$domain.' ';
+
+ $html->get('install', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('desing', $desing);
+ $html->set('domains', $domains);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/aes/free/manage.php b/system/sections/web/aes/free/manage.php
new file mode 100644
index 0000000..68bfdf0
--- /dev/null
+++ b/system/sections/web/aes/free/manage.php
@@ -0,0 +1,29 @@
+query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/web/subsection/'.$url['subsection'].'/action/install');
+
+ $web = $sql->get();
+
+ $html->nav('Управление '.$aWebname[$url['subsection']]);
+
+ $html->get('manage', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('url', $web['domain']);
+ $html->set('passwd', $web['passwd']);
+ $html->set('config', base64_decode($web['config']));
+ $html->set('servers', '#'.$id.' '.$server['name'].' ('.$server['address'].') ');
+
+ if(in_array('update', $aAction[$url['subsection']]))
+ $html->unit('update', 1);
+ else
+ $html->unit('update');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/aes/free/update.php b/system/sections/web/aes/free/update.php
new file mode 100644
index 0000000..3971e3c
--- /dev/null
+++ b/system/sections/web/aes/free/update.php
@@ -0,0 +1,65 @@
+query('SELECT `id`, `uid`, `desing`, `domain`, `update` FROM `web` WHERE `server`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `uid`, `desing`, `domain`, `update` FROM `web` WHERE `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `uid`, `desing`, `domain`, `update` FROM `web` WHERE `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Дополнительная услуга не установлена.'), $nmch);
+
+ $web = $sql->get();
+
+ // Проверка времени последнего обновления
+ include(LIB.'games/games.php');
+
+ $upd = $web['update']+86400;
+
+ if($upd > $start_point)
+ sys::outjs(array('e' => 'Для повторного обновления должно пройти: '.games::date('max', $upd)));
+
+ include(LIB.'ssh.php');
+
+ if($aWebUnit['unit'][$url['subsection']] == 'local')
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+ }else{
+ $unit = array(
+ 'address' => $aWebUnit['address'],
+ 'passwd' => $aWebUnit['passwd'],
+ );
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $install = $aWebUnit['install'][$aWebUnit['unit'][$url['subsection']]][$url['subsection']].$web['domain'];
+
+ $path = $aWebUnit['path'][$aWebUnit['unit'][$url['subsection']]][$url['subsection']].$web['desing'];
+
+ $ssh->set('cat '.$install.'/include/db.config.inc.php > '.$path.'/include/db.config.inc.php;'
+ .'cd '.$install.' && sudo -u web'.$web['uid'].' screen -dmS u_w_'.$web['uid'].' sh -c "YES | cp -rf '.$path.'/. .; '.$aWebChmod[$url['subsection']].'"');
+
+ $sql->query('UPDATE `web` set `update`="'.$start_point.'" WHERE `id`="'.$web['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/amxbans/free/connect.php b/system/sections/web/amxbans/free/connect.php
new file mode 100644
index 0000000..732a9ed
--- /dev/null
+++ b/system/sections/web/amxbans/free/connect.php
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/system/sections/web/amxbans/free/delete.php b/system/sections/web/amxbans/free/delete.php
new file mode 100644
index 0000000..0f47665
--- /dev/null
+++ b/system/sections/web/amxbans/free/delete.php
@@ -0,0 +1,19 @@
+ 'Чтобы удалить услугу, создайте вопрос выбрав свой сервер с причиной удаления.'), $nmch);
+
+ include(LIB.'web/free.php');
+
+ $aData = array(
+ 'type' => $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::delete($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/amxbans/free/install.php b/system/sections/web/amxbans/free/install.php
new file mode 100644
index 0000000..8b7ff24
--- /dev/null
+++ b/system/sections/web/amxbans/free/install.php
@@ -0,0 +1,60 @@
+ 'Необходимо указать адрес.'), $nmch);
+ $aData['domain'] = isset($_POST['domain']) ? strtolower($_POST['domain']) : sys::outjs(array('e' => 'Необходимо выбрать домен.'), $nmch);
+ $aData['desing'] = isset($_POST['desing']) ? strtolower($_POST['desing']) : sys::outjs(array('e' => 'Необходимо выбрать шаблон.'), $nmch);
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : sys::passwd($aWebParam[$url['subsection']]['passwd']);
+
+ $aData['type'] = $url['subsection'];
+ $aData['server'] = array_merge($server, array('id' => $id));
+
+ $aData['config_sql'] = 'amx_sql_host "[host]"'.PHP_EOL
+ .'amx_sql_user "[login]"'.PHP_EOL
+ .'amx_sql_pass "[passwd]"'.PHP_EOL
+ .'amx_sql_db "[login]"'.PHP_EOL
+ .'amx_sql_table "admins"'.PHP_EOL
+ .'amx_sql_type "mysql"';
+
+ $aData['config_php'] = 'db_host = "[host]";'.PHP_EOL
+ .' $config->db_user = "[login]";'.PHP_EOL
+ .' $config->db_pass = "[passwd]";'.PHP_EOL
+ .' $config->db_db = "[login]";'.PHP_EOL
+ .' $config->db_prefix = "amx";'.PHP_EOL
+ .'?>';
+
+ web::install($aData, $nmch);
+ }
+
+ $html->nav('Установка '.$aWebname[$url['subsection']]);
+
+ $desing = '';
+
+ // Генерация списка шаблонов
+ foreach($aWebParam[$url['subsection']]['desing'] as $name => $desc)
+ $desing .= ''.$desc.' ';
+
+ $domains = '';
+
+ // Генерация списка доменов
+ foreach($aWebUnit['domains'] as $domain)
+ $domains .= '.'.$domain.' ';
+
+ $html->get('install', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('desing', $desing);
+ $html->set('domains', $domains);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/amxbans/free/manage.php b/system/sections/web/amxbans/free/manage.php
new file mode 100644
index 0000000..bf62ce5
--- /dev/null
+++ b/system/sections/web/amxbans/free/manage.php
@@ -0,0 +1,57 @@
+query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ $options = '#'.$id.' '.$server['name'].' ('.$server['address'].') ';
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `user`="'.$server['user'].'" AND `game`="cs" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `unit`="'.$server['unit'].'" AND `user`="'.$server['user'].'" AND `game`="cs" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/web/subsection/'.$url['subsection'].'/action/install');
+
+ $web = $sql->get();
+
+ $html->nav('Управление '.$aWebname[$url['subsection']]);
+
+ $html->get('manage', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('url', $web['domain']);
+ $html->set('passwd', $web['passwd']);
+ $html->set('config', base64_decode($web['config']));
+ $html->set('servers', $options);
+
+ if(in_array('update', $aAction[$url['subsection']]))
+ $html->unit('update', 1);
+ else
+ $html->unit('update');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/amxbans/free/passwd.php b/system/sections/web/amxbans/free/passwd.php
new file mode 100644
index 0000000..5945d09
--- /dev/null
+++ b/system/sections/web/amxbans/free/passwd.php
@@ -0,0 +1,16 @@
+ $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::passwd($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/amxbans/free/update.php b/system/sections/web/amxbans/free/update.php
new file mode 100644
index 0000000..97689c3
--- /dev/null
+++ b/system/sections/web/amxbans/free/update.php
@@ -0,0 +1,16 @@
+ $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::update($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/astats/free/connect.php b/system/sections/web/astats/free/connect.php
new file mode 100644
index 0000000..21d8862
--- /dev/null
+++ b/system/sections/web/astats/free/connect.php
@@ -0,0 +1,97 @@
+ 'Необходимо выбрать игровой сервер.'), $nmch);
+ $aData['type'] = $url['subsection'];
+
+ switch($aWebInstall[$server['game']][$aData['type']])
+ {
+ case 'server':
+ $sql->query('SELECT `unit`, `domain`, `login` FROM `web` WHERE `type`="'.$aData['type'].'" AND `server`="'.$server['id'].'" LIMIT 1');
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `unit`, `domain`, `login` FROM `web` WHERE `type`="'.$aData['type'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `unit`, `domain`, `login` FROM `web` WHERE `type`="'.$aData['type'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Дополнительная услуга не установлена.'), $nmch);
+
+ $web = $sql->get();
+
+ $aData['config'] = 'query('SELECT `id`, `uid`, `unit`, `user`, `address`, `game`, `ftp_use`, `ftp`, `ftp_root`, `ftp_passwd` FROM `servers` WHERE `id`="'.$sid.'" AND `user`="'.$server['user'].'" AND `game`="cs" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $server = $sql->get();
+
+ $address = explode(':', $server['address']);
+
+ if(!$server['ftp_use'])
+ continue;
+
+ if(!$server['ftp'])
+ sys::outjs(array('r' => 'Для подключения игрового сервера необходимо включить FileTP.', 'url' => $cfg['http'].'servers/id/'.$sid.'/section/filetp'), $nmch);
+
+ $stack = web::stack($aData, '`login`');
+
+ if(!$sql->num($stack))
+ continue;
+
+ // Каталог логов сервера
+ $dir = ($cfg['ftp']['root'][$server['game']] || $server['ftp_root']) ? '/cstrike/addons/amxmodx/data' : '/addons/amxmodx/data';
+
+ $i+=1;
+
+ $aData['config'] .= $i.' => array('.PHP_EOL
+ .'\'ip\' => \''.$address[0].'\','.PHP_EOL
+ .'\'port\' => '.$address[1].','.PHP_EOL
+ .'\'engine\' => \'GOLDSOURCE\','.PHP_EOL
+ .'\'ftp_host\' => \''.$address[0].'\','.PHP_EOL
+ .'\'ftp_port\' => 21,'.PHP_EOL
+ .'\'ftp_user\' => \''.$server['uid'].'\','.PHP_EOL
+ .'\'ftp_pass\' => \''.$server['ftp_passwd'].'\','.PHP_EOL
+ .'\'ftp_path\' => \''.$dir.'\''.PHP_EOL
+ .'),'.PHP_EOL;
+
+ }
+
+ include(LIB.'ssh.php');
+
+ $unit = web::unit($aWebUnit, $aData['type'], $web['unit']);
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('ssh', 'error')), $nmch);
+
+ // Директория дополнительной услуги
+ $install = $aWebUnit['install'][$aWebUnit['unit'][$aData['type']]][$aData['type']].$web['domain'];
+
+ $temp = sys::temp($aData['config'].');');
+ $ssh->setfile($temp, $install.'/config/servers.config.php', 0644);
+
+ unlink($temp);
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/astats/free/delete.php b/system/sections/web/astats/free/delete.php
new file mode 100644
index 0000000..0f47665
--- /dev/null
+++ b/system/sections/web/astats/free/delete.php
@@ -0,0 +1,19 @@
+ 'Чтобы удалить услугу, создайте вопрос выбрав свой сервер с причиной удаления.'), $nmch);
+
+ include(LIB.'web/free.php');
+
+ $aData = array(
+ 'type' => $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::delete($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/astats/free/install.php b/system/sections/web/astats/free/install.php
new file mode 100644
index 0000000..9824f1a
--- /dev/null
+++ b/system/sections/web/astats/free/install.php
@@ -0,0 +1,47 @@
+ 'Необходимо указать адрес.'), $nmch);
+ $aData['domain'] = isset($_POST['domain']) ? strtolower($_POST['domain']) : sys::outjs(array('e' => 'Необходимо выбрать домен.'), $nmch);
+ $aData['desing'] = isset($_POST['desing']) ? strtolower($_POST['desing']) : sys::outjs(array('e' => 'Необходимо выбрать шаблон.'), $nmch);
+
+ $aData['type'] = $url['subsection'];
+ $aData['server'] = array_merge($server, array('id' => $id));
+
+ $aData['config_sql'] = '';
+ $aData['config_php'] = '';
+
+ web::install($aData, $nmch);
+ }
+
+ $html->nav('Установка '.$aWebname[$url['subsection']]);
+
+ $desing = '';
+
+ // Генерация списка шаблонов
+ foreach($aWebParam[$url['subsection']]['desing'] as $name => $desc)
+ $desing .= ''.$desc.' ';
+
+ $domains = '';
+
+ // Генерация списка доменов
+ foreach($aWebUnit['domains'] as $domain)
+ $domains .= '.'.$domain.' ';
+
+ $html->get('install', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('desing', $desing);
+ $html->set('domains', $domains);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/astats/free/manage.php b/system/sections/web/astats/free/manage.php
new file mode 100644
index 0000000..bf62ce5
--- /dev/null
+++ b/system/sections/web/astats/free/manage.php
@@ -0,0 +1,57 @@
+query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ $options = '#'.$id.' '.$server['name'].' ('.$server['address'].') ';
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `user`="'.$server['user'].'" AND `game`="cs" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `unit`="'.$server['unit'].'" AND `user`="'.$server['user'].'" AND `game`="cs" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/web/subsection/'.$url['subsection'].'/action/install');
+
+ $web = $sql->get();
+
+ $html->nav('Управление '.$aWebname[$url['subsection']]);
+
+ $html->get('manage', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('url', $web['domain']);
+ $html->set('passwd', $web['passwd']);
+ $html->set('config', base64_decode($web['config']));
+ $html->set('servers', $options);
+
+ if(in_array('update', $aAction[$url['subsection']]))
+ $html->unit('update', 1);
+ else
+ $html->unit('update');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/astats/free/update.php b/system/sections/web/astats/free/update.php
new file mode 100644
index 0000000..97689c3
--- /dev/null
+++ b/system/sections/web/astats/free/update.php
@@ -0,0 +1,16 @@
+ $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::update($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/bp/free/delete.php b/system/sections/web/bp/free/delete.php
new file mode 100644
index 0000000..1ea4a90
--- /dev/null
+++ b/system/sections/web/bp/free/delete.php
@@ -0,0 +1,19 @@
+ 'Чтобы удалить услугу, создайте вопрос выбрав свой сервер с причиной удаления.'), $name_mcache);
+
+ include(LIB.'web/free.php');
+
+ $aData = array(
+ 'type' => $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::delete($aData, $name_mcache);
+?>
\ No newline at end of file
diff --git a/system/sections/web/bp/free/install.php b/system/sections/web/bp/free/install.php
new file mode 100644
index 0000000..f1d8120
--- /dev/null
+++ b/system/sections/web/bp/free/install.php
@@ -0,0 +1,103 @@
+ 'Необходимо указать адрес.'), $name_mcache);
+ $aData['domain'] = isset($_POST['domain']) ? strtolower($_POST['domain']) : sys::outjs(array('e' => 'Необходимо выбрать домен.'), $name_mcache);
+ $aData['desing'] = isset($_POST['desing']) ? strtolower($_POST['desing']) : sys::outjs(array('e' => 'Необходимо выбрать шаблон.'), $name_mcache);
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : sys::passwd($aWebParam[$url['subsection']]['passwd']);
+
+ $aData['type'] = $url['subsection'];
+ $aData['server'] = array_merge($server, array('id' => $id));
+
+ $sql->query('SELECT `mail`, `contacts` FROM `users` WHERE `id`="'.$server['user'].'" LIMIT 1');
+ $us = $sql->get();
+
+ if($us['contacts'] == '')
+ sys::outjs(array('e' => 'Укажите в профиле контактную информацию'));
+
+ if(strpos($us['contacts'], 'ttp', 1))
+ {
+ $vk = $us['contacts'];
+ $skype = '';
+ }else{
+ $vk = '';
+ $skype = $us['contacts'];
+ }
+
+ $aData['config_sql'] = '';
+
+ $aData['config_php'] = ' "[host]",'.PHP_EOL
+ ."\t\t\t".'\'db_user\' => "[login]",'.PHP_EOL
+ ."\t\t\t".'\'db_pass\' => "[passwd]",'.PHP_EOL
+ ."\t\t\t".'\'db_name\' => "[login]",'.PHP_EOL
+ ."\t\t\t".'\'db_prefix\' => "bp"'.PHP_EOL
+ ."\t\t".');'.PHP_EOL
+ ."\t".'}'.PHP_EOL
+ ."\t".'@mb_internal_encoding("UTF-8");'.PHP_EOL
+ ."\t".'@date_default_timezone_set("Europe/Moscow");'.PHP_EOL
+ ."\t".'$site_name = "Online Market";'.PHP_EOL
+ ."\t".'$url = "http://'.$aData['subdomain'].'.'.$aData['domain'].'/";'.PHP_EOL
+ ."\t".'$num_page = "15";'.PHP_EOL
+ ."\t".'$cron = "874319";'.PHP_EOL
+ ."\n\t".'$wmr_on = "0";'.PHP_EOL
+ ."\t".'$purse = "";'.PHP_EOL
+ ."\t".'$secret_key = "";'.PHP_EOL
+ ."\t".'$uni_on = "0";'.PHP_EOL
+ ."\t".'$uni_purse = "";'.PHP_EOL
+ ."\t".'$uni_secret_key = "";'.PHP_EOL
+ ."\t".'$robo_on = "0";'.PHP_EOL
+ ."\t".'$robo_login = "";'.PHP_EOL
+ ."\t".'$robo_pass1 = "";'.PHP_EOL
+ ."\t".'$robo_pass2 = "";'.PHP_EOL
+ ."\t".'$curr = "руб.";'.PHP_EOL
+ ."\t".'$adm_login = "admin";'.PHP_EOL
+ ."\t".'$adm_pass = "[passwd]";'.PHP_EOL
+ ."\t".'$adm_ip = "";'.PHP_EOL
+ ."\t".'$to = "'.$us['mail'].'";'.PHP_EOL
+ ."\t".'$vk = "'.$vk.'";'.PHP_EOL
+ ."\t".'$skype = "'.$skype.'";'.PHP_EOL
+ ."\t".'require_once "core.php";'.PHP_EOL
+ ."\t".'$db = new DataBase();'.PHP_EOL
+ ."\t".'$eng = new Engine();'.PHP_EOL
+ ."\t".'$at = new Auth();'.PHP_EOL
+ .'?>';
+
+ include(LIB.'web/free.php');
+
+ web::install($aData, $name_mcache);
+ }
+
+ $html->nav('Установка '.$aWebname[$url['subsection']]);
+
+
+ $desing = '';
+
+ // Генерация списка шаблонов
+ foreach($aWebParam[$url['subsection']]['desing'] as $name => $desc)
+ $desing .= ''.$desc.' ';
+
+ $domains = '';
+
+ // Генерация списка доменов
+ foreach($aWebUnit['domains'] as $domain)
+ $domains .= '.'.$domain.' ';
+
+ $html->get('install', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('desing', $desing);
+ $html->set('domains', $domains);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/bp/free/manage.php b/system/sections/web/bp/free/manage.php
new file mode 100644
index 0000000..de16d4e
--- /dev/null
+++ b/system/sections/web/bp/free/manage.php
@@ -0,0 +1,40 @@
+query('SELECT `domain`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `domain`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `domain`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/web/subsection/'.$url['subsection'].'/action/install');
+
+ $web = $sql->get();
+
+ $html->nav('Управление '.$aWebname[$url['subsection']]);
+
+ $html->get('manage', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('url', $web['domain']);
+
+ if(in_array('update', $aAction[$url['subsection']]))
+ $html->unit('update', 1);
+ else
+ $html->unit('update');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/bp/free/settings.php b/system/sections/web/bp/free/settings.php
new file mode 100644
index 0000000..ef93d4e
--- /dev/null
+++ b/system/sections/web/bp/free/settings.php
@@ -0,0 +1,141 @@
+query('SELECT `id`, `domain` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `domain` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `domain` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ }
+
+ if(!$sql->num())
+ exit;
+
+ $web = $sql->get();
+
+ include(DATA.'web.php');
+
+ include(LIB.'web/free.php');
+ include(LIB.'ssh.php');
+
+ $unit = web::unit($aWebUnit, $aData['type'], $web['unit']);
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $name_mcache);
+
+ // Директория дополнительной услуги
+ $install = $aWebUnit['install'][$aWebUnit['unit'][$url['subsection']]][$url['subsection']].$web['domain'];
+
+ $unit = web::unit($aWebUnit, $aData['type'], $web['unit']);
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $name_mcache);
+
+ $arSel = array('$adm_login', '$adm_pass', '$wmr_on', '$purse', '$secret_key', '$to', '$vk', '$skype');
+
+ $conf = explode("\n", $ssh->get('cat '.$install.'/core/cfg.php'));
+
+ $aData = array();
+
+ if($go)
+ {
+ $aData['bp_login'] = isset($_POST['bp_login']) ? trim($_POST['bp_login']) : '';
+ $aData['bp_passwd'] = isset($_POST['bp_passwd']) ? trim($_POST['bp_passwd']) : '';
+ $aData['bp_mail'] = isset($_POST['bp_mail']) ? trim($_POST['bp_mail']) : '';
+ $aData['bp_vk'] = isset($_POST['bp_vk']) ? trim($_POST['bp_vk']) : '';
+ $aData['bp_skype'] = isset($_POST['bp_skype']) ? trim($_POST['bp_skype']) : '';
+ $aData['bp_webmoney'] = isset($_POST['bp_webmoney']) ? $_POST['bp_webmoney'] : '0';
+ $aData['bp_wmr'] = isset($_POST['bp_wmr']) ? trim($_POST['bp_wmr']) : '';
+ $aData['bp_sign_key'] = isset($_POST['bp_sign_key']) ? trim($_POST['bp_sign_key']) : '';
+
+ $aData['bp_webmoney'] = $aData['bp_webmoney'] == 'on' ? '1' : '0';
+
+ foreach($aData as $var => $val)
+ $aData[$var] = str_replace('"', '', $val);
+
+ $str_search = array(
+ '#\$adm_login = ".*"#iu',
+ '#\$adm_pass = ".*"#iu',
+ '#\$to = ".*"#iu',
+ '#\$vk = ".*"#iu',
+ '#\$skype = ".*"#iu',
+ '#\$wmr_on = ".*"#iu',
+ '#\$purse = ".*"#iu',
+ '#\$secret_key = ".*"#iu',
+ );
+
+ $str_replace = array(
+ '$adm_login = "'.$aData['bp_login'].'"',
+ '$adm_pass = "'.$aData['bp_passwd'].'"',
+ '$to = "'.$aData['bp_mail'].'"',
+ '$vk = "'.$aData['bp_vk'].'"',
+ '$skype = "'.$aData['bp_skype'].'"',
+ '$wmr_on = "'.$aData['bp_webmoney'].'"',
+ '$purse = "'.$aData['bp_wmr'].'"',
+ '$secret_key = "'.$aData['bp_sign_key'].'"'
+ );
+
+ $data = '';
+
+ foreach($conf as $line => $val)
+ {
+ if($val == 'setfile($temp, $install.'/core/cfg.php', 0644);
+
+ sys::outjs(array('s' => 'ok'), $name_mcache);
+ }
+
+ foreach($conf as $str)
+ {
+ $aStr = explode('=', $str);
+
+ if(!isset($aStr[0]) || !isset($aStr[1]))
+ continue;
+
+ $var = trim($aStr[0]);
+ $val = str_replace(array('"', ';'), '', trim($aStr[1]));
+
+ if(!in_array($var, $arSel))
+ continue;
+
+ $aData[$var] = isset($val{0}) ? $val : '';
+ }
+
+ $webmoney = $aData['$wmr_on'] == 'on' ? 'Включено Выключено ' : 'Выключено Включено ';
+
+ sys::outjs(array(
+ 's' => 'ok',
+ 'bp_login' => $aData['$adm_login'],
+ 'bp_passwd' => $aData['$adm_pass'],
+ 'bp_mail' => $aData['$to'],
+ 'bp_vk' => $aData['$vk'],
+ 'bp_skype' => $aData['$skype'],
+ 'bp_wmr' => $aData['$purse'],
+ 'bp_sign_key' => $aData['$secret_key'],
+ 'bp_webmoney' => $webmoney), $name_mcache);
+?>
\ No newline at end of file
diff --git a/system/sections/web/csbans/free/connect.php b/system/sections/web/csbans/free/connect.php
new file mode 100644
index 0000000..732a9ed
--- /dev/null
+++ b/system/sections/web/csbans/free/connect.php
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/system/sections/web/csbans/free/delete.php b/system/sections/web/csbans/free/delete.php
new file mode 100644
index 0000000..0f47665
--- /dev/null
+++ b/system/sections/web/csbans/free/delete.php
@@ -0,0 +1,19 @@
+ 'Чтобы удалить услугу, создайте вопрос выбрав свой сервер с причиной удаления.'), $nmch);
+
+ include(LIB.'web/free.php');
+
+ $aData = array(
+ 'type' => $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::delete($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/csbans/free/install.php b/system/sections/web/csbans/free/install.php
new file mode 100644
index 0000000..8b7ff24
--- /dev/null
+++ b/system/sections/web/csbans/free/install.php
@@ -0,0 +1,60 @@
+ 'Необходимо указать адрес.'), $nmch);
+ $aData['domain'] = isset($_POST['domain']) ? strtolower($_POST['domain']) : sys::outjs(array('e' => 'Необходимо выбрать домен.'), $nmch);
+ $aData['desing'] = isset($_POST['desing']) ? strtolower($_POST['desing']) : sys::outjs(array('e' => 'Необходимо выбрать шаблон.'), $nmch);
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : sys::passwd($aWebParam[$url['subsection']]['passwd']);
+
+ $aData['type'] = $url['subsection'];
+ $aData['server'] = array_merge($server, array('id' => $id));
+
+ $aData['config_sql'] = 'amx_sql_host "[host]"'.PHP_EOL
+ .'amx_sql_user "[login]"'.PHP_EOL
+ .'amx_sql_pass "[passwd]"'.PHP_EOL
+ .'amx_sql_db "[login]"'.PHP_EOL
+ .'amx_sql_table "admins"'.PHP_EOL
+ .'amx_sql_type "mysql"';
+
+ $aData['config_php'] = 'db_host = "[host]";'.PHP_EOL
+ .' $config->db_user = "[login]";'.PHP_EOL
+ .' $config->db_pass = "[passwd]";'.PHP_EOL
+ .' $config->db_db = "[login]";'.PHP_EOL
+ .' $config->db_prefix = "amx";'.PHP_EOL
+ .'?>';
+
+ web::install($aData, $nmch);
+ }
+
+ $html->nav('Установка '.$aWebname[$url['subsection']]);
+
+ $desing = '';
+
+ // Генерация списка шаблонов
+ foreach($aWebParam[$url['subsection']]['desing'] as $name => $desc)
+ $desing .= ''.$desc.' ';
+
+ $domains = '';
+
+ // Генерация списка доменов
+ foreach($aWebUnit['domains'] as $domain)
+ $domains .= '.'.$domain.' ';
+
+ $html->get('install', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('desing', $desing);
+ $html->set('domains', $domains);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/csbans/free/manage.php b/system/sections/web/csbans/free/manage.php
new file mode 100644
index 0000000..bf62ce5
--- /dev/null
+++ b/system/sections/web/csbans/free/manage.php
@@ -0,0 +1,57 @@
+query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ $options = '#'.$id.' '.$server['name'].' ('.$server['address'].') ';
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `user`="'.$server['user'].'" AND `game`="cs" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `unit`="'.$server['unit'].'" AND `user`="'.$server['user'].'" AND `game`="cs" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/web/subsection/'.$url['subsection'].'/action/install');
+
+ $web = $sql->get();
+
+ $html->nav('Управление '.$aWebname[$url['subsection']]);
+
+ $html->get('manage', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('url', $web['domain']);
+ $html->set('passwd', $web['passwd']);
+ $html->set('config', base64_decode($web['config']));
+ $html->set('servers', $options);
+
+ if(in_array('update', $aAction[$url['subsection']]))
+ $html->unit('update', 1);
+ else
+ $html->unit('update');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/csbans/free/passwd.php b/system/sections/web/csbans/free/passwd.php
new file mode 100644
index 0000000..5945d09
--- /dev/null
+++ b/system/sections/web/csbans/free/passwd.php
@@ -0,0 +1,16 @@
+ $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::passwd($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/csbans/free/update.php b/system/sections/web/csbans/free/update.php
new file mode 100644
index 0000000..97689c3
--- /dev/null
+++ b/system/sections/web/csbans/free/update.php
@@ -0,0 +1,16 @@
+ $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::update($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/csstats/free/connect.php b/system/sections/web/csstats/free/connect.php
new file mode 100644
index 0000000..3f5090b
--- /dev/null
+++ b/system/sections/web/csstats/free/connect.php
@@ -0,0 +1,30 @@
+ 'Длина ключа не должна превышать 32 символа.'), $nmch);
+
+ include(LIB.'web/free.php');
+
+ $aData = array();
+
+ $aData['server'] = sys::int($url['server']);
+ $aData['type'] = $url['subsection'];
+ $aData['user'] = $server['user'];
+ $aData['file'] = 'cstrike/addons/amxmodx/configs/csstats_mysql.cfg';
+ $aData['cfg'] = 'cstrike/server.cfg';
+
+ $aData['orcfg'] = array(
+ 'key' => $key
+ );
+
+ $aData['orsql'] = array();
+
+ web::connect($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/csstats/free/delete.php b/system/sections/web/csstats/free/delete.php
new file mode 100644
index 0000000..0f47665
--- /dev/null
+++ b/system/sections/web/csstats/free/delete.php
@@ -0,0 +1,19 @@
+ 'Чтобы удалить услугу, создайте вопрос выбрав свой сервер с причиной удаления.'), $nmch);
+
+ include(LIB.'web/free.php');
+
+ $aData = array(
+ 'type' => $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::delete($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/csstats/free/install.php b/system/sections/web/csstats/free/install.php
new file mode 100644
index 0000000..c631da5
--- /dev/null
+++ b/system/sections/web/csstats/free/install.php
@@ -0,0 +1,75 @@
+ 'Необходимо указать адрес.'), $nmch);
+ $aData['domain'] = isset($_POST['domain']) ? strtolower($_POST['domain']) : sys::outjs(array('e' => 'Необходимо выбрать домен.'), $nmch);
+ $aData['desing'] = isset($_POST['desing']) ? strtolower($_POST['desing']) : sys::outjs(array('e' => 'Необходимо выбрать шаблон.'), $nmch);
+
+ $aData['type'] = $url['subsection'];
+ $aData['server'] = array_merge($server, array('id' => $id));
+
+ $aData['config_sql'] = 'csstats_key "[key]"'.PHP_EOL
+ .'csstats_url "[domain]"'.PHP_EOL
+ .'csstats_sort "-2"'.PHP_EOL
+ .'csstats_double "0"'.PHP_EOL
+ .'csstats_host "[host]"'.PHP_EOL
+ .'csstats_user "[login]"'.PHP_EOL
+ .'csstats_pass "[passwd]"'.PHP_EOL
+ .'csstats_db "[login]"'.PHP_EOL
+ .'csstats_table_players "csstats_players"'.PHP_EOL
+ .'csstats_table_settings "csstats_settings"'.PHP_EOL
+ .'csstats_delete_time "15 120 2"'.PHP_EOL
+ .'csstats_insert_time "60 1"'.PHP_EOL
+ .'csstats_update_time "30 0"'.PHP_EOL
+ .'csstats_slow "0"';
+
+ $aData['config_php'] = '';
+
+ web::install($aData, $nmch);
+ }
+
+ $html->nav('Установка '.$aWebname[$url['subsection']]);
+
+ $desing = '';
+
+ // Генерация списка шаблонов
+ foreach($aWebParam[$url['subsection']]['desing'] as $name => $desc)
+ $desing .= ''.$desc.' ';
+
+ $domains = '';
+
+ // Генерация списка доменов
+ foreach($aWebUnit['domains'] as $domain)
+ $domains .= '.'.$domain.' ';
+
+ $html->get('install', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('desing', $desing);
+ $html->set('domains', $domains);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/csstats/free/manage.php b/system/sections/web/csstats/free/manage.php
new file mode 100644
index 0000000..68bfdf0
--- /dev/null
+++ b/system/sections/web/csstats/free/manage.php
@@ -0,0 +1,29 @@
+query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/web/subsection/'.$url['subsection'].'/action/install');
+
+ $web = $sql->get();
+
+ $html->nav('Управление '.$aWebname[$url['subsection']]);
+
+ $html->get('manage', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('url', $web['domain']);
+ $html->set('passwd', $web['passwd']);
+ $html->set('config', base64_decode($web['config']));
+ $html->set('servers', '#'.$id.' '.$server['name'].' ('.$server['address'].') ');
+
+ if(in_array('update', $aAction[$url['subsection']]))
+ $html->unit('update', 1);
+ else
+ $html->unit('update');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/csstats/free/update.php b/system/sections/web/csstats/free/update.php
new file mode 100644
index 0000000..3971e3c
--- /dev/null
+++ b/system/sections/web/csstats/free/update.php
@@ -0,0 +1,65 @@
+query('SELECT `id`, `uid`, `desing`, `domain`, `update` FROM `web` WHERE `server`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `uid`, `desing`, `domain`, `update` FROM `web` WHERE `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `uid`, `desing`, `domain`, `update` FROM `web` WHERE `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Дополнительная услуга не установлена.'), $nmch);
+
+ $web = $sql->get();
+
+ // Проверка времени последнего обновления
+ include(LIB.'games/games.php');
+
+ $upd = $web['update']+86400;
+
+ if($upd > $start_point)
+ sys::outjs(array('e' => 'Для повторного обновления должно пройти: '.games::date('max', $upd)));
+
+ include(LIB.'ssh.php');
+
+ if($aWebUnit['unit'][$url['subsection']] == 'local')
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+ }else{
+ $unit = array(
+ 'address' => $aWebUnit['address'],
+ 'passwd' => $aWebUnit['passwd'],
+ );
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $install = $aWebUnit['install'][$aWebUnit['unit'][$url['subsection']]][$url['subsection']].$web['domain'];
+
+ $path = $aWebUnit['path'][$aWebUnit['unit'][$url['subsection']]][$url['subsection']].$web['desing'];
+
+ $ssh->set('cat '.$install.'/include/db.config.inc.php > '.$path.'/include/db.config.inc.php;'
+ .'cd '.$install.' && sudo -u web'.$web['uid'].' screen -dmS u_w_'.$web['uid'].' sh -c "YES | cp -rf '.$path.'/. .; '.$aWebChmod[$url['subsection']].'"');
+
+ $sql->query('UPDATE `web` set `update`="'.$start_point.'" WHERE `id`="'.$web['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/hosting/free/delete.php b/system/sections/web/hosting/free/delete.php
new file mode 100644
index 0000000..5b683c9
--- /dev/null
+++ b/system/sections/web/hosting/free/delete.php
@@ -0,0 +1,42 @@
+ 'Чтобы удалить услугу, создайте вопрос выбрав свой сервер с причиной удаления.'), $nmch);
+
+ // Проверка на наличие установленной услуги
+ switch($aWebInstall[$server['game']][$url['subsection']])
+ {
+ case 'server':
+ $sql->query('SELECT `id`, `login` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `login` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `login` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ break;
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('i' => 'Дополнительная услуга не установлена.'), $nmch);
+
+ $web = $sql->get();
+
+ // Удаление вирт. хостинга
+ $result = json_decode(file_get_contents(sys::updtext($aWebUnit['isp']['account']['delete'], array('login' => $web['login']))), true);
+
+ if(!isset($result['result']) || strtolower($result['result']) != 'ok')
+ sys::outjs(array('e' => 'Не удалось удалить виртуальный хостинг.'), $nmch);
+
+ // Обновление данных
+ $sql->query('DELETE FROM `web` WHERE `id`="'.$web['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/hosting/free/install.php b/system/sections/web/hosting/free/install.php
new file mode 100644
index 0000000..c28d0b5
--- /dev/null
+++ b/system/sections/web/hosting/free/install.php
@@ -0,0 +1,163 @@
+ 'Недоступно для данного тарифа.'), $nmch);
+
+ if($aWebVHTtype && in_array($server['tarif'], $aWebVHT))
+ sys::outjs(array('e' => 'Недоступно для данного тарифа.'), $nmch);
+
+ $aData = array();
+
+ $aData['subdomain'] = isset($_POST['subdomain']) ? strtolower($_POST['subdomain']) : sys::outjs(array('e' => 'Необходимо указать адрес.'), $nmch);
+ $aData['domain'] = isset($_POST['domain']) ? strtolower($_POST['domain']) : sys::outjs(array('e' => 'Необходимо выбрать домен.'), $nmch);
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : sys::passwd($aWebParam[$url['subsection']]['passwd']);
+
+ $aData['type'] = $url['subsection'];
+
+ if(!$aWeb[$server['game']][$aData['type']])
+ sys::outjs(array('e' => 'Дополнительная услуга недоступна для установки.'), $nmch);
+
+ // Проверка на наличие уже установленной выбранной услуги
+ switch($aWebInstall[$server['game']][$aData['type']])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$aData['type'].'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$aData['type'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$aData['type'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ break;
+ }
+
+ if($sql->num())
+ sys::outjs(array('i' => 'Дополнительная услуга уже установлена.'), $nmch);
+
+ // Проверка на наличие уже установленной подобной услуги
+ switch($aWebInstall[$server['game']][$aData['type']])
+ {
+ case 'server':
+ foreach($aWebOne[$server['game']][$aData['type']] as $type)
+ {
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$type.'" AND `server`="'.$id.'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('i' => 'Подобная услуга уже установлена.', 'type' => $type), $nmch);
+ }
+ break;
+
+ case 'user':
+ foreach($aWebOne[$server['game']][$aData['type']] as $type)
+ {
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$type.'" AND `user`="'.$server['user'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('i' => 'Подобная услуга уже установлена.', 'type' => $type), $nmch);
+ }
+ break;
+
+ case 'unit':
+ foreach($aWebOne[$server['game']][$aData['type']] as $type)
+ {
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$type.'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('i' => 'Подобная услуга уже установлена.', 'type' => $type), $nmch);
+ }
+ }
+
+ // Проверка валидности поддомена
+ if(sys::valid($aData['subdomain'], 'other', "/^[a-z0-9]+$/"))
+ sys::outjs(array('e' => 'Адрес должен состоять из букв a-z и цифр.'), $nmch);
+
+ // Проверка длины поддомена
+ if(!isset($aData['subdomain']{3}) || isset($aData['subdomain']{15}))
+ sys::outjs(array('e' => 'Длина адреса не должна превышать 16-и символов и быть не менее 4-х символов.'), $nmch);
+
+ // Проверка наличия основного домена
+ if(!in_array($aData['domain'], $aWebUnit['domains']))
+ sys::outjs(array('e' => 'Выбранный домен не найден.'), $nmch);
+
+ // Проверка запрещенного поддомена
+ if(in_array($aData['subdomain'], $aWebUnit['subdomains']))
+ sys::outjs(array('e' => 'Нельзя создать данный адрес, придумайте другой.'), $nmch);
+
+ // Проверка поддомена на занятость
+ $sql->query('SELECT `id` FROM `web` WHERE `domain`="'.$aData['subdomain'].'.'.$aData['domain'].'" LIMIT 1');
+ if($sql->num())
+ sys::outjs(array('e' => 'Данный адрес уже занят.'), $nmch);
+
+ // Если не указан пароль сгенерировать
+ if($aData['passwd'] == '')
+ $aData['passwd'] = sys::passwd($aWebParam[$aData['type']]['passwd']);
+
+ // Проверка длинны пароля
+ if(!isset($aData['passwd']{5}))
+ sys::outjs(array('e' => 'Необходимо указать пароль длинной не менее 6-и символов.'), $nmch);
+
+ // Проверка валидности пароля
+ if(sys::valid($aData['passwd'], 'other', "/^[A-Za-z0-9]{6,16}$/"))
+ sys::outjs(array('e' => 'Пароль должен состоять из букв a-z и цифр, не менее 4-х и не более 16-и символов.'), $nmch);
+
+ $sql->query('SELECT `mail` FROM `users` WHERE `id`="'.$server['user'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Необходимо указать пользователя игрового сервера.'), $nmch);
+
+ $u = $sql->get();
+
+ $sql->query('INSERT INTO `web` set `type`="'.$aData['type'].'", `server`="'.$id.'", `user`="'.$server['user'].'", `unit`="0", `config`=""');
+ $wid = $sql->id();
+ $uid = $wid+10000;
+
+ $login = 'h'.$uid;
+
+ if(in_array($aData['subdomain'], $aWebUnit['isp']['subdomains']))
+ {
+ $sql->query('DELETE FROM `web` WHERE `id`="'.$wid.'" LIMIT 1');
+
+ sys::outjs(array('e' => 'Нельзя создать указанный поддомен, придумайте что-то другое.'), $mcache);
+ }
+
+ // Создание вирт. хостинга
+ $result = json_decode(file_get_contents(sys::updtext($aWebUnit['isp']['account']['create'],
+ array('login' => $login,
+ 'mail' => $u['mail'],
+ 'passwd' => $aData['passwd'],
+ 'domain' => $aData['subdomain'].'.'.$aData['domain'])
+ )), true);
+
+ if(!isset($result['result']) || strtolower($result['result']) != 'ok')
+ {
+ $sql->query('DELETE FROM `web` WHERE `id`="'.$wid.'" LIMIT 1');
+
+ sys::outjs(array('e' => 'Не удалось создать виртуальный хостинг, обратитесь в тех.поддержку.'), $nmch);
+ }
+
+ // Обновление данных
+ $sql->query('UPDATE `web` set `uid`="'.$uid.'", '
+ .'`domain`="'.$aData['subdomain'].'.'.$aData['domain'].'", '
+ .'`passwd`="'.$aData['passwd'].'", '
+ .'`login`="'.$login.'", `date`="'.$start_point.'" '
+ .'WHERE `id`="'.$wid.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $html->nav('Установка '.$aWebname[$url['subsection']]);
+
+ $domains = '';
+
+ // Генерация списка доменов
+ foreach($aWebUnit['domains'] as $domain)
+ $domains .= '.'.$domain.' ';
+
+ $html->get('install', 'sections/web/'.$url['subsection'].'/free');
+ $html->set('id', $id);
+ $html->set('domains', $domains);
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/hosting/free/manage.php b/system/sections/web/hosting/free/manage.php
new file mode 100644
index 0000000..a9cbd71
--- /dev/null
+++ b/system/sections/web/hosting/free/manage.php
@@ -0,0 +1,52 @@
+query('SELECT `domain`, `passwd`, `login`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ $options = '#'.$id.' '.$server['name'].' ('.$server['address'].') ';
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `user`="'.$server['user'].'" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `login`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `unit`="'.$server['unit'].'" AND `user`="'.$server['user'].'" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `login`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/web/subsection/'.$url['subsection'].'/action/install');
+
+ $web = $sql->get();
+
+ $html->nav('Управление '.$aWebname[$url['subsection']]);
+
+ $html->get('manage', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('url', $aWebUnit['isp']['panel']);
+ $html->set('login', $web['login']);
+ $html->set('passwd', $web['passwd']);
+ $html->set('date', sys::today($web['date']));
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/hosting/free/passwd.php b/system/sections/web/hosting/free/passwd.php
new file mode 100644
index 0000000..564651b
--- /dev/null
+++ b/system/sections/web/hosting/free/passwd.php
@@ -0,0 +1,52 @@
+query('SELECT `id`, `login`, `user` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `login`, `user` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `login`, `user` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ break;
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('i' => 'Дополнительная услуга не установлена.'), $nmch);
+
+ $web = $sql->get();
+
+ $sql->query('SELECT `mail` FROM `users` WHERE `id`="'.$web['user'].'" LIMIT 1');
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Необходимо указать пользователя доп. услуги.'), $nmch);
+
+ $u = $sql->get();
+
+ $passwd = sys::passwd($aWebParam[$url['subsection']]['passwd']);
+
+ // Смена пароля вирт. хостинга
+ $result = json_decode(file_get_contents(sys::updtext($aWebUnit['isp']['account']['passwd'],
+ array('login' => $web['login'],
+ 'mail' => $u['mail'],
+ 'hdd' => $aWebUnit['isp']['hdd'],
+ 'passwd' => $passwd)
+ )), true);
+
+ if(!isset($result['result']) || strtolower($result['result']) != 'ok')
+ sys::outjs(array('e' => 'Не удалось изменить пароль виртуального хостинга, обратитесь в тех.поддержку.'), $nmch);
+
+ // Обновление данных
+ $sql->query('UPDATE `web` set `passwd`="'.$passwd.'" WHERE `id`="'.$web['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/mysql/free/delete.php b/system/sections/web/mysql/free/delete.php
new file mode 100644
index 0000000..4aaac0e
--- /dev/null
+++ b/system/sections/web/mysql/free/delete.php
@@ -0,0 +1,55 @@
+ 'Чтобы удалить услугу, создайте вопрос выбрав свой сервер с причиной удаления.'), $nmch);
+
+ switch($aWebInstall[$server['game']][$url['subsection']])
+ {
+ case 'server':
+ $sql->query('SELECT `id`, `uid`, `unit`, `login` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `uid`, `unit`, `login` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `uid`, `unit`, `login` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Дополнительная услуга не установлена.'), $nmch);
+
+ $web = $sql->get();
+
+ if($aWebUnit['unit'][$url['subsection']] == 'local')
+ {
+ $sql->query('SELECT `address`, `passwd` FROM `units` WHERE `id`="'.$web['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+ }else{
+ $unit = array(
+ 'address' => $aWebUnit['address'],
+ 'passwd' => $aWebUnit['passwd'],
+ );
+ }
+
+ include(LIB.'ssh.php');
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('error', 'ssh')), $nmch);
+
+ $ssh->set("mysql --login-path=local -e \"DROP DATABASE IF EXISTS ".$web['login']."; DROP USER ".$web['login']."\"");
+
+ $sql->query('DELETE FROM `web` WHERE `id`="'.$web['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/mysql/free/install.php b/system/sections/web/mysql/free/install.php
new file mode 100644
index 0000000..9b44c06
--- /dev/null
+++ b/system/sections/web/mysql/free/install.php
@@ -0,0 +1,99 @@
+ 'Дополнительная услуга недоступна для установки.'), $nmch);
+
+ // Проверка на наличие уже установленной выбранной услуги
+ switch($aWebInstall[$server['game']][$url['subsection']])
+ {
+ case 'server':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ break;
+ }
+
+ if($sql->num())
+ sys::outjs(array('s' => 'ok'), $nmch);
+
+ include(LIB.'ssh.php');
+
+ if($aWebUnit['unit'][$url['subsection']] == 'local')
+ {
+ $sql->query('SELECT `address`, `passwd`, `domain` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+
+ $pma = $unit['domain'];
+ }else{
+ $unit = array(
+ 'address' => $aWebUnit['address'],
+ 'passwd' => $aWebUnit['passwd'],
+ );
+
+ $pma = $aWebUnit['pma'];
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('ssh', 'error')), $nmch);
+
+ if(isset($_POST['passwd']))
+ {
+ // Если не указан пароль сгенерировать
+ if($_POST['passwd'] == '')
+ $passwd = sys::passwd($aWebParam[$url['subsection']]['passwd']);
+ else{
+ // Проверка длинны пароля
+ if(!isset($_POST['passwd']{5}) || isset($_POST['passwd']{16}))
+ sys::outjs(array('e' => 'Необходимо указать пароль длинной не менее 6-и символов и не более 16-и.'), $nmch);
+
+ // Проверка валидности пароля
+ if(sys::valid($_POST['passwd'], 'other', "/^[A-Za-z0-9]{6,16}$/"))
+ sys::outjs(array('e' => 'Пароль должен состоять из букв a-z и цифр.'), $nmch);
+
+ $passwd = $_POST['passwd'];
+ }
+ }else
+ $passwd = sys::passwd($aWebParam[$url['subsection']]['passwd']);
+
+ $sql->query('INSERT INTO `web` set `type`="'.$url['subsection'].'", `server`="'.$id.'", `user`="'.$server['user'].'", `unit`="'.$server['unit'].'", `config`=""');
+ $wid = $sql->id();
+ $uid = $wid+10000;
+
+ // Данные
+ $login = substr('sql_'.$wid.'_free', 0, 14);
+
+ $sql_q = 'mysql --login-path=local -e "CREATE DATABASE '.$login.';'
+ ."CREATE USER '".$login."'@'%' IDENTIFIED BY '".$passwd."';"
+ .'GRANT ALL PRIVILEGES ON '.$login.' . * TO \''.$login.'\'@\'%\';";';
+
+ $ssh->set($sql_q);
+
+ // Обновление данных
+ $sql->query('UPDATE `web` set `uid`="'.$uid.'",'
+ .'`domain`="'.$pma.'",'
+ .'`passwd`="'.$passwd.'",'
+ .'`login`="'.$login.'", `date`="'.$start_point.'" '
+ .'WHERE `id`="'.$wid.'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+ }
+
+ $html->nav('Установка '.$aWebname[$url['subsection']]);
+
+ $html->get('install', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/mysql/free/manage.php b/system/sections/web/mysql/free/manage.php
new file mode 100644
index 0000000..23725c1
--- /dev/null
+++ b/system/sections/web/mysql/free/manage.php
@@ -0,0 +1,39 @@
+query('SELECT `domain`, `passwd`, `login`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `domain`, `passwd`, `login`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `domain`, `passwd`, `login`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/web/subsection/'.$url['subsection'].'/action/install');
+
+ $web = $sql->get();
+
+ $html->nav('Управление '.$aWebname[$url['subsection']]);
+
+ $html->get('manage', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('url', $web['domain']);
+ $html->set('login', $web['login']);
+ $html->set('passwd', $web['passwd']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/mysql/free/passwd.php b/system/sections/web/mysql/free/passwd.php
new file mode 100644
index 0000000..e7319e7
--- /dev/null
+++ b/system/sections/web/mysql/free/passwd.php
@@ -0,0 +1,53 @@
+query('SELECT `id`, `login` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `login` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `login` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+ break;
+ }
+
+ if(!$sql->num())
+ sys::outjs(array('e' => 'Дополнительная услуга не установлена.'), $nmch);
+
+ $web = $sql->get();
+
+ include(LIB.'ssh.php');
+
+ if($aWebUnit['unit'][$url['subsection']] == 'local')
+ {
+ $sql->query('SELECT `address`, `passwd`, `domain` FROM `units` WHERE `id`="'.$server['unit'].'" LIMIT 1');
+ $unit = $sql->get();
+ }else{
+ $unit = array(
+ 'address' => $aWebUnit['address'],
+ 'passwd' => $aWebUnit['passwd'],
+ );
+ }
+
+ if(!$ssh->auth($unit['passwd'], $unit['address']))
+ sys::outjs(array('e' => sys::text('ssh', 'error')), $nmch);
+
+ $passwd = sys::passwd($aWebParam[$url['subsection']]['passwd']);
+
+ $ssh->set('mysql --login-path=local -e "SET PASSWORD FOR \''.$web['login'].'\'@\'%\' = PASSWORD(\''.$passwd.'\');"');
+
+ // Обновление данных
+ $sql->query('UPDATE `web` set `passwd`="'.$passwd.'" WHERE `id`="'.$web['id'].'" LIMIT 1');
+
+ sys::outjs(array('s' => 'ok'), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/sourcebans/free/connect.php b/system/sections/web/sourcebans/free/connect.php
new file mode 100644
index 0000000..ffc8e67
--- /dev/null
+++ b/system/sections/web/sourcebans/free/connect.php
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/system/sections/web/sourcebans/free/delete.php b/system/sections/web/sourcebans/free/delete.php
new file mode 100644
index 0000000..0f47665
--- /dev/null
+++ b/system/sections/web/sourcebans/free/delete.php
@@ -0,0 +1,19 @@
+ 'Чтобы удалить услугу, создайте вопрос выбрав свой сервер с причиной удаления.'), $nmch);
+
+ include(LIB.'web/free.php');
+
+ $aData = array(
+ 'type' => $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::delete($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/sourcebans/free/install.php b/system/sections/web/sourcebans/free/install.php
new file mode 100644
index 0000000..d4884d4
--- /dev/null
+++ b/system/sections/web/sourcebans/free/install.php
@@ -0,0 +1,81 @@
+ 'Необходимо указать адрес.'), $nmch);
+ $aData['domain'] = isset($_POST['domain']) ? strtolower($_POST['domain']) : sys::outjs(array('e' => 'Необходимо выбрать домен.'), $nmch);
+ $aData['desing'] = isset($_POST['desing']) ? strtolower($_POST['desing']) : sys::outjs(array('e' => 'Необходимо выбрать шаблон.'), $nmch);
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : sys::passwd($aWebParam[$url['subsection']]['passwd']);
+
+ $aData['type'] = $url['subsection'];
+ $aData['server'] = array_merge($server, array('id' => $id));
+
+ $aData['config_sql'] = '"Databases"'.PHP_EOL
+ .'{'.PHP_EOL
+ .' "driver_default" "mysql"'.PHP_EOL
+ .''.PHP_EOL
+ .' "sourcebans"'.PHP_EOL
+ .' {'.PHP_EOL
+ .' "driver" "mysql"'.PHP_EOL
+ .' "host" "[host]"'.PHP_EOL
+ .' "database" "[login]"'.PHP_EOL
+ .' "user" "[login]"'.PHP_EOL
+ .' "pass" "[passwd]"'.PHP_EOL
+ .' //"timeout" "0"'.PHP_EOL
+ .' "port" "3306"'.PHP_EOL
+ .' }'.PHP_EOL
+ .''.PHP_EOL
+ .' "storage-local"'.PHP_EOL
+ .' {'.PHP_EOL
+ .' "driver" "sqlite"'.PHP_EOL
+ .' "database" "sourcemod-local"'.PHP_EOL
+ .' }'.PHP_EOL
+ .'}';
+
+ $aData['config_php'] = '';
+
+ web::install($aData, $nmch);
+ }
+
+ $html->nav('Установка '.$aWebname[$url['subsection']]);
+
+ $desing = '';
+
+ // Генерация списка шаблонов
+ foreach($aWebParam[$url['subsection']]['desing'] as $name => $desc)
+ $desing .= ''.$desc.' ';
+
+ $domains = '';
+
+ // Генерация списка доменов
+ foreach($aWebUnit['domains'] as $domain)
+ $domains .= '.'.$domain.' ';
+
+ $html->get('install', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('desing', $desing);
+ $html->set('domains', $domains);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/sourcebans/free/manage.php b/system/sections/web/sourcebans/free/manage.php
new file mode 100644
index 0000000..210f5f4
--- /dev/null
+++ b/system/sections/web/sourcebans/free/manage.php
@@ -0,0 +1,57 @@
+query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ $options = '#'.$id.' '.$server['name'].' ('.$server['address'].') ';
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `game`="'.$server['game'].'" AND `user`="'.$server['user'].'" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `unit`="'.$server['unit'].'" AND `game`="'.$server['game'].'" AND `user`="'.$server['user'].'" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/web/subsection/'.$url['subsection'].'/action/install');
+
+ $web = $sql->get();
+
+ $html->nav('Управление '.$aWebname[$url['subsection']]);
+
+ $html->get('manage', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('url', $web['domain']);
+ $html->set('passwd', $web['passwd']);
+ $html->set('config', base64_decode($web['config']));
+ $html->set('servers', $options);
+
+ if(in_array('update', $aAction[$url['subsection']]))
+ $html->unit('update', 1);
+ else
+ $html->unit('update');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/sourcebans/free/passwd.php b/system/sections/web/sourcebans/free/passwd.php
new file mode 100644
index 0000000..5945d09
--- /dev/null
+++ b/system/sections/web/sourcebans/free/passwd.php
@@ -0,0 +1,16 @@
+ $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::passwd($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/sourcebans/free/update.php b/system/sections/web/sourcebans/free/update.php
new file mode 100644
index 0000000..97689c3
--- /dev/null
+++ b/system/sections/web/sourcebans/free/update.php
@@ -0,0 +1,16 @@
+ $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::update($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/superban/free/connect.php b/system/sections/web/superban/free/connect.php
new file mode 100644
index 0000000..732a9ed
--- /dev/null
+++ b/system/sections/web/superban/free/connect.php
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/system/sections/web/superban/free/delete.php b/system/sections/web/superban/free/delete.php
new file mode 100644
index 0000000..0f47665
--- /dev/null
+++ b/system/sections/web/superban/free/delete.php
@@ -0,0 +1,19 @@
+ 'Чтобы удалить услугу, создайте вопрос выбрав свой сервер с причиной удаления.'), $nmch);
+
+ include(LIB.'web/free.php');
+
+ $aData = array(
+ 'type' => $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::delete($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/superban/free/install.php b/system/sections/web/superban/free/install.php
new file mode 100644
index 0000000..8b7ff24
--- /dev/null
+++ b/system/sections/web/superban/free/install.php
@@ -0,0 +1,60 @@
+ 'Необходимо указать адрес.'), $nmch);
+ $aData['domain'] = isset($_POST['domain']) ? strtolower($_POST['domain']) : sys::outjs(array('e' => 'Необходимо выбрать домен.'), $nmch);
+ $aData['desing'] = isset($_POST['desing']) ? strtolower($_POST['desing']) : sys::outjs(array('e' => 'Необходимо выбрать шаблон.'), $nmch);
+ $aData['passwd'] = isset($_POST['passwd']) ? $_POST['passwd'] : sys::passwd($aWebParam[$url['subsection']]['passwd']);
+
+ $aData['type'] = $url['subsection'];
+ $aData['server'] = array_merge($server, array('id' => $id));
+
+ $aData['config_sql'] = 'amx_sql_host "[host]"'.PHP_EOL
+ .'amx_sql_user "[login]"'.PHP_EOL
+ .'amx_sql_pass "[passwd]"'.PHP_EOL
+ .'amx_sql_db "[login]"'.PHP_EOL
+ .'amx_sql_table "admins"'.PHP_EOL
+ .'amx_sql_type "mysql"';
+
+ $aData['config_php'] = 'db_host = "[host]";'.PHP_EOL
+ .' $config->db_user = "[login]";'.PHP_EOL
+ .' $config->db_pass = "[passwd]";'.PHP_EOL
+ .' $config->db_db = "[login]";'.PHP_EOL
+ .' $config->db_prefix = "amx";'.PHP_EOL
+ .'?>';
+
+ web::install($aData, $nmch);
+ }
+
+ $html->nav('Установка '.$aWebname[$url['subsection']]);
+
+ $desing = '';
+
+ // Генерация списка шаблонов
+ foreach($aWebParam[$url['subsection']]['desing'] as $name => $desc)
+ $desing .= ''.$desc.' ';
+
+ $domains = '';
+
+ // Генерация списка доменов
+ foreach($aWebUnit['domains'] as $domain)
+ $domains .= '.'.$domain.' ';
+
+ $html->get('install', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('desing', $desing);
+ $html->set('domains', $domains);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/superban/free/manage.php b/system/sections/web/superban/free/manage.php
new file mode 100644
index 0000000..bf62ce5
--- /dev/null
+++ b/system/sections/web/superban/free/manage.php
@@ -0,0 +1,57 @@
+query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `server`="'.$id.'" LIMIT 1');
+
+ $options = '#'.$id.' '.$server['name'].' ('.$server['address'].') ';
+
+ break;
+
+ case 'user':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `user`="'.$server['user'].'" AND `game`="cs" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" LIMIT 1');
+
+ break;
+
+ case 'unit':
+ $sql->query('SELECT `id`, `address`, `name` FROM `servers` WHERE `unit`="'.$server['unit'].'" AND `user`="'.$server['user'].'" AND `game`="cs" AND (`status`!="overdue" OR `status`!="block")');
+ while($sers = $sql->get())
+ $options .= '#'.$sers['id'].' '.$sers['name'].' ('.$sers['address'].') ';
+
+ $sql->query('SELECT `domain`, `passwd`, `config`, `date` FROM `web` WHERE `type`="'.$url['subsection'].'" AND `user`="'.$server['user'].'" AND `unit`="'.$server['unit'].'" LIMIT 1');
+
+ break;
+ }
+
+ if(!$sql->num())
+ sys::back($cfg['http'].'servers/id/'.$id.'/section/web/subsection/'.$url['subsection'].'/action/install');
+
+ $web = $sql->get();
+
+ $html->nav('Управление '.$aWebname[$url['subsection']]);
+
+ $html->get('manage', 'sections/web/'.$url['subsection'].'/free');
+
+ $html->set('id', $id);
+
+ $html->set('url', $web['domain']);
+ $html->set('passwd', $web['passwd']);
+ $html->set('config', base64_decode($web['config']));
+ $html->set('servers', $options);
+
+ if(in_array('update', $aAction[$url['subsection']]))
+ $html->unit('update', 1);
+ else
+ $html->unit('update');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/web/superban/free/passwd.php b/system/sections/web/superban/free/passwd.php
new file mode 100644
index 0000000..5945d09
--- /dev/null
+++ b/system/sections/web/superban/free/passwd.php
@@ -0,0 +1,16 @@
+ $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::passwd($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/web/superban/free/update.php b/system/sections/web/superban/free/update.php
new file mode 100644
index 0000000..97689c3
--- /dev/null
+++ b/system/sections/web/superban/free/update.php
@@ -0,0 +1,16 @@
+ $url['subsection'],
+ 'server' => array_merge($server, array('id' => $id))
+ );
+
+ web::update($aData, $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/wiki/answer.php b/system/sections/wiki/answer.php
new file mode 100644
index 0000000..6310fe5
--- /dev/null
+++ b/system/sections/wiki/answer.php
@@ -0,0 +1,44 @@
+nav('Категории вопросов', $cfg['http'].'wiki');
+
+ $quest = isset($url['question']) ? sys::int($url['question']) : sys::back($cfg['http'].'wiki');
+
+ $sql->query('SELECT `name`, `cat`, `tags` FROM `wiki` WHERE `id`="'.$quest.'" LIMIT 1');
+ if(!$sql->num())
+ sys::back($cfg['http'].'wiki');
+
+ $wiki = $sql->get();
+
+ $sql->query('SELECT `name` FROM `wiki_category` WHERE `id`="'.$wiki['cat'].'" LIMIT 1');
+ $cat = $sql->get();
+
+ $title = $wiki['name'];
+ $description = $answer['text'];
+ $keywords = $wiki['tags'];
+
+ $html->nav($cat['name'], $cfg['http'].'wiki/section/question/category/'.$wiki['cat']);
+ $html->nav('Ответ на вопрос');
+
+ $sql->query('SELECT `text` FROM `wiki_answer` WHERE `wiki`="'.$quest.'" LIMIT 1');
+ $answer = $sql->get();
+
+ $aTags = explode(',', $wiki['tags']);
+
+ $tags = '';
+
+ foreach($aTags as $tag)
+ {
+ $tag = trim($tag);
+ $tags .= ''.$tag.' ';
+ }
+
+ $html->get('answer', 'sections/wiki');
+ $html->set('id', $quest);
+ $html->set('question', $wiki['name']);
+ $html->set('text', htmlspecialchars_decode($answer['text']));
+ $html->set('tags', $tags != '' ? $tags : 'Теги отсутствуют');
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/wiki/index.php b/system/sections/wiki/index.php
new file mode 100644
index 0000000..4571016
--- /dev/null
+++ b/system/sections/wiki/index.php
@@ -0,0 +1,27 @@
+nav('Категории вопросов');
+
+ $cats = $sql->query('SELECT `id`, `name` FROM `wiki_category` ORDER BY `sort` ASC');
+ while($cat = $sql->get($cats))
+ {
+ $sql->query('SELECT `id` FROM `wiki` WHERE `cat`="'.$cat['id'].'" LIMIT 1');
+ if(!$sql->num())
+ continue;
+
+ $html->get('list', 'sections/wiki/category');
+
+ $html->set('id', $cat['id']);
+ $html->set('name', $cat['name']);
+
+ $html->pack('category');
+ }
+
+ $html->get('category', 'sections/wiki');
+
+ $html->set('list', isset($html->arr['category']) ? $html->arr['category'] : '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/wiki/msearch.php b/system/sections/wiki/msearch.php
new file mode 100644
index 0000000..a278599
--- /dev/null
+++ b/system/sections/wiki/msearch.php
@@ -0,0 +1,156 @@
+ '')));
+
+ $mkey = md5($text.'wiki');
+
+ if($mcache->get($mkey) != '' AND !isset($url['tag']))
+ sys::outjs(array('s' => $mcache->get($mkey)));
+
+ if(!isset($text{2}) AND !isset($url['tag']))
+ sys::outjs(array('s' => 'Для выполнения поиска, необходимо больше данных', $nmch));
+
+ $aWiki_q = array();
+ $aNswer_q = array();
+
+ // Поиск по вопросу
+ $wiki_q = $sql->query('SELECT `id` FROM `wiki` WHERE `name` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') OR `tags` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') LIMIT 3');
+
+ // Поиск по тексту (ответу)
+ $answer_q = $sql->query('SELECT `wiki` FROM `wiki_answer` WHERE `text` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') LIMIT 5');
+
+ // Если нет ниодного совпадения по вводимому тексту
+ if(!$sql->num($wiki_q) AND !$sql->num($answer_q) AND !isset($url['tag']))
+ {
+ // Поиск по словам
+ if(!strpos($text, ' '))
+ {
+ $mcache->set($mkey, 'По вашему запросу ничего не найдено', false, 15);
+
+ sys::outjs(array('s' => 'По вашему запросу ничего не найдено'));
+ }
+
+ // Массив слов
+ $aText = explode(' ', $text);
+
+ // Метка, которая изменится в процессе, если будет найдено хоть одно совпадение
+ $sWord = false;
+
+ foreach($aText as $word)
+ {
+ if($word == '' || !isset($word{2}))
+ continue;
+
+ // Поиск по вопросу
+ $wiki_q = $sql->query('SELECT `id` FROM `wiki` WHERE `name` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') OR `tags` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') LIMIT 3');
+
+ // Поиск по тексту (ответу)
+ $answer_q = $sql->query('SELECT `wiki` FROM `wiki_answer` WHERE `text` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') LIMIT 5');
+
+ if($sql->num($wiki_q))
+ $aWiki_q[] = $wiki_q;
+
+ if($sql->num($answer_q))
+ $aNswer_q[] = $answer_q;
+ }
+
+ }else{
+ $aWiki_q[] = $wiki_q;
+ $aNswer_q[] = $answer_q;
+ }
+
+ if(!count($aWiki_q) AND !count($aNswer_q) AND !isset($url['tag']))
+ {
+ $mcache->set($mkey, 'По вашему запросу ничего не найдено', false, 15);
+
+ sys::outjs(array('s' => 'По вашему запросу ничего не найдено'));
+ }
+
+ // Защита от дублирования
+ $aResult = array();
+
+ foreach($aWiki_q as $index => $wiki_q)
+ {
+ // Генерация списка (совпадение по вопросу)
+ while($wiki = $sql->get($wiki_q))
+ {
+ // Проверка дублирования
+ if(in_array($wiki['id'], $aResult))
+ continue;
+
+ $sql->query('SELECT `id`, `name`, `tags`, `date` FROM `wiki` WHERE `id`="'.$wiki['id'].'" LIMIT 1');
+ $quest = $sql->get();
+
+ $aTags = explode(',', $quest['tags']);
+
+ $tags = '';
+
+ foreach($aTags as $tag)
+ {
+ $tag = trim($tag);
+
+ $tags .= ''.$tag.' ';
+ }
+
+ $html->get('list', 'sections/wiki/question');
+
+ $html->set('id', $quest['id']);
+ $html->set('name', sys::find($quest['name'], $text));
+ $html->set('tags', $tags != '' ? $tags : 'Теги отсутствуют');
+ $html->set('date', sys::today($quest['date']));
+
+ $html->set('home', $cfg['http']);
+
+ $html->pack('question');
+
+ array_push($aResult, $wiki['id']);
+ }
+ }
+
+ foreach($aNswer_q as $index => $answer_q)
+ {
+ // Генерация списка (совпадение по ответу)
+ while($answer = $sql->get($answer_q))
+ {
+ // Проверка дублирования
+ if(in_array($answer['wiki'], $aResult))
+ continue;
+
+ $sql->query('SELECT `id`, `name`, `tags`, `date` FROM `wiki` WHERE `id`="'.$answer['wiki'].'" LIMIT 1');
+ $quest = $sql->get();
+
+ $aTags = explode(',', $quest['tags']);
+
+ $tags = '';
+
+ foreach($aTags as $tag)
+ {
+ $tag = trim($tag);
+
+ $tags .= ''.$tag.' ';
+ }
+
+ $html->get('list', 'sections/wiki/question');
+
+ $html->set('id', $quest['id']);
+ $html->set('name', sys::find($quest['name'], $text));
+ $html->set('tags', $tags != '' ? $tags : 'Теги отсутствуют');
+ $html->set('date', sys::today($quest['date']));
+
+ $html->set('home', $cfg['http']);
+
+ $html->pack('question');
+ }
+ }
+
+ $html->arr['question'] = isset($html->arr['question']) ? $html->arr['question'] : 'По вашему запросу ничего не найдено';
+
+ $mcache->set($mkey, $html->arr['question'], false, 15);
+
+ sys::outjs(array('s' => $html->arr['question']), $nmch);
+?>
\ No newline at end of file
diff --git a/system/sections/wiki/question.php b/system/sections/wiki/question.php
new file mode 100644
index 0000000..1b66637
--- /dev/null
+++ b/system/sections/wiki/question.php
@@ -0,0 +1,46 @@
+nav('Категории вопросов', $cfg['http'].'wiki');
+ $html->nav('Часто задаваемые вопросы');
+
+ $cat = isset($url['category']) ? sys::int($url['category']) : sys::back($cfg['http'].'wiki');
+
+ $sql->query('SELECT `name` FROM `wiki_category` WHERE `id`="'.$cat.'" LIMIT 1');
+ if(!$sql->num())
+ sys::back($cfg['http'].'wiki');
+
+ $category = $sql->get();
+
+ $sql->query('SELECT `id`, `name`, `tags`, `date` FROM `wiki` WHERE `cat`="'.$cat.'" ORDER BY `id` ASC');
+ while($quest = $sql->get())
+ {
+ $aTags = explode(',', $quest['tags']);
+
+ $tags = '';
+
+ foreach($aTags as $tag)
+ {
+ $tag = trim($tag);
+
+ $tags .= ''.$tag.' ';
+ }
+
+ $html->get('list', 'sections/wiki/question');
+
+ $html->set('id', $quest['id']);
+ $html->set('name', $quest['name']);
+ $html->set('tags', $tags != '' ? $tags : 'Теги отсутствуют');
+ $html->set('date', sys::today($quest['date']));
+
+ $html->pack('question');
+ }
+
+ $html->get('question', 'sections/wiki');
+
+ $html->set('category', $category['name']);
+ $html->set('list', isset($html->arr['question']) ? $html->arr['question'] : '');
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/system/sections/wiki/search.php b/system/sections/wiki/search.php
new file mode 100644
index 0000000..a4fee85
--- /dev/null
+++ b/system/sections/wiki/search.php
@@ -0,0 +1,167 @@
+ '')));
+
+ $mkey = md5($text.'wiki');
+
+ if($mcache->get($mkey) != '' AND !isset($url['tag']))
+ sys::outjs(array('s' => $mcache->get($mkey)));
+
+ if(!isset($text{2}) AND !isset($url['tag']))
+ sys::outjs(array('s' => 'Для выполнения поиска, необходимо больше данных', $nmch));
+
+ $aWiki_q = array();
+ $aNswer_q = array();
+
+ // Поиск по вопросу
+ $wiki_q = $sql->query('SELECT `id` FROM `wiki` WHERE `name` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') OR `tags` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') LIMIT 5');
+
+ // Поиск по тексту (ответу)
+ $answer_q = $sql->query('SELECT `wiki` FROM `wiki_answer` WHERE `text` LIKE FROM_BASE64(\''.base64_encode('%'.$text.'%').'\') LIMIT 5');
+
+ // Если нет ниодного совпадения по вводимому тексту
+ if(!$sql->num($wiki_q) AND !$sql->num($answer_q) AND !isset($url['tag']))
+ {
+ // Поиск по словам
+ if(!strpos($text, ' '))
+ {
+ $mcache->set($mkey, 'По вашему запросу ничего не найдено', false, 15);
+
+ sys::outjs(array('s' => 'По вашему запросу ничего не найдено'));
+ }
+
+ // Массив слов
+ $aText = explode(' ', $text);
+
+ // Метка, которая изменится в процессе, если будет найдено хоть одно совпадение
+ $sWord = false;
+
+ foreach($aText as $word)
+ {
+ if($word == '' || !isset($word{2}))
+ continue;
+
+ // Поиск по вопросу
+ $wiki_q = $sql->query('SELECT `id` FROM `wiki` WHERE `name` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') OR `tags` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') LIMIT 5');
+
+ // Поиск по тексту (ответу)
+ $answer_q = $sql->query('SELECT `wiki` FROM `wiki_answer` WHERE `text` LIKE FROM_BASE64(\''.base64_encode('%'.$word.'%').'\') LIMIT 5');
+
+ if($sql->num($wiki_q))
+ $aWiki_q[] = $wiki_q;
+
+ if($sql->num($answer_q))
+ $aNswer_q[] = $answer_q;
+ }
+
+ }else{
+ $aWiki_q[] = $wiki_q;
+ $aNswer_q[] = $answer_q;
+ }
+
+ if(!count($aWiki_q) AND !count($aNswer_q) AND !isset($url['tag']))
+ {
+ $mcache->set($mkey, 'По вашему запросу ничего не найдено', false, 15);
+
+ sys::outjs(array('s' => 'По вашему запросу ничего не найдено'));
+ }
+
+ // Защита от дублирования
+ $aResult = array();
+
+ foreach($aWiki_q as $index => $wiki_q)
+ {
+ // Генерация списка (совпадение по вопросу)
+ while($wiki = $sql->get($wiki_q))
+ {
+ // Проверка дублирования
+ if(in_array($wiki['id'], $aResult))
+ continue;
+
+ $sql->query('SELECT `id`, `name`, `tags`, `date` FROM `wiki` WHERE `id`="'.$wiki['id'].'" LIMIT 1');
+ $quest = $sql->get();
+
+ $aTags = explode(',', $quest['tags']);
+
+ $tags = '';
+
+ foreach($aTags as $tag)
+ {
+ $tag = trim($tag);
+
+ $tags .= ''.$tag.' ';
+ }
+
+ $html->get('list', 'sections/wiki/question');
+
+ $html->set('id', $quest['id']);
+ $html->set('name', sys::find($quest['name'], $text));
+ $html->set('tags', $tags != '' ? $tags : 'Теги отсутствуют');
+ $html->set('date', sys::today($quest['date']));
+
+ $html->set('home', $cfg['http']);
+
+ $html->pack('question');
+
+ array_push($aResult, $wiki['id']);
+ }
+ }
+
+ foreach($aNswer_q as $index => $answer_q)
+ {
+ // Генерация списка (совпадение по ответу)
+ while($answer = $sql->get($answer_q))
+ {
+ // Проверка дублирования
+ if(in_array($answer['wiki'], $aResult))
+ continue;
+
+ $sql->query('SELECT `id`, `name`, `tags`, `date` FROM `wiki` WHERE `id`="'.$answer['wiki'].'" LIMIT 1');
+ $quest = $sql->get();
+
+ $aTags = explode(',', $quest['tags']);
+
+ $tags = '';
+
+ foreach($aTags as $tag)
+ {
+ $tag = trim($tag);
+
+ $tags .= ''.$tag.' ';
+ }
+
+ $html->get('list', 'sections/wiki/question');
+
+ $html->set('id', $quest['id']);
+ $html->set('name', sys::find($quest['name'], $text));
+ $html->set('tags', $tags != '' ? $tags : 'Теги отсутствуют');
+ $html->set('date', sys::today($quest['date']));
+
+ $html->set('home', $cfg['http']);
+
+ $html->pack('question');
+ }
+ }
+
+ $html->arr['question'] = isset($html->arr['question']) ? $html->arr['question'] : 'По вашему запросу ничего не найдено';
+
+ $mcache->set($mkey, $html->arr['question'], false, 15);
+
+ if(!isset($url['tag']))
+ sys::outjs(array('s' => $html->arr['question']), $nmch);
+
+ $html->nav('Категории вопросов', $cfg['http'].'wiki');
+ $html->nav('Поиск по тегам');
+
+ $html->get('search', 'sections/wiki');
+
+ $html->set('text', $text);
+ $html->set('result', $html->arr['question']);
+
+ $html->pack('main');
+?>
\ No newline at end of file
diff --git a/temp/.htaccess b/temp/.htaccess
new file mode 100644
index 0000000..e69de29
diff --git a/template/acp/all.html b/template/acp/all.html
new file mode 100644
index 0000000..ce763b9
--- /dev/null
+++ b/template/acp/all.html
@@ -0,0 +1,145 @@
+
+
+
+ Панель администатора
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |section|
+
+ |_section|
+
+
+
+
+
+
+ |section||_section|
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/cashback.html b/template/acp/cashback.html
new file mode 100644
index 0000000..a62d9f8
--- /dev/null
+++ b/template/acp/cashback.html
@@ -0,0 +1,9 @@
+
+ #[id]
+ USER_[user]
+ [mail]
+ [purse]
+ [money] (Выводить: [cashback])
+ [type]
+ Вывести
+
\ No newline at end of file
diff --git a/template/acp/css/bootbox.css b/template/acp/css/bootbox.css
new file mode 100644
index 0000000..2e7cbcd
--- /dev/null
+++ b/template/acp/css/bootbox.css
@@ -0,0 +1,123 @@
+.fade
+{
+ opacity:0;
+ -webkit-transition:opacity .15s linear;
+ -moz-transition:opacity .15s linear;
+ -o-transition:opacity .15s linear;
+ transition:opacity .15s linear
+}
+.fade.in{
+ opacity:1
+}
+.modal-backdrop
+{
+ position:fixed;
+ top:0;
+ right:0;
+ bottom:0;
+ left:0;
+ z-index:1040;
+ background-color:#000
+}
+.modal-backdrop.fade{
+ opacity:0
+}
+.modal-backdrop,.modal-backdrop.fade.in
+{
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+ background-color: #90D3F4;
+}
+.modal
+{
+ position:fixed;
+ top:40%;
+ left:45%;
+ z-index:1050;
+ width:560px;
+ margin-left:-280px;
+ background-color:#fff;
+ border:1px solid #EEE;
+ *border:1px solid #EEE;
+ outline:0;
+ -webkit-background-clip:padding-box;
+ -moz-background-clip:padding-box;
+ background-clip:padding-box
+}
+.modal.fade
+{
+ top:-25%;
+ -webkit-transition:opacity .3s linear,top .3s ease-out;
+ -moz-transition:opacity .3s linear,top .3s ease-out;
+ -o-transition:opacity .3s linear,top .3s ease-out;
+ transition:opacity .3s linear,top .3s ease-out
+}
+.modal.fade.in
+{
+ top:40%;
+ left: 50%;
+}
+.modal-header
+{
+ padding:9px 15px;
+ border-bottom:1px solid #eee
+}
+.modal-header .close
+{
+ margin-top:2px
+}
+.modal-header h3
+{
+ margin:0;
+ line-height:30px
+}
+.modal-body
+{
+ position:relative;
+ max-height:400px;
+ padding:15px;
+ overflow-y:auto
+}
+.modal-form
+{
+ margin-bottom:0
+}
+.modal-footer
+{
+ padding:14px 15px 15px;
+ margin-bottom:0;
+ text-align:right;
+ background-color:#f5f5f5;
+ border-top:1px solid #ddd;
+ -webkit-border-radius:0 0 6px 6px;
+ -moz-border-radius:0 0 6px 6px;
+ border-radius:0 0 6px 6px;
+ *zoom:1;
+ -webkit-box-shadow:inset 0 1px 0 #fff;
+ -moz-box-shadow:inset 0 1px 0 #fff;
+ box-shadow:inset 0 1px 0 #fff
+}
+.modal-footer:before,.modal-footer:after
+{
+ display:table;
+ line-height:0;
+ content:""
+}
+.modal-footer:after
+{
+ clear:both
+}
+.modal-footer .btn+.btn
+{
+ margin-bottom:0;
+ margin-left:5px
+}
+.modal-footer .btn-group .btn+.btn
+{
+ margin-left:-1px
+}
+.modal-footer .btn-block+.btn-block
+{
+ margin-left:0
+}
+
diff --git a/template/acp/css/fonts.css b/template/acp/css/fonts.css
new file mode 100644
index 0000000..e3ac4b3
--- /dev/null
+++ b/template/acp/css/fonts.css
@@ -0,0 +1,2337 @@
+/*!
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */
+/* FONT PATH
+ * -------------------------- */
+@font-face {
+ font-family: 'FontAwesome';
+ src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
+ src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+.fa {
+ display: inline-block;
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+/* makes the font 33% larger relative to the icon container */
+.fa-lg {
+ font-size: 1.33333333em;
+ line-height: 0.75em;
+ vertical-align: -15%;
+}
+.fa-2x {
+ font-size: 2em;
+}
+.fa-3x {
+ font-size: 3em;
+}
+.fa-4x {
+ font-size: 4em;
+}
+.fa-5x {
+ font-size: 5em;
+}
+.fa-fw {
+ width: 1.28571429em;
+ text-align: center;
+}
+.fa-ul {
+ padding-left: 0;
+ margin-left: 2.14285714em;
+ list-style-type: none;
+}
+.fa-ul > li {
+ position: relative;
+}
+.fa-li {
+ position: absolute;
+ left: -2.14285714em;
+ width: 2.14285714em;
+ top: 0.14285714em;
+ text-align: center;
+}
+.fa-li.fa-lg {
+ left: -1.85714286em;
+}
+.fa-border {
+ padding: .2em .25em .15em;
+ border: solid 0.08em #eee;
+ border-radius: .1em;
+}
+.fa-pull-left {
+ float: left;
+}
+.fa-pull-right {
+ float: right;
+}
+.fa.fa-pull-left {
+ margin-right: .3em;
+}
+.fa.fa-pull-right {
+ margin-left: .3em;
+}
+/* Deprecated as of 4.4.0 */
+.pull-right {
+ float: right;
+}
+.pull-left {
+ float: left;
+}
+.fa.pull-left {
+ margin-right: .3em;
+}
+.fa.pull-right {
+ margin-left: .3em;
+}
+.fa-spin {
+ -webkit-animation: fa-spin 2s infinite linear;
+ animation: fa-spin 2s infinite linear;
+}
+.fa-pulse {
+ -webkit-animation: fa-spin 1s infinite steps(8);
+ animation: fa-spin 1s infinite steps(8);
+}
+@-webkit-keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+@keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+.fa-rotate-90 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
+ -webkit-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+.fa-rotate-180 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
+ -webkit-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+.fa-rotate-270 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
+ -webkit-transform: rotate(270deg);
+ -ms-transform: rotate(270deg);
+ transform: rotate(270deg);
+}
+.fa-flip-horizontal {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
+ -webkit-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, 1);
+}
+.fa-flip-vertical {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
+ -webkit-transform: scale(1, -1);
+ -ms-transform: scale(1, -1);
+ transform: scale(1, -1);
+}
+:root .fa-rotate-90,
+:root .fa-rotate-180,
+:root .fa-rotate-270,
+:root .fa-flip-horizontal,
+:root .fa-flip-vertical {
+ filter: none;
+}
+.fa-stack {
+ position: relative;
+ display: inline-block;
+ width: 2em;
+ height: 2em;
+ line-height: 2em;
+ vertical-align: middle;
+}
+.fa-stack-1x,
+.fa-stack-2x {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ text-align: center;
+}
+.fa-stack-1x {
+ line-height: inherit;
+}
+.fa-stack-2x {
+ font-size: 2em;
+}
+.fa-inverse {
+ color: #fff;
+}
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
+ readers do not read off random characters that represent icons */
+.fa-glass:before {
+ content: "\f000";
+}
+.fa-music:before {
+ content: "\f001";
+}
+.fa-search:before {
+ content: "\f002";
+}
+.fa-envelope-o:before {
+ content: "\f003";
+}
+.fa-heart:before {
+ content: "\f004";
+}
+.fa-star:before {
+ content: "\f005";
+}
+.fa-star-o:before {
+ content: "\f006";
+}
+.fa-user:before {
+ content: "\f007";
+}
+.fa-film:before {
+ content: "\f008";
+}
+.fa-th-large:before {
+ content: "\f009";
+}
+.fa-th:before {
+ content: "\f00a";
+}
+.fa-th-list:before {
+ content: "\f00b";
+}
+.fa-check:before {
+ content: "\f00c";
+}
+.fa-remove:before,
+.fa-close:before,
+.fa-times:before {
+ content: "\f00d";
+}
+.fa-search-plus:before {
+ content: "\f00e";
+}
+.fa-search-minus:before {
+ content: "\f010";
+}
+.fa-power-off:before {
+ content: "\f011";
+}
+.fa-signal:before {
+ content: "\f012";
+}
+.fa-gear:before,
+.fa-cog:before {
+ content: "\f013";
+}
+.fa-trash-o:before {
+ content: "\f014";
+}
+.fa-home:before {
+ content: "\f015";
+}
+.fa-file-o:before {
+ content: "\f016";
+}
+.fa-clock-o:before {
+ content: "\f017";
+}
+.fa-road:before {
+ content: "\f018";
+}
+.fa-download:before {
+ content: "\f019";
+}
+.fa-arrow-circle-o-down:before {
+ content: "\f01a";
+}
+.fa-arrow-circle-o-up:before {
+ content: "\f01b";
+}
+.fa-inbox:before {
+ content: "\f01c";
+}
+.fa-play-circle-o:before {
+ content: "\f01d";
+}
+.fa-rotate-right:before,
+.fa-repeat:before {
+ content: "\f01e";
+}
+.fa-refresh:before {
+ content: "\f021";
+}
+.fa-list-alt:before {
+ content: "\f022";
+}
+.fa-lock:before {
+ content: "\f023";
+}
+.fa-flag:before {
+ content: "\f024";
+}
+.fa-headphones:before {
+ content: "\f025";
+}
+.fa-volume-off:before {
+ content: "\f026";
+}
+.fa-volume-down:before {
+ content: "\f027";
+}
+.fa-volume-up:before {
+ content: "\f028";
+}
+.fa-qrcode:before {
+ content: "\f029";
+}
+.fa-barcode:before {
+ content: "\f02a";
+}
+.fa-tag:before {
+ content: "\f02b";
+}
+.fa-tags:before {
+ content: "\f02c";
+}
+.fa-book:before {
+ content: "\f02d";
+}
+.fa-bookmark:before {
+ content: "\f02e";
+}
+.fa-print:before {
+ content: "\f02f";
+}
+.fa-camera:before {
+ content: "\f030";
+}
+.fa-font:before {
+ content: "\f031";
+}
+.fa-bold:before {
+ content: "\f032";
+}
+.fa-italic:before {
+ content: "\f033";
+}
+.fa-text-height:before {
+ content: "\f034";
+}
+.fa-text-width:before {
+ content: "\f035";
+}
+.fa-align-left:before {
+ content: "\f036";
+}
+.fa-align-center:before {
+ content: "\f037";
+}
+.fa-align-right:before {
+ content: "\f038";
+}
+.fa-align-justify:before {
+ content: "\f039";
+}
+.fa-list:before {
+ content: "\f03a";
+}
+.fa-dedent:before,
+.fa-outdent:before {
+ content: "\f03b";
+}
+.fa-indent:before {
+ content: "\f03c";
+}
+.fa-video-camera:before {
+ content: "\f03d";
+}
+.fa-photo:before,
+.fa-image:before,
+.fa-picture-o:before {
+ content: "\f03e";
+}
+.fa-pencil:before {
+ content: "\f040";
+}
+.fa-map-marker:before {
+ content: "\f041";
+}
+.fa-adjust:before {
+ content: "\f042";
+}
+.fa-tint:before {
+ content: "\f043";
+}
+.fa-edit:before,
+.fa-pencil-square-o:before {
+ content: "\f044";
+}
+.fa-share-square-o:before {
+ content: "\f045";
+}
+.fa-check-square-o:before {
+ content: "\f046";
+}
+.fa-arrows:before {
+ content: "\f047";
+}
+.fa-step-backward:before {
+ content: "\f048";
+}
+.fa-fast-backward:before {
+ content: "\f049";
+}
+.fa-backward:before {
+ content: "\f04a";
+}
+.fa-play:before {
+ content: "\f04b";
+}
+.fa-pause:before {
+ content: "\f04c";
+}
+.fa-stop:before {
+ content: "\f04d";
+}
+.fa-forward:before {
+ content: "\f04e";
+}
+.fa-fast-forward:before {
+ content: "\f050";
+}
+.fa-step-forward:before {
+ content: "\f051";
+}
+.fa-eject:before {
+ content: "\f052";
+}
+.fa-chevron-left:before {
+ content: "\f053";
+}
+.fa-chevron-right:before {
+ content: "\f054";
+}
+.fa-plus-circle:before {
+ content: "\f055";
+}
+.fa-minus-circle:before {
+ content: "\f056";
+}
+.fa-times-circle:before {
+ content: "\f057";
+}
+.fa-check-circle:before {
+ content: "\f058";
+}
+.fa-question-circle:before {
+ content: "\f059";
+}
+.fa-info-circle:before {
+ content: "\f05a";
+}
+.fa-crosshairs:before {
+ content: "\f05b";
+}
+.fa-times-circle-o:before {
+ content: "\f05c";
+}
+.fa-check-circle-o:before {
+ content: "\f05d";
+}
+.fa-ban:before {
+ content: "\f05e";
+}
+.fa-arrow-left:before {
+ content: "\f060";
+}
+.fa-arrow-right:before {
+ content: "\f061";
+}
+.fa-arrow-up:before {
+ content: "\f062";
+}
+.fa-arrow-down:before {
+ content: "\f063";
+}
+.fa-mail-forward:before,
+.fa-share:before {
+ content: "\f064";
+}
+.fa-expand:before {
+ content: "\f065";
+}
+.fa-compress:before {
+ content: "\f066";
+}
+.fa-plus:before {
+ content: "\f067";
+}
+.fa-minus:before {
+ content: "\f068";
+}
+.fa-asterisk:before {
+ content: "\f069";
+}
+.fa-exclamation-circle:before {
+ content: "\f06a";
+}
+.fa-gift:before {
+ content: "\f06b";
+}
+.fa-leaf:before {
+ content: "\f06c";
+}
+.fa-fire:before {
+ content: "\f06d";
+}
+.fa-eye:before {
+ content: "\f06e";
+}
+.fa-eye-slash:before {
+ content: "\f070";
+}
+.fa-warning:before,
+.fa-exclamation-triangle:before {
+ content: "\f071";
+}
+.fa-plane:before {
+ content: "\f072";
+}
+.fa-calendar:before {
+ content: "\f073";
+}
+.fa-random:before {
+ content: "\f074";
+}
+.fa-comment:before {
+ content: "\f075";
+}
+.fa-magnet:before {
+ content: "\f076";
+}
+.fa-chevron-up:before {
+ content: "\f077";
+}
+.fa-chevron-down:before {
+ content: "\f078";
+}
+.fa-retweet:before {
+ content: "\f079";
+}
+.fa-shopping-cart:before {
+ content: "\f07a";
+}
+.fa-folder:before {
+ content: "\f07b";
+}
+.fa-folder-open:before {
+ content: "\f07c";
+}
+.fa-arrows-v:before {
+ content: "\f07d";
+}
+.fa-arrows-h:before {
+ content: "\f07e";
+}
+.fa-bar-chart-o:before,
+.fa-bar-chart:before {
+ content: "\f080";
+}
+.fa-twitter-square:before {
+ content: "\f081";
+}
+.fa-facebook-square:before {
+ content: "\f082";
+}
+.fa-camera-retro:before {
+ content: "\f083";
+}
+.fa-key:before {
+ content: "\f084";
+}
+.fa-gears:before,
+.fa-cogs:before {
+ content: "\f085";
+}
+.fa-comments:before {
+ content: "\f086";
+}
+.fa-thumbs-o-up:before {
+ content: "\f087";
+}
+.fa-thumbs-o-down:before {
+ content: "\f088";
+}
+.fa-star-half:before {
+ content: "\f089";
+}
+.fa-heart-o:before {
+ content: "\f08a";
+}
+.fa-sign-out:before {
+ content: "\f08b";
+}
+.fa-linkedin-square:before {
+ content: "\f08c";
+}
+.fa-thumb-tack:before {
+ content: "\f08d";
+}
+.fa-external-link:before {
+ content: "\f08e";
+}
+.fa-sign-in:before {
+ content: "\f090";
+}
+.fa-trophy:before {
+ content: "\f091";
+}
+.fa-github-square:before {
+ content: "\f092";
+}
+.fa-upload:before {
+ content: "\f093";
+}
+.fa-lemon-o:before {
+ content: "\f094";
+}
+.fa-phone:before {
+ content: "\f095";
+}
+.fa-square-o:before {
+ content: "\f096";
+}
+.fa-bookmark-o:before {
+ content: "\f097";
+}
+.fa-phone-square:before {
+ content: "\f098";
+}
+.fa-twitter:before {
+ content: "\f099";
+}
+.fa-facebook-f:before,
+.fa-facebook:before {
+ content: "\f09a";
+}
+.fa-github:before {
+ content: "\f09b";
+}
+.fa-unlock:before {
+ content: "\f09c";
+}
+.fa-credit-card:before {
+ content: "\f09d";
+}
+.fa-feed:before,
+.fa-rss:before {
+ content: "\f09e";
+}
+.fa-hdd-o:before {
+ content: "\f0a0";
+}
+.fa-bullhorn:before {
+ content: "\f0a1";
+}
+.fa-bell:before {
+ content: "\f0f3";
+}
+.fa-certificate:before {
+ content: "\f0a3";
+}
+.fa-hand-o-right:before {
+ content: "\f0a4";
+}
+.fa-hand-o-left:before {
+ content: "\f0a5";
+}
+.fa-hand-o-up:before {
+ content: "\f0a6";
+}
+.fa-hand-o-down:before {
+ content: "\f0a7";
+}
+.fa-arrow-circle-left:before {
+ content: "\f0a8";
+}
+.fa-arrow-circle-right:before {
+ content: "\f0a9";
+}
+.fa-arrow-circle-up:before {
+ content: "\f0aa";
+}
+.fa-arrow-circle-down:before {
+ content: "\f0ab";
+}
+.fa-globe:before {
+ content: "\f0ac";
+}
+.fa-wrench:before {
+ content: "\f0ad";
+}
+.fa-tasks:before {
+ content: "\f0ae";
+}
+.fa-filter:before {
+ content: "\f0b0";
+}
+.fa-briefcase:before {
+ content: "\f0b1";
+}
+.fa-arrows-alt:before {
+ content: "\f0b2";
+}
+.fa-group:before,
+.fa-users:before {
+ content: "\f0c0";
+}
+.fa-chain:before,
+.fa-link:before {
+ content: "\f0c1";
+}
+.fa-cloud:before {
+ content: "\f0c2";
+}
+.fa-flask:before {
+ content: "\f0c3";
+}
+.fa-cut:before,
+.fa-scissors:before {
+ content: "\f0c4";
+}
+.fa-copy:before,
+.fa-files-o:before {
+ content: "\f0c5";
+}
+.fa-paperclip:before {
+ content: "\f0c6";
+}
+.fa-save:before,
+.fa-floppy-o:before {
+ content: "\f0c7";
+}
+.fa-square:before {
+ content: "\f0c8";
+}
+.fa-navicon:before,
+.fa-reorder:before,
+.fa-bars:before {
+ content: "\f0c9";
+}
+.fa-list-ul:before {
+ content: "\f0ca";
+}
+.fa-list-ol:before {
+ content: "\f0cb";
+}
+.fa-strikethrough:before {
+ content: "\f0cc";
+}
+.fa-underline:before {
+ content: "\f0cd";
+}
+.fa-table:before {
+ content: "\f0ce";
+}
+.fa-magic:before {
+ content: "\f0d0";
+}
+.fa-truck:before {
+ content: "\f0d1";
+}
+.fa-pinterest:before {
+ content: "\f0d2";
+}
+.fa-pinterest-square:before {
+ content: "\f0d3";
+}
+.fa-google-plus-square:before {
+ content: "\f0d4";
+}
+.fa-google-plus:before {
+ content: "\f0d5";
+}
+.fa-money:before {
+ content: "\f0d6";
+}
+.fa-caret-down:before {
+ content: "\f0d7";
+}
+.fa-caret-up:before {
+ content: "\f0d8";
+}
+.fa-caret-left:before {
+ content: "\f0d9";
+}
+.fa-caret-right:before {
+ content: "\f0da";
+}
+.fa-columns:before {
+ content: "\f0db";
+}
+.fa-unsorted:before,
+.fa-sort:before {
+ content: "\f0dc";
+}
+.fa-sort-down:before,
+.fa-sort-desc:before {
+ content: "\f0dd";
+}
+.fa-sort-up:before,
+.fa-sort-asc:before {
+ content: "\f0de";
+}
+.fa-envelope:before {
+ content: "\f0e0";
+}
+.fa-linkedin:before {
+ content: "\f0e1";
+}
+.fa-rotate-left:before,
+.fa-undo:before {
+ content: "\f0e2";
+}
+.fa-legal:before,
+.fa-gavel:before {
+ content: "\f0e3";
+}
+.fa-dashboard:before,
+.fa-tachometer:before {
+ content: "\f0e4";
+}
+.fa-comment-o:before {
+ content: "\f0e5";
+}
+.fa-comments-o:before {
+ content: "\f0e6";
+}
+.fa-flash:before,
+.fa-bolt:before {
+ content: "\f0e7";
+}
+.fa-sitemap:before {
+ content: "\f0e8";
+}
+.fa-umbrella:before {
+ content: "\f0e9";
+}
+.fa-paste:before,
+.fa-clipboard:before {
+ content: "\f0ea";
+}
+.fa-lightbulb-o:before {
+ content: "\f0eb";
+}
+.fa-exchange:before {
+ content: "\f0ec";
+}
+.fa-cloud-download:before {
+ content: "\f0ed";
+}
+.fa-cloud-upload:before {
+ content: "\f0ee";
+}
+.fa-user-md:before {
+ content: "\f0f0";
+}
+.fa-stethoscope:before {
+ content: "\f0f1";
+}
+.fa-suitcase:before {
+ content: "\f0f2";
+}
+.fa-bell-o:before {
+ content: "\f0a2";
+}
+.fa-coffee:before {
+ content: "\f0f4";
+}
+.fa-cutlery:before {
+ content: "\f0f5";
+}
+.fa-file-text-o:before {
+ content: "\f0f6";
+}
+.fa-building-o:before {
+ content: "\f0f7";
+}
+.fa-hospital-o:before {
+ content: "\f0f8";
+}
+.fa-ambulance:before {
+ content: "\f0f9";
+}
+.fa-medkit:before {
+ content: "\f0fa";
+}
+.fa-fighter-jet:before {
+ content: "\f0fb";
+}
+.fa-beer:before {
+ content: "\f0fc";
+}
+.fa-h-square:before {
+ content: "\f0fd";
+}
+.fa-plus-square:before {
+ content: "\f0fe";
+}
+.fa-angle-double-left:before {
+ content: "\f100";
+}
+.fa-angle-double-right:before {
+ content: "\f101";
+}
+.fa-angle-double-up:before {
+ content: "\f102";
+}
+.fa-angle-double-down:before {
+ content: "\f103";
+}
+.fa-angle-left:before {
+ content: "\f104";
+}
+.fa-angle-right:before {
+ content: "\f105";
+}
+.fa-angle-up:before {
+ content: "\f106";
+}
+.fa-angle-down:before {
+ content: "\f107";
+}
+.fa-desktop:before {
+ content: "\f108";
+}
+.fa-laptop:before {
+ content: "\f109";
+}
+.fa-tablet:before {
+ content: "\f10a";
+}
+.fa-mobile-phone:before,
+.fa-mobile:before {
+ content: "\f10b";
+}
+.fa-circle-o:before {
+ content: "\f10c";
+}
+.fa-quote-left:before {
+ content: "\f10d";
+}
+.fa-quote-right:before {
+ content: "\f10e";
+}
+.fa-spinner:before {
+ content: "\f110";
+}
+.fa-circle:before {
+ content: "\f111";
+}
+.fa-mail-reply:before,
+.fa-reply:before {
+ content: "\f112";
+}
+.fa-github-alt:before {
+ content: "\f113";
+}
+.fa-folder-o:before {
+ content: "\f114";
+}
+.fa-folder-open-o:before {
+ content: "\f115";
+}
+.fa-smile-o:before {
+ content: "\f118";
+}
+.fa-frown-o:before {
+ content: "\f119";
+}
+.fa-meh-o:before {
+ content: "\f11a";
+}
+.fa-gamepad:before {
+ content: "\f11b";
+}
+.fa-keyboard-o:before {
+ content: "\f11c";
+}
+.fa-flag-o:before {
+ content: "\f11d";
+}
+.fa-flag-checkered:before {
+ content: "\f11e";
+}
+.fa-terminal:before {
+ content: "\f120";
+}
+.fa-code:before {
+ content: "\f121";
+}
+.fa-mail-reply-all:before,
+.fa-reply-all:before {
+ content: "\f122";
+}
+.fa-star-half-empty:before,
+.fa-star-half-full:before,
+.fa-star-half-o:before {
+ content: "\f123";
+}
+.fa-location-arrow:before {
+ content: "\f124";
+}
+.fa-crop:before {
+ content: "\f125";
+}
+.fa-code-fork:before {
+ content: "\f126";
+}
+.fa-unlink:before,
+.fa-chain-broken:before {
+ content: "\f127";
+}
+.fa-question:before {
+ content: "\f128";
+}
+.fa-info:before {
+ content: "\f129";
+}
+.fa-exclamation:before {
+ content: "\f12a";
+}
+.fa-superscript:before {
+ content: "\f12b";
+}
+.fa-subscript:before {
+ content: "\f12c";
+}
+.fa-eraser:before {
+ content: "\f12d";
+}
+.fa-puzzle-piece:before {
+ content: "\f12e";
+}
+.fa-microphone:before {
+ content: "\f130";
+}
+.fa-microphone-slash:before {
+ content: "\f131";
+}
+.fa-shield:before {
+ content: "\f132";
+}
+.fa-calendar-o:before {
+ content: "\f133";
+}
+.fa-fire-extinguisher:before {
+ content: "\f134";
+}
+.fa-rocket:before {
+ content: "\f135";
+}
+.fa-maxcdn:before {
+ content: "\f136";
+}
+.fa-chevron-circle-left:before {
+ content: "\f137";
+}
+.fa-chevron-circle-right:before {
+ content: "\f138";
+}
+.fa-chevron-circle-up:before {
+ content: "\f139";
+}
+.fa-chevron-circle-down:before {
+ content: "\f13a";
+}
+.fa-html5:before {
+ content: "\f13b";
+}
+.fa-css3:before {
+ content: "\f13c";
+}
+.fa-anchor:before {
+ content: "\f13d";
+}
+.fa-unlock-alt:before {
+ content: "\f13e";
+}
+.fa-bullseye:before {
+ content: "\f140";
+}
+.fa-ellipsis-h:before {
+ content: "\f141";
+}
+.fa-ellipsis-v:before {
+ content: "\f142";
+}
+.fa-rss-square:before {
+ content: "\f143";
+}
+.fa-play-circle:before {
+ content: "\f144";
+}
+.fa-ticket:before {
+ content: "\f145";
+}
+.fa-minus-square:before {
+ content: "\f146";
+}
+.fa-minus-square-o:before {
+ content: "\f147";
+}
+.fa-level-up:before {
+ content: "\f148";
+}
+.fa-level-down:before {
+ content: "\f149";
+}
+.fa-check-square:before {
+ content: "\f14a";
+}
+.fa-pencil-square:before {
+ content: "\f14b";
+}
+.fa-external-link-square:before {
+ content: "\f14c";
+}
+.fa-share-square:before {
+ content: "\f14d";
+}
+.fa-compass:before {
+ content: "\f14e";
+}
+.fa-toggle-down:before,
+.fa-caret-square-o-down:before {
+ content: "\f150";
+}
+.fa-toggle-up:before,
+.fa-caret-square-o-up:before {
+ content: "\f151";
+}
+.fa-toggle-right:before,
+.fa-caret-square-o-right:before {
+ content: "\f152";
+}
+.fa-euro:before,
+.fa-eur:before {
+ content: "\f153";
+}
+.fa-gbp:before {
+ content: "\f154";
+}
+.fa-dollar:before,
+.fa-usd:before {
+ content: "\f155";
+}
+.fa-rupee:before,
+.fa-inr:before {
+ content: "\f156";
+}
+.fa-cny:before,
+.fa-rmb:before,
+.fa-yen:before,
+.fa-jpy:before {
+ content: "\f157";
+}
+.fa-ruble:before,
+.fa-rouble:before,
+.fa-rub:before {
+ content: "\f158";
+}
+.fa-won:before,
+.fa-krw:before {
+ content: "\f159";
+}
+.fa-bitcoin:before,
+.fa-btc:before {
+ content: "\f15a";
+}
+.fa-file:before {
+ content: "\f15b";
+}
+.fa-file-text:before {
+ content: "\f15c";
+}
+.fa-sort-alpha-asc:before {
+ content: "\f15d";
+}
+.fa-sort-alpha-desc:before {
+ content: "\f15e";
+}
+.fa-sort-amount-asc:before {
+ content: "\f160";
+}
+.fa-sort-amount-desc:before {
+ content: "\f161";
+}
+.fa-sort-numeric-asc:before {
+ content: "\f162";
+}
+.fa-sort-numeric-desc:before {
+ content: "\f163";
+}
+.fa-thumbs-up:before {
+ content: "\f164";
+}
+.fa-thumbs-down:before {
+ content: "\f165";
+}
+.fa-youtube-square:before {
+ content: "\f166";
+}
+.fa-youtube:before {
+ content: "\f167";
+}
+.fa-xing:before {
+ content: "\f168";
+}
+.fa-xing-square:before {
+ content: "\f169";
+}
+.fa-youtube-play:before {
+ content: "\f16a";
+}
+.fa-dropbox:before {
+ content: "\f16b";
+}
+.fa-stack-overflow:before {
+ content: "\f16c";
+}
+.fa-instagram:before {
+ content: "\f16d";
+}
+.fa-flickr:before {
+ content: "\f16e";
+}
+.fa-adn:before {
+ content: "\f170";
+}
+.fa-bitbucket:before {
+ content: "\f171";
+}
+.fa-bitbucket-square:before {
+ content: "\f172";
+}
+.fa-tumblr:before {
+ content: "\f173";
+}
+.fa-tumblr-square:before {
+ content: "\f174";
+}
+.fa-long-arrow-down:before {
+ content: "\f175";
+}
+.fa-long-arrow-up:before {
+ content: "\f176";
+}
+.fa-long-arrow-left:before {
+ content: "\f177";
+}
+.fa-long-arrow-right:before {
+ content: "\f178";
+}
+.fa-apple:before {
+ content: "\f179";
+}
+.fa-windows:before {
+ content: "\f17a";
+}
+.fa-android:before {
+ content: "\f17b";
+}
+.fa-linux:before {
+ content: "\f17c";
+}
+.fa-dribbble:before {
+ content: "\f17d";
+}
+.fa-skype:before {
+ content: "\f17e";
+}
+.fa-foursquare:before {
+ content: "\f180";
+}
+.fa-trello:before {
+ content: "\f181";
+}
+.fa-female:before {
+ content: "\f182";
+}
+.fa-male:before {
+ content: "\f183";
+}
+.fa-gittip:before,
+.fa-gratipay:before {
+ content: "\f184";
+}
+.fa-sun-o:before {
+ content: "\f185";
+}
+.fa-moon-o:before {
+ content: "\f186";
+}
+.fa-archive:before {
+ content: "\f187";
+}
+.fa-bug:before {
+ content: "\f188";
+}
+.fa-vk:before {
+ content: "\f189";
+}
+.fa-weibo:before {
+ content: "\f18a";
+}
+.fa-renren:before {
+ content: "\f18b";
+}
+.fa-pagelines:before {
+ content: "\f18c";
+}
+.fa-stack-exchange:before {
+ content: "\f18d";
+}
+.fa-arrow-circle-o-right:before {
+ content: "\f18e";
+}
+.fa-arrow-circle-o-left:before {
+ content: "\f190";
+}
+.fa-toggle-left:before,
+.fa-caret-square-o-left:before {
+ content: "\f191";
+}
+.fa-dot-circle-o:before {
+ content: "\f192";
+}
+.fa-wheelchair:before {
+ content: "\f193";
+}
+.fa-vimeo-square:before {
+ content: "\f194";
+}
+.fa-turkish-lira:before,
+.fa-try:before {
+ content: "\f195";
+}
+.fa-plus-square-o:before {
+ content: "\f196";
+}
+.fa-space-shuttle:before {
+ content: "\f197";
+}
+.fa-slack:before {
+ content: "\f198";
+}
+.fa-envelope-square:before {
+ content: "\f199";
+}
+.fa-wordpress:before {
+ content: "\f19a";
+}
+.fa-openid:before {
+ content: "\f19b";
+}
+.fa-institution:before,
+.fa-bank:before,
+.fa-university:before {
+ content: "\f19c";
+}
+.fa-mortar-board:before,
+.fa-graduation-cap:before {
+ content: "\f19d";
+}
+.fa-yahoo:before {
+ content: "\f19e";
+}
+.fa-google:before {
+ content: "\f1a0";
+}
+.fa-reddit:before {
+ content: "\f1a1";
+}
+.fa-reddit-square:before {
+ content: "\f1a2";
+}
+.fa-stumbleupon-circle:before {
+ content: "\f1a3";
+}
+.fa-stumbleupon:before {
+ content: "\f1a4";
+}
+.fa-delicious:before {
+ content: "\f1a5";
+}
+.fa-digg:before {
+ content: "\f1a6";
+}
+.fa-pied-piper-pp:before {
+ content: "\f1a7";
+}
+.fa-pied-piper-alt:before {
+ content: "\f1a8";
+}
+.fa-drupal:before {
+ content: "\f1a9";
+}
+.fa-joomla:before {
+ content: "\f1aa";
+}
+.fa-language:before {
+ content: "\f1ab";
+}
+.fa-fax:before {
+ content: "\f1ac";
+}
+.fa-building:before {
+ content: "\f1ad";
+}
+.fa-child:before {
+ content: "\f1ae";
+}
+.fa-paw:before {
+ content: "\f1b0";
+}
+.fa-spoon:before {
+ content: "\f1b1";
+}
+.fa-cube:before {
+ content: "\f1b2";
+}
+.fa-cubes:before {
+ content: "\f1b3";
+}
+.fa-behance:before {
+ content: "\f1b4";
+}
+.fa-behance-square:before {
+ content: "\f1b5";
+}
+.fa-steam:before {
+ content: "\f1b6";
+}
+.fa-steam-square:before {
+ content: "\f1b7";
+}
+.fa-recycle:before {
+ content: "\f1b8";
+}
+.fa-automobile:before,
+.fa-car:before {
+ content: "\f1b9";
+}
+.fa-cab:before,
+.fa-taxi:before {
+ content: "\f1ba";
+}
+.fa-tree:before {
+ content: "\f1bb";
+}
+.fa-spotify:before {
+ content: "\f1bc";
+}
+.fa-deviantart:before {
+ content: "\f1bd";
+}
+.fa-soundcloud:before {
+ content: "\f1be";
+}
+.fa-database:before {
+ content: "\f1c0";
+}
+.fa-file-pdf-o:before {
+ content: "\f1c1";
+}
+.fa-file-word-o:before {
+ content: "\f1c2";
+}
+.fa-file-excel-o:before {
+ content: "\f1c3";
+}
+.fa-file-powerpoint-o:before {
+ content: "\f1c4";
+}
+.fa-file-photo-o:before,
+.fa-file-picture-o:before,
+.fa-file-image-o:before {
+ content: "\f1c5";
+}
+.fa-file-zip-o:before,
+.fa-file-archive-o:before {
+ content: "\f1c6";
+}
+.fa-file-sound-o:before,
+.fa-file-audio-o:before {
+ content: "\f1c7";
+}
+.fa-file-movie-o:before,
+.fa-file-video-o:before {
+ content: "\f1c8";
+}
+.fa-file-code-o:before {
+ content: "\f1c9";
+}
+.fa-vine:before {
+ content: "\f1ca";
+}
+.fa-codepen:before {
+ content: "\f1cb";
+}
+.fa-jsfiddle:before {
+ content: "\f1cc";
+}
+.fa-life-bouy:before,
+.fa-life-buoy:before,
+.fa-life-saver:before,
+.fa-support:before,
+.fa-life-ring:before {
+ content: "\f1cd";
+}
+.fa-circle-o-notch:before {
+ content: "\f1ce";
+}
+.fa-ra:before,
+.fa-resistance:before,
+.fa-rebel:before {
+ content: "\f1d0";
+}
+.fa-ge:before,
+.fa-empire:before {
+ content: "\f1d1";
+}
+.fa-git-square:before {
+ content: "\f1d2";
+}
+.fa-git:before {
+ content: "\f1d3";
+}
+.fa-y-combinator-square:before,
+.fa-yc-square:before,
+.fa-hacker-news:before {
+ content: "\f1d4";
+}
+.fa-tencent-weibo:before {
+ content: "\f1d5";
+}
+.fa-qq:before {
+ content: "\f1d6";
+}
+.fa-wechat:before,
+.fa-weixin:before {
+ content: "\f1d7";
+}
+.fa-send:before,
+.fa-paper-plane:before {
+ content: "\f1d8";
+}
+.fa-send-o:before,
+.fa-paper-plane-o:before {
+ content: "\f1d9";
+}
+.fa-history:before {
+ content: "\f1da";
+}
+.fa-circle-thin:before {
+ content: "\f1db";
+}
+.fa-header:before {
+ content: "\f1dc";
+}
+.fa-paragraph:before {
+ content: "\f1dd";
+}
+.fa-sliders:before {
+ content: "\f1de";
+}
+.fa-share-alt:before {
+ content: "\f1e0";
+}
+.fa-share-alt-square:before {
+ content: "\f1e1";
+}
+.fa-bomb:before {
+ content: "\f1e2";
+}
+.fa-soccer-ball-o:before,
+.fa-futbol-o:before {
+ content: "\f1e3";
+}
+.fa-tty:before {
+ content: "\f1e4";
+}
+.fa-binoculars:before {
+ content: "\f1e5";
+}
+.fa-plug:before {
+ content: "\f1e6";
+}
+.fa-slideshare:before {
+ content: "\f1e7";
+}
+.fa-twitch:before {
+ content: "\f1e8";
+}
+.fa-yelp:before {
+ content: "\f1e9";
+}
+.fa-newspaper-o:before {
+ content: "\f1ea";
+}
+.fa-wifi:before {
+ content: "\f1eb";
+}
+.fa-calculator:before {
+ content: "\f1ec";
+}
+.fa-paypal:before {
+ content: "\f1ed";
+}
+.fa-google-wallet:before {
+ content: "\f1ee";
+}
+.fa-cc-visa:before {
+ content: "\f1f0";
+}
+.fa-cc-mastercard:before {
+ content: "\f1f1";
+}
+.fa-cc-discover:before {
+ content: "\f1f2";
+}
+.fa-cc-amex:before {
+ content: "\f1f3";
+}
+.fa-cc-paypal:before {
+ content: "\f1f4";
+}
+.fa-cc-stripe:before {
+ content: "\f1f5";
+}
+.fa-bell-slash:before {
+ content: "\f1f6";
+}
+.fa-bell-slash-o:before {
+ content: "\f1f7";
+}
+.fa-trash:before {
+ content: "\f1f8";
+}
+.fa-copyright:before {
+ content: "\f1f9";
+}
+.fa-at:before {
+ content: "\f1fa";
+}
+.fa-eyedropper:before {
+ content: "\f1fb";
+}
+.fa-paint-brush:before {
+ content: "\f1fc";
+}
+.fa-birthday-cake:before {
+ content: "\f1fd";
+}
+.fa-area-chart:before {
+ content: "\f1fe";
+}
+.fa-pie-chart:before {
+ content: "\f200";
+}
+.fa-line-chart:before {
+ content: "\f201";
+}
+.fa-lastfm:before {
+ content: "\f202";
+}
+.fa-lastfm-square:before {
+ content: "\f203";
+}
+.fa-toggle-off:before {
+ content: "\f204";
+}
+.fa-toggle-on:before {
+ content: "\f205";
+}
+.fa-bicycle:before {
+ content: "\f206";
+}
+.fa-bus:before {
+ content: "\f207";
+}
+.fa-ioxhost:before {
+ content: "\f208";
+}
+.fa-angellist:before {
+ content: "\f209";
+}
+.fa-cc:before {
+ content: "\f20a";
+}
+.fa-shekel:before,
+.fa-sheqel:before,
+.fa-ils:before {
+ content: "\f20b";
+}
+.fa-meanpath:before {
+ content: "\f20c";
+}
+.fa-buysellads:before {
+ content: "\f20d";
+}
+.fa-connectdevelop:before {
+ content: "\f20e";
+}
+.fa-dashcube:before {
+ content: "\f210";
+}
+.fa-forumbee:before {
+ content: "\f211";
+}
+.fa-leanpub:before {
+ content: "\f212";
+}
+.fa-sellsy:before {
+ content: "\f213";
+}
+.fa-shirtsinbulk:before {
+ content: "\f214";
+}
+.fa-simplybuilt:before {
+ content: "\f215";
+}
+.fa-skyatlas:before {
+ content: "\f216";
+}
+.fa-cart-plus:before {
+ content: "\f217";
+}
+.fa-cart-arrow-down:before {
+ content: "\f218";
+}
+.fa-diamond:before {
+ content: "\f219";
+}
+.fa-ship:before {
+ content: "\f21a";
+}
+.fa-user-secret:before {
+ content: "\f21b";
+}
+.fa-motorcycle:before {
+ content: "\f21c";
+}
+.fa-street-view:before {
+ content: "\f21d";
+}
+.fa-heartbeat:before {
+ content: "\f21e";
+}
+.fa-venus:before {
+ content: "\f221";
+}
+.fa-mars:before {
+ content: "\f222";
+}
+.fa-mercury:before {
+ content: "\f223";
+}
+.fa-intersex:before,
+.fa-transgender:before {
+ content: "\f224";
+}
+.fa-transgender-alt:before {
+ content: "\f225";
+}
+.fa-venus-double:before {
+ content: "\f226";
+}
+.fa-mars-double:before {
+ content: "\f227";
+}
+.fa-venus-mars:before {
+ content: "\f228";
+}
+.fa-mars-stroke:before {
+ content: "\f229";
+}
+.fa-mars-stroke-v:before {
+ content: "\f22a";
+}
+.fa-mars-stroke-h:before {
+ content: "\f22b";
+}
+.fa-neuter:before {
+ content: "\f22c";
+}
+.fa-genderless:before {
+ content: "\f22d";
+}
+.fa-facebook-official:before {
+ content: "\f230";
+}
+.fa-pinterest-p:before {
+ content: "\f231";
+}
+.fa-whatsapp:before {
+ content: "\f232";
+}
+.fa-server:before {
+ content: "\f233";
+}
+.fa-user-plus:before {
+ content: "\f234";
+}
+.fa-user-times:before {
+ content: "\f235";
+}
+.fa-hotel:before,
+.fa-bed:before {
+ content: "\f236";
+}
+.fa-viacoin:before {
+ content: "\f237";
+}
+.fa-train:before {
+ content: "\f238";
+}
+.fa-subway:before {
+ content: "\f239";
+}
+.fa-medium:before {
+ content: "\f23a";
+}
+.fa-yc:before,
+.fa-y-combinator:before {
+ content: "\f23b";
+}
+.fa-optin-monster:before {
+ content: "\f23c";
+}
+.fa-opencart:before {
+ content: "\f23d";
+}
+.fa-expeditedssl:before {
+ content: "\f23e";
+}
+.fa-battery-4:before,
+.fa-battery:before,
+.fa-battery-full:before {
+ content: "\f240";
+}
+.fa-battery-3:before,
+.fa-battery-three-quarters:before {
+ content: "\f241";
+}
+.fa-battery-2:before,
+.fa-battery-half:before {
+ content: "\f242";
+}
+.fa-battery-1:before,
+.fa-battery-quarter:before {
+ content: "\f243";
+}
+.fa-battery-0:before,
+.fa-battery-empty:before {
+ content: "\f244";
+}
+.fa-mouse-pointer:before {
+ content: "\f245";
+}
+.fa-i-cursor:before {
+ content: "\f246";
+}
+.fa-object-group:before {
+ content: "\f247";
+}
+.fa-object-ungroup:before {
+ content: "\f248";
+}
+.fa-sticky-note:before {
+ content: "\f249";
+}
+.fa-sticky-note-o:before {
+ content: "\f24a";
+}
+.fa-cc-jcb:before {
+ content: "\f24b";
+}
+.fa-cc-diners-club:before {
+ content: "\f24c";
+}
+.fa-clone:before {
+ content: "\f24d";
+}
+.fa-balance-scale:before {
+ content: "\f24e";
+}
+.fa-hourglass-o:before {
+ content: "\f250";
+}
+.fa-hourglass-1:before,
+.fa-hourglass-start:before {
+ content: "\f251";
+}
+.fa-hourglass-2:before,
+.fa-hourglass-half:before {
+ content: "\f252";
+}
+.fa-hourglass-3:before,
+.fa-hourglass-end:before {
+ content: "\f253";
+}
+.fa-hourglass:before {
+ content: "\f254";
+}
+.fa-hand-grab-o:before,
+.fa-hand-rock-o:before {
+ content: "\f255";
+}
+.fa-hand-stop-o:before,
+.fa-hand-paper-o:before {
+ content: "\f256";
+}
+.fa-hand-scissors-o:before {
+ content: "\f257";
+}
+.fa-hand-lizard-o:before {
+ content: "\f258";
+}
+.fa-hand-spock-o:before {
+ content: "\f259";
+}
+.fa-hand-pointer-o:before {
+ content: "\f25a";
+}
+.fa-hand-peace-o:before {
+ content: "\f25b";
+}
+.fa-trademark:before {
+ content: "\f25c";
+}
+.fa-registered:before {
+ content: "\f25d";
+}
+.fa-creative-commons:before {
+ content: "\f25e";
+}
+.fa-gg:before {
+ content: "\f260";
+}
+.fa-gg-circle:before {
+ content: "\f261";
+}
+.fa-tripadvisor:before {
+ content: "\f262";
+}
+.fa-odnoklassniki:before {
+ content: "\f263";
+}
+.fa-odnoklassniki-square:before {
+ content: "\f264";
+}
+.fa-get-pocket:before {
+ content: "\f265";
+}
+.fa-wikipedia-w:before {
+ content: "\f266";
+}
+.fa-safari:before {
+ content: "\f267";
+}
+.fa-chrome:before {
+ content: "\f268";
+}
+.fa-firefox:before {
+ content: "\f269";
+}
+.fa-opera:before {
+ content: "\f26a";
+}
+.fa-internet-explorer:before {
+ content: "\f26b";
+}
+.fa-tv:before,
+.fa-television:before {
+ content: "\f26c";
+}
+.fa-contao:before {
+ content: "\f26d";
+}
+.fa-500px:before {
+ content: "\f26e";
+}
+.fa-amazon:before {
+ content: "\f270";
+}
+.fa-calendar-plus-o:before {
+ content: "\f271";
+}
+.fa-calendar-minus-o:before {
+ content: "\f272";
+}
+.fa-calendar-times-o:before {
+ content: "\f273";
+}
+.fa-calendar-check-o:before {
+ content: "\f274";
+}
+.fa-industry:before {
+ content: "\f275";
+}
+.fa-map-pin:before {
+ content: "\f276";
+}
+.fa-map-signs:before {
+ content: "\f277";
+}
+.fa-map-o:before {
+ content: "\f278";
+}
+.fa-map:before {
+ content: "\f279";
+}
+.fa-commenting:before {
+ content: "\f27a";
+}
+.fa-commenting-o:before {
+ content: "\f27b";
+}
+.fa-houzz:before {
+ content: "\f27c";
+}
+.fa-vimeo:before {
+ content: "\f27d";
+}
+.fa-black-tie:before {
+ content: "\f27e";
+}
+.fa-fonticons:before {
+ content: "\f280";
+}
+.fa-reddit-alien:before {
+ content: "\f281";
+}
+.fa-edge:before {
+ content: "\f282";
+}
+.fa-credit-card-alt:before {
+ content: "\f283";
+}
+.fa-codiepie:before {
+ content: "\f284";
+}
+.fa-modx:before {
+ content: "\f285";
+}
+.fa-fort-awesome:before {
+ content: "\f286";
+}
+.fa-usb:before {
+ content: "\f287";
+}
+.fa-product-hunt:before {
+ content: "\f288";
+}
+.fa-mixcloud:before {
+ content: "\f289";
+}
+.fa-scribd:before {
+ content: "\f28a";
+}
+.fa-pause-circle:before {
+ content: "\f28b";
+}
+.fa-pause-circle-o:before {
+ content: "\f28c";
+}
+.fa-stop-circle:before {
+ content: "\f28d";
+}
+.fa-stop-circle-o:before {
+ content: "\f28e";
+}
+.fa-shopping-bag:before {
+ content: "\f290";
+}
+.fa-shopping-basket:before {
+ content: "\f291";
+}
+.fa-hashtag:before {
+ content: "\f292";
+}
+.fa-bluetooth:before {
+ content: "\f293";
+}
+.fa-bluetooth-b:before {
+ content: "\f294";
+}
+.fa-percent:before {
+ content: "\f295";
+}
+.fa-gitlab:before {
+ content: "\f296";
+}
+.fa-wpbeginner:before {
+ content: "\f297";
+}
+.fa-wpforms:before {
+ content: "\f298";
+}
+.fa-envira:before {
+ content: "\f299";
+}
+.fa-universal-access:before {
+ content: "\f29a";
+}
+.fa-wheelchair-alt:before {
+ content: "\f29b";
+}
+.fa-question-circle-o:before {
+ content: "\f29c";
+}
+.fa-blind:before {
+ content: "\f29d";
+}
+.fa-audio-description:before {
+ content: "\f29e";
+}
+.fa-volume-control-phone:before {
+ content: "\f2a0";
+}
+.fa-braille:before {
+ content: "\f2a1";
+}
+.fa-assistive-listening-systems:before {
+ content: "\f2a2";
+}
+.fa-asl-interpreting:before,
+.fa-american-sign-language-interpreting:before {
+ content: "\f2a3";
+}
+.fa-deafness:before,
+.fa-hard-of-hearing:before,
+.fa-deaf:before {
+ content: "\f2a4";
+}
+.fa-glide:before {
+ content: "\f2a5";
+}
+.fa-glide-g:before {
+ content: "\f2a6";
+}
+.fa-signing:before,
+.fa-sign-language:before {
+ content: "\f2a7";
+}
+.fa-low-vision:before {
+ content: "\f2a8";
+}
+.fa-viadeo:before {
+ content: "\f2a9";
+}
+.fa-viadeo-square:before {
+ content: "\f2aa";
+}
+.fa-snapchat:before {
+ content: "\f2ab";
+}
+.fa-snapchat-ghost:before {
+ content: "\f2ac";
+}
+.fa-snapchat-square:before {
+ content: "\f2ad";
+}
+.fa-pied-piper:before {
+ content: "\f2ae";
+}
+.fa-first-order:before {
+ content: "\f2b0";
+}
+.fa-yoast:before {
+ content: "\f2b1";
+}
+.fa-themeisle:before {
+ content: "\f2b2";
+}
+.fa-google-plus-circle:before,
+.fa-google-plus-official:before {
+ content: "\f2b3";
+}
+.fa-fa:before,
+.fa-font-awesome:before {
+ content: "\f2b4";
+}
+.fa-handshake-o:before {
+ content: "\f2b5";
+}
+.fa-envelope-open:before {
+ content: "\f2b6";
+}
+.fa-envelope-open-o:before {
+ content: "\f2b7";
+}
+.fa-linode:before {
+ content: "\f2b8";
+}
+.fa-address-book:before {
+ content: "\f2b9";
+}
+.fa-address-book-o:before {
+ content: "\f2ba";
+}
+.fa-vcard:before,
+.fa-address-card:before {
+ content: "\f2bb";
+}
+.fa-vcard-o:before,
+.fa-address-card-o:before {
+ content: "\f2bc";
+}
+.fa-user-circle:before {
+ content: "\f2bd";
+}
+.fa-user-circle-o:before {
+ content: "\f2be";
+}
+.fa-user-o:before {
+ content: "\f2c0";
+}
+.fa-id-badge:before {
+ content: "\f2c1";
+}
+.fa-drivers-license:before,
+.fa-id-card:before {
+ content: "\f2c2";
+}
+.fa-drivers-license-o:before,
+.fa-id-card-o:before {
+ content: "\f2c3";
+}
+.fa-quora:before {
+ content: "\f2c4";
+}
+.fa-free-code-camp:before {
+ content: "\f2c5";
+}
+.fa-telegram:before {
+ content: "\f2c6";
+}
+.fa-thermometer-4:before,
+.fa-thermometer:before,
+.fa-thermometer-full:before {
+ content: "\f2c7";
+}
+.fa-thermometer-3:before,
+.fa-thermometer-three-quarters:before {
+ content: "\f2c8";
+}
+.fa-thermometer-2:before,
+.fa-thermometer-half:before {
+ content: "\f2c9";
+}
+.fa-thermometer-1:before,
+.fa-thermometer-quarter:before {
+ content: "\f2ca";
+}
+.fa-thermometer-0:before,
+.fa-thermometer-empty:before {
+ content: "\f2cb";
+}
+.fa-shower:before {
+ content: "\f2cc";
+}
+.fa-bathtub:before,
+.fa-s15:before,
+.fa-bath:before {
+ content: "\f2cd";
+}
+.fa-podcast:before {
+ content: "\f2ce";
+}
+.fa-window-maximize:before {
+ content: "\f2d0";
+}
+.fa-window-minimize:before {
+ content: "\f2d1";
+}
+.fa-window-restore:before {
+ content: "\f2d2";
+}
+.fa-times-rectangle:before,
+.fa-window-close:before {
+ content: "\f2d3";
+}
+.fa-times-rectangle-o:before,
+.fa-window-close-o:before {
+ content: "\f2d4";
+}
+.fa-bandcamp:before {
+ content: "\f2d5";
+}
+.fa-grav:before {
+ content: "\f2d6";
+}
+.fa-etsy:before {
+ content: "\f2d7";
+}
+.fa-imdb:before {
+ content: "\f2d8";
+}
+.fa-ravelry:before {
+ content: "\f2d9";
+}
+.fa-eercast:before {
+ content: "\f2da";
+}
+.fa-microchip:before {
+ content: "\f2db";
+}
+.fa-snowflake-o:before {
+ content: "\f2dc";
+}
+.fa-superpowers:before {
+ content: "\f2dd";
+}
+.fa-wpexplorer:before {
+ content: "\f2de";
+}
+.fa-meetup:before {
+ content: "\f2e0";
+}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+}
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
+}
diff --git a/template/acp/css/jquery.datetimepicker.css b/template/acp/css/jquery.datetimepicker.css
new file mode 100644
index 0000000..4ed981a
--- /dev/null
+++ b/template/acp/css/jquery.datetimepicker.css
@@ -0,0 +1,568 @@
+.xdsoft_datetimepicker {
+ box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.506);
+ background: #fff;
+ border-bottom: 1px solid #bbb;
+ border-left: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ border-top: 1px solid #ccc;
+ color: #333;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ padding: 8px;
+ padding-left: 0;
+ padding-top: 2px;
+ position: absolute;
+ z-index: 9999;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ display: none;
+}
+.xdsoft_datetimepicker.xdsoft_rtl {
+ padding: 8px 0 8px 8px;
+}
+
+.xdsoft_datetimepicker iframe {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 75px;
+ height: 210px;
+ background: transparent;
+ border: none;
+}
+
+/*For IE8 or lower*/
+.xdsoft_datetimepicker button {
+ border: none !important;
+}
+
+.xdsoft_noselect {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+
+.xdsoft_noselect::selection { background: transparent }
+.xdsoft_noselect::-moz-selection { background: transparent }
+
+.xdsoft_datetimepicker.xdsoft_inline {
+ display: inline-block;
+ position: static;
+ box-shadow: none;
+}
+
+.xdsoft_datetimepicker * {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_datepicker, .xdsoft_datetimepicker .xdsoft_timepicker {
+ display: none;
+}
+
+.xdsoft_datetimepicker .xdsoft_datepicker.active, .xdsoft_datetimepicker .xdsoft_timepicker.active {
+ display: block;
+}
+
+.xdsoft_datetimepicker .xdsoft_datepicker {
+ width: 224px;
+ float: left;
+ margin-left: 8px;
+}
+.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_datepicker {
+ float: right;
+ margin-right: 8px;
+ margin-left: 0;
+}
+
+.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_datepicker {
+ width: 256px;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker {
+ width: 58px;
+ float: left;
+ text-align: center;
+ margin-left: 8px;
+ margin-top: 0;
+}
+.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_timepicker {
+ float: right;
+ margin-right: 8px;
+ margin-left: 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_datepicker.active+.xdsoft_timepicker {
+ margin-top: 8px;
+ margin-bottom: 3px
+}
+
+.xdsoft_datetimepicker .xdsoft_monthpicker {
+ position: relative;
+ text-align: center;
+}
+
+.xdsoft_datetimepicker .xdsoft_label i,
+.xdsoft_datetimepicker .xdsoft_prev,
+.xdsoft_datetimepicker .xdsoft_next,
+.xdsoft_datetimepicker .xdsoft_today_button {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Q0NBRjI1NjM0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Q0NBRjI1NjQ0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDQ0FGMjU2MTQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDQ0FGMjU2MjQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoNEP54AAAIOSURBVHja7Jq9TsMwEMcxrZD4WpBYeKUCe+kTMCACHZh4BFfHO/AAIHZGFhYkBBsSEqxsLCAgXKhbXYOTxh9pfJVP+qutnZ5s/5Lz2Y5I03QhWji2GIcgAokWgfCxNvcOCCGKqiSqhUp0laHOne05vdEyGMfkdxJDVjgwDlEQgYQBgx+ULJaWSXXS6r/ER5FBVR8VfGftTKcITNs+a1XpcFoExREIDF14AVIFxgQUS+h520cdud6wNkC0UBw6BCO/HoCYwBhD8QCkQ/x1mwDyD4plh4D6DDV0TAGyo4HcawLIBBSLDkHeH0Mg2yVP3l4TQMZQDDsEOl/MgHQqhMNuE0D+oBh0CIr8MAKyazBH9WyBuKxDWgbXfjNf32TZ1KWm/Ap1oSk/R53UtQ5xTh3LUlMmT8gt6g51Q9p+SobxgJQ/qmsfZhWywGFSl0yBjCLJCMgXail3b7+rumdVJ2YRss4cN+r6qAHDkPWjPjdJCF4n9RmAD/V9A/Wp4NQassDjwlB6XBiCxcJQWmZZb8THFilfy/lfrTvLghq2TqTHrRMTKNJ0sIhdo15RT+RpyWwFdY96UZ/LdQKBGjcXpcc1AlSFEfLmouD+1knuxBDUVrvOBmoOC/rEcN7OQxKVeJTCiAdUzUJhA2Oez9QTkp72OTVcxDcXY8iKNkxGAJXmJCOQwOa6dhyXsOa6XwEGAKdeb5ET3rQdAAAAAElFTkSuQmCC);
+}
+
+.xdsoft_datetimepicker .xdsoft_label i {
+ opacity: 0.5;
+ background-position: -92px -19px;
+ display: inline-block;
+ width: 9px;
+ height: 20px;
+ vertical-align: middle;
+}
+
+.xdsoft_datetimepicker .xdsoft_prev {
+ float: left;
+ background-position: -20px 0;
+}
+.xdsoft_datetimepicker .xdsoft_today_button {
+ float: left;
+ background-position: -70px 0;
+ margin-left: 5px;
+}
+
+.xdsoft_datetimepicker .xdsoft_next {
+ float: right;
+ background-position: 0 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_next,
+.xdsoft_datetimepicker .xdsoft_prev ,
+.xdsoft_datetimepicker .xdsoft_today_button {
+ background-color: transparent;
+ background-repeat: no-repeat;
+ border: 0 none;
+ cursor: pointer;
+ display: block;
+ height: 30px;
+ opacity: 0.5;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
+ outline: medium none;
+ overflow: hidden;
+ padding: 0;
+ position: relative;
+ text-indent: 100%;
+ white-space: nowrap;
+ width: 20px;
+ min-width: 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev,
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_next {
+ float: none;
+ background-position: -40px -15px;
+ height: 15px;
+ width: 30px;
+ display: block;
+ margin-left: 14px;
+ margin-top: 7px;
+}
+.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_timepicker .xdsoft_prev,
+.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_timepicker .xdsoft_next {
+ float: none;
+ margin-left: 0;
+ margin-right: 14px;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev {
+ background-position: -40px 0;
+ margin-bottom: 7px;
+ margin-top: 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box {
+ height: 151px;
+ overflow: hidden;
+ border-bottom: 1px solid #ddd;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div {
+ background: #f5f5f5;
+ border-top: 1px solid #ddd;
+ color: #666;
+ font-size: 12px;
+ text-align: center;
+ border-collapse: collapse;
+ cursor: pointer;
+ border-bottom-width: 0;
+ height: 25px;
+ line-height: 25px;
+}
+
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div > div:first-child {
+ border-top-width: 0;
+}
+
+.xdsoft_datetimepicker .xdsoft_today_button:hover,
+.xdsoft_datetimepicker .xdsoft_next:hover,
+.xdsoft_datetimepicker .xdsoft_prev:hover {
+ opacity: 1;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
+}
+
+.xdsoft_datetimepicker .xdsoft_label {
+ display: inline;
+ position: relative;
+ z-index: 9999;
+ margin: 0;
+ padding: 5px 3px;
+ font-size: 14px;
+ line-height: 20px;
+ font-weight: bold;
+ background-color: #fff;
+ float: left;
+ width: 182px;
+ text-align: center;
+ cursor: pointer;
+}
+
+.xdsoft_datetimepicker .xdsoft_label:hover>span {
+ text-decoration: underline;
+}
+
+.xdsoft_datetimepicker .xdsoft_label:hover i {
+ opacity: 1.0;
+}
+
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select {
+ border: 1px solid #ccc;
+ position: absolute;
+ right: 0;
+ top: 30px;
+ z-index: 101;
+ display: none;
+ background: #fff;
+ max-height: 160px;
+ overflow-y: hidden;
+}
+
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select.xdsoft_monthselect{ right: -7px }
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select.xdsoft_yearselect{ right: 2px }
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option:hover {
+ color: #fff;
+ background: #ff8000;
+}
+
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option {
+ padding: 2px 10px 2px 5px;
+ text-decoration: none !important;
+}
+
+.xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current {
+ background: #33aaff;
+ box-shadow: #178fe5 0 1px 3px 0 inset;
+ color: #fff;
+ font-weight: 700;
+}
+
+.xdsoft_datetimepicker .xdsoft_month {
+ width: 100px;
+ text-align: right;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar {
+ clear: both;
+}
+
+.xdsoft_datetimepicker .xdsoft_year{
+ width: 48px;
+ margin-left: 5px;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar table {
+ border-collapse: collapse;
+ width: 100%;
+
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td > div {
+ padding-right: 5px;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar th {
+ height: 25px;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td,.xdsoft_datetimepicker .xdsoft_calendar th {
+ width: 14.2857142%;
+ background: #f5f5f5;
+ border: 1px solid #ddd;
+ color: #666;
+ font-size: 12px;
+ text-align: right;
+ vertical-align: middle;
+ padding: 0;
+ border-collapse: collapse;
+ cursor: pointer;
+ height: 25px;
+}
+.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar th {
+ width: 12.5%;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar th {
+ background: #f1f1f1;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_today {
+ color: #33aaff;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_default {
+ background: #ffe9d2;
+ box-shadow: #ffb871 0 1px 4px 0 inset;
+ color: #000;
+}
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_mint {
+ background: #c1ffc9;
+ box-shadow: #00dd1c 0 1px 4px 0 inset;
+ color: #000;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_default,
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current,
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_current {
+ background: #33aaff;
+ box-shadow: #178fe5 0 1px 3px 0 inset;
+ color: #fff;
+ font-weight: 700;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month,
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled,
+.xdsoft_datetimepicker .xdsoft_time_box >div >div.xdsoft_disabled {
+ opacity: 0.5;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
+ cursor: default;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month.xdsoft_disabled {
+ opacity: 0.2;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td:hover,
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div:hover {
+ color: #fff !important;
+ background: #ff8000 !important;
+ box-shadow: none !important;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current.xdsoft_disabled:hover,
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current.xdsoft_disabled:hover {
+ background: #33aaff !important;
+ box-shadow: #178fe5 0 1px 3px 0 inset !important;
+ color: #fff !important;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled:hover,
+.xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_disabled:hover {
+ color: inherit !important;
+ background: inherit !important;
+ box-shadow: inherit !important;
+}
+
+.xdsoft_datetimepicker .xdsoft_calendar th {
+ font-weight: 700;
+ text-align: center;
+ color: #999;
+ cursor: default;
+}
+
+.xdsoft_datetimepicker .xdsoft_copyright {
+ color: #ccc !important;
+ font-size: 10px;
+ clear: both;
+ float: none;
+ margin-left: 8px;
+}
+
+.xdsoft_datetimepicker .xdsoft_copyright a { color: #eee !important }
+.xdsoft_datetimepicker .xdsoft_copyright a:hover { color: #aaa !important }
+
+.xdsoft_time_box {
+ position: relative;
+ border: 1px solid #ccc;
+}
+.xdsoft_scrollbar >.xdsoft_scroller {
+ background: #ccc !important;
+ height: 20px;
+ border-radius: 3px;
+}
+.xdsoft_scrollbar {
+ position: absolute;
+ width: 7px;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ cursor: pointer;
+}
+.xdsoft_datetimepicker.xdsoft_rtl .xdsoft_scrollbar {
+ left: 0;
+ right: auto;
+}
+.xdsoft_scroller_box {
+ position: relative;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark {
+ box-shadow: 0 5px 15px -5px rgba(255, 255, 255, 0.506);
+ background: #000;
+ border-bottom: 1px solid #444;
+ border-left: 1px solid #333;
+ border-right: 1px solid #333;
+ border-top: 1px solid #333;
+ color: #ccc;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box {
+ border-bottom: 1px solid #222;
+}
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div {
+ background: #0a0a0a;
+ border-top: 1px solid #222;
+ color: #999;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label {
+ background-color: #000;
+}
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select {
+ border: 1px solid #333;
+ background: #000;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select > div > .xdsoft_option:hover {
+ color: #000;
+ background: #007fff;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current {
+ background: #cc5500;
+ box-shadow: #b03e00 0 1px 3px 0 inset;
+ color: #000;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_label i,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_prev,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_next,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_today_button {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUExQUUzOTA0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUExQUUzOTE0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQTFBRTM4RTQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQTFBRTM4RjQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp0VxGEAAAIASURBVHja7JrNSgMxEMebtgh+3MSLr1T1Xn2CHoSKB08+QmR8Bx9A8e7RixdB9CKCoNdexIugxFlJa7rNZneTbLIpM/CnNLsdMvNjM8l0mRCiQ9Ye61IKCAgZAUnH+mU3MMZaHYChBnJUDzWOFZdVfc5+ZFLbrWDeXPwbxIqrLLfaeS0hEBVGIRQCEiZoHQwtlGSByCCdYBl8g8egTTAWoKQMRBRBcZxYlhzhKegqMOageErsCHVkk3hXIFooDgHB1KkHIHVgzKB4ADJQ/A1jAFmAYhkQqA5TOBtocrKrgXwQA8gcFIuAIO8sQSA7hidvPwaQGZSaAYHOUWJABhWWw2EMIH9QagQERU4SArJXo0ZZL18uvaxejXt/Em8xjVBXmvFr1KVm/AJ10tRe2XnraNqaJvKE3KHuUbfK1E+VHB0q40/y3sdQSxY4FHWeKJCunP8UyDdqJZenT3ntVV5jIYCAh20vT7ioP8tpf6E2lfEMwERe+whV1MHjwZB7PBiCxcGQWwKZKD62lfGNnP/1poFAA60T7rF1UgcKd2id3KDeUS+oLWV8DfWAepOfq00CgQabi9zjcgJVYVD7PVzQUAUGAQkbNJTBICDhgwYTjDYD6XeW08ZKh+A4pYkzenOxXUbvZcWz7E8ykRMnIHGX1XPl+1m2vPYpL+2qdb8CDAARlKFEz/ZVkAAAAABJRU5ErkJggg==);
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
+ background: #0a0a0a;
+ border: 1px solid #222;
+ color: #999;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
+ background: #0e0e0e;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_today {
+ color: #cc5500;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_default {
+ background: #ffe9d2;
+ box-shadow: #ffb871 0 1px 4px 0 inset;
+ color:#000;
+}
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_mint {
+ background: #c1ffc9;
+ box-shadow: #00dd1c 0 1px 4px 0 inset;
+ color:#000;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_default,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_current,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_current {
+ background: #cc5500;
+ box-shadow: #b03e00 0 1px 3px 0 inset;
+ color: #000;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td:hover,
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div:hover {
+ color: #000 !important;
+ background: #007fff !important;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
+ color: #666;
+}
+
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright { color: #333 !important }
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a { color: #111 !important }
+.xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a:hover { color: #555 !important }
+
+.xdsoft_dark .xdsoft_time_box {
+ border: 1px solid #333;
+}
+
+.xdsoft_dark .xdsoft_scrollbar >.xdsoft_scroller {
+ background: #333 !important;
+}
+.xdsoft_datetimepicker .xdsoft_save_selected {
+ display: block;
+ border: 1px solid #dddddd !important;
+ margin-top: 5px;
+ width: 100%;
+ color: #454551;
+ font-size: 13px;
+}
+.xdsoft_datetimepicker .blue-gradient-button {
+ font-family: "museo-sans", "Book Antiqua", sans-serif;
+ font-size: 12px;
+ font-weight: 300;
+ color: #82878c;
+ height: 28px;
+ position: relative;
+ padding: 4px 17px 4px 33px;
+ border: 1px solid #d7d8da;
+ background: -moz-linear-gradient(top, #fff 0%, #f4f8fa 73%);
+ /* FF3.6+ */
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fff), color-stop(73%, #f4f8fa));
+ /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(top, #fff 0%, #f4f8fa 73%);
+ /* Chrome10+,Safari5.1+ */
+ background: -o-linear-gradient(top, #fff 0%, #f4f8fa 73%);
+ /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #fff 0%, #f4f8fa 73%);
+ /* IE10+ */
+ background: linear-gradient(to bottom, #fff 0%, #f4f8fa 73%);
+ /* W3C */
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff', endColorstr='#f4f8fa',GradientType=0 );
+/* IE6-9 */
+}
+.xdsoft_datetimepicker .blue-gradient-button:hover, .xdsoft_datetimepicker .blue-gradient-button:focus, .xdsoft_datetimepicker .blue-gradient-button:hover span, .xdsoft_datetimepicker .blue-gradient-button:focus span {
+ color: #454551;
+ background: -moz-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
+ /* FF3.6+ */
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f4f8fa), color-stop(73%, #FFF));
+ /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
+ /* Chrome10+,Safari5.1+ */
+ background: -o-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
+ /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
+ /* IE10+ */
+ background: linear-gradient(to bottom, #f4f8fa 0%, #FFF 73%);
+ /* W3C */
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f8fa', endColorstr='#FFF',GradientType=0 );
+ /* IE6-9 */
+}
diff --git a/template/acp/css/style.css b/template/acp/css/style.css
new file mode 100644
index 0000000..9319cc2
--- /dev/null
+++ b/template/acp/css/style.css
@@ -0,0 +1,644 @@
+@font-face {
+ font-family: 'Cuprum';
+ src: local('Cuprum'), local('Cuprum-Regular'), url('../fonts/cuprum.woff2') format('woff2'), url('../fonts/cuprum.woff') format('woff'), url('../fonts/cuprum.ttf') format('truetype');
+ font-weight: 400;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'Cuprum';
+ src: local('Cuprum Italic'), local('Cuprum-Italic'), url('../fonts/cuprumitalic.woff2') format('woff2'), url('../fonts/cuprumitalic.woff') format('woff'), url('../fonts/cuprumitalic.ttf') format('truetype');
+ font-weight: 400;
+ font-style: italic;
+}
+@font-face {
+ font-family: 'Cuprum';
+ src: local('Cuprum Bold'), local('Cuprum-Bold'), url('../fonts/cuprumbold.woff2') format('woff2'), url('../fonts/cuprumbold.woff') format('woff'), url('../fonts/cuprumbold.ttf') format('truetype');
+ font-weight: 700;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'Cuprum';
+ src: local('Cuprum Bold Italic'), local('Cuprum-BoldItalic'), url('../fonts/cuprumbolditalic.woff2') format('woff2'), url('../fonts/cuprumbolditalic.woff') format('woff'), url('../fonts/cuprumbolditalic.ttf') format('truetype');
+ font-weight: 700;
+ font-style: italic;
+}
+*
+{
+ padding: 0;
+ margin: 0;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+html
+{
+ background-color: #252b30;
+}
+body, header, main, footer
+{
+ width: 1200px;
+}
+main
+{
+ display: flex
+}
+html, body, main, .content, .sidebar, .sidebar-second, .height-max
+{
+ height: 100%;
+ font: 13px Cuprum;
+}
+body
+{
+ margin: 0 auto
+}
+main, header
+{
+ margin: 0 auto;
+}
+main
+{
+ display: table-cell;
+ background-color: #eef0f2;
+}
+header
+{
+ height: 43px;
+}
+a
+{
+ text-decoration: underline;
+ color: #222;
+}
+a:hover
+{
+ text-decoration: none;
+}
+input::-webkit-input-placeholder {color: #d1d1d1; text-overflow: ellipsis; text-indent: 0px; transition: text-indent 0.3s ease;}
+input::-moz-placeholder {color: #d1d1d1; text-overflow: ellipsis; text-indent: 0px; transition: text-indent 0.3s ease;}
+input:-moz-placeholder {color: #d1d1d1; text-overflow: ellipsis; text-indent: 0px; transition: text-indent 0.3s ease;}
+input:-ms-input-placeholder {color: #d1d1d1; text-overflow: ellipsis; text-indent: 0px; transition: text-indent 0.3s ease;}
+input:focus::-webkit-input-placeholder {color: #d1d1d1; text-overflow: ellipsis; text-indent: 1000px; transition: text-indent 0.3s ease;}
+input:focus::-moz-placeholder {color: #d1d1d1; text-overflow: ellipsis; text-indent: 1000px; transition: text-indent 0.3s ease;}
+input:focus:-moz-placeholder {color: #d1d1d1; text-overflow: ellipsis; text-indent: 1000px; transition: text-indent 0.3s ease;}
+input:focus:-ms-input-placeholder {color: #d1d1d1; text-overflow: ellipsis; text-indent: 1000px; transition: text-indent 0.3s ease;}
+textarea::-webkit-input-placeholder {color: #d1d1d1;}
+textarea::-moz-placeholder {color: #d1d1d1;}
+textarea:-moz-placeholder {color: #d1d1d1;}
+textarea:-ms-input-placeholder {color: #d1d1d1;}
+
+.sidebar, .sidebar-second, .content
+{
+ padding: 5px;
+ float: left;
+}
+.sidebar, .sidebar-second
+{
+ width: 200px;
+}
+.content
+{
+ width: 1000px;
+}
+.content-mini
+{
+ width: 800px;
+}
+.main
+{
+ margin: 5px;
+}
+.bg-gray
+{
+ background-color: #2e3439;
+ color: #8e9ca8;
+}
+.bg-gray-mild
+{
+ background-color: #3f464c;
+}
+.bg-white
+{
+ background-color: #ffffff;
+}
+.bg-green
+{
+ background-color: #00c09e;
+}
+.br-gray-top
+{
+ border-top: 1px solid #282e33;
+}
+.br-gray-bot
+{
+ border-bottom: 1px solid #282e33;
+}
+.hr-gray, .hr-white
+{
+ margin: 15px 0;
+ width: 100%;
+ height: 1px;
+}
+.hr-gray
+{
+ background-color: #282e33;
+}
+.hr-white
+{
+ background-color: #f3f4f7;
+}
+.sidebar > h3
+{
+ font-size: 15px;
+ text-align: center;
+ line-height: 33px;
+}
+.sidebar > h5, .sidebar-second > h5
+{
+ padding: 5px 10px;
+ font-size: 11px;
+}
+.sidebar > ul li, .sidebar-second > ul li
+{
+ list-style: none;
+}
+.sidebar > ul li a
+{
+ color: #8e9ca8;
+ font-size: 13px;
+ text-decoration: none;
+ padding: 7px;
+ display: block;
+}
+.sidebar > ul li a:hover
+{
+ background-color: #293035;
+ color: #c1d6e3;
+}
+.sidebar > ul li a:active, .sidebar .active
+{
+ background-color: #252b30;
+ color: #c1d6e3;
+}
+.sidebar > ul li a i
+{
+ font-size: 14px;
+ width: 22px;
+}
+.sidebar-second > h3
+{
+ color: #008067;
+ text-align: right;
+ padding: 0 10px;
+ font-size: 12px;
+ line-height: 33px;
+}
+.sidebar-second > h3 i
+{
+ font-size: 14px;
+ float: left;
+ margin-top: 8px;
+}
+.sidebar-second > ul li a
+{
+ color: #b3babf;
+ font-size: 13px;
+ text-decoration: none;
+ padding: 7px;
+ display: block;
+}
+.sidebar-second > ul li a:hover
+{
+ background-color: #fafbfc;
+ color: #78858f;
+}
+.sidebar-second > ul li a:active, .sidebar-second .active
+{
+ background-color: #f3f4f7;
+ color: #78858f;
+}
+.sidebar-second > ul li a i
+{
+ font-size: 14px;
+ width: 22px;
+}
+.sidebar-second > ul li a span
+{
+ float: right;
+ font-size: 10px;
+ background-color: #EEE;
+ padding: 2px 5px;
+ border-radius: 50%;
+}
+.sidebar-second > h5
+{
+ font-size: 14px;
+ color: #7e8890;
+}
+header > .content a
+{
+ color: #7b8893;
+ background-color: #2e3439;
+ padding: 5px 7px;
+ font-size: 14px;
+ text-decoration: none;
+ border-radius: 2px;
+ margin-left: 5px;
+}
+header > .content a:hover
+{
+ color: #abb7bf;
+ background-color: #2b3135;
+}
+header .fl-right
+{
+ float: right;
+ margin-top: 9px;
+}
+header .fl-left
+{
+ float: left;
+ margin-top: 9px;
+}
+
+.inputs input, .inputs select, .inputs textarea
+{
+ padding: 5px;
+ font-size: 12px;
+ color: #000;
+ border: 1px solid #E8EBF0;
+ box-sizing: border-box;
+ outline: 0;
+}
+.inputs textarea
+{
+ resize: vertical;
+ width: 100%;
+}
+
+.inputs-max input[type="text"], .inputs-max select
+{
+ width: 100%;
+}
+
+label
+{
+ cursor: pointer;
+ display: inline-block;
+ padding: 5px 10px;
+ border-radius: 10px;
+ border: 1px solid #EEE;
+ background-color: #FAFAFA;
+ margin-right: 5px;
+}
+
+label input
+{
+ cursor: pointer;
+ float: left;
+ margin: 1px 5px;
+}
+
+table
+{
+ width: 100%;
+ background-color: #ffffff;
+ box-shadow: 1px 1px 10px #e0e0e0;
+ border-collapse: collapse;
+}
+
+table th
+{
+ background-color: #3f464c;
+ color: #abb7bf;
+ text-align: left;
+}
+table th span
+{
+ cursor: pointer;
+}
+table td
+{
+ color: #000;
+}
+table th, table td
+{
+ padding: 8px;
+}
+
+table tr:nth-child(even) td
+{
+ background-color: #f7f8f8;
+}
+
+table a
+{
+ color: #222;
+}
+
+.trh tr:hover td
+{
+ background-color: #f9fae5;
+}
+
+.search
+{
+ width: 300px;
+ float: right;
+ margin-bottom: 10px;
+}
+.search input
+{
+ padding: 5px;
+ width: 300px;
+ font-size: 12px;
+ color: #000;
+ background-color: #fff;
+ border: 1px solid #dedede;
+ outline: 0;
+ border-radius: 10px;
+}
+.search button
+{
+ text-align: center;
+ border-radius: 10px;
+ border: 1px solid #ececec;
+ background-color: #ffffff;
+ color: #000000;
+ font-size: 13px;
+ outline: 0;
+ cursor: pointer;
+ position: absolute;
+ width: 24px;
+ height: 24px;
+ margin: -25px 0 0 274px;
+ overflow: hidden;
+ line-height: 24px;
+ text-overflow: ellipsis;
+ transition: all 0.23s ease-in-out;
+ -moz-transition: all 0.23s ease-in-out;
+ -webkit-transition: all 0.23s ease-in-out;
+ -o-transition: all 0.23s ease-in-out;
+}
+.search button:hover
+{
+ width: 70px;
+ margin-left: 229px;
+}
+
+
+
+.w5p
+{
+ width: 5% !important;
+}
+.w10p
+{
+ width: 10% !important;
+}
+.w15p
+{
+ width: 15% !important;
+}
+.w20p
+{
+ width: 20% !important;
+}
+.w25p
+{
+ width: 25% !important;
+}
+.w35p
+{
+ width: 35% !important;
+}
+.w50p
+{
+ width: 50% !important;
+}
+
+.left
+{
+ float: left;
+}
+.right
+{
+ float: right;
+}
+.text-left
+{
+ text-align: left;
+}
+.text-center
+{
+ text-align: center;
+}
+.text-right
+{
+ text-align: right;
+}
+
+.text-red
+{
+ color: #bd2727;
+}
+.text-green
+{
+ color: #27bd32;
+}
+
+.vtop
+{
+ vertical-align: top;
+}
+
+.space
+{
+ clear: both;
+ margin-top: 15px;
+}
+
+.informer
+{
+ margin-bottom: 10px;
+ border-left: 5px solid #29a3f2;
+ padding: 5px;
+ font-size: 14px;
+ background-color: #ffffff;
+ color: #000;
+ text-transform: uppercase;
+}
+
+.informer.green
+{
+ border-color: #28b727;
+}
+
+.informer.red
+{
+ border-color: #f22929;
+}
+
+.pages
+{
+ display: inline-block;
+ width: 100%;
+ text-align: right;
+}
+.pages > div
+{
+ margin-top: 10px;
+}
+.pages > i
+{
+ margin: 0 2px;
+}
+.pages a
+{
+ display: inline-block;
+ background-color: #3f464c;
+ color: #8e9ca8;
+ font-size: 13px;
+ font-family: nav;
+ padding: 3px 8px;
+ position: relative;
+ text-decoration: none;
+ text-transform: capitalize;
+}
+.pages a:hover, .pages a.active
+{
+ background-color: #252b30;
+ color: #c1d6e3;
+}
+
+#search_error
+{
+ display: none;
+ width: 100%;
+ background-color: #dadde0;
+ padding: 10px;
+ color: #777;
+}
+
+#loadinginfo
+{
+ display: none;
+/* background-color: #fff; */
+ height: 15px;
+ width: 100%;
+ left: 90%;
+ top: 0px;
+ position: fixed;
+}
+
+.loader
+{
+ z-index: 99;
+ position: fixed;
+ top: 10px;
+ width: 100%;
+}
+.loader > span
+{
+ display: inline-block;
+ background-color: #dbdbdb;
+ width: 5px;
+ height: 5px;
+ border-radius: 5px;
+ -o-border-radius: 5px;
+ -ms-border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ margin: 0px;
+ position: fixed;
+ top: 5px;
+ left: 90%;
+ transition: 2.8s all cubic-bezier(0.030, 0.615, 0.995, 0.415);
+ -o-transition: 2.8s all cubic-bezier(0.030, 0.615, 0.995, 0.415);
+ -ms-transition: 2.8s all cubic-bezier(0.030, 0.615, 0.995, 0.415);
+ -moz-transition: 2.8s all cubic-bezier(0.030, 0.615, 0.995, 0.415);
+ -webkit-transition: 2.8s all cubic-bezier(0.030, 0.615, 0.995, 0.415);
+ z-index: 101;
+}
+.loader > span.jmp
+{
+ transition: none !important;
+ -o-transition: none !important;
+ -ms-transition: none !important;
+ -moz-transition: none !important;
+ -webkit-transition: none !important;
+}
+.loader span.l-1 { background-color: #e74c3c;}
+.loader span.l-2 { background-color: #e67e22;}
+.loader span.l-3 { background-color: #f1c40f;}
+.loader span.l-4 { background-color: #2ecc71;}
+.loader span.l-5 { background-color: #3498db;}
+.loader span.l-6 { background-color: #9b59b6;}
+
+.p_info p
+{
+ padding: 5px;
+ border-bottom: 1px dotted #EEE;
+}
+
+.pointer
+{
+ cursor: pointer;
+}
+.none
+{
+ display: none;
+}
+.btn
+{
+ font-size: 12px;
+ padding: 5px;
+ background-color: #2e3439;
+ border: 1px solid #3f464c;
+ color: #7b8893;
+ cursor: pointer;
+ outline: 0;
+}
+
+.btn:hover
+{
+ color: #abb7bf;
+ background-color: #2b3135;
+}
+
+.btn-green
+{
+ background-color: #41d192;
+ border: 1px solid #55ac5e;
+ color: #ffffff;
+}
+.btn-green:hover
+{
+ background-color: #37da92;
+ border: 1px solid #55ac5e;
+ color: #ffffff;
+}
+
+.btn-red
+{
+ background-color: #d14141;
+ border: 1px solid #ac5555;
+ color: #ffffff;
+}
+.btn-red:hover
+{
+ background-color: #da1010;
+ border: 1px solid #ac5555;
+ color: #ffffff;
+}
+
+.double-color td
+{
+ background-color: #ffffff !important;
+}
+
+.double-color tr:nth-child(2n) td
+{
+ border-bottom: 2px solid #3f464c;
+}
\ No newline at end of file
diff --git a/template/acp/favicon.ico b/template/acp/favicon.ico
new file mode 100644
index 0000000..500901c
Binary files /dev/null and b/template/acp/favicon.ico differ
diff --git a/template/acp/fonts/FontAwesome.otf b/template/acp/fonts/FontAwesome.otf
new file mode 100644
index 0000000..401ec0f
Binary files /dev/null and b/template/acp/fonts/FontAwesome.otf differ
diff --git a/template/acp/fonts/cuprum.ttf b/template/acp/fonts/cuprum.ttf
new file mode 100644
index 0000000..36580b6
Binary files /dev/null and b/template/acp/fonts/cuprum.ttf differ
diff --git a/template/acp/fonts/cuprum.woff b/template/acp/fonts/cuprum.woff
new file mode 100644
index 0000000..3c47d52
Binary files /dev/null and b/template/acp/fonts/cuprum.woff differ
diff --git a/template/acp/fonts/cuprum.woff2 b/template/acp/fonts/cuprum.woff2
new file mode 100644
index 0000000..b9dd7fe
Binary files /dev/null and b/template/acp/fonts/cuprum.woff2 differ
diff --git a/template/acp/fonts/cuprumbold.ttf b/template/acp/fonts/cuprumbold.ttf
new file mode 100644
index 0000000..68dbf3c
Binary files /dev/null and b/template/acp/fonts/cuprumbold.ttf differ
diff --git a/template/acp/fonts/cuprumbold.woff b/template/acp/fonts/cuprumbold.woff
new file mode 100644
index 0000000..8f69fab
Binary files /dev/null and b/template/acp/fonts/cuprumbold.woff differ
diff --git a/template/acp/fonts/cuprumbold.woff2 b/template/acp/fonts/cuprumbold.woff2
new file mode 100644
index 0000000..21f5c56
Binary files /dev/null and b/template/acp/fonts/cuprumbold.woff2 differ
diff --git a/template/acp/fonts/cuprumbolditalic.ttf b/template/acp/fonts/cuprumbolditalic.ttf
new file mode 100644
index 0000000..f8027a2
Binary files /dev/null and b/template/acp/fonts/cuprumbolditalic.ttf differ
diff --git a/template/acp/fonts/cuprumbolditalic.woff b/template/acp/fonts/cuprumbolditalic.woff
new file mode 100644
index 0000000..09a4bb0
Binary files /dev/null and b/template/acp/fonts/cuprumbolditalic.woff differ
diff --git a/template/acp/fonts/cuprumbolditalic.woff2 b/template/acp/fonts/cuprumbolditalic.woff2
new file mode 100644
index 0000000..8c8a25d
Binary files /dev/null and b/template/acp/fonts/cuprumbolditalic.woff2 differ
diff --git a/template/acp/fonts/cuprumitalic.ttf b/template/acp/fonts/cuprumitalic.ttf
new file mode 100644
index 0000000..e72f033
Binary files /dev/null and b/template/acp/fonts/cuprumitalic.ttf differ
diff --git a/template/acp/fonts/cuprumitalic.woff b/template/acp/fonts/cuprumitalic.woff
new file mode 100644
index 0000000..f3c0677
Binary files /dev/null and b/template/acp/fonts/cuprumitalic.woff differ
diff --git a/template/acp/fonts/cuprumitalic.woff2 b/template/acp/fonts/cuprumitalic.woff2
new file mode 100644
index 0000000..56a1a8c
Binary files /dev/null and b/template/acp/fonts/cuprumitalic.woff2 differ
diff --git a/template/acp/fonts/fontawesome-webfont.eot b/template/acp/fonts/fontawesome-webfont.eot
new file mode 100644
index 0000000..e9f60ca
Binary files /dev/null and b/template/acp/fonts/fontawesome-webfont.eot differ
diff --git a/template/acp/fonts/fontawesome-webfont.svg b/template/acp/fonts/fontawesome-webfont.svg
new file mode 100644
index 0000000..855c845
--- /dev/null
+++ b/template/acp/fonts/fontawesome-webfont.svg
@@ -0,0 +1,2671 @@
+
+
+
+
+Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016
+ By ,,,
+Copyright Dave Gandy 2016. All rights reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/acp/fonts/fontawesome-webfont.ttf b/template/acp/fonts/fontawesome-webfont.ttf
new file mode 100644
index 0000000..35acda2
Binary files /dev/null and b/template/acp/fonts/fontawesome-webfont.ttf differ
diff --git a/template/acp/fonts/fontawesome-webfont.woff b/template/acp/fonts/fontawesome-webfont.woff
new file mode 100644
index 0000000..400014a
Binary files /dev/null and b/template/acp/fonts/fontawesome-webfont.woff differ
diff --git a/template/acp/fonts/fontawesome-webfont.woff2 b/template/acp/fonts/fontawesome-webfont.woff2
new file mode 100644
index 0000000..4d13fc6
Binary files /dev/null and b/template/acp/fonts/fontawesome-webfont.woff2 differ
diff --git a/template/acp/images/.htaccess b/template/acp/images/.htaccess
new file mode 100644
index 0000000..e69de29
diff --git a/template/acp/js/addons.js b/template/acp/js/addons.js
new file mode 100644
index 0000000..4a0921a
--- /dev/null
+++ b/template/acp/js/addons.js
@@ -0,0 +1,329 @@
+$('#addons').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+
+ loading(0)
+ }
+});
+
+function maps_reset_game()
+{
+ $('#game').prop('selectedIndex', 0);
+}
+
+function maps_list()
+{
+ $.get(home+'addons/section/updmp/get/list/unit/'+$('#unit').val()+'/game/'+$('#game').val(),
+ function(data)
+ {
+ $('#maps').val(data);
+ });
+
+ return false;
+}
+
+function maps_update()
+{
+ loading(1);
+
+ $.get(home+'addons/section/updmp/unit/'+$('#unit').val()+'/game/'+$('#game').val()+'/go',
+ function(data)
+ {
+ location.reload();
+ });
+
+ return false;
+}
+
+function plugins_category()
+{
+ $.get(home+'addons/section/addpl/get/cat/game/'+$('#game').val(),
+ function(data)
+ {
+ $('#category').html('Выберете категорию '+data);
+ });
+
+ return false;
+}
+
+function plugins_update_del(id)
+{
+ bootbox.dialog('Внимание Вы уверены, что хотите удалить обновление плагина?',
+ [{
+ "label" : "Удалить",
+ callback : function(){plugins_update_del_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function plugins_update_del_go(id)
+{
+ $.get(home+'addons/section/delete/type/update/id/'+id,
+ function(data)
+ {
+ location.reload();
+ });
+}
+
+function cats_delete(id)
+{
+ $.get(home+'addons/section/delete/type/cat/id/'+id,
+ function(data)
+ {
+ location.reload();
+ });
+
+ return false;
+}
+
+function plugins_sort(sort)
+{
+ switch(sort)
+ {
+ case 'id':
+ if(sort_id == 'asc')
+ sort_id = 'desc';
+ else
+ sort_id = 'asc';
+
+ sorting = sort_id;
+
+ break;
+
+ case 'cat':
+ sort_cat = sort_cat == 'asc' ? 'desc' : 'asc';
+ sorting = sort_cat;
+
+ break;
+
+ case 'game':
+ sort_game = sort_game == 'asc' ? 'desc' : 'asc';
+ sorting = sort_game;
+
+ }
+
+ location.href=home+'addons/sort/'+sort+'/sorting/'+sorting;
+}
+
+function plugins_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'addons/subsection/search'+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function plugins_delete(id)
+{
+ bootbox.dialog('Внимание Вы уверены, что хотите удалить плагин?',
+ [{
+ "label" : "Удалить",
+ callback : function(){plugins_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function plugins_delete_go(id)
+{
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'addons/section/delete/type/plugin/id/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function add_plugin_type()
+{
+ if($("#update").val() == 0)
+ {
+ $('#new_plugin').css('display', 'table-row');
+ $('#upd_plugin').css('display', 'none');
+ }else{
+ $('#new_plugin').css('display', 'none');
+ $('#upd_plugin').css('display', 'table-row');
+ }
+}
+
+function config_files_form()
+{
+ if(!$("#cfa").prop('checked'))
+ $('#config_files_form').css('display', 'table');
+ else{
+ $('#config_files_form').css('display', 'none');
+ $('#config_files_all').html('');
+ }
+}
+
+var cf = 999999;
+var cc = 999999;
+var cw = 999999;
+var cwe = 999999;
+var fd = 999999;
+
+function config_files_add()
+{
+ cf += 1;
+
+ $('#config_files_all').append(''
+ +' '
+ +' '
+ +'Удалить '
+ +' ');
+}
+
+function config_clear_add()
+{
+ cc += 1;
+
+ $('#config_clear_all').append(''
+ +' '
+ +' '
+ +' '
+ +'Удалить '
+ +' ');
+}
+
+function config_write_add()
+{
+ cw += 1;
+
+ $('#config_write_all').append(''
+ +' '
+ +' '
+ +' '
+ +'Удалить '
+ +' ');
+}
+
+function config_write_del_add()
+{
+ cwe += 1;
+
+ $('#config_write_del_all').append(''
+ +' '
+ +' '
+ +' '
+ +'Удалить '
+ +' ');
+}
+
+function files_delete_add()
+{
+ fd += 1;
+
+ $('#files_delete_all').append(''
+ +' '
+ +'Удалить '
+ +' ');
+}
+
+function config_files_del(id)
+{
+ $('#cf_'+id).empty();
+
+ return false;
+}
+
+function config_clear_del(id)
+{
+ $('#cc_'+id).empty();
+
+ return false;
+}
+
+function config_write_del(id)
+{
+ $('#cw_'+id).empty();
+
+ return false;
+}
+
+function config_write_del_del(id)
+{
+ $('#cwe_'+id).empty();
+
+ return false;
+}
+
+function files_delete_del(id)
+{
+ $('#fd_'+id).empty();
+
+ return false;
+}
\ No newline at end of file
diff --git a/template/acp/js/bootbox.js b/template/acp/js/bootbox.js
new file mode 100644
index 0000000..e78f8d7
--- /dev/null
+++ b/template/acp/js/bootbox.js
@@ -0,0 +1,6 @@
+/**
+ * bootbox.js v3.3.0
+ *
+ * http://bootboxjs.com/license.txt
+ */
+var bootbox=window.bootbox||function(a,b){function c(a,b){return"undefined"==typeof b&&(b=d),"string"==typeof m[b][a]?m[b][a]:b!=e?c(a,e):a}var d="en",e="en",f=!0,g="static",h="javascript:;",i="",j={},k={},l={};l.setLocale=function(a){for(var b in m)if(b==a)return d=a,void 0;throw new Error("Invalid locale: "+a)},l.addLocale=function(a,b){"undefined"==typeof m[a]&&(m[a]={});for(var c in b)m[a][c]=b[c]},l.setIcons=function(a){k=a,("object"!=typeof k||null===k)&&(k={})},l.setBtnClasses=function(a){j=a,("object"!=typeof j||null===j)&&(j={})},l.alert=function(){var a="",b=c("OK"),d=null;switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"function"==typeof arguments[1]?d=arguments[1]:b=arguments[1];break;case 3:a=arguments[0],b=arguments[1],d=arguments[2];break;default:throw new Error("Incorrect number of arguments: expected 1-3")}return l.dialog(a,{label:b,icon:k.OK,"class":j.OK,callback:d},{onEscape:d||!0})},l.confirm=function(){var a="",b=c("CANCEL"),d=c("CONFIRM"),e=null;switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"function"==typeof arguments[1]?e=arguments[1]:b=arguments[1];break;case 3:a=arguments[0],b=arguments[1],"function"==typeof arguments[2]?e=arguments[2]:d=arguments[2];break;case 4:a=arguments[0],b=arguments[1],d=arguments[2],e=arguments[3];break;default:throw new Error("Incorrect number of arguments: expected 1-4")}var f=function(){return"function"==typeof e?e(!1):void 0},g=function(){return"function"==typeof e?e(!0):void 0};return l.dialog(a,[{label:b,icon:k.CANCEL,"class":j.CANCEL,callback:f},{label:d,icon:k.CONFIRM,"class":j.CONFIRM,callback:g}],{onEscape:f})},l.prompt=function(){var a="",d=c("CANCEL"),e=c("CONFIRM"),f=null,g="";switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"function"==typeof arguments[1]?f=arguments[1]:d=arguments[1];break;case 3:a=arguments[0],d=arguments[1],"function"==typeof arguments[2]?f=arguments[2]:e=arguments[2];break;case 4:a=arguments[0],d=arguments[1],e=arguments[2],f=arguments[3];break;case 5:a=arguments[0],d=arguments[1],e=arguments[2],f=arguments[3],g=arguments[4];break;default:throw new Error("Incorrect number of arguments: expected 1-5")}var h=a,i=b("");i.append(" ");var m=function(){return"function"==typeof f?f(null):void 0},n=function(){return"function"==typeof f?f(i.find("input[type=text]").val()):void 0},o=l.dialog(i,[{label:d,icon:k.CANCEL,"class":j.CANCEL,callback:m},{label:e,icon:k.CONFIRM,"class":j.CONFIRM,callback:n}],{header:h,show:!1,onEscape:m});return o.on("shown",function(){i.find("input[type=text]").focus(),i.on("submit",function(a){a.preventDefault(),o.find(".btn-primary").click()})}),o.modal("show"),o},l.dialog=function(c,d,e){function j(){var a=null;"function"==typeof e.onEscape&&(a=e.onEscape()),a!==!1&&x.modal("hide")}var k="",l=[];e||(e={}),"undefined"==typeof d?d=[]:"undefined"==typeof d.length&&(d=[d]);for(var m=d.length;m--;){var n=null,o=null,p=null,q="",r=null;if("undefined"==typeof d[m].label&&"undefined"==typeof d[m]["class"]&&"undefined"==typeof d[m].callback){var s=0,t=null;for(var u in d[m])if(t=u,++s>1)break;1==s&&"function"==typeof d[m][u]&&(d[m].label=t,d[m].callback=d[m][u])}"function"==typeof d[m].callback&&(r=d[m].callback),d[m]["class"]?p=d[m]["class"]:m==d.length-1&&d.length<=2&&(p="btn-primary"),d[m].link!==!0&&(p="btn "+p),n=d[m].label?d[m].label:"Option "+(m+1),d[m].icon&&(q=" "),o=d[m].href?d[m].href:h,k=""+q+n+" "+k,l[m]=r}var v=[""];if(e.header){var w="";("undefined"==typeof e.headerCloseButton||e.headerCloseButton)&&(w="
× "),v.push("")}v.push("
"),k&&v.push(""),v.push("
");var x=b(v.join("\n")),y="undefined"==typeof e.animate?f:e.animate;y&&x.addClass("fade");var z="undefined"==typeof e.classes?i:e.classes;return z&&x.addClass(z),x.find(".modal-body").html(c),x.on("keyup.dismiss.modal",function(a){27===a.which&&e.onEscape&&j("escape")}),x.on("click","a.close",function(a){a.preventDefault(),j("close")}),x.on("shown",function(){x.find("a.btn-primary:first").focus()}),x.on("hidden",function(a){a.target===this&&x.remove()}),x.on("click",".modal-footer a",function(a){var c=b(this).data("handler"),e=l[c],f=null;("undefined"==typeof c||"undefined"==typeof d[c].href)&&(a.preventDefault(),"function"==typeof e&&(f=e(a)),f!==!1&&x.modal("hide"))}),b("body").append(x),x.modal({backdrop:"undefined"==typeof e.backdrop?g:e.backdrop,keyboard:!1,show:!1}),x.on("show",function(){b(a).off("focusin.modal")}),("undefined"==typeof e.show||e.show===!0)&&x.modal("show"),x},l.modal=function(){var a,c,d,e={onEscape:null,keyboard:!0,backdrop:g};switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"object"==typeof arguments[1]?d=arguments[1]:c=arguments[1];break;case 3:a=arguments[0],c=arguments[1],d=arguments[2];break;default:throw new Error("Incorrect number of arguments: expected 1-3")}return e.header=c,d="object"==typeof d?b.extend(e,d):e,l.dialog(a,[],d)},l.hideAll=function(){b(".bootbox").modal("hide")},l.animate=function(a){f=a},l.backdrop=function(a){g=a},l.classes=function(a){i=a};var m={br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},zh_CN:{OK:"OK",CANCEL:"å–消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"å–消",CONFIRM:"確èª"}};return l}(document,window.jQuery);window.bootbox=bootbox;
\ No newline at end of file
diff --git a/template/acp/js/bootstrap.js b/template/acp/js/bootstrap.js
new file mode 100644
index 0000000..f9cbdae
--- /dev/null
+++ b/template/acp/js/bootstrap.js
@@ -0,0 +1,6 @@
+/*!
+* Bootstrap.js by @fat & @mdo
+* Copyright 2012 Twitter, Inc.
+* http://www.apache.org/licenses/LICENSE-2.0.txt
+*/
+!function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(".dropdown-backdrop").remove(),e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||("ontouchstart"in document.documentElement&&e('
').insertBefore(e(this)).on("click",r),s.toggleClass("open")),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f ').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:''}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:' ',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);
\ No newline at end of file
diff --git a/template/acp/js/control.js b/template/acp/js/control.js
new file mode 100644
index 0000000..b273644
--- /dev/null
+++ b/template/acp/js/control.js
@@ -0,0 +1,196 @@
+$('#control').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+
+ loading(0)
+ }
+});
+
+function control_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'control/subsection/search'+url_search+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+
+ if(i == 'url')
+ url_search = val;
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function control_overdue(id, time)
+{
+ bootbox.dialog('Установка даты:
',
+ [{
+ "label" : "Установить",
+ callback : function(){control_overdue_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function control_block(id, time)
+{
+ bootbox.dialog('Установка даты:
',
+ [{
+ "label" : "Заблокировать",
+ callback : function(){control_block_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function datepick(input, time)
+{
+ if($('#'+input).val() != '')
+ time = $('#'+input).val();
+
+ $('#'+input).datetimepicker({value: time, format: 'd/m/Y H:i'});
+}
+
+function control_overdue_go(id)
+{
+ $.ajax({
+ type: 'POST',
+ url: home+'control/type/overdue/id/'+id+'/go',
+ data: 'time='+$('#date_overdue').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+ }
+ });
+}
+
+function control_block_go(id)
+{
+ $.ajax({
+ type: 'POST',
+ url: home+'control/type/block/id/'+id+'/go',
+ data: 'time='+$('#date_block').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+ }
+ });
+}
+
+function control_delete(id)
+{
+ bootbox.dialog('Внимание Вы уверены, что хотите удалить подключенный сервер?',
+ [{
+ "label" : "Удалить",
+ callback : function(){control_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function control_delete_go(id)
+{
+
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'control/section/delete/id/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/date/jquery.datetimepicker.full.js b/template/acp/js/date/jquery.datetimepicker.full.js
new file mode 100644
index 0000000..ebed876
--- /dev/null
+++ b/template/acp/js/date/jquery.datetimepicker.full.js
@@ -0,0 +1,3192 @@
+/*!
+ * @copyright Copyright © Kartik Visweswaran, Krajee.com, 2014 - 2015
+ * @version 1.3.3
+ *
+ * Date formatter utility library that allows formatting date/time variables or Date objects using PHP DateTime format.
+ * @see http://php.net/manual/en/function.date.php
+ *
+ * For more JQuery plugins visit http://plugins.krajee.com
+ * For more Yii related demos visit http://demos.krajee.com
+ */
+var DateFormatter;
+(function () {
+ "use strict";
+
+ var _compare, _lpad, _extend, defaultSettings, DAY, HOUR;
+ DAY = 1000 * 60 * 60 * 24;
+ HOUR = 3600;
+
+ _compare = function (str1, str2) {
+ return typeof(str1) === 'string' && typeof(str2) === 'string' && str1.toLowerCase() === str2.toLowerCase();
+ };
+ _lpad = function (value, length, char) {
+ var chr = char || '0', val = value.toString();
+ return val.length < length ? _lpad(chr + val, length) : val;
+ };
+ _extend = function (out) {
+ var i, obj;
+ out = out || {};
+ for (i = 1; i < arguments.length; i++) {
+ obj = arguments[i];
+ if (!obj) {
+ continue;
+ }
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ if (typeof obj[key] === 'object') {
+ _extend(out[key], obj[key]);
+ } else {
+ out[key] = obj[key];
+ }
+ }
+ }
+ }
+ return out;
+ };
+ defaultSettings = {
+ dateSettings: {
+ days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
+ daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+ months: [
+ 'January', 'February', 'March', 'April', 'May', 'June', 'July',
+ 'August', 'September', 'October', 'November', 'December'
+ ],
+ monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+ meridiem: ['AM', 'PM'],
+ ordinal: function (number) {
+ var n = number % 10, suffixes = {1: 'st', 2: 'nd', 3: 'rd'};
+ return Math.floor(number % 100 / 10) === 1 || !suffixes[n] ? 'th' : suffixes[n];
+ }
+ },
+ separators: /[ \-+\/\.T:@]/g,
+ validParts: /[dDjlNSwzWFmMntLoYyaABgGhHisueTIOPZcrU]/g,
+ intParts: /[djwNzmnyYhHgGis]/g,
+ tzParts: /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
+ tzClip: /[^-+\dA-Z]/g
+ };
+
+ DateFormatter = function (options) {
+ var self = this, config = _extend(defaultSettings, options);
+ self.dateSettings = config.dateSettings;
+ self.separators = config.separators;
+ self.validParts = config.validParts;
+ self.intParts = config.intParts;
+ self.tzParts = config.tzParts;
+ self.tzClip = config.tzClip;
+ };
+
+ DateFormatter.prototype = {
+ constructor: DateFormatter,
+ parseDate: function (vDate, vFormat) {
+ var self = this, vFormatParts, vDateParts, i, vDateFlag = false, vTimeFlag = false, vDatePart, iDatePart,
+ vSettings = self.dateSettings, vMonth, vMeriIndex, vMeriOffset, len, mer,
+ out = {date: null, year: null, month: null, day: null, hour: 0, min: 0, sec: 0};
+ if (!vDate) {
+ return undefined;
+ }
+ if (vDate instanceof Date) {
+ return vDate;
+ }
+ if (typeof vDate === 'number') {
+ return new Date(vDate);
+ }
+ if (vFormat === 'U') {
+ i = parseInt(vDate);
+ return i ? new Date(i * 1000) : vDate;
+ }
+ if (typeof vDate !== 'string') {
+ return '';
+ }
+ vFormatParts = vFormat.match(self.validParts);
+ if (!vFormatParts || vFormatParts.length === 0) {
+ throw new Error("Invalid date format definition.");
+ }
+ vDateParts = vDate.replace(self.separators, '\0').split('\0');
+ for (i = 0; i < vDateParts.length; i++) {
+ vDatePart = vDateParts[i];
+ iDatePart = parseInt(vDatePart);
+ switch (vFormatParts[i]) {
+ case 'y':
+ case 'Y':
+ len = vDatePart.length;
+ if (len === 2) {
+ out.year = parseInt((iDatePart < 70 ? '20' : '19') + vDatePart);
+ } else if (len === 4) {
+ out.year = iDatePart;
+ }
+ vDateFlag = true;
+ break;
+ case 'm':
+ case 'n':
+ case 'M':
+ case 'F':
+ if (isNaN(vDatePart)) {
+ vMonth = vSettings.monthsShort.indexOf(vDatePart);
+ if (vMonth > -1) {
+ out.month = vMonth + 1;
+ }
+ vMonth = vSettings.months.indexOf(vDatePart);
+ if (vMonth > -1) {
+ out.month = vMonth + 1;
+ }
+ } else {
+ if (iDatePart >= 1 && iDatePart <= 12) {
+ out.month = iDatePart;
+ }
+ }
+ vDateFlag = true;
+ break;
+ case 'd':
+ case 'j':
+ if (iDatePart >= 1 && iDatePart <= 31) {
+ out.day = iDatePart;
+ }
+ vDateFlag = true;
+ break;
+ case 'g':
+ case 'h':
+ vMeriIndex = (vFormatParts.indexOf('a') > -1) ? vFormatParts.indexOf('a') :
+ (vFormatParts.indexOf('A') > -1) ? vFormatParts.indexOf('A') : -1;
+ mer = vDateParts[vMeriIndex];
+ if (vMeriIndex > -1) {
+ vMeriOffset = _compare(mer, vSettings.meridiem[0]) ? 0 :
+ (_compare(mer, vSettings.meridiem[1]) ? 12 : -1);
+ if (iDatePart >= 1 && iDatePart <= 12 && vMeriOffset > -1) {
+ out.hour = iDatePart + vMeriOffset;
+ } else if (iDatePart >= 0 && iDatePart <= 23) {
+ out.hour = iDatePart;
+ }
+ } else if (iDatePart >= 0 && iDatePart <= 23) {
+ out.hour = iDatePart;
+ }
+ vTimeFlag = true;
+ break;
+ case 'G':
+ case 'H':
+ if (iDatePart >= 0 && iDatePart <= 23) {
+ out.hour = iDatePart;
+ }
+ vTimeFlag = true;
+ break;
+ case 'i':
+ if (iDatePart >= 0 && iDatePart <= 59) {
+ out.min = iDatePart;
+ }
+ vTimeFlag = true;
+ break;
+ case 's':
+ if (iDatePart >= 0 && iDatePart <= 59) {
+ out.sec = iDatePart;
+ }
+ vTimeFlag = true;
+ break;
+ }
+ }
+ if (vDateFlag === true && out.year && out.month && out.day) {
+ out.date = new Date(out.year, out.month - 1, out.day, out.hour, out.min, out.sec, 0);
+ } else {
+ if (vTimeFlag !== true) {
+ return false;
+ }
+ out.date = new Date(0, 0, 0, out.hour, out.min, out.sec, 0);
+ }
+ return out.date;
+ },
+ guessDate: function (vDateStr, vFormat) {
+ if (typeof vDateStr !== 'string') {
+ return vDateStr;
+ }
+ var self = this, vParts = vDateStr.replace(self.separators, '\0').split('\0'), vPattern = /^[djmn]/g,
+ vFormatParts = vFormat.match(self.validParts), vDate = new Date(), vDigit = 0, vYear, i, iPart, iSec;
+
+ if (!vPattern.test(vFormatParts[0])) {
+ return vDateStr;
+ }
+
+ for (i = 0; i < vParts.length; i++) {
+ vDigit = 2;
+ iPart = vParts[i];
+ iSec = parseInt(iPart.substr(0, 2));
+ switch (i) {
+ case 0:
+ if (vFormatParts[0] === 'm' || vFormatParts[0] === 'n') {
+ vDate.setMonth(iSec - 1);
+ } else {
+ vDate.setDate(iSec);
+ }
+ break;
+ case 1:
+ if (vFormatParts[0] === 'm' || vFormatParts[0] === 'n') {
+ vDate.setDate(iSec);
+ } else {
+ vDate.setMonth(iSec - 1);
+ }
+ break;
+ case 2:
+ vYear = vDate.getFullYear();
+ if (iPart.length < 4) {
+ vDate.setFullYear(parseInt(vYear.toString().substr(0, 4 - iPart.length) + iPart));
+ vDigit = iPart.length;
+ } else {
+ vDate.setFullYear = parseInt(iPart.substr(0, 4));
+ vDigit = 4;
+ }
+ break;
+ case 3:
+ vDate.setHours(iSec);
+ break;
+ case 4:
+ vDate.setMinutes(iSec);
+ break;
+ case 5:
+ vDate.setSeconds(iSec);
+ break;
+ }
+ if (iPart.substr(vDigit).length > 0) {
+ vParts.splice(i + 1, 0, iPart.substr(vDigit));
+ }
+ }
+ return vDate;
+ },
+ parseFormat: function (vChar, vDate) {
+ var self = this, vSettings = self.dateSettings, fmt, backspace = /\\?(.?)/gi, doFormat = function (t, s) {
+ return fmt[t] ? fmt[t]() : s;
+ };
+ fmt = {
+ /////////
+ // DAY //
+ /////////
+ /**
+ * Day of month with leading 0: `01..31`
+ * @return {string}
+ */
+ d: function () {
+ return _lpad(fmt.j(), 2);
+ },
+ /**
+ * Shorthand day name: `Mon...Sun`
+ * @return {string}
+ */
+ D: function () {
+ return vSettings.daysShort[fmt.w()];
+ },
+ /**
+ * Day of month: `1..31`
+ * @return {number}
+ */
+ j: function () {
+ return vDate.getDate();
+ },
+ /**
+ * Full day name: `Monday...Sunday`
+ * @return {number}
+ */
+ l: function () {
+ return vSettings.days[fmt.w()];
+ },
+ /**
+ * ISO-8601 day of week: `1[Mon]..7[Sun]`
+ * @return {number}
+ */
+ N: function () {
+ return fmt.w() || 7;
+ },
+ /**
+ * Day of week: `0[Sun]..6[Sat]`
+ * @return {number}
+ */
+ w: function () {
+ return vDate.getDay();
+ },
+ /**
+ * Day of year: `0..365`
+ * @return {number}
+ */
+ z: function () {
+ var a = new Date(fmt.Y(), fmt.n() - 1, fmt.j()), b = new Date(fmt.Y(), 0, 1);
+ return Math.round((a - b) / DAY);
+ },
+
+ //////////
+ // WEEK //
+ //////////
+ /**
+ * ISO-8601 week number
+ * @return {number}
+ */
+ W: function () {
+ var a = new Date(fmt.Y(), fmt.n() - 1, fmt.j() - fmt.N() + 3), b = new Date(a.getFullYear(), 0, 4);
+ return _lpad(1 + Math.round((a - b) / DAY / 7), 2);
+ },
+
+ ///////////
+ // MONTH //
+ ///////////
+ /**
+ * Full month name: `January...December`
+ * @return {string}
+ */
+ F: function () {
+ return vSettings.months[vDate.getMonth()];
+ },
+ /**
+ * Month w/leading 0: `01..12`
+ * @return {string}
+ */
+ m: function () {
+ return _lpad(fmt.n(), 2);
+ },
+ /**
+ * Shorthand month name; `Jan...Dec`
+ * @return {string}
+ */
+ M: function () {
+ return vSettings.monthsShort[vDate.getMonth()];
+ },
+ /**
+ * Month: `1...12`
+ * @return {number}
+ */
+ n: function () {
+ return vDate.getMonth() + 1;
+ },
+ /**
+ * Days in month: `28...31`
+ * @return {number}
+ */
+ t: function () {
+ return (new Date(fmt.Y(), fmt.n(), 0)).getDate();
+ },
+
+ //////////
+ // YEAR //
+ //////////
+ /**
+ * Is leap year? `0 or 1`
+ * @return {number}
+ */
+ L: function () {
+ var Y = fmt.Y();
+ return (Y % 4 === 0 && Y % 100 !== 0 || Y % 400 === 0) ? 1 : 0;
+ },
+ /**
+ * ISO-8601 year
+ * @return {number}
+ */
+ o: function () {
+ var n = fmt.n(), W = fmt.W(), Y = fmt.Y();
+ return Y + (n === 12 && W < 9 ? 1 : n === 1 && W > 9 ? -1 : 0);
+ },
+ /**
+ * Full year: `e.g. 1980...2010`
+ * @return {number}
+ */
+ Y: function () {
+ return vDate.getFullYear();
+ },
+ /**
+ * Last two digits of year: `00...99`
+ * @return {string}
+ */
+ y: function () {
+ return fmt.Y().toString().slice(-2);
+ },
+
+ //////////
+ // TIME //
+ //////////
+ /**
+ * Meridian lower: `am or pm`
+ * @return {string}
+ */
+ a: function () {
+ return fmt.A().toLowerCase();
+ },
+ /**
+ * Meridian upper: `AM or PM`
+ * @return {string}
+ */
+ A: function () {
+ var n = fmt.G() < 12 ? 0 : 1;
+ return vSettings.meridiem[n];
+ },
+ /**
+ * Swatch Internet time: `000..999`
+ * @return {string}
+ */
+ B: function () {
+ var H = vDate.getUTCHours() * HOUR, i = vDate.getUTCMinutes() * 60, s = vDate.getUTCSeconds();
+ return _lpad(Math.floor((H + i + s + HOUR) / 86.4) % 1000, 3);
+ },
+ /**
+ * 12-Hours: `1..12`
+ * @return {number}
+ */
+ g: function () {
+ return fmt.G() % 12 || 12;
+ },
+ /**
+ * 24-Hours: `0..23`
+ * @return {number}
+ */
+ G: function () {
+ return vDate.getHours();
+ },
+ /**
+ * 12-Hours with leading 0: `01..12`
+ * @return {string}
+ */
+ h: function () {
+ return _lpad(fmt.g(), 2);
+ },
+ /**
+ * 24-Hours w/leading 0: `00..23`
+ * @return {string}
+ */
+ H: function () {
+ return _lpad(fmt.G(), 2);
+ },
+ /**
+ * Minutes w/leading 0: `00..59`
+ * @return {string}
+ */
+ i: function () {
+ return _lpad(vDate.getMinutes(), 2);
+ },
+ /**
+ * Seconds w/leading 0: `00..59`
+ * @return {string}
+ */
+ s: function () {
+ return _lpad(vDate.getSeconds(), 2);
+ },
+ /**
+ * Microseconds: `000000-999000`
+ * @return {string}
+ */
+ u: function () {
+ return _lpad(vDate.getMilliseconds() * 1000, 6);
+ },
+
+ //////////////
+ // TIMEZONE //
+ //////////////
+ /**
+ * Timezone identifier: `e.g. Atlantic/Azores, ...`
+ * @return {string}
+ */
+ e: function () {
+ var str = /\((.*)\)/.exec(String(vDate))[1];
+ return str || 'Coordinated Universal Time';
+ },
+ /**
+ * Timezone abbreviation: `e.g. EST, MDT, ...`
+ * @return {string}
+ */
+ T: function () {
+ var str = (String(vDate).match(self.tzParts) || [""]).pop().replace(self.tzClip, "");
+ return str || 'UTC';
+ },
+ /**
+ * DST observed? `0 or 1`
+ * @return {number}
+ */
+ I: function () {
+ var a = new Date(fmt.Y(), 0), c = Date.UTC(fmt.Y(), 0),
+ b = new Date(fmt.Y(), 6), d = Date.UTC(fmt.Y(), 6);
+ return ((a - c) !== (b - d)) ? 1 : 0;
+ },
+ /**
+ * Difference to GMT in hour format: `e.g. +0200`
+ * @return {string}
+ */
+ O: function () {
+ var tzo = vDate.getTimezoneOffset(), a = Math.abs(tzo);
+ return (tzo > 0 ? '-' : '+') + _lpad(Math.floor(a / 60) * 100 + a % 60, 4);
+ },
+ /**
+ * Difference to GMT with colon: `e.g. +02:00`
+ * @return {string}
+ */
+ P: function () {
+ var O = fmt.O();
+ return (O.substr(0, 3) + ':' + O.substr(3, 2));
+ },
+ /**
+ * Timezone offset in seconds: `-43200...50400`
+ * @return {number}
+ */
+ Z: function () {
+ return -vDate.getTimezoneOffset() * 60;
+ },
+
+ ////////////////////
+ // FULL DATE TIME //
+ ////////////////////
+ /**
+ * ISO-8601 date
+ * @return {string}
+ */
+ c: function () {
+ return 'Y-m-d\\TH:i:sP'.replace(backspace, doFormat);
+ },
+ /**
+ * RFC 2822 date
+ * @return {string}
+ */
+ r: function () {
+ return 'D, d M Y H:i:s O'.replace(backspace, doFormat);
+ },
+ /**
+ * Seconds since UNIX epoch
+ * @return {number}
+ */
+ U: function () {
+ return vDate.getTime() / 1000 || 0;
+ }
+ };
+ return doFormat(vChar, vChar);
+ },
+ formatDate: function (vDate, vFormat) {
+ var self = this, i, n, len, str, vChar, vDateStr = '';
+ if (typeof vDate === 'string') {
+ vDate = self.parseDate(vDate, vFormat);
+ if (vDate === false) {
+ return false;
+ }
+ }
+ if (vDate instanceof Date) {
+ len = vFormat.length;
+ for (i = 0; i < len; i++) {
+ vChar = vFormat.charAt(i);
+ if (vChar === 'S') {
+ continue;
+ }
+ str = self.parseFormat(vChar, vDate);
+ if (i !== (len - 1) && self.intParts.test(vChar) && vFormat.charAt(i + 1) === 'S') {
+ n = parseInt(str);
+ str += self.dateSettings.ordinal(n);
+ }
+ vDateStr += str;
+ }
+ return vDateStr;
+ }
+ return '';
+ }
+ };
+})();/**
+ * @preserve jQuery DateTimePicker plugin v2.5.4
+ * @homepage http://xdsoft.net/jqplugins/datetimepicker/
+ * @author Chupurnov Valeriy ()
+ */
+/*global DateFormatter, document,window,jQuery,setTimeout,clearTimeout,HighlightedDate,getCurrentValue*/
+;(function (factory) {
+ if ( typeof define === 'function' && define.amd ) {
+ // AMD. Register as an anonymous module.
+ define(['jquery', 'jquery-mousewheel'], factory);
+ } else if (typeof exports === 'object') {
+ // Node/CommonJS style for Browserify
+ module.exports = factory;
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+}(function ($) {
+ 'use strict';
+
+ var currentlyScrollingTimeDiv = false;
+
+ var default_options = {
+ i18n: {
+ ar: { // Arabic
+ months: [
+ "كانون الثاني", "شباط", "آذار", "نيسان", "مايو", "حزيران", "تموز", "آب", "أيلول", "تشرين الأول", "تشرين الثاني", "كانون الأول"
+ ],
+ dayOfWeekShort: [
+ "ن", "ث", "ع", "خ", "ج", "س", "ح"
+ ],
+ dayOfWeek: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"]
+ },
+ ro: { // Romanian
+ months: [
+ "Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie"
+ ],
+ dayOfWeekShort: [
+ "Du", "Lu", "Ma", "Mi", "Jo", "Vi", "Sâ"
+ ],
+ dayOfWeek: ["Duminică", "Luni", "Marţi", "Miercuri", "Joi", "Vineri", "Sâmbătă"]
+ },
+ id: { // Indonesian
+ months: [
+ "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"
+ ],
+ dayOfWeekShort: [
+ "Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"
+ ],
+ dayOfWeek: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"]
+ },
+ is: { // Icelandic
+ months: [
+ "Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember"
+ ],
+ dayOfWeekShort: [
+ "Sun", "Mán", "Þrið", "Mið", "Fim", "Fös", "Lau"
+ ],
+ dayOfWeek: ["Sunnudagur", "Mánudagur", "Þriðjudagur", "Miðvikudagur", "Fimmtudagur", "Föstudagur", "Laugardagur"]
+ },
+ bg: { // Bulgarian
+ months: [
+ "Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"
+ ],
+ dayOfWeekShort: [
+ "Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"
+ ],
+ dayOfWeek: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота"]
+ },
+ fa: { // Persian/Farsi
+ months: [
+ 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'
+ ],
+ dayOfWeekShort: [
+ 'یکشنبه', 'دوشنبه', 'سه شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'
+ ],
+ dayOfWeek: ["یکشنبه", "دوشنبه", "سهشنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه", "یکشنبه"]
+ },
+ ru: { // Russian
+ months: [
+ 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'
+ ],
+ dayOfWeekShort: [
+ "Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"
+ ],
+ dayOfWeek: ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"]
+ },
+ uk: { // Ukrainian
+ months: [
+ 'Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень'
+ ],
+ dayOfWeekShort: [
+ "Ндл", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Сбт"
+ ],
+ dayOfWeek: ["Неділя", "Понеділок", "Вівторок", "Середа", "Четвер", "П'ятниця", "Субота"]
+ },
+ en: { // English
+ months: [
+ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
+ ],
+ dayOfWeekShort: [
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ ],
+ dayOfWeek: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
+ },
+ el: { // Ελληνικά
+ months: [
+ "Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"
+ ],
+ dayOfWeekShort: [
+ "Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ"
+ ],
+ dayOfWeek: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο"]
+ },
+ de: { // German
+ months: [
+ 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'
+ ],
+ dayOfWeekShort: [
+ "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"
+ ],
+ dayOfWeek: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"]
+ },
+ nl: { // Dutch
+ months: [
+ "januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"
+ ],
+ dayOfWeekShort: [
+ "zo", "ma", "di", "wo", "do", "vr", "za"
+ ],
+ dayOfWeek: ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"]
+ },
+ tr: { // Turkish
+ months: [
+ "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"
+ ],
+ dayOfWeekShort: [
+ "Paz", "Pts", "Sal", "Çar", "Per", "Cum", "Cts"
+ ],
+ dayOfWeek: ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"]
+ },
+ fr: { //French
+ months: [
+ "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"
+ ],
+ dayOfWeekShort: [
+ "Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"
+ ],
+ dayOfWeek: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]
+ },
+ es: { // Spanish
+ months: [
+ "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"
+ ],
+ dayOfWeekShort: [
+ "Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"
+ ],
+ dayOfWeek: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"]
+ },
+ th: { // Thai
+ months: [
+ 'มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม'
+ ],
+ dayOfWeekShort: [
+ 'อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'
+ ],
+ dayOfWeek: ["อาทิตย์", "จันทร์", "อังคาร", "พุธ", "พฤหัส", "ศุกร์", "เสาร์", "อาทิตย์"]
+ },
+ pl: { // Polish
+ months: [
+ "styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", "lipiec", "sierpień", "wrzesień", "październik", "listopad", "grudzień"
+ ],
+ dayOfWeekShort: [
+ "nd", "pn", "wt", "śr", "cz", "pt", "sb"
+ ],
+ dayOfWeek: ["niedziela", "poniedziałek", "wtorek", "środa", "czwartek", "piątek", "sobota"]
+ },
+ pt: { // Portuguese
+ months: [
+ "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"
+ ],
+ dayOfWeekShort: [
+ "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab"
+ ],
+ dayOfWeek: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"]
+ },
+ ch: { // Simplified Chinese
+ months: [
+ "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
+ ],
+ dayOfWeekShort: [
+ "日", "一", "二", "三", "四", "五", "六"
+ ]
+ },
+ se: { // Swedish
+ months: [
+ "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"
+ ],
+ dayOfWeekShort: [
+ "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör"
+ ]
+ },
+ kr: { // Korean
+ months: [
+ "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"
+ ],
+ dayOfWeekShort: [
+ "일", "월", "화", "수", "목", "금", "토"
+ ],
+ dayOfWeek: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"]
+ },
+ it: { // Italian
+ months: [
+ "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"
+ ],
+ dayOfWeekShort: [
+ "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"
+ ],
+ dayOfWeek: ["Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato"]
+ },
+ da: { // Dansk
+ months: [
+ "January", "Februar", "Marts", "April", "Maj", "Juni", "July", "August", "September", "Oktober", "November", "December"
+ ],
+ dayOfWeekShort: [
+ "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør"
+ ],
+ dayOfWeek: ["søndag", "mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag"]
+ },
+ no: { // Norwegian
+ months: [
+ "Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"
+ ],
+ dayOfWeekShort: [
+ "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør"
+ ],
+ dayOfWeek: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag']
+ },
+ ja: { // Japanese
+ months: [
+ "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"
+ ],
+ dayOfWeekShort: [
+ "日", "月", "火", "水", "木", "金", "土"
+ ],
+ dayOfWeek: ["日曜", "月曜", "火曜", "水曜", "木曜", "金曜", "土曜"]
+ },
+ vi: { // Vietnamese
+ months: [
+ "Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12"
+ ],
+ dayOfWeekShort: [
+ "CN", "T2", "T3", "T4", "T5", "T6", "T7"
+ ],
+ dayOfWeek: ["Chủ nhật", "Thứ hai", "Thứ ba", "Thứ tư", "Thứ năm", "Thứ sáu", "Thứ bảy"]
+ },
+ sl: { // Slovenščina
+ months: [
+ "Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"
+ ],
+ dayOfWeekShort: [
+ "Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob"
+ ],
+ dayOfWeek: ["Nedelja", "Ponedeljek", "Torek", "Sreda", "Četrtek", "Petek", "Sobota"]
+ },
+ cs: { // Čeština
+ months: [
+ "Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"
+ ],
+ dayOfWeekShort: [
+ "Ne", "Po", "Út", "St", "Čt", "Pá", "So"
+ ]
+ },
+ hu: { // Hungarian
+ months: [
+ "Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December"
+ ],
+ dayOfWeekShort: [
+ "Va", "Hé", "Ke", "Sze", "Cs", "Pé", "Szo"
+ ],
+ dayOfWeek: ["vasárnap", "hétfő", "kedd", "szerda", "csütörtök", "péntek", "szombat"]
+ },
+ az: { //Azerbaijanian (Azeri)
+ months: [
+ "Yanvar", "Fevral", "Mart", "Aprel", "May", "Iyun", "Iyul", "Avqust", "Sentyabr", "Oktyabr", "Noyabr", "Dekabr"
+ ],
+ dayOfWeekShort: [
+ "B", "Be", "Ça", "Ç", "Ca", "C", "Ş"
+ ],
+ dayOfWeek: ["Bazar", "Bazar ertəsi", "Çərşənbə axşamı", "Çərşənbə", "Cümə axşamı", "Cümə", "Şənbə"]
+ },
+ bs: { //Bosanski
+ months: [
+ "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"
+ ],
+ dayOfWeekShort: [
+ "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"
+ ],
+ dayOfWeek: ["Nedjelja","Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"]
+ },
+ ca: { //Català
+ months: [
+ "Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre"
+ ],
+ dayOfWeekShort: [
+ "Dg", "Dl", "Dt", "Dc", "Dj", "Dv", "Ds"
+ ],
+ dayOfWeek: ["Diumenge", "Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte"]
+ },
+ 'en-GB': { //English (British)
+ months: [
+ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
+ ],
+ dayOfWeekShort: [
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ ],
+ dayOfWeek: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
+ },
+ et: { //"Eesti"
+ months: [
+ "Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember"
+ ],
+ dayOfWeekShort: [
+ "P", "E", "T", "K", "N", "R", "L"
+ ],
+ dayOfWeek: ["Pühapäev", "Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev"]
+ },
+ eu: { //Euskara
+ months: [
+ "Urtarrila", "Otsaila", "Martxoa", "Apirila", "Maiatza", "Ekaina", "Uztaila", "Abuztua", "Iraila", "Urria", "Azaroa", "Abendua"
+ ],
+ dayOfWeekShort: [
+ "Ig.", "Al.", "Ar.", "Az.", "Og.", "Or.", "La."
+ ],
+ dayOfWeek: ['Igandea', 'Astelehena', 'Asteartea', 'Asteazkena', 'Osteguna', 'Ostirala', 'Larunbata']
+ },
+ fi: { //Finnish (Suomi)
+ months: [
+ "Tammikuu", "Helmikuu", "Maaliskuu", "Huhtikuu", "Toukokuu", "Kesäkuu", "Heinäkuu", "Elokuu", "Syyskuu", "Lokakuu", "Marraskuu", "Joulukuu"
+ ],
+ dayOfWeekShort: [
+ "Su", "Ma", "Ti", "Ke", "To", "Pe", "La"
+ ],
+ dayOfWeek: ["sunnuntai", "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai"]
+ },
+ gl: { //Galego
+ months: [
+ "Xan", "Feb", "Maz", "Abr", "Mai", "Xun", "Xul", "Ago", "Set", "Out", "Nov", "Dec"
+ ],
+ dayOfWeekShort: [
+ "Dom", "Lun", "Mar", "Mer", "Xov", "Ven", "Sab"
+ ],
+ dayOfWeek: ["Domingo", "Luns", "Martes", "Mércores", "Xoves", "Venres", "Sábado"]
+ },
+ hr: { //Hrvatski
+ months: [
+ "Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac"
+ ],
+ dayOfWeekShort: [
+ "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"
+ ],
+ dayOfWeek: ["Nedjelja", "Ponedjeljak", "Utorak", "Srijeda", "Četvrtak", "Petak", "Subota"]
+ },
+ ko: { //Korean (한국어)
+ months: [
+ "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"
+ ],
+ dayOfWeekShort: [
+ "일", "월", "화", "수", "목", "금", "토"
+ ],
+ dayOfWeek: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"]
+ },
+ lt: { //Lithuanian (lietuvių)
+ months: [
+ "Sausio", "Vasario", "Kovo", "Balandžio", "Gegužės", "Birželio", "Liepos", "Rugpjūčio", "Rugsėjo", "Spalio", "Lapkričio", "Gruodžio"
+ ],
+ dayOfWeekShort: [
+ "Sek", "Pir", "Ant", "Tre", "Ket", "Pen", "Šeš"
+ ],
+ dayOfWeek: ["Sekmadienis", "Pirmadienis", "Antradienis", "Trečiadienis", "Ketvirtadienis", "Penktadienis", "Šeštadienis"]
+ },
+ lv: { //Latvian (Latviešu)
+ months: [
+ "Janvāris", "Februāris", "Marts", "Aprīlis ", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"
+ ],
+ dayOfWeekShort: [
+ "Sv", "Pr", "Ot", "Tr", "Ct", "Pk", "St"
+ ],
+ dayOfWeek: ["Svētdiena", "Pirmdiena", "Otrdiena", "Trešdiena", "Ceturtdiena", "Piektdiena", "Sestdiena"]
+ },
+ mk: { //Macedonian (Македонски)
+ months: [
+ "јануари", "февруари", "март", "април", "мај", "јуни", "јули", "август", "септември", "октомври", "ноември", "декември"
+ ],
+ dayOfWeekShort: [
+ "нед", "пон", "вто", "сре", "чет", "пет", "саб"
+ ],
+ dayOfWeek: ["Недела", "Понеделник", "Вторник", "Среда", "Четврток", "Петок", "Сабота"]
+ },
+ mn: { //Mongolian (Монгол)
+ months: [
+ "1-р сар", "2-р сар", "3-р сар", "4-р сар", "5-р сар", "6-р сар", "7-р сар", "8-р сар", "9-р сар", "10-р сар", "11-р сар", "12-р сар"
+ ],
+ dayOfWeekShort: [
+ "Дав", "Мяг", "Лха", "Пүр", "Бсн", "Бям", "Ням"
+ ],
+ dayOfWeek: ["Даваа", "Мягмар", "Лхагва", "Пүрэв", "Баасан", "Бямба", "Ням"]
+ },
+ 'pt-BR': { //Português(Brasil)
+ months: [
+ "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"
+ ],
+ dayOfWeekShort: [
+ "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"
+ ],
+ dayOfWeek: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"]
+ },
+ sk: { //Slovenčina
+ months: [
+ "Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December"
+ ],
+ dayOfWeekShort: [
+ "Ne", "Po", "Ut", "St", "Št", "Pi", "So"
+ ],
+ dayOfWeek: ["Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota"]
+ },
+ sq: { //Albanian (Shqip)
+ months: [
+ "Janar", "Shkurt", "Mars", "Prill", "Maj", "Qershor", "Korrik", "Gusht", "Shtator", "Tetor", "Nëntor", "Dhjetor"
+ ],
+ dayOfWeekShort: [
+ "Die", "Hën", "Mar", "Mër", "Enj", "Pre", "Shtu"
+ ],
+ dayOfWeek: ["E Diel", "E Hënë", "E Martē", "E Mërkurë", "E Enjte", "E Premte", "E Shtunë"]
+ },
+ 'sr-YU': { //Serbian (Srpski)
+ months: [
+ "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"
+ ],
+ dayOfWeekShort: [
+ "Ned", "Pon", "Uto", "Sre", "čet", "Pet", "Sub"
+ ],
+ dayOfWeek: ["Nedelja","Ponedeljak", "Utorak", "Sreda", "Četvrtak", "Petak", "Subota"]
+ },
+ sr: { //Serbian Cyrillic (Српски)
+ months: [
+ "јануар", "фебруар", "март", "април", "мај", "јун", "јул", "август", "септембар", "октобар", "новембар", "децембар"
+ ],
+ dayOfWeekShort: [
+ "нед", "пон", "уто", "сре", "чет", "пет", "суб"
+ ],
+ dayOfWeek: ["Недеља","Понедељак", "Уторак", "Среда", "Четвртак", "Петак", "Субота"]
+ },
+ sv: { //Svenska
+ months: [
+ "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"
+ ],
+ dayOfWeekShort: [
+ "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör"
+ ],
+ dayOfWeek: ["Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag"]
+ },
+ 'zh-TW': { //Traditional Chinese (繁體中文)
+ months: [
+ "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
+ ],
+ dayOfWeekShort: [
+ "日", "一", "二", "三", "四", "五", "六"
+ ],
+ dayOfWeek: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]
+ },
+ zh: { //Simplified Chinese (简体中文)
+ months: [
+ "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
+ ],
+ dayOfWeekShort: [
+ "日", "一", "二", "三", "四", "五", "六"
+ ],
+ dayOfWeek: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]
+ },
+ he: { //Hebrew (עברית)
+ months: [
+ 'ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'
+ ],
+ dayOfWeekShort: [
+ 'א\'', 'ב\'', 'ג\'', 'ד\'', 'ה\'', 'ו\'', 'שבת'
+ ],
+ dayOfWeek: ["ראשון", "שני", "שלישי", "רביעי", "חמישי", "שישי", "שבת", "ראשון"]
+ },
+ hy: { // Armenian
+ months: [
+ "Հունվար", "Փետրվար", "Մարտ", "Ապրիլ", "Մայիս", "Հունիս", "Հուլիս", "Օգոստոս", "Սեպտեմբեր", "Հոկտեմբեր", "Նոյեմբեր", "Դեկտեմբեր"
+ ],
+ dayOfWeekShort: [
+ "Կի", "Երկ", "Երք", "Չոր", "Հնգ", "Ուրբ", "Շբթ"
+ ],
+ dayOfWeek: ["Կիրակի", "Երկուշաբթի", "Երեքշաբթի", "Չորեքշաբթի", "Հինգշաբթի", "Ուրբաթ", "Շաբաթ"]
+ },
+ kg: { // Kyrgyz
+ months: [
+ 'Үчтүн айы', 'Бирдин айы', 'Жалган Куран', 'Чын Куран', 'Бугу', 'Кулжа', 'Теке', 'Баш Оона', 'Аяк Оона', 'Тогуздун айы', 'Жетинин айы', 'Бештин айы'
+ ],
+ dayOfWeekShort: [
+ "Жек", "Дүй", "Шей", "Шар", "Бей", "Жум", "Ише"
+ ],
+ dayOfWeek: [
+ "Жекшемб", "Дүйшөмб", "Шейшемб", "Шаршемб", "Бейшемби", "Жума", "Ишенб"
+ ]
+ },
+ rm: { // Romansh
+ months: [
+ "Schaner", "Favrer", "Mars", "Avrigl", "Matg", "Zercladur", "Fanadur", "Avust", "Settember", "October", "November", "December"
+ ],
+ dayOfWeekShort: [
+ "Du", "Gli", "Ma", "Me", "Gie", "Ve", "So"
+ ],
+ dayOfWeek: [
+ "Dumengia", "Glindesdi", "Mardi", "Mesemna", "Gievgia", "Venderdi", "Sonda"
+ ]
+ },
+ ka: { // Georgian
+ months: [
+ 'იანვარი', 'თებერვალი', 'მარტი', 'აპრილი', 'მაისი', 'ივნისი', 'ივლისი', 'აგვისტო', 'სექტემბერი', 'ოქტომბერი', 'ნოემბერი', 'დეკემბერი'
+ ],
+ dayOfWeekShort: [
+ "კვ", "ორშ", "სამშ", "ოთხ", "ხუთ", "პარ", "შაბ"
+ ],
+ dayOfWeek: ["კვირა", "ორშაბათი", "სამშაბათი", "ოთხშაბათი", "ხუთშაბათი", "პარასკევი", "შაბათი"]
+ },
+ },
+ value: '',
+ rtl: false,
+
+ format: 'Y/m/d H:i',
+ formatTime: 'H:i',
+ formatDate: 'Y/m/d',
+
+ startDate: false, // new Date(), '1986/12/08', '-1970/01/05','-1970/01/05',
+ step: 60,
+ monthChangeSpinner: true,
+
+ closeOnDateSelect: false,
+ closeOnTimeSelect: true,
+ closeOnWithoutClick: true,
+ closeOnInputClick: true,
+
+ timepicker: true,
+ datepicker: true,
+ weeks: false,
+
+ defaultTime: false, // use formatTime format (ex. '10:00' for formatTime: 'H:i')
+ defaultDate: false, // use formatDate format (ex new Date() or '1986/12/08' or '-1970/01/05' or '-1970/01/05')
+
+ minDate: false,
+ maxDate: false,
+ minTime: false,
+ maxTime: false,
+ disabledMinTime: false,
+ disabledMaxTime: false,
+
+ allowTimes: [],
+ opened: false,
+ initTime: true,
+ inline: false,
+ theme: '',
+
+ onSelectDate: function () {},
+ onSelectTime: function () {},
+ onChangeMonth: function () {},
+ onGetWeekOfYear: function () {},
+ onChangeYear: function () {},
+ onChangeDateTime: function () {},
+ onShow: function () {},
+ onClose: function () {},
+ onGenerate: function () {},
+
+ withoutCopyright: true,
+ inverseButton: false,
+ hours12: false,
+ next: 'xdsoft_next',
+ prev : 'xdsoft_prev',
+ dayOfWeekStart: 0,
+ parentID: 'body',
+ timeHeightInTimePicker: 25,
+ timepickerScrollbar: true,
+ todayButton: true,
+ prevButton: true,
+ nextButton: true,
+ defaultSelect: true,
+
+ scrollMonth: true,
+ scrollTime: true,
+ scrollInput: true,
+
+ lazyInit: false,
+ mask: false,
+ validateOnBlur: true,
+ allowBlank: true,
+ yearStart: 1950,
+ yearEnd: 2050,
+ monthStart: 0,
+ monthEnd: 11,
+ style: '',
+ id: '',
+ fixed: false,
+ roundTime: 'round', // ceil, floor
+ className: '',
+ weekends: [],
+ highlightedDates: [],
+ highlightedPeriods: [],
+ allowDates : [],
+ allowDateRe : null,
+ disabledDates : [],
+ disabledWeekDays: [],
+ yearOffset: 0,
+ beforeShowDay: null,
+
+ enterLikeTab: true,
+ showApplyButton: false
+ };
+
+ var dateHelper = null,
+ globalLocaleDefault = 'en',
+ globalLocale = 'en';
+
+ var dateFormatterOptionsDefault = {
+ meridiem: ['AM', 'PM']
+ };
+
+ var initDateFormatter = function(){
+ var locale = default_options.i18n[globalLocale],
+ opts = {
+ days: locale.dayOfWeek,
+ daysShort: locale.dayOfWeekShort,
+ months: locale.months,
+ monthsShort: $.map(locale.months, function(n){ return n.substring(0, 3) }),
+ };
+
+ dateHelper = new DateFormatter({
+ dateSettings: $.extend({}, dateFormatterOptionsDefault, opts)
+ });
+ };
+
+ // for locale settings
+ $.datetimepicker = {
+ setLocale: function(locale){
+ var newLocale = default_options.i18n[locale]?locale:globalLocaleDefault;
+ if(globalLocale != newLocale){
+ globalLocale = newLocale;
+ // reinit date formatter
+ initDateFormatter();
+ }
+ },
+ setDateFormatter: function(dateFormatter) {
+ dateHelper = dateFormatter;
+ },
+ RFC_2822: 'D, d M Y H:i:s O',
+ ATOM: 'Y-m-d\TH:i:sP',
+ ISO_8601: 'Y-m-d\TH:i:sO',
+ RFC_822: 'D, d M y H:i:s O',
+ RFC_850: 'l, d-M-y H:i:s T',
+ RFC_1036: 'D, d M y H:i:s O',
+ RFC_1123: 'D, d M Y H:i:s O',
+ RSS: 'D, d M Y H:i:s O',
+ W3C: 'Y-m-d\TH:i:sP'
+ };
+
+ // first init date formatter
+ initDateFormatter();
+
+ // fix for ie8
+ if (!window.getComputedStyle) {
+ window.getComputedStyle = function (el, pseudo) {
+ this.el = el;
+ this.getPropertyValue = function (prop) {
+ var re = /(\-([a-z]){1})/g;
+ if (prop === 'float') {
+ prop = 'styleFloat';
+ }
+ if (re.test(prop)) {
+ prop = prop.replace(re, function (a, b, c) {
+ return c.toUpperCase();
+ });
+ }
+ return el.currentStyle[prop] || null;
+ };
+ return this;
+ };
+ }
+ if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function (obj, start) {
+ var i, j;
+ for (i = (start || 0), j = this.length; i < j; i += 1) {
+ if (this[i] === obj) { return i; }
+ }
+ return -1;
+ };
+ }
+ Date.prototype.countDaysInMonth = function () {
+ return new Date(this.getFullYear(), this.getMonth() + 1, 0).getDate();
+ };
+ $.fn.xdsoftScroller = function (percent) {
+ return this.each(function () {
+ var timeboxparent = $(this),
+ pointerEventToXY = function (e) {
+ var out = {x: 0, y: 0},
+ touch;
+ if (e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend' || e.type === 'touchcancel') {
+ touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
+ out.x = touch.clientX;
+ out.y = touch.clientY;
+ } else if (e.type === 'mousedown' || e.type === 'mouseup' || e.type === 'mousemove' || e.type === 'mouseover' || e.type === 'mouseout' || e.type === 'mouseenter' || e.type === 'mouseleave') {
+ out.x = e.clientX;
+ out.y = e.clientY;
+ }
+ return out;
+ },
+ timebox,
+ parentHeight,
+ height,
+ scrollbar,
+ scroller,
+ maximumOffset = 100,
+ start = false,
+ startY = 0,
+ startTop = 0,
+ h1 = 0,
+ touchStart = false,
+ startTopScroll = 0,
+ calcOffset = function () {};
+ if (percent === 'hide') {
+ timeboxparent.find('.xdsoft_scrollbar').hide();
+ return;
+ }
+ if (!$(this).hasClass('xdsoft_scroller_box')) {
+ timebox = timeboxparent.children().eq(0);
+ parentHeight = timeboxparent[0].clientHeight;
+ height = timebox[0].offsetHeight;
+ scrollbar = $('
');
+ scroller = $('
');
+ scrollbar.append(scroller);
+
+ timeboxparent.addClass('xdsoft_scroller_box').append(scrollbar);
+ calcOffset = function calcOffset(event) {
+ var offset = pointerEventToXY(event).y - startY + startTopScroll;
+ if (offset < 0) {
+ offset = 0;
+ }
+ if (offset + scroller[0].offsetHeight > h1) {
+ offset = h1 - scroller[0].offsetHeight;
+ }
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [maximumOffset ? offset / maximumOffset : 0]);
+ };
+
+ scroller
+ .on('touchstart.xdsoft_scroller mousedown.xdsoft_scroller', function (event) {
+ if (!parentHeight) {
+ timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]);
+ }
+
+ startY = pointerEventToXY(event).y;
+ startTopScroll = parseInt(scroller.css('margin-top'), 10);
+ h1 = scrollbar[0].offsetHeight;
+
+ if (event.type === 'mousedown' || event.type === 'touchstart') {
+ if (document) {
+ $(document.body).addClass('xdsoft_noselect');
+ }
+ $([document.body, window]).on('touchend mouseup.xdsoft_scroller', function arguments_callee() {
+ $([document.body, window]).off('touchend mouseup.xdsoft_scroller', arguments_callee)
+ .off('mousemove.xdsoft_scroller', calcOffset)
+ .removeClass('xdsoft_noselect');
+ });
+ $(document.body).on('mousemove.xdsoft_scroller', calcOffset);
+ } else {
+ touchStart = true;
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ })
+ .on('touchmove', function (event) {
+ if (touchStart) {
+ event.preventDefault();
+ calcOffset(event);
+ }
+ })
+ .on('touchend touchcancel', function () {
+ touchStart = false;
+ startTopScroll = 0;
+ });
+
+ timeboxparent
+ .on('scroll_element.xdsoft_scroller', function (event, percentage) {
+ if (!parentHeight) {
+ timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percentage, true]);
+ }
+ percentage = percentage > 1 ? 1 : (percentage < 0 || isNaN(percentage)) ? 0 : percentage;
+
+ scroller.css('margin-top', maximumOffset * percentage);
+
+ setTimeout(function () {
+ timebox.css('marginTop', -parseInt((timebox[0].offsetHeight - parentHeight) * percentage, 10));
+ }, 10);
+ })
+ .on('resize_scroll.xdsoft_scroller', function (event, percentage, noTriggerScroll) {
+ var percent, sh;
+ parentHeight = timeboxparent[0].clientHeight;
+ height = timebox[0].offsetHeight;
+ percent = parentHeight / height;
+ sh = percent * scrollbar[0].offsetHeight;
+ if (percent > 1) {
+ scroller.hide();
+ } else {
+ scroller.show();
+ scroller.css('height', parseInt(sh > 10 ? sh : 10, 10));
+ maximumOffset = scrollbar[0].offsetHeight - scroller[0].offsetHeight;
+ if (noTriggerScroll !== true) {
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [percentage || Math.abs(parseInt(timebox.css('marginTop'), 10)) / (height - parentHeight)]);
+ }
+ }
+ });
+
+ timeboxparent.on('mousewheel', function (event) {
+ var top = Math.abs(parseInt(timebox.css('marginTop'), 10));
+
+ top = top - (event.deltaY * 20);
+ if (top < 0) {
+ top = 0;
+ }
+
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [top / (height - parentHeight)]);
+ event.stopPropagation();
+ return false;
+ });
+
+ timeboxparent.on('touchstart', function (event) {
+ start = pointerEventToXY(event);
+ startTop = Math.abs(parseInt(timebox.css('marginTop'), 10));
+ });
+
+ timeboxparent.on('touchmove', function (event) {
+ if (start) {
+ event.preventDefault();
+ var coord = pointerEventToXY(event);
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [(startTop - (coord.y - start.y)) / (height - parentHeight)]);
+ }
+ });
+
+ timeboxparent.on('touchend touchcancel', function () {
+ start = false;
+ startTop = 0;
+ });
+ }
+ timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]);
+ });
+ };
+
+ $.fn.datetimepicker = function (opt, opt2) {
+ var result = this,
+ KEY0 = 48,
+ KEY9 = 57,
+ _KEY0 = 96,
+ _KEY9 = 105,
+ CTRLKEY = 17,
+ DEL = 46,
+ ENTER = 13,
+ ESC = 27,
+ BACKSPACE = 8,
+ ARROWLEFT = 37,
+ ARROWUP = 38,
+ ARROWRIGHT = 39,
+ ARROWDOWN = 40,
+ TAB = 9,
+ F5 = 116,
+ AKEY = 65,
+ CKEY = 67,
+ VKEY = 86,
+ ZKEY = 90,
+ YKEY = 89,
+ ctrlDown = false,
+ options = ($.isPlainObject(opt) || !opt) ? $.extend(true, {}, default_options, opt) : $.extend(true, {}, default_options),
+
+ lazyInitTimer = 0,
+ createDateTimePicker,
+ destroyDateTimePicker,
+
+ lazyInit = function (input) {
+ input
+ .on('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', function initOnActionCallback() {
+ if (input.is(':disabled') || input.data('xdsoft_datetimepicker')) {
+ return;
+ }
+ clearTimeout(lazyInitTimer);
+ lazyInitTimer = setTimeout(function () {
+
+ if (!input.data('xdsoft_datetimepicker')) {
+ createDateTimePicker(input);
+ }
+ input
+ .off('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', initOnActionCallback)
+ .trigger('open.xdsoft');
+ }, 100);
+ });
+ };
+
+ createDateTimePicker = function (input) {
+ var datetimepicker = $('
'),
+ xdsoft_copyright = $(''),
+ datepicker = $('
'),
+ month_picker = $(''),
+ calendar = $('
'),
+ timepicker = $(''),
+ timeboxparent = timepicker.find('.xdsoft_time_box').eq(0),
+ timebox = $('
'),
+ applyButton = $('Save Selected '),
+
+ monthselect = $(''),
+ yearselect = $(''),
+ triggerAfterOpen = false,
+ XDSoft_datetime,
+
+ xchangeTimer,
+ timerclick,
+ current_time_index,
+ setPos,
+ timer = 0,
+ _xdsoft_datetime,
+ forEachAncestorOf,
+ throttle;
+
+ if (options.id) {
+ datetimepicker.attr('id', options.id);
+ }
+ if (options.style) {
+ datetimepicker.attr('style', options.style);
+ }
+ if (options.weeks) {
+ datetimepicker.addClass('xdsoft_showweeks');
+ }
+ if (options.rtl) {
+ datetimepicker.addClass('xdsoft_rtl');
+ }
+
+ datetimepicker.addClass('xdsoft_' + options.theme);
+ datetimepicker.addClass(options.className);
+
+ month_picker
+ .find('.xdsoft_month span')
+ .after(monthselect);
+ month_picker
+ .find('.xdsoft_year span')
+ .after(yearselect);
+
+ month_picker
+ .find('.xdsoft_month,.xdsoft_year')
+ .on('touchstart mousedown.xdsoft', function (event) {
+ var select = $(this).find('.xdsoft_select').eq(0),
+ val = 0,
+ top = 0,
+ visible = select.is(':visible'),
+ items,
+ i;
+
+ month_picker
+ .find('.xdsoft_select')
+ .hide();
+ if (_xdsoft_datetime.currentTime) {
+ val = _xdsoft_datetime.currentTime[$(this).hasClass('xdsoft_month') ? 'getMonth' : 'getFullYear']();
+ }
+
+ select[visible ? 'hide' : 'show']();
+ for (items = select.find('div.xdsoft_option'), i = 0; i < items.length; i += 1) {
+ if (items.eq(i).data('value') === val) {
+ break;
+ } else {
+ top += items[0].offsetHeight;
+ }
+ }
+
+ select.xdsoftScroller(top / (select.children()[0].offsetHeight - (select[0].clientHeight)));
+ event.stopPropagation();
+ return false;
+ });
+
+ month_picker
+ .find('.xdsoft_select')
+ .xdsoftScroller()
+ .on('touchstart mousedown.xdsoft', function (event) {
+ event.stopPropagation();
+ event.preventDefault();
+ })
+ .on('touchstart mousedown.xdsoft', '.xdsoft_option', function () {
+ if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) {
+ _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
+ }
+
+ var year = _xdsoft_datetime.currentTime.getFullYear();
+ if (_xdsoft_datetime && _xdsoft_datetime.currentTime) {
+ _xdsoft_datetime.currentTime[$(this).parent().parent().hasClass('xdsoft_monthselect') ? 'setMonth' : 'setFullYear']($(this).data('value'));
+ }
+
+ $(this).parent().parent().hide();
+
+ datetimepicker.trigger('xchange.xdsoft');
+ if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) {
+ options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
+ }
+
+ if (year !== _xdsoft_datetime.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) {
+ options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
+ }
+ });
+
+ datetimepicker.getValue = function () {
+ return _xdsoft_datetime.getCurrentTime();
+ };
+
+ datetimepicker.setOptions = function (_options) {
+ var highlightedDates = {};
+
+ options = $.extend(true, {}, options, _options);
+
+ if (_options.allowTimes && $.isArray(_options.allowTimes) && _options.allowTimes.length) {
+ options.allowTimes = $.extend(true, [], _options.allowTimes);
+ }
+
+ if (_options.weekends && $.isArray(_options.weekends) && _options.weekends.length) {
+ options.weekends = $.extend(true, [], _options.weekends);
+ }
+
+ if (_options.allowDates && $.isArray(_options.allowDates) && _options.allowDates.length) {
+ options.allowDates = $.extend(true, [], _options.allowDates);
+ }
+
+ if (_options.allowDateRe && Object.prototype.toString.call(_options.allowDateRe)==="[object String]") {
+ options.allowDateRe = new RegExp(_options.allowDateRe);
+ }
+
+ if (_options.highlightedDates && $.isArray(_options.highlightedDates) && _options.highlightedDates.length) {
+ $.each(_options.highlightedDates, function (index, value) {
+ var splitData = $.map(value.split(','), $.trim),
+ exDesc,
+ hDate = new HighlightedDate(dateHelper.parseDate(splitData[0], options.formatDate), splitData[1], splitData[2]), // date, desc, style
+ keyDate = dateHelper.formatDate(hDate.date, options.formatDate);
+ if (highlightedDates[keyDate] !== undefined) {
+ exDesc = highlightedDates[keyDate].desc;
+ if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) {
+ highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc;
+ }
+ } else {
+ highlightedDates[keyDate] = hDate;
+ }
+ });
+
+ options.highlightedDates = $.extend(true, [], highlightedDates);
+ }
+
+ if (_options.highlightedPeriods && $.isArray(_options.highlightedPeriods) && _options.highlightedPeriods.length) {
+ highlightedDates = $.extend(true, [], options.highlightedDates);
+ $.each(_options.highlightedPeriods, function (index, value) {
+ var dateTest, // start date
+ dateEnd,
+ desc,
+ hDate,
+ keyDate,
+ exDesc,
+ style;
+ if ($.isArray(value)) {
+ dateTest = value[0];
+ dateEnd = value[1];
+ desc = value[2];
+ style = value[3];
+ }
+ else {
+ var splitData = $.map(value.split(','), $.trim);
+ dateTest = dateHelper.parseDate(splitData[0], options.formatDate);
+ dateEnd = dateHelper.parseDate(splitData[1], options.formatDate);
+ desc = splitData[2];
+ style = splitData[3];
+ }
+
+ while (dateTest <= dateEnd) {
+ hDate = new HighlightedDate(dateTest, desc, style);
+ keyDate = dateHelper.formatDate(dateTest, options.formatDate);
+ dateTest.setDate(dateTest.getDate() + 1);
+ if (highlightedDates[keyDate] !== undefined) {
+ exDesc = highlightedDates[keyDate].desc;
+ if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) {
+ highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc;
+ }
+ } else {
+ highlightedDates[keyDate] = hDate;
+ }
+ }
+ });
+
+ options.highlightedDates = $.extend(true, [], highlightedDates);
+ }
+
+ if (_options.disabledDates && $.isArray(_options.disabledDates) && _options.disabledDates.length) {
+ options.disabledDates = $.extend(true, [], _options.disabledDates);
+ }
+
+ if (_options.disabledWeekDays && $.isArray(_options.disabledWeekDays) && _options.disabledWeekDays.length) {
+ options.disabledWeekDays = $.extend(true, [], _options.disabledWeekDays);
+ }
+
+ if ((options.open || options.opened) && (!options.inline)) {
+ input.trigger('open.xdsoft');
+ }
+
+ if (options.inline) {
+ triggerAfterOpen = true;
+ datetimepicker.addClass('xdsoft_inline');
+ input.after(datetimepicker).hide();
+ }
+
+ if (options.inverseButton) {
+ options.next = 'xdsoft_prev';
+ options.prev = 'xdsoft_next';
+ }
+
+ if (options.datepicker) {
+ datepicker.addClass('active');
+ } else {
+ datepicker.removeClass('active');
+ }
+
+ if (options.timepicker) {
+ timepicker.addClass('active');
+ } else {
+ timepicker.removeClass('active');
+ }
+
+ if (options.value) {
+ _xdsoft_datetime.setCurrentTime(options.value);
+ if (input && input.val) {
+ input.val(_xdsoft_datetime.str);
+ }
+ }
+
+ if (isNaN(options.dayOfWeekStart)) {
+ options.dayOfWeekStart = 0;
+ } else {
+ options.dayOfWeekStart = parseInt(options.dayOfWeekStart, 10) % 7;
+ }
+
+ if (!options.timepickerScrollbar) {
+ timeboxparent.xdsoftScroller('hide');
+ }
+
+ if (options.minDate && /^[\+\-](.*)$/.test(options.minDate)) {
+ options.minDate = dateHelper.formatDate(_xdsoft_datetime.strToDateTime(options.minDate), options.formatDate);
+ }
+
+ if (options.maxDate && /^[\+\-](.*)$/.test(options.maxDate)) {
+ options.maxDate = dateHelper.formatDate(_xdsoft_datetime.strToDateTime(options.maxDate), options.formatDate);
+ }
+
+ applyButton.toggle(options.showApplyButton);
+
+ month_picker
+ .find('.xdsoft_today_button')
+ .css('visibility', !options.todayButton ? 'hidden' : 'visible');
+
+ month_picker
+ .find('.' + options.prev)
+ .css('visibility', !options.prevButton ? 'hidden' : 'visible');
+
+ month_picker
+ .find('.' + options.next)
+ .css('visibility', !options.nextButton ? 'hidden' : 'visible');
+
+ setMask(options);
+
+ if (options.validateOnBlur) {
+ input
+ .off('blur.xdsoft')
+ .on('blur.xdsoft', function () {
+ if (options.allowBlank && (!$.trim($(this).val()).length || (typeof options.mask == "string" && $.trim($(this).val()) === options.mask.replace(/[0-9]/g, '_')))) {
+ $(this).val(null);
+ datetimepicker.data('xdsoft_datetime').empty();
+ } else {
+ var d = dateHelper.parseDate($(this).val(), options.format);
+ if (d) { // parseDate() may skip some invalid parts like date or time, so make it clear for user: show parsed date/time
+ $(this).val(dateHelper.formatDate(d, options.format));
+ } else {
+ var splittedHours = +([$(this).val()[0], $(this).val()[1]].join('')),
+ splittedMinutes = +([$(this).val()[2], $(this).val()[3]].join(''));
+
+ // parse the numbers as 0312 => 03:12
+ if (!options.datepicker && options.timepicker && splittedHours >= 0 && splittedHours < 24 && splittedMinutes >= 0 && splittedMinutes < 60) {
+ $(this).val([splittedHours, splittedMinutes].map(function (item) {
+ return item > 9 ? item : '0' + item;
+ }).join(':'));
+ } else {
+ $(this).val(dateHelper.formatDate(_xdsoft_datetime.now(), options.format));
+ }
+ }
+ datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val());
+ }
+
+ datetimepicker.trigger('changedatetime.xdsoft');
+ datetimepicker.trigger('close.xdsoft');
+ });
+ }
+ options.dayOfWeekStartPrev = (options.dayOfWeekStart === 0) ? 6 : options.dayOfWeekStart - 1;
+
+ datetimepicker
+ .trigger('xchange.xdsoft')
+ .trigger('afterOpen.xdsoft');
+ };
+
+ datetimepicker
+ .data('options', options)
+ .on('touchstart mousedown.xdsoft', function (event) {
+ event.stopPropagation();
+ event.preventDefault();
+ yearselect.hide();
+ monthselect.hide();
+ return false;
+ });
+
+ //scroll_element = timepicker.find('.xdsoft_time_box');
+ timeboxparent.append(timebox);
+ timeboxparent.xdsoftScroller();
+
+ datetimepicker.on('afterOpen.xdsoft', function () {
+ timeboxparent.xdsoftScroller();
+ });
+
+ datetimepicker
+ .append(datepicker)
+ .append(timepicker);
+
+ if (options.withoutCopyright !== true) {
+ datetimepicker
+ .append(xdsoft_copyright);
+ }
+
+ datepicker
+ .append(month_picker)
+ .append(calendar)
+ .append(applyButton);
+
+ $(options.parentID)
+ .append(datetimepicker);
+
+ XDSoft_datetime = function () {
+ var _this = this;
+ _this.now = function (norecursion) {
+ var d = new Date(),
+ date,
+ time;
+
+ if (!norecursion && options.defaultDate) {
+ date = _this.strToDateTime(options.defaultDate);
+ d.setFullYear(date.getFullYear());
+ d.setMonth(date.getMonth());
+ d.setDate(date.getDate());
+ }
+
+ if (options.yearOffset) {
+ d.setFullYear(d.getFullYear() + options.yearOffset);
+ }
+
+ if (!norecursion && options.defaultTime) {
+ time = _this.strtotime(options.defaultTime);
+ d.setHours(time.getHours());
+ d.setMinutes(time.getMinutes());
+ }
+ return d;
+ };
+
+ _this.isValidDate = function (d) {
+ if (Object.prototype.toString.call(d) !== "[object Date]") {
+ return false;
+ }
+ return !isNaN(d.getTime());
+ };
+
+ _this.setCurrentTime = function (dTime, requireValidDate) {
+ if (typeof dTime === 'string') {
+ _this.currentTime = _this.strToDateTime(dTime);
+ }
+ else if (_this.isValidDate(dTime)) {
+ _this.currentTime = dTime;
+ }
+ else if (!dTime && !requireValidDate && options.allowBlank) {
+ _this.currentTime = null;
+ }
+ else {
+ _this.currentTime = _this.now();
+ }
+
+ datetimepicker.trigger('xchange.xdsoft');
+ };
+
+ _this.empty = function () {
+ _this.currentTime = null;
+ };
+
+ _this.getCurrentTime = function (dTime) {
+ return _this.currentTime;
+ };
+
+ _this.nextMonth = function () {
+
+ if (_this.currentTime === undefined || _this.currentTime === null) {
+ _this.currentTime = _this.now();
+ }
+
+ var month = _this.currentTime.getMonth() + 1,
+ year;
+ if (month === 12) {
+ _this.currentTime.setFullYear(_this.currentTime.getFullYear() + 1);
+ month = 0;
+ }
+
+ year = _this.currentTime.getFullYear();
+
+ _this.currentTime.setDate(
+ Math.min(
+ new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(),
+ _this.currentTime.getDate()
+ )
+ );
+ _this.currentTime.setMonth(month);
+
+ if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) {
+ options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
+ }
+
+ if (year !== _this.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) {
+ options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
+ }
+
+ datetimepicker.trigger('xchange.xdsoft');
+ return month;
+ };
+
+ _this.prevMonth = function () {
+
+ if (_this.currentTime === undefined || _this.currentTime === null) {
+ _this.currentTime = _this.now();
+ }
+
+ var month = _this.currentTime.getMonth() - 1;
+ if (month === -1) {
+ _this.currentTime.setFullYear(_this.currentTime.getFullYear() - 1);
+ month = 11;
+ }
+ _this.currentTime.setDate(
+ Math.min(
+ new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(),
+ _this.currentTime.getDate()
+ )
+ );
+ _this.currentTime.setMonth(month);
+ if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) {
+ options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
+ }
+ datetimepicker.trigger('xchange.xdsoft');
+ return month;
+ };
+
+ _this.getWeekOfYear = function (datetime) {
+ if (options.onGetWeekOfYear && $.isFunction(options.onGetWeekOfYear)) {
+ var week = options.onGetWeekOfYear.call(datetimepicker, datetime);
+ if (typeof week !== 'undefined') {
+ return week;
+ }
+ }
+ var onejan = new Date(datetime.getFullYear(), 0, 1);
+ //First week of the year is th one with the first Thursday according to ISO8601
+ if(onejan.getDay()!=4)
+ onejan.setMonth(0, 1 + ((4 - onejan.getDay()+ 7) % 7));
+ return Math.ceil((((datetime - onejan) / 86400000) + onejan.getDay() + 1) / 7);
+ };
+
+ _this.strToDateTime = function (sDateTime) {
+ var tmpDate = [], timeOffset, currentTime;
+
+ if (sDateTime && sDateTime instanceof Date && _this.isValidDate(sDateTime)) {
+ return sDateTime;
+ }
+
+ tmpDate = /^(\+|\-)(.*)$/.exec(sDateTime);
+ if (tmpDate) {
+ tmpDate[2] = dateHelper.parseDate(tmpDate[2], options.formatDate);
+ }
+ if (tmpDate && tmpDate[2]) {
+ timeOffset = tmpDate[2].getTime() - (tmpDate[2].getTimezoneOffset()) * 60000;
+ currentTime = new Date((_this.now(true)).getTime() + parseInt(tmpDate[1] + '1', 10) * timeOffset);
+ } else {
+ currentTime = sDateTime ? dateHelper.parseDate(sDateTime, options.format) : _this.now();
+ }
+
+ if (!_this.isValidDate(currentTime)) {
+ currentTime = _this.now();
+ }
+
+ return currentTime;
+ };
+
+ _this.strToDate = function (sDate) {
+ if (sDate && sDate instanceof Date && _this.isValidDate(sDate)) {
+ return sDate;
+ }
+
+ var currentTime = sDate ? dateHelper.parseDate(sDate, options.formatDate) : _this.now(true);
+ if (!_this.isValidDate(currentTime)) {
+ currentTime = _this.now(true);
+ }
+ return currentTime;
+ };
+
+ _this.strtotime = function (sTime) {
+ if (sTime && sTime instanceof Date && _this.isValidDate(sTime)) {
+ return sTime;
+ }
+ var currentTime = sTime ? dateHelper.parseDate(sTime, options.formatTime) : _this.now(true);
+ if (!_this.isValidDate(currentTime)) {
+ currentTime = _this.now(true);
+ }
+ return currentTime;
+ };
+
+ _this.str = function () {
+ return dateHelper.formatDate(_this.currentTime, options.format);
+ };
+ _this.currentTime = this.now();
+ };
+
+ _xdsoft_datetime = new XDSoft_datetime();
+
+ applyButton.on('touchend click', function (e) {//pathbrite
+ e.preventDefault();
+ datetimepicker.data('changed', true);
+ _xdsoft_datetime.setCurrentTime(getCurrentValue());
+ input.val(_xdsoft_datetime.str());
+ datetimepicker.trigger('close.xdsoft');
+ });
+ month_picker
+ .find('.xdsoft_today_button')
+ .on('touchend mousedown.xdsoft', function () {
+ datetimepicker.data('changed', true);
+ _xdsoft_datetime.setCurrentTime(0, true);
+ datetimepicker.trigger('afterOpen.xdsoft');
+ }).on('dblclick.xdsoft', function () {
+ var currentDate = _xdsoft_datetime.getCurrentTime(), minDate, maxDate;
+ currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
+ minDate = _xdsoft_datetime.strToDate(options.minDate);
+ minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
+ if (currentDate < minDate) {
+ return;
+ }
+ maxDate = _xdsoft_datetime.strToDate(options.maxDate);
+ maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate());
+ if (currentDate > maxDate) {
+ return;
+ }
+ input.val(_xdsoft_datetime.str());
+ input.trigger('change');
+ datetimepicker.trigger('close.xdsoft');
+ });
+ month_picker
+ .find('.xdsoft_prev,.xdsoft_next')
+ .on('touchend mousedown.xdsoft', function () {
+ var $this = $(this),
+ timer = 0,
+ stop = false;
+
+ (function arguments_callee1(v) {
+ if ($this.hasClass(options.next)) {
+ _xdsoft_datetime.nextMonth();
+ } else if ($this.hasClass(options.prev)) {
+ _xdsoft_datetime.prevMonth();
+ }
+ if (options.monthChangeSpinner) {
+ if (!stop) {
+ timer = setTimeout(arguments_callee1, v || 100);
+ }
+ }
+ }(500));
+
+ $([document.body, window]).on('touchend mouseup.xdsoft', function arguments_callee2() {
+ clearTimeout(timer);
+ stop = true;
+ $([document.body, window]).off('touchend mouseup.xdsoft', arguments_callee2);
+ });
+ });
+
+ timepicker
+ .find('.xdsoft_prev,.xdsoft_next')
+ .on('touchend mousedown.xdsoft', function () {
+ var $this = $(this),
+ timer = 0,
+ stop = false,
+ period = 110;
+ (function arguments_callee4(v) {
+ var pheight = timeboxparent[0].clientHeight,
+ height = timebox[0].offsetHeight,
+ top = Math.abs(parseInt(timebox.css('marginTop'), 10));
+ if ($this.hasClass(options.next) && (height - pheight) - options.timeHeightInTimePicker >= top) {
+ timebox.css('marginTop', '-' + (top + options.timeHeightInTimePicker) + 'px');
+ } else if ($this.hasClass(options.prev) && top - options.timeHeightInTimePicker >= 0) {
+ timebox.css('marginTop', '-' + (top - options.timeHeightInTimePicker) + 'px');
+ }
+ /**
+ * Fixed bug:
+ * When using css3 transition, it will cause a bug that you cannot scroll the timepicker list.
+ * The reason is that the transition-duration time, if you set it to 0, all things fine, otherwise, this
+ * would cause a bug when you use jquery.css method.
+ * Let's say: * { transition: all .5s ease; }
+ * jquery timebox.css('marginTop') will return the original value which is before you clicking the next/prev button,
+ * meanwhile the timebox[0].style.marginTop will return the right value which is after you clicking the
+ * next/prev button.
+ *
+ * What we should do:
+ * Replace timebox.css('marginTop') with timebox[0].style.marginTop.
+ */
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [Math.abs(parseInt(timebox[0].style.marginTop, 10) / (height - pheight))]);
+ period = (period > 10) ? 10 : period - 10;
+ if (!stop) {
+ timer = setTimeout(arguments_callee4, v || period);
+ }
+ }(500));
+ $([document.body, window]).on('touchend mouseup.xdsoft', function arguments_callee5() {
+ clearTimeout(timer);
+ stop = true;
+ $([document.body, window])
+ .off('touchend mouseup.xdsoft', arguments_callee5);
+ });
+ });
+
+ xchangeTimer = 0;
+ // base handler - generating a calendar and timepicker
+ datetimepicker
+ .on('xchange.xdsoft', function (event) {
+ clearTimeout(xchangeTimer);
+ xchangeTimer = setTimeout(function () {
+
+ if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) {
+ //In case blanks are allowed, delay construction until we have a valid date
+ if (options.allowBlank)
+ return;
+
+ _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
+ }
+
+ var table = '',
+ start = new Date(_xdsoft_datetime.currentTime.getFullYear(), _xdsoft_datetime.currentTime.getMonth(), 1, 12, 0, 0),
+ i = 0,
+ j,
+ today = _xdsoft_datetime.now(),
+ maxDate = false,
+ minDate = false,
+ hDate,
+ day,
+ d,
+ y,
+ m,
+ w,
+ classes = [],
+ customDateSettings,
+ newRow = true,
+ time = '',
+ h = '',
+ line_time,
+ description;
+
+ while (start.getDay() !== options.dayOfWeekStart) {
+ start.setDate(start.getDate() - 1);
+ }
+
+ table += '';
+
+ if (options.weeks) {
+ table += ' ';
+ }
+
+ for (j = 0; j < 7; j += 1) {
+ table += '' + options.i18n[globalLocale].dayOfWeekShort[(j + options.dayOfWeekStart) % 7] + ' ';
+ }
+
+ table += ' ';
+ table += '';
+
+ if (options.maxDate !== false) {
+ maxDate = _xdsoft_datetime.strToDate(options.maxDate);
+ maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate(), 23, 59, 59, 999);
+ }
+
+ if (options.minDate !== false) {
+ minDate = _xdsoft_datetime.strToDate(options.minDate);
+ minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
+ }
+
+ while (i < _xdsoft_datetime.currentTime.countDaysInMonth() || start.getDay() !== options.dayOfWeekStart || _xdsoft_datetime.currentTime.getMonth() === start.getMonth()) {
+ classes = [];
+ i += 1;
+
+ day = start.getDay();
+ d = start.getDate();
+ y = start.getFullYear();
+ m = start.getMonth();
+ w = _xdsoft_datetime.getWeekOfYear(start);
+ description = '';
+
+ classes.push('xdsoft_date');
+
+ if (options.beforeShowDay && $.isFunction(options.beforeShowDay.call)) {
+ customDateSettings = options.beforeShowDay.call(datetimepicker, start);
+ } else {
+ customDateSettings = null;
+ }
+
+ if(options.allowDateRe && Object.prototype.toString.call(options.allowDateRe) === "[object RegExp]"){
+ if(!options.allowDateRe.test(dateHelper.formatDate(start, options.formatDate))){
+ classes.push('xdsoft_disabled');
+ }
+ } else if(options.allowDates && options.allowDates.length>0){
+ if(options.allowDates.indexOf(dateHelper.formatDate(start, options.formatDate)) === -1){
+ classes.push('xdsoft_disabled');
+ }
+ } else if ((maxDate !== false && start > maxDate) || (minDate !== false && start < minDate) || (customDateSettings && customDateSettings[0] === false)) {
+ classes.push('xdsoft_disabled');
+ } else if (options.disabledDates.indexOf(dateHelper.formatDate(start, options.formatDate)) !== -1) {
+ classes.push('xdsoft_disabled');
+ } else if (options.disabledWeekDays.indexOf(day) !== -1) {
+ classes.push('xdsoft_disabled');
+ }else if (input.is('[readonly]')) {
+ classes.push('xdsoft_disabled');
+ }
+
+ if (customDateSettings && customDateSettings[1] !== "") {
+ classes.push(customDateSettings[1]);
+ }
+
+ if (_xdsoft_datetime.currentTime.getMonth() !== m) {
+ classes.push('xdsoft_other_month');
+ }
+
+ if ((options.defaultSelect || datetimepicker.data('changed')) && dateHelper.formatDate(_xdsoft_datetime.currentTime, options.formatDate) === dateHelper.formatDate(start, options.formatDate)) {
+ classes.push('xdsoft_current');
+ }
+
+ if (dateHelper.formatDate(today, options.formatDate) === dateHelper.formatDate(start, options.formatDate)) {
+ classes.push('xdsoft_today');
+ }
+
+ if (start.getDay() === 0 || start.getDay() === 6 || options.weekends.indexOf(dateHelper.formatDate(start, options.formatDate)) !== -1) {
+ classes.push('xdsoft_weekend');
+ }
+
+ if (options.highlightedDates[dateHelper.formatDate(start, options.formatDate)] !== undefined) {
+ hDate = options.highlightedDates[dateHelper.formatDate(start, options.formatDate)];
+ classes.push(hDate.style === undefined ? 'xdsoft_highlighted_default' : hDate.style);
+ description = hDate.desc === undefined ? '' : hDate.desc;
+ }
+
+ if (options.beforeShowDay && $.isFunction(options.beforeShowDay)) {
+ classes.push(options.beforeShowDay(start));
+ }
+
+ if (newRow) {
+ table += '';
+ newRow = false;
+ if (options.weeks) {
+ table += '' + w + ' ';
+ }
+ }
+
+ table += '' +
+ '' + d + '
' +
+ ' ';
+
+ if (start.getDay() === options.dayOfWeekStartPrev) {
+ table += ' ';
+ newRow = true;
+ }
+
+ start.setDate(d + 1);
+ }
+ table += '
';
+
+ calendar.html(table);
+
+ month_picker.find('.xdsoft_label span').eq(0).text(options.i18n[globalLocale].months[_xdsoft_datetime.currentTime.getMonth()]);
+ month_picker.find('.xdsoft_label span').eq(1).text(_xdsoft_datetime.currentTime.getFullYear());
+
+ // generate timebox
+ time = '';
+ h = '';
+ m = '';
+
+ line_time = function line_time(h, m) {
+ var now = _xdsoft_datetime.now(), optionDateTime, current_time,
+ isALlowTimesInit = options.allowTimes && $.isArray(options.allowTimes) && options.allowTimes.length;
+ now.setHours(h);
+ h = parseInt(now.getHours(), 10);
+ now.setMinutes(m);
+ m = parseInt(now.getMinutes(), 10);
+ optionDateTime = new Date(_xdsoft_datetime.currentTime);
+ optionDateTime.setHours(h);
+ optionDateTime.setMinutes(m);
+ classes = [];
+ if ((options.minDateTime !== false && options.minDateTime > optionDateTime) || (options.maxTime !== false && _xdsoft_datetime.strtotime(options.maxTime).getTime() < now.getTime()) || (options.minTime !== false && _xdsoft_datetime.strtotime(options.minTime).getTime() > now.getTime())) {
+ classes.push('xdsoft_disabled');
+ } else if ((options.minDateTime !== false && options.minDateTime > optionDateTime) || ((options.disabledMinTime !== false && now.getTime() > _xdsoft_datetime.strtotime(options.disabledMinTime).getTime()) && (options.disabledMaxTime !== false && now.getTime() < _xdsoft_datetime.strtotime(options.disabledMaxTime).getTime()))) {
+ classes.push('xdsoft_disabled');
+ } else if (input.is('[readonly]')) {
+ classes.push('xdsoft_disabled');
+ }
+
+ current_time = new Date(_xdsoft_datetime.currentTime);
+ current_time.setHours(parseInt(_xdsoft_datetime.currentTime.getHours(), 10));
+
+ if (!isALlowTimesInit) {
+ current_time.setMinutes(Math[options.roundTime](_xdsoft_datetime.currentTime.getMinutes() / options.step) * options.step);
+ }
+
+ if ((options.initTime || options.defaultSelect || datetimepicker.data('changed')) && current_time.getHours() === parseInt(h, 10) && ((!isALlowTimesInit && options.step > 59) || current_time.getMinutes() === parseInt(m, 10))) {
+ if (options.defaultSelect || datetimepicker.data('changed')) {
+ classes.push('xdsoft_current');
+ } else if (options.initTime) {
+ classes.push('xdsoft_init_time');
+ }
+ }
+ if (parseInt(today.getHours(), 10) === parseInt(h, 10) && parseInt(today.getMinutes(), 10) === parseInt(m, 10)) {
+ classes.push('xdsoft_today');
+ }
+ time += '' + dateHelper.formatDate(now, options.formatTime) + '
';
+ };
+
+ if (!options.allowTimes || !$.isArray(options.allowTimes) || !options.allowTimes.length) {
+ for (i = 0, j = 0; i < (options.hours12 ? 12 : 24); i += 1) {
+ for (j = 0; j < 60; j += options.step) {
+ h = (i < 10 ? '0' : '') + i;
+ m = (j < 10 ? '0' : '') + j;
+ line_time(h, m);
+ }
+ }
+ } else {
+ for (i = 0; i < options.allowTimes.length; i += 1) {
+ h = _xdsoft_datetime.strtotime(options.allowTimes[i]).getHours();
+ m = _xdsoft_datetime.strtotime(options.allowTimes[i]).getMinutes();
+ line_time(h, m);
+ }
+ }
+
+ timebox.html(time);
+
+ opt = '';
+ i = 0;
+
+ for (i = parseInt(options.yearStart, 10) + options.yearOffset; i <= parseInt(options.yearEnd, 10) + options.yearOffset; i += 1) {
+ opt += '' + i + '
';
+ }
+ yearselect.children().eq(0)
+ .html(opt);
+
+ for (i = parseInt(options.monthStart, 10), opt = ''; i <= parseInt(options.monthEnd, 10); i += 1) {
+ opt += '' + options.i18n[globalLocale].months[i] + '
';
+ }
+ monthselect.children().eq(0).html(opt);
+ $(datetimepicker)
+ .trigger('generate.xdsoft');
+ }, 10);
+ event.stopPropagation();
+ })
+ .on('afterOpen.xdsoft', function () {
+ if (options.timepicker) {
+ var classType, pheight, height, top;
+ if (timebox.find('.xdsoft_current').length) {
+ classType = '.xdsoft_current';
+ } else if (timebox.find('.xdsoft_init_time').length) {
+ classType = '.xdsoft_init_time';
+ }
+ if (classType) {
+ pheight = timeboxparent[0].clientHeight;
+ height = timebox[0].offsetHeight;
+ top = timebox.find(classType).index() * options.timeHeightInTimePicker + 1;
+ if ((height - pheight) < top) {
+ top = height - pheight;
+ }
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [parseInt(top, 10) / (height - pheight)]);
+ } else {
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [0]);
+ }
+ }
+ });
+
+ timerclick = 0;
+ calendar
+ .on('touchend click.xdsoft', 'td', function (xdevent) {
+ xdevent.stopPropagation(); // Prevents closing of Pop-ups, Modals and Flyouts in Bootstrap
+ timerclick += 1;
+ var $this = $(this),
+ currentTime = _xdsoft_datetime.currentTime;
+
+ if (currentTime === undefined || currentTime === null) {
+ _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
+ currentTime = _xdsoft_datetime.currentTime;
+ }
+
+ if ($this.hasClass('xdsoft_disabled')) {
+ return false;
+ }
+
+ currentTime.setDate(1);
+ currentTime.setFullYear($this.data('year'));
+ currentTime.setMonth($this.data('month'));
+ currentTime.setDate($this.data('date'));
+
+ datetimepicker.trigger('select.xdsoft', [currentTime]);
+
+ input.val(_xdsoft_datetime.str());
+
+ if (options.onSelectDate && $.isFunction(options.onSelectDate)) {
+ options.onSelectDate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent);
+ }
+
+ datetimepicker.data('changed', true);
+ datetimepicker.trigger('xchange.xdsoft');
+ datetimepicker.trigger('changedatetime.xdsoft');
+ if ((timerclick > 1 || (options.closeOnDateSelect === true || (options.closeOnDateSelect === false && !options.timepicker))) && !options.inline) {
+ datetimepicker.trigger('close.xdsoft');
+ }
+ setTimeout(function () {
+ timerclick = 0;
+ }, 200);
+ });
+
+ timebox
+ .on('touchmove', 'div', function () { currentlyScrollingTimeDiv = true; })
+ .on('touchend click.xdsoft', 'div', function (xdevent) {
+ xdevent.stopPropagation();
+ if (currentlyScrollingTimeDiv) {
+ currentlyScrollingTimeDiv = false;
+ return;
+ }
+ var $this = $(this),
+ currentTime = _xdsoft_datetime.currentTime;
+
+ if (currentTime === undefined || currentTime === null) {
+ _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
+ currentTime = _xdsoft_datetime.currentTime;
+ }
+
+ if ($this.hasClass('xdsoft_disabled')) {
+ return false;
+ }
+ currentTime.setHours($this.data('hour'));
+ currentTime.setMinutes($this.data('minute'));
+ datetimepicker.trigger('select.xdsoft', [currentTime]);
+
+ datetimepicker.data('input').val(_xdsoft_datetime.str());
+
+ if (options.onSelectTime && $.isFunction(options.onSelectTime)) {
+ options.onSelectTime.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent);
+ }
+ datetimepicker.data('changed', true);
+ datetimepicker.trigger('xchange.xdsoft');
+ datetimepicker.trigger('changedatetime.xdsoft');
+ if (options.inline !== true && options.closeOnTimeSelect === true) {
+ datetimepicker.trigger('close.xdsoft');
+ }
+ });
+
+ datepicker
+ .on('mousewheel.xdsoft', function (event) {
+ if (!options.scrollMonth) {
+ return true;
+ }
+ if (event.deltaY < 0) {
+ _xdsoft_datetime.nextMonth();
+ } else {
+ _xdsoft_datetime.prevMonth();
+ }
+ return false;
+ });
+
+ input
+ .on('mousewheel.xdsoft', function (event) {
+ if (!options.scrollInput) {
+ return true;
+ }
+ if (!options.datepicker && options.timepicker) {
+ current_time_index = timebox.find('.xdsoft_current').length ? timebox.find('.xdsoft_current').eq(0).index() : 0;
+ if (current_time_index + event.deltaY >= 0 && current_time_index + event.deltaY < timebox.children().length) {
+ current_time_index += event.deltaY;
+ }
+ if (timebox.children().eq(current_time_index).length) {
+ timebox.children().eq(current_time_index).trigger('mousedown');
+ }
+ return false;
+ }
+ if (options.datepicker && !options.timepicker) {
+ datepicker.trigger(event, [event.deltaY, event.deltaX, event.deltaY]);
+ if (input.val) {
+ input.val(_xdsoft_datetime.str());
+ }
+ datetimepicker.trigger('changedatetime.xdsoft');
+ return false;
+ }
+ });
+
+ datetimepicker
+ .on('changedatetime.xdsoft', function (event) {
+ if (options.onChangeDateTime && $.isFunction(options.onChangeDateTime)) {
+ var $input = datetimepicker.data('input');
+ options.onChangeDateTime.call(datetimepicker, _xdsoft_datetime.currentTime, $input, event);
+ delete options.value;
+ $input.trigger('change');
+ }
+ })
+ .on('generate.xdsoft', function () {
+ if (options.onGenerate && $.isFunction(options.onGenerate)) {
+ options.onGenerate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
+ }
+ if (triggerAfterOpen) {
+ datetimepicker.trigger('afterOpen.xdsoft');
+ triggerAfterOpen = false;
+ }
+ })
+ .on('click.xdsoft', function (xdevent) {
+ xdevent.stopPropagation();
+ });
+
+ current_time_index = 0;
+
+ /**
+ * Runs the callback for each of the specified node's ancestors.
+ *
+ * Return FALSE from the callback to stop ascending.
+ *
+ * @param {DOMNode} node
+ * @param {Function} callback
+ * @returns {undefined}
+ */
+ forEachAncestorOf = function (node, callback) {
+ do {
+ node = node.parentNode;
+
+ if (callback(node) === false) {
+ break;
+ }
+ } while (node.nodeName !== 'HTML');
+ };
+
+ /**
+ * Sets the position of the picker.
+ *
+ * @returns {undefined}
+ */
+ setPos = function () {
+ var dateInputOffset,
+ dateInputElem,
+ verticalPosition,
+ left,
+ position,
+ datetimepickerElem,
+ dateInputHasFixedAncestor,
+ $dateInput,
+ windowWidth,
+ verticalAnchorEdge,
+ datetimepickerCss,
+ windowHeight,
+ windowScrollTop;
+
+ $dateInput = datetimepicker.data('input');
+ dateInputOffset = $dateInput.offset();
+ dateInputElem = $dateInput[0];
+
+ verticalAnchorEdge = 'top';
+ verticalPosition = (dateInputOffset.top + dateInputElem.offsetHeight) - 1;
+ left = dateInputOffset.left;
+ position = "absolute";
+
+ windowWidth = $(window).width();
+ windowHeight = $(window).height();
+ windowScrollTop = $(window).scrollTop();
+
+ if ((document.documentElement.clientWidth - dateInputOffset.left) < datepicker.parent().outerWidth(true)) {
+ var diff = datepicker.parent().outerWidth(true) - dateInputElem.offsetWidth;
+ left = left - diff;
+ }
+
+ if ($dateInput.parent().css('direction') === 'rtl') {
+ left -= (datetimepicker.outerWidth() - $dateInput.outerWidth());
+ }
+
+ if (options.fixed) {
+ verticalPosition -= windowScrollTop;
+ left -= $(window).scrollLeft();
+ position = "fixed";
+ } else {
+ dateInputHasFixedAncestor = false;
+
+ forEachAncestorOf(dateInputElem, function (ancestorNode) {
+ if (window.getComputedStyle(ancestorNode).getPropertyValue('position') === 'fixed') {
+ dateInputHasFixedAncestor = true;
+ return false;
+ }
+ });
+
+ if (dateInputHasFixedAncestor) {
+ position = 'fixed';
+
+ //If the picker won't fit entirely within the viewport then display it above the date input.
+ if (verticalPosition + datetimepicker.outerHeight() > windowHeight + windowScrollTop) {
+ verticalAnchorEdge = 'bottom';
+ verticalPosition = (windowHeight + windowScrollTop) - dateInputOffset.top;
+ } else {
+ verticalPosition -= windowScrollTop;
+ }
+ } else {
+ if (verticalPosition + dateInputElem.offsetHeight > windowHeight + windowScrollTop) {
+ verticalPosition = dateInputOffset.top - dateInputElem.offsetHeight + 1;
+ }
+ }
+
+ if (verticalPosition < 0) {
+ verticalPosition = 0;
+ }
+
+ if (left + dateInputElem.offsetWidth > windowWidth) {
+ left = windowWidth - dateInputElem.offsetWidth;
+ }
+ }
+
+ datetimepickerElem = datetimepicker[0];
+
+ forEachAncestorOf(datetimepickerElem, function (ancestorNode) {
+ var ancestorNodePosition;
+
+ ancestorNodePosition = window.getComputedStyle(ancestorNode).getPropertyValue('position');
+
+ if (ancestorNodePosition === 'relative' && windowWidth >= ancestorNode.offsetWidth) {
+ left = left - ((windowWidth - ancestorNode.offsetWidth) / 2);
+ return false;
+ }
+ });
+
+ datetimepickerCss = {
+ position: position,
+ left: left,
+ top: '', //Initialize to prevent previous values interfering with new ones.
+ bottom: '' //Initialize to prevent previous values interfering with new ones.
+ };
+
+ datetimepickerCss[verticalAnchorEdge] = verticalPosition;
+
+ datetimepicker.css(datetimepickerCss);
+ };
+
+ datetimepicker
+ .on('open.xdsoft', function (event) {
+ var onShow = true;
+ if (options.onShow && $.isFunction(options.onShow)) {
+ onShow = options.onShow.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event);
+ }
+ if (onShow !== false) {
+ datetimepicker.show();
+ setPos();
+ $(window)
+ .off('resize.xdsoft', setPos)
+ .on('resize.xdsoft', setPos);
+
+ if (options.closeOnWithoutClick) {
+ $([document.body, window]).on('touchstart mousedown.xdsoft', function arguments_callee6() {
+ datetimepicker.trigger('close.xdsoft');
+ $([document.body, window]).off('touchstart mousedown.xdsoft', arguments_callee6);
+ });
+ }
+ }
+ })
+ .on('close.xdsoft', function (event) {
+ var onClose = true;
+ month_picker
+ .find('.xdsoft_month,.xdsoft_year')
+ .find('.xdsoft_select')
+ .hide();
+ if (options.onClose && $.isFunction(options.onClose)) {
+ onClose = options.onClose.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event);
+ }
+ if (onClose !== false && !options.opened && !options.inline) {
+ datetimepicker.hide();
+ }
+ event.stopPropagation();
+ })
+ .on('toggle.xdsoft', function () {
+ if (datetimepicker.is(':visible')) {
+ datetimepicker.trigger('close.xdsoft');
+ } else {
+ datetimepicker.trigger('open.xdsoft');
+ }
+ })
+ .data('input', input);
+
+ timer = 0;
+
+ datetimepicker.data('xdsoft_datetime', _xdsoft_datetime);
+ datetimepicker.setOptions(options);
+
+ function getCurrentValue() {
+ var ct = false, time;
+
+ if (options.startDate) {
+ ct = _xdsoft_datetime.strToDate(options.startDate);
+ } else {
+ ct = options.value || ((input && input.val && input.val()) ? input.val() : '');
+ if (ct) {
+ ct = _xdsoft_datetime.strToDateTime(ct);
+ } else if (options.defaultDate) {
+ ct = _xdsoft_datetime.strToDateTime(options.defaultDate);
+ if (options.defaultTime) {
+ time = _xdsoft_datetime.strtotime(options.defaultTime);
+ ct.setHours(time.getHours());
+ ct.setMinutes(time.getMinutes());
+ }
+ }
+ }
+
+ if (ct && _xdsoft_datetime.isValidDate(ct)) {
+ datetimepicker.data('changed', true);
+ } else {
+ ct = '';
+ }
+
+ return ct || 0;
+ }
+
+ function setMask(options) {
+
+ var isValidValue = function (mask, value) {
+ var reg = mask
+ .replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g, '\\$1')
+ .replace(/_/g, '{digit+}')
+ .replace(/([0-9]{1})/g, '{digit$1}')
+ .replace(/\{digit([0-9]{1})\}/g, '[0-$1_]{1}')
+ .replace(/\{digit[\+]\}/g, '[0-9_]{1}');
+ return (new RegExp(reg)).test(value);
+ },
+ getCaretPos = function (input) {
+ try {
+ if (document.selection && document.selection.createRange) {
+ var range = document.selection.createRange();
+ return range.getBookmark().charCodeAt(2) - 2;
+ }
+ if (input.setSelectionRange) {
+ return input.selectionStart;
+ }
+ } catch (e) {
+ return 0;
+ }
+ },
+ setCaretPos = function (node, pos) {
+ node = (typeof node === "string" || node instanceof String) ? document.getElementById(node) : node;
+ if (!node) {
+ return false;
+ }
+ if (node.createTextRange) {
+ var textRange = node.createTextRange();
+ textRange.collapse(true);
+ textRange.moveEnd('character', pos);
+ textRange.moveStart('character', pos);
+ textRange.select();
+ return true;
+ }
+ if (node.setSelectionRange) {
+ node.setSelectionRange(pos, pos);
+ return true;
+ }
+ return false;
+ };
+ if(options.mask) {
+ input.off('keydown.xdsoft');
+ }
+ if (options.mask === true) {
+ if (typeof moment != 'undefined') {
+ options.mask = options.format
+ .replace(/Y{4}/g, '9999')
+ .replace(/Y{2}/g, '99')
+ .replace(/M{2}/g, '19')
+ .replace(/D{2}/g, '39')
+ .replace(/H{2}/g, '29')
+ .replace(/m{2}/g, '59')
+ .replace(/s{2}/g, '59');
+ } else {
+ options.mask = options.format
+ .replace(/Y/g, '9999')
+ .replace(/F/g, '9999')
+ .replace(/m/g, '19')
+ .replace(/d/g, '39')
+ .replace(/H/g, '29')
+ .replace(/i/g, '59')
+ .replace(/s/g, '59');
+ }
+ }
+
+ if ($.type(options.mask) === 'string') {
+ if (!isValidValue(options.mask, input.val())) {
+ input.val(options.mask.replace(/[0-9]/g, '_'));
+ setCaretPos(input[0], 0);
+ }
+
+ input.on('keydown.xdsoft', function (event) {
+ var val = this.value,
+ key = event.which,
+ pos,
+ digit;
+
+ if (((key >= KEY0 && key <= KEY9) || (key >= _KEY0 && key <= _KEY9)) || (key === BACKSPACE || key === DEL)) {
+ pos = getCaretPos(this);
+ digit = (key !== BACKSPACE && key !== DEL) ? String.fromCharCode((_KEY0 <= key && key <= _KEY9) ? key - KEY0 : key) : '_';
+
+ if ((key === BACKSPACE || key === DEL) && pos) {
+ pos -= 1;
+ digit = '_';
+ }
+
+ while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) {
+ pos += (key === BACKSPACE || key === DEL) ? -1 : 1;
+ }
+
+ val = val.substr(0, pos) + digit + val.substr(pos + 1);
+ if ($.trim(val) === '') {
+ val = options.mask.replace(/[0-9]/g, '_');
+ } else {
+ if (pos === options.mask.length) {
+ event.preventDefault();
+ return false;
+ }
+ }
+
+ pos += (key === BACKSPACE || key === DEL) ? 0 : 1;
+ while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) {
+ pos += (key === BACKSPACE || key === DEL) ? -1 : 1;
+ }
+
+ if (isValidValue(options.mask, val)) {
+ this.value = val;
+ setCaretPos(this, pos);
+ } else if ($.trim(val) === '') {
+ this.value = options.mask.replace(/[0-9]/g, '_');
+ } else {
+ input.trigger('error_input.xdsoft');
+ }
+ } else {
+ if (([AKEY, CKEY, VKEY, ZKEY, YKEY].indexOf(key) !== -1 && ctrlDown) || [ESC, ARROWUP, ARROWDOWN, ARROWLEFT, ARROWRIGHT, F5, CTRLKEY, TAB, ENTER].indexOf(key) !== -1) {
+ return true;
+ }
+ }
+
+ event.preventDefault();
+ return false;
+ });
+ }
+ }
+
+ _xdsoft_datetime.setCurrentTime(getCurrentValue());
+
+ input
+ .data('xdsoft_datetimepicker', datetimepicker)
+ .on('open.xdsoft focusin.xdsoft mousedown.xdsoft touchstart', function () {
+ if (input.is(':disabled') || (input.data('xdsoft_datetimepicker').is(':visible') && options.closeOnInputClick)) {
+ return;
+ }
+ clearTimeout(timer);
+ timer = setTimeout(function () {
+ if (input.is(':disabled')) {
+ return;
+ }
+
+ triggerAfterOpen = true;
+ _xdsoft_datetime.setCurrentTime(getCurrentValue(), true);
+ if(options.mask) {
+ setMask(options);
+ }
+ datetimepicker.trigger('open.xdsoft');
+ }, 100);
+ })
+ .on('keydown.xdsoft', function (event) {
+ var elementSelector,
+ key = event.which;
+ if ([ENTER].indexOf(key) !== -1 && options.enterLikeTab) {
+ elementSelector = $("input:visible,textarea:visible,button:visible,a:visible");
+ datetimepicker.trigger('close.xdsoft');
+ elementSelector.eq(elementSelector.index(this) + 1).focus();
+ return false;
+ }
+ if ([TAB].indexOf(key) !== -1) {
+ datetimepicker.trigger('close.xdsoft');
+ return true;
+ }
+ })
+ .on('blur.xdsoft', function () {
+ datetimepicker.trigger('close.xdsoft');
+ });
+ };
+ destroyDateTimePicker = function (input) {
+ var datetimepicker = input.data('xdsoft_datetimepicker');
+ if (datetimepicker) {
+ datetimepicker.data('xdsoft_datetime', null);
+ datetimepicker.remove();
+ input
+ .data('xdsoft_datetimepicker', null)
+ .off('.xdsoft');
+ $(window).off('resize.xdsoft');
+ $([window, document.body]).off('mousedown.xdsoft touchstart');
+ if (input.unmousewheel) {
+ input.unmousewheel();
+ }
+ }
+ };
+ $(document)
+ .off('keydown.xdsoftctrl keyup.xdsoftctrl')
+ .on('keydown.xdsoftctrl', function (e) {
+ if (e.keyCode === CTRLKEY) {
+ ctrlDown = true;
+ }
+ })
+ .on('keyup.xdsoftctrl', function (e) {
+ if (e.keyCode === CTRLKEY) {
+ ctrlDown = false;
+ }
+ });
+
+ this.each(function () {
+ var datetimepicker = $(this).data('xdsoft_datetimepicker'), $input;
+ if (datetimepicker) {
+ if ($.type(opt) === 'string') {
+ switch (opt) {
+ case 'show':
+ $(this).select().focus();
+ datetimepicker.trigger('open.xdsoft');
+ break;
+ case 'hide':
+ datetimepicker.trigger('close.xdsoft');
+ break;
+ case 'toggle':
+ datetimepicker.trigger('toggle.xdsoft');
+ break;
+ case 'destroy':
+ destroyDateTimePicker($(this));
+ break;
+ case 'reset':
+ this.value = this.defaultValue;
+ if (!this.value || !datetimepicker.data('xdsoft_datetime').isValidDate(dateHelper.parseDate(this.value, options.format))) {
+ datetimepicker.data('changed', false);
+ }
+ datetimepicker.data('xdsoft_datetime').setCurrentTime(this.value);
+ break;
+ case 'validate':
+ $input = datetimepicker.data('input');
+ $input.trigger('blur.xdsoft');
+ break;
+ default:
+ if (datetimepicker[opt] && $.isFunction(datetimepicker[opt])) {
+ result = datetimepicker[opt](opt2);
+ }
+ }
+ } else {
+ datetimepicker
+ .setOptions(opt);
+ }
+ return 0;
+ }
+ if ($.type(opt) !== 'string') {
+ if (!options.lazyInit || options.open || options.inline) {
+ createDateTimePicker($(this));
+ } else {
+ lazyInit($(this));
+ }
+ }
+ });
+
+ return result;
+ };
+
+ $.fn.datetimepicker.defaults = default_options;
+
+ function HighlightedDate(date, desc, style) {
+ "use strict";
+ this.date = date;
+ this.desc = desc;
+ this.style = style;
+ }
+}));
+/*!
+ * jQuery Mousewheel 3.1.13
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ */
+
+(function (factory) {
+ if ( typeof define === 'function' && define.amd ) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else if (typeof exports === 'object') {
+ // Node/CommonJS style for Browserify
+ module.exports = factory;
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+}(function ($) {
+
+ var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
+ toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
+ ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
+ slice = Array.prototype.slice,
+ nullLowestDeltaTimeout, lowestDelta;
+
+ if ( $.event.fixHooks ) {
+ for ( var i = toFix.length; i; ) {
+ $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
+ }
+ }
+
+ var special = $.event.special.mousewheel = {
+ version: '3.1.12',
+
+ setup: function() {
+ if ( this.addEventListener ) {
+ for ( var i = toBind.length; i; ) {
+ this.addEventListener( toBind[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = handler;
+ }
+ // Store the line height and page height for this particular element
+ $.data(this, 'mousewheel-line-height', special.getLineHeight(this));
+ $.data(this, 'mousewheel-page-height', special.getPageHeight(this));
+ },
+
+ teardown: function() {
+ if ( this.removeEventListener ) {
+ for ( var i = toBind.length; i; ) {
+ this.removeEventListener( toBind[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = null;
+ }
+ // Clean up the data we added to the element
+ $.removeData(this, 'mousewheel-line-height');
+ $.removeData(this, 'mousewheel-page-height');
+ },
+
+ getLineHeight: function(elem) {
+ var $elem = $(elem),
+ $parent = $elem['offsetParent' in $.fn ? 'offsetParent' : 'parent']();
+ if (!$parent.length) {
+ $parent = $('body');
+ }
+ return parseInt($parent.css('fontSize'), 10) || parseInt($elem.css('fontSize'), 10) || 16;
+ },
+
+ getPageHeight: function(elem) {
+ return $(elem).height();
+ },
+
+ settings: {
+ adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
+ normalizeOffset: true // calls getBoundingClientRect for each event
+ }
+ };
+
+ $.fn.extend({
+ mousewheel: function(fn) {
+ return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
+ },
+
+ unmousewheel: function(fn) {
+ return this.unbind('mousewheel', fn);
+ }
+ });
+
+
+ function handler(event) {
+ var orgEvent = event || window.event,
+ args = slice.call(arguments, 1),
+ delta = 0,
+ deltaX = 0,
+ deltaY = 0,
+ absDelta = 0,
+ offsetX = 0,
+ offsetY = 0;
+ event = $.event.fix(orgEvent);
+ event.type = 'mousewheel';
+
+ // Old school scrollwheel delta
+ if ( 'detail' in orgEvent ) { deltaY = orgEvent.detail * -1; }
+ if ( 'wheelDelta' in orgEvent ) { deltaY = orgEvent.wheelDelta; }
+ if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY; }
+ if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }
+
+ // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
+ if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
+ deltaX = deltaY * -1;
+ deltaY = 0;
+ }
+
+ // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
+ delta = deltaY === 0 ? deltaX : deltaY;
+
+ // New school wheel delta (wheel event)
+ if ( 'deltaY' in orgEvent ) {
+ deltaY = orgEvent.deltaY * -1;
+ delta = deltaY;
+ }
+ if ( 'deltaX' in orgEvent ) {
+ deltaX = orgEvent.deltaX;
+ if ( deltaY === 0 ) { delta = deltaX * -1; }
+ }
+
+ // No change actually happened, no reason to go any further
+ if ( deltaY === 0 && deltaX === 0 ) { return; }
+
+ // Need to convert lines and pages to pixels if we aren't already in pixels
+ // There are three delta modes:
+ // * deltaMode 0 is by pixels, nothing to do
+ // * deltaMode 1 is by lines
+ // * deltaMode 2 is by pages
+ if ( orgEvent.deltaMode === 1 ) {
+ var lineHeight = $.data(this, 'mousewheel-line-height');
+ delta *= lineHeight;
+ deltaY *= lineHeight;
+ deltaX *= lineHeight;
+ } else if ( orgEvent.deltaMode === 2 ) {
+ var pageHeight = $.data(this, 'mousewheel-page-height');
+ delta *= pageHeight;
+ deltaY *= pageHeight;
+ deltaX *= pageHeight;
+ }
+
+ // Store lowest absolute delta to normalize the delta values
+ absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
+
+ if ( !lowestDelta || absDelta < lowestDelta ) {
+ lowestDelta = absDelta;
+
+ // Adjust older deltas if necessary
+ if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
+ lowestDelta /= 40;
+ }
+ }
+
+ // Adjust older deltas if necessary
+ if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
+ // Divide all the things by 40!
+ delta /= 40;
+ deltaX /= 40;
+ deltaY /= 40;
+ }
+
+ // Get a whole, normalized value for the deltas
+ delta = Math[ delta >= 1 ? 'floor' : 'ceil' ](delta / lowestDelta);
+ deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
+ deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);
+
+ // Normalise offsetX and offsetY properties
+ if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
+ var boundingRect = this.getBoundingClientRect();
+ offsetX = event.clientX - boundingRect.left;
+ offsetY = event.clientY - boundingRect.top;
+ }
+
+ // Add information to the event object
+ event.deltaX = deltaX;
+ event.deltaY = deltaY;
+ event.deltaFactor = lowestDelta;
+ event.offsetX = offsetX;
+ event.offsetY = offsetY;
+ // Go ahead and set deltaMode to 0 since we converted to pixels
+ // Although this is a little odd since we overwrite the deltaX/Y
+ // properties with normalized deltas.
+ event.deltaMode = 0;
+
+ // Add event and delta to the front of the arguments
+ args.unshift(event, delta, deltaX, deltaY);
+
+ // Clearout lowestDelta after sometime to better
+ // handle multiple device types that give different
+ // a different lowestDelta
+ // Ex: trackpad = 3 and mouse wheel = 120
+ if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
+ nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);
+
+ return ($.event.dispatch || $.event.handle).apply(this, args);
+ }
+
+ function nullLowestDelta() {
+ lowestDelta = null;
+ }
+
+ function shouldAdjustOldDeltas(orgEvent, absDelta) {
+ // If this is an older event and the delta is divisable by 120,
+ // then we are assuming that the browser is treating this as an
+ // older mouse wheel event and that we should divide the deltas
+ // by 40 to try and get a more usable deltaFactor.
+ // Side note, this actually impacts the reported scroll distance
+ // in older browsers and can cause scrolling to be slower than native.
+ // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
+ return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
+ }
+
+}));
diff --git a/template/acp/js/form.js b/template/acp/js/form.js
new file mode 100644
index 0000000..c67fc33
--- /dev/null
+++ b/template/acp/js/form.js
@@ -0,0 +1,1277 @@
+/*!
+ * jQuery Form Plugin
+ * version: 3.51.0-2014.06.20
+ * Requires jQuery v1.5 or later
+ * Copyright (c) 2014 M. Alsup
+ * Examples and documentation at: http://malsup.com/jquery/form/
+ * Project repository: https://github.com/malsup/form
+ * Dual licensed under the MIT and GPL licenses.
+ * https://github.com/malsup/form#copyright-and-license
+ */
+/*global ActiveXObject */
+
+// AMD support
+(function (factory) {
+ "use strict";
+ if (typeof define === 'function' && define.amd) {
+ // using AMD; register as anon module
+ define(['jquery'], factory);
+ } else {
+ // no AMD; invoke directly
+ factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
+ }
+}
+
+(function($) {
+"use strict";
+
+/*
+ Usage Note:
+ -----------
+ Do not use both ajaxSubmit and ajaxForm on the same form. These
+ functions are mutually exclusive. Use ajaxSubmit if you want
+ to bind your own submit handler to the form. For example,
+
+ $(document).ready(function() {
+ $('#myForm').on('submit', function(e) {
+ e.preventDefault(); // <-- important
+ $(this).ajaxSubmit({
+ target: '#output'
+ });
+ });
+ });
+
+ Use ajaxForm when you want the plugin to manage all the event binding
+ for you. For example,
+
+ $(document).ready(function() {
+ $('#myForm').ajaxForm({
+ target: '#output'
+ });
+ });
+
+ You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
+ form does not have to exist when you invoke ajaxForm:
+
+ $('#myForm').ajaxForm({
+ delegation: true,
+ target: '#output'
+ });
+
+ When using ajaxForm, the ajaxSubmit function will be invoked for you
+ at the appropriate time.
+*/
+
+/**
+ * Feature detection
+ */
+var feature = {};
+feature.fileapi = $(" ").get(0).files !== undefined;
+feature.formdata = window.FormData !== undefined;
+
+var hasProp = !!$.fn.prop;
+
+// attr2 uses prop when it can but checks the return type for
+// an expected string. this accounts for the case where a form
+// contains inputs with names like "action" or "method"; in those
+// cases "prop" returns the element
+$.fn.attr2 = function() {
+ if ( ! hasProp ) {
+ return this.attr.apply(this, arguments);
+ }
+ var val = this.prop.apply(this, arguments);
+ if ( ( val && val.jquery ) || typeof val === 'string' ) {
+ return val;
+ }
+ return this.attr.apply(this, arguments);
+};
+
+/**
+ * ajaxSubmit() provides a mechanism for immediately submitting
+ * an HTML form using AJAX.
+ */
+$.fn.ajaxSubmit = function(options) {
+ /*jshint scripturl:true */
+
+ // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
+ if (!this.length) {
+ log('ajaxSubmit: skipping submit process - no element selected');
+ return this;
+ }
+
+ var method, action, url, $form = this;
+
+ if (typeof options == 'function') {
+ options = { success: options };
+ }
+ else if ( options === undefined ) {
+ options = {};
+ }
+
+ method = options.type || this.attr2('method');
+ action = options.url || this.attr2('action');
+
+ url = (typeof action === 'string') ? $.trim(action) : '';
+ url = url || window.location.href || '';
+ if (url) {
+ // clean url (don't include hash vaue)
+ url = (url.match(/^([^#]+)/)||[])[1];
+ }
+
+ options = $.extend(true, {
+ url: url,
+ success: $.ajaxSettings.success,
+ type: method || $.ajaxSettings.type,
+ iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
+ }, options);
+
+ // hook for manipulating the form data before it is extracted;
+ // convenient for use with rich editors like tinyMCE or FCKEditor
+ var veto = {};
+ this.trigger('form-pre-serialize', [this, options, veto]);
+ if (veto.veto) {
+ log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
+ return this;
+ }
+
+ // provide opportunity to alter form data before it is serialized
+ if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
+ log('ajaxSubmit: submit aborted via beforeSerialize callback');
+ return this;
+ }
+
+ var traditional = options.traditional;
+ if ( traditional === undefined ) {
+ traditional = $.ajaxSettings.traditional;
+ }
+
+ var elements = [];
+ var qx, a = this.formToArray(options.semantic, elements);
+ if (options.data) {
+ options.extraData = options.data;
+ qx = $.param(options.data, traditional);
+ }
+
+ // give pre-submit callback an opportunity to abort the submit
+ if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
+ log('ajaxSubmit: submit aborted via beforeSubmit callback');
+ return this;
+ }
+
+ // fire vetoable 'validate' event
+ this.trigger('form-submit-validate', [a, this, options, veto]);
+ if (veto.veto) {
+ log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
+ return this;
+ }
+
+ var q = $.param(a, traditional);
+ if (qx) {
+ q = ( q ? (q + '&' + qx) : qx );
+ }
+ if (options.type.toUpperCase() == 'GET') {
+ options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
+ options.data = null; // data is null for 'get'
+ }
+ else {
+ options.data = q; // data is the query string for 'post'
+ }
+
+ var callbacks = [];
+ if (options.resetForm) {
+ callbacks.push(function() { $form.resetForm(); });
+ }
+ if (options.clearForm) {
+ callbacks.push(function() { $form.clearForm(options.includeHidden); });
+ }
+
+ // perform a load on the target only if dataType is not provided
+ if (!options.dataType && options.target) {
+ var oldSuccess = options.success || function(){};
+ callbacks.push(function(data) {
+ var fn = options.replaceTarget ? 'replaceWith' : 'html';
+ $(options.target)[fn](data).each(oldSuccess, arguments);
+ });
+ }
+ else if (options.success) {
+ callbacks.push(options.success);
+ }
+
+ options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
+ var context = options.context || this ; // jQuery 1.4+ supports scope context
+ for (var i=0, max=callbacks.length; i < max; i++) {
+ callbacks[i].apply(context, [data, status, xhr || $form, $form]);
+ }
+ };
+
+ if (options.error) {
+ var oldError = options.error;
+ options.error = function(xhr, status, error) {
+ var context = options.context || this;
+ oldError.apply(context, [xhr, status, error, $form]);
+ };
+ }
+
+ if (options.complete) {
+ var oldComplete = options.complete;
+ options.complete = function(xhr, status) {
+ var context = options.context || this;
+ oldComplete.apply(context, [xhr, status, $form]);
+ };
+ }
+
+ // are there files to upload?
+
+ // [value] (issue #113), also see comment:
+ // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
+ var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; });
+
+ var hasFileInputs = fileInputs.length > 0;
+ var mp = 'multipart/form-data';
+ var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
+
+ var fileAPI = feature.fileapi && feature.formdata;
+ log("fileAPI :" + fileAPI);
+ var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
+
+ var jqxhr;
+
+ // options.iframe allows user to force iframe mode
+ // 06-NOV-09: now defaulting to iframe mode if file input is detected
+ if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
+ // hack to fix Safari hang (thanks to Tim Molendijk for this)
+ // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
+ if (options.closeKeepAlive) {
+ $.get(options.closeKeepAlive, function() {
+ jqxhr = fileUploadIframe(a);
+ });
+ }
+ else {
+ jqxhr = fileUploadIframe(a);
+ }
+ }
+ else if ((hasFileInputs || multipart) && fileAPI) {
+ jqxhr = fileUploadXhr(a);
+ }
+ else {
+ jqxhr = $.ajax(options);
+ }
+
+ $form.removeData('jqxhr').data('jqxhr', jqxhr);
+
+ // clear element array
+ for (var k=0; k < elements.length; k++) {
+ elements[k] = null;
+ }
+
+ // fire 'notify' event
+ this.trigger('form-submit-notify', [this, options]);
+ return this;
+
+ // utility fn for deep serialization
+ function deepSerialize(extraData){
+ var serialized = $.param(extraData, options.traditional).split('&');
+ var len = serialized.length;
+ var result = [];
+ var i, part;
+ for (i=0; i < len; i++) {
+ // #252; undo param space replacement
+ serialized[i] = serialized[i].replace(/\+/g,' ');
+ part = serialized[i].split('=');
+ // #278; use array instead of object storage, favoring array serializations
+ result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
+ }
+ return result;
+ }
+
+ // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
+ function fileUploadXhr(a) {
+ var formdata = new FormData();
+
+ for (var i=0; i < a.length; i++) {
+ formdata.append(a[i].name, a[i].value);
+ }
+
+ if (options.extraData) {
+ var serializedData = deepSerialize(options.extraData);
+ for (i=0; i < serializedData.length; i++) {
+ if (serializedData[i]) {
+ formdata.append(serializedData[i][0], serializedData[i][1]);
+ }
+ }
+ }
+
+ options.data = null;
+
+ var s = $.extend(true, {}, $.ajaxSettings, options, {
+ contentType: false,
+ processData: false,
+ cache: false,
+ type: method || 'POST'
+ });
+
+ if (options.uploadProgress) {
+ // workaround because jqXHR does not expose upload property
+ s.xhr = function() {
+ var xhr = $.ajaxSettings.xhr();
+ if (xhr.upload) {
+ xhr.upload.addEventListener('progress', function(event) {
+ var percent = 0;
+ var position = event.loaded || event.position; /*event.position is deprecated*/
+ var total = event.total;
+ if (event.lengthComputable) {
+ percent = Math.ceil(position / total * 100);
+ }
+ options.uploadProgress(event, position, total, percent);
+ }, false);
+ }
+ return xhr;
+ };
+ }
+
+ s.data = null;
+ var beforeSend = s.beforeSend;
+ s.beforeSend = function(xhr, o) {
+ //Send FormData() provided by user
+ if (options.formData) {
+ o.data = options.formData;
+ }
+ else {
+ o.data = formdata;
+ }
+ if(beforeSend) {
+ beforeSend.call(this, xhr, o);
+ }
+ };
+ return $.ajax(s);
+ }
+
+ // private function for handling file uploads (hat tip to YAHOO!)
+ function fileUploadIframe(a) {
+ var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
+ var deferred = $.Deferred();
+
+ // #341
+ deferred.abort = function(status) {
+ xhr.abort(status);
+ };
+
+ if (a) {
+ // ensure that every serialized input is still enabled
+ for (i=0; i < elements.length; i++) {
+ el = $(elements[i]);
+ if ( hasProp ) {
+ el.prop('disabled', false);
+ }
+ else {
+ el.removeAttr('disabled');
+ }
+ }
+ }
+
+ s = $.extend(true, {}, $.ajaxSettings, options);
+ s.context = s.context || s;
+ id = 'jqFormIO' + (new Date().getTime());
+ if (s.iframeTarget) {
+ $io = $(s.iframeTarget);
+ n = $io.attr2('name');
+ if (!n) {
+ $io.attr2('name', id);
+ }
+ else {
+ id = n;
+ }
+ }
+ else {
+ $io = $('');
+ $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
+ }
+ io = $io[0];
+
+
+ xhr = { // mock object
+ aborted: 0,
+ responseText: null,
+ responseXML: null,
+ status: 0,
+ statusText: 'n/a',
+ getAllResponseHeaders: function() {},
+ getResponseHeader: function() {},
+ setRequestHeader: function() {},
+ abort: function(status) {
+ var e = (status === 'timeout' ? 'timeout' : 'aborted');
+ log('aborting upload... ' + e);
+ this.aborted = 1;
+
+ try { // #214, #257
+ if (io.contentWindow.document.execCommand) {
+ io.contentWindow.document.execCommand('Stop');
+ }
+ }
+ catch(ignore) {}
+
+ $io.attr('src', s.iframeSrc); // abort op in progress
+ xhr.error = e;
+ if (s.error) {
+ s.error.call(s.context, xhr, e, status);
+ }
+ if (g) {
+ $.event.trigger("ajaxError", [xhr, s, e]);
+ }
+ if (s.complete) {
+ s.complete.call(s.context, xhr, e);
+ }
+ }
+ };
+
+ g = s.global;
+ // trigger ajax global events so that activity/block indicators work like normal
+ if (g && 0 === $.active++) {
+ $.event.trigger("ajaxStart");
+ }
+ if (g) {
+ $.event.trigger("ajaxSend", [xhr, s]);
+ }
+
+ if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
+ if (s.global) {
+ $.active--;
+ }
+ deferred.reject();
+ return deferred;
+ }
+ if (xhr.aborted) {
+ deferred.reject();
+ return deferred;
+ }
+
+ // add submitting element to data if we know it
+ sub = form.clk;
+ if (sub) {
+ n = sub.name;
+ if (n && !sub.disabled) {
+ s.extraData = s.extraData || {};
+ s.extraData[n] = sub.value;
+ if (sub.type == "image") {
+ s.extraData[n+'.x'] = form.clk_x;
+ s.extraData[n+'.y'] = form.clk_y;
+ }
+ }
+ }
+
+ var CLIENT_TIMEOUT_ABORT = 1;
+ var SERVER_ABORT = 2;
+
+ function getDoc(frame) {
+ /* it looks like contentWindow or contentDocument do not
+ * carry the protocol property in ie8, when running under ssl
+ * frame.document is the only valid response document, since
+ * the protocol is know but not on the other two objects. strange?
+ * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
+ */
+
+ var doc = null;
+
+ // IE8 cascading access check
+ try {
+ if (frame.contentWindow) {
+ doc = frame.contentWindow.document;
+ }
+ } catch(err) {
+ // IE8 access denied under ssl & missing protocol
+ log('cannot get iframe.contentWindow document: ' + err);
+ }
+
+ if (doc) { // successful getting content
+ return doc;
+ }
+
+ try { // simply checking may throw in ie8 under ssl or mismatched protocol
+ doc = frame.contentDocument ? frame.contentDocument : frame.document;
+ } catch(err) {
+ // last attempt
+ log('cannot get iframe.contentDocument: ' + err);
+ doc = frame.document;
+ }
+ return doc;
+ }
+
+ // Rails CSRF hack (thanks to Yvan Barthelemy)
+ var csrf_token = $('meta[name=csrf-token]').attr('content');
+ var csrf_param = $('meta[name=csrf-param]').attr('content');
+ if (csrf_param && csrf_token) {
+ s.extraData = s.extraData || {};
+ s.extraData[csrf_param] = csrf_token;
+ }
+
+ // take a breath so that pending repaints get some cpu time before the upload starts
+ function doSubmit() {
+ // make sure form attrs are set
+ var t = $form.attr2('target'),
+ a = $form.attr2('action'),
+ mp = 'multipart/form-data',
+ et = $form.attr('enctype') || $form.attr('encoding') || mp;
+
+ // update form attrs in IE friendly way
+ form.setAttribute('target',id);
+ if (!method || /post/i.test(method) ) {
+ form.setAttribute('method', 'POST');
+ }
+ if (a != s.url) {
+ form.setAttribute('action', s.url);
+ }
+
+ // ie borks in some cases when setting encoding
+ if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
+ $form.attr({
+ encoding: 'multipart/form-data',
+ enctype: 'multipart/form-data'
+ });
+ }
+
+ // support timout
+ if (s.timeout) {
+ timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
+ }
+
+ // look for server aborts
+ function checkState() {
+ try {
+ var state = getDoc(io).readyState;
+ log('state = ' + state);
+ if (state && state.toLowerCase() == 'uninitialized') {
+ setTimeout(checkState,50);
+ }
+ }
+ catch(e) {
+ log('Server abort: ' , e, ' (', e.name, ')');
+ cb(SERVER_ABORT);
+ if (timeoutHandle) {
+ clearTimeout(timeoutHandle);
+ }
+ timeoutHandle = undefined;
+ }
+ }
+
+ // add "extra" data to form if provided in options
+ var extraInputs = [];
+ try {
+ if (s.extraData) {
+ for (var n in s.extraData) {
+ if (s.extraData.hasOwnProperty(n)) {
+ // if using the $.param format that allows for multiple values with the same name
+ if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
+ extraInputs.push(
+ $(' ').val(s.extraData[n].value)
+ .appendTo(form)[0]);
+ } else {
+ extraInputs.push(
+ $(' ').val(s.extraData[n])
+ .appendTo(form)[0]);
+ }
+ }
+ }
+ }
+
+ if (!s.iframeTarget) {
+ // add iframe to doc and submit the form
+ $io.appendTo('body');
+ }
+ if (io.attachEvent) {
+ io.attachEvent('onload', cb);
+ }
+ else {
+ io.addEventListener('load', cb, false);
+ }
+ setTimeout(checkState,15);
+
+ try {
+ form.submit();
+ } catch(err) {
+ // just in case form has element with name/id of 'submit'
+ var submitFn = document.createElement('form').submit;
+ submitFn.apply(form);
+ }
+ }
+ finally {
+ // reset attrs and remove "extra" input elements
+ form.setAttribute('action',a);
+ form.setAttribute('enctype', et); // #380
+ if(t) {
+ form.setAttribute('target', t);
+ } else {
+ $form.removeAttr('target');
+ }
+ $(extraInputs).remove();
+ }
+ }
+
+ if (s.forceSync) {
+ doSubmit();
+ }
+ else {
+ setTimeout(doSubmit, 10); // this lets dom updates render
+ }
+
+ var data, doc, domCheckCount = 50, callbackProcessed;
+
+ function cb(e) {
+ if (xhr.aborted || callbackProcessed) {
+ return;
+ }
+
+ doc = getDoc(io);
+ if(!doc) {
+ log('cannot access response document');
+ e = SERVER_ABORT;
+ }
+ if (e === CLIENT_TIMEOUT_ABORT && xhr) {
+ xhr.abort('timeout');
+ deferred.reject(xhr, 'timeout');
+ return;
+ }
+ else if (e == SERVER_ABORT && xhr) {
+ xhr.abort('server abort');
+ deferred.reject(xhr, 'error', 'server abort');
+ return;
+ }
+
+ if (!doc || doc.location.href == s.iframeSrc) {
+ // response not received yet
+ if (!timedOut) {
+ return;
+ }
+ }
+ if (io.detachEvent) {
+ io.detachEvent('onload', cb);
+ }
+ else {
+ io.removeEventListener('load', cb, false);
+ }
+
+ var status = 'success', errMsg;
+ try {
+ if (timedOut) {
+ throw 'timeout';
+ }
+
+ var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
+ log('isXml='+isXml);
+ if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
+ if (--domCheckCount) {
+ // in some browsers (Opera) the iframe DOM is not always traversable when
+ // the onload callback fires, so we loop a bit to accommodate
+ log('requeing onLoad callback, DOM not available');
+ setTimeout(cb, 250);
+ return;
+ }
+ // let this fall through because server response could be an empty document
+ //log('Could not access iframe DOM after mutiple tries.');
+ //throw 'DOMException: not available';
+ }
+
+ //log('response detected');
+ var docRoot = doc.body ? doc.body : doc.documentElement;
+ xhr.responseText = docRoot ? docRoot.innerHTML : null;
+ xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
+ if (isXml) {
+ s.dataType = 'xml';
+ }
+ xhr.getResponseHeader = function(header){
+ var headers = {'content-type': s.dataType};
+ return headers[header.toLowerCase()];
+ };
+ // support for XHR 'status' & 'statusText' emulation :
+ if (docRoot) {
+ xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
+ xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
+ }
+
+ var dt = (s.dataType || '').toLowerCase();
+ var scr = /(json|script|text)/.test(dt);
+ if (scr || s.textarea) {
+ // see if user embedded response in textarea
+ var ta = doc.getElementsByTagName('textarea')[0];
+ if (ta) {
+ xhr.responseText = ta.value;
+ // support for XHR 'status' & 'statusText' emulation :
+ xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
+ xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
+ }
+ else if (scr) {
+ // account for browsers injecting pre around json response
+ var pre = doc.getElementsByTagName('pre')[0];
+ var b = doc.getElementsByTagName('body')[0];
+ if (pre) {
+ xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
+ }
+ else if (b) {
+ xhr.responseText = b.textContent ? b.textContent : b.innerText;
+ }
+ }
+ }
+ else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
+ xhr.responseXML = toXml(xhr.responseText);
+ }
+
+ try {
+ data = httpData(xhr, dt, s);
+ }
+ catch (err) {
+ status = 'parsererror';
+ xhr.error = errMsg = (err || status);
+ }
+ }
+ catch (err) {
+ log('error caught: ',err);
+ status = 'error';
+ xhr.error = errMsg = (err || status);
+ }
+
+ if (xhr.aborted) {
+ log('upload aborted');
+ status = null;
+ }
+
+ if (xhr.status) { // we've set xhr.status
+ status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
+ }
+
+ // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
+ if (status === 'success') {
+ if (s.success) {
+ s.success.call(s.context, data, 'success', xhr);
+ }
+ deferred.resolve(xhr.responseText, 'success', xhr);
+ if (g) {
+ $.event.trigger("ajaxSuccess", [xhr, s]);
+ }
+ }
+ else if (status) {
+ if (errMsg === undefined) {
+ errMsg = xhr.statusText;
+ }
+ if (s.error) {
+ s.error.call(s.context, xhr, status, errMsg);
+ }
+ deferred.reject(xhr, 'error', errMsg);
+ if (g) {
+ $.event.trigger("ajaxError", [xhr, s, errMsg]);
+ }
+ }
+
+ if (g) {
+ $.event.trigger("ajaxComplete", [xhr, s]);
+ }
+
+ if (g && ! --$.active) {
+ $.event.trigger("ajaxStop");
+ }
+
+ if (s.complete) {
+ s.complete.call(s.context, xhr, status);
+ }
+
+ callbackProcessed = true;
+ if (s.timeout) {
+ clearTimeout(timeoutHandle);
+ }
+
+ // clean up
+ setTimeout(function() {
+ if (!s.iframeTarget) {
+ $io.remove();
+ }
+ else { //adding else to clean up existing iframe response.
+ $io.attr('src', s.iframeSrc);
+ }
+ xhr.responseXML = null;
+ }, 100);
+ }
+
+ var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
+ if (window.ActiveXObject) {
+ doc = new ActiveXObject('Microsoft.XMLDOM');
+ doc.async = 'false';
+ doc.loadXML(s);
+ }
+ else {
+ doc = (new DOMParser()).parseFromString(s, 'text/xml');
+ }
+ return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
+ };
+ var parseJSON = $.parseJSON || function(s) {
+ /*jslint evil:true */
+ return window['eval']('(' + s + ')');
+ };
+
+ var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
+
+ var ct = xhr.getResponseHeader('content-type') || '',
+ xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if (xml && data.documentElement.nodeName === 'parsererror') {
+ if ($.error) {
+ $.error('parsererror');
+ }
+ }
+ if (s && s.dataFilter) {
+ data = s.dataFilter(data, type);
+ }
+ if (typeof data === 'string') {
+ if (type === 'json' || !type && ct.indexOf('json') >= 0) {
+ data = parseJSON(data);
+ } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
+ $.globalEval(data);
+ }
+ }
+ return data;
+ };
+
+ return deferred;
+ }
+};
+
+/**
+ * ajaxForm() provides a mechanism for fully automating form submission.
+ *
+ * The advantages of using this method instead of ajaxSubmit() are:
+ *
+ * 1: This method will include coordinates for elements (if the element
+ * is used to submit the form).
+ * 2. This method will include the submit element's name/value data (for the element that was
+ * used to submit the form).
+ * 3. This method binds the submit() method to the form for you.
+ *
+ * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
+ * passes the options argument along after properly binding events for submit elements and
+ * the form itself.
+ */
+$.fn.ajaxForm = function(options) {
+ options = options || {};
+ options.delegation = options.delegation && $.isFunction($.fn.on);
+
+ // in jQuery 1.3+ we can fix mistakes with the ready state
+ if (!options.delegation && this.length === 0) {
+ var o = { s: this.selector, c: this.context };
+ if (!$.isReady && o.s) {
+ log('DOM not ready, queuing ajaxForm');
+ $(function() {
+ $(o.s,o.c).ajaxForm(options);
+ });
+ return this;
+ }
+ // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
+ log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
+ return this;
+ }
+
+ if ( options.delegation ) {
+ $(document)
+ .off('submit.form-plugin', this.selector, doAjaxSubmit)
+ .off('click.form-plugin', this.selector, captureSubmittingElement)
+ .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
+ .on('click.form-plugin', this.selector, options, captureSubmittingElement);
+ return this;
+ }
+
+ return this.ajaxFormUnbind()
+ .bind('submit.form-plugin', options, doAjaxSubmit)
+ .bind('click.form-plugin', options, captureSubmittingElement);
+};
+
+// private event handlers
+function doAjaxSubmit(e) {
+ /*jshint validthis:true */
+ var options = e.data;
+ if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
+ e.preventDefault();
+ $(e.target).ajaxSubmit(options); // #365
+ }
+}
+
+function captureSubmittingElement(e) {
+ /*jshint validthis:true */
+ var target = e.target;
+ var $el = $(target);
+ if (!($el.is("[type=submit],[type=image]"))) {
+ // is this a child element of the submit el? (ex: a span within a button)
+ var t = $el.closest('[type=submit]');
+ if (t.length === 0) {
+ return;
+ }
+ target = t[0];
+ }
+ var form = this;
+ form.clk = target;
+ if (target.type == 'image') {
+ if (e.offsetX !== undefined) {
+ form.clk_x = e.offsetX;
+ form.clk_y = e.offsetY;
+ } else if (typeof $.fn.offset == 'function') {
+ var offset = $el.offset();
+ form.clk_x = e.pageX - offset.left;
+ form.clk_y = e.pageY - offset.top;
+ } else {
+ form.clk_x = e.pageX - target.offsetLeft;
+ form.clk_y = e.pageY - target.offsetTop;
+ }
+ }
+ // clear form vars
+ setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
+}
+
+
+// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
+$.fn.ajaxFormUnbind = function() {
+ return this.unbind('submit.form-plugin click.form-plugin');
+};
+
+/**
+ * formToArray() gathers form element data into an array of objects that can
+ * be passed to any of the following ajax functions: $.get, $.post, or load.
+ * Each object in the array has both a 'name' and 'value' property. An example of
+ * an array for a simple login form might be:
+ *
+ * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
+ *
+ * It is this array that is passed to pre-submit callback functions provided to the
+ * ajaxSubmit() and ajaxForm() methods.
+ */
+$.fn.formToArray = function(semantic, elements) {
+ var a = [];
+ if (this.length === 0) {
+ return a;
+ }
+
+ var form = this[0];
+ var formId = this.attr('id');
+ var els = semantic ? form.getElementsByTagName('*') : form.elements;
+ var els2;
+
+ if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
+ els = $(els).get(); // convert to standard array
+ }
+
+ // #386; account for inputs outside the form which use the 'form' attribute
+ if ( formId ) {
+ els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
+ if ( els2.length ) {
+ els = (els || []).concat(els2);
+ }
+ }
+
+ if (!els || !els.length) {
+ return a;
+ }
+
+ var i,j,n,v,el,max,jmax;
+ for(i=0, max=els.length; i < max; i++) {
+ el = els[i];
+ n = el.name;
+ if (!n || el.disabled) {
+ continue;
+ }
+
+ if (semantic && form.clk && el.type == "image") {
+ // handle image inputs on the fly when semantic == true
+ if(form.clk == el) {
+ a.push({name: n, value: $(el).val(), type: el.type });
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
+ }
+ continue;
+ }
+
+ v = $.fieldValue(el, true);
+ if (v && v.constructor == Array) {
+ if (elements) {
+ elements.push(el);
+ }
+ for(j=0, jmax=v.length; j < jmax; j++) {
+ a.push({name: n, value: v[j]});
+ }
+ }
+ else if (feature.fileapi && el.type == 'file') {
+ if (elements) {
+ elements.push(el);
+ }
+ var files = el.files;
+ if (files.length) {
+ for (j=0; j < files.length; j++) {
+ a.push({name: n, value: files[j], type: el.type});
+ }
+ }
+ else {
+ // #180
+ a.push({ name: n, value: '', type: el.type });
+ }
+ }
+ else if (v !== null && typeof v != 'undefined') {
+ if (elements) {
+ elements.push(el);
+ }
+ a.push({name: n, value: v, type: el.type, required: el.required});
+ }
+ }
+
+ if (!semantic && form.clk) {
+ // input type=='image' are not found in elements array! handle it here
+ var $input = $(form.clk), input = $input[0];
+ n = input.name;
+ if (n && !input.disabled && input.type == 'image') {
+ a.push({name: n, value: $input.val()});
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
+ }
+ }
+ return a;
+};
+
+/**
+ * Serializes form data into a 'submittable' string. This method will return a string
+ * in the format: name1=value1&name2=value2
+ */
+$.fn.formSerialize = function(semantic) {
+ //hand off to jQuery.param for proper encoding
+ return $.param(this.formToArray(semantic));
+};
+
+/**
+ * Serializes all field elements in the jQuery object into a query string.
+ * This method will return a string in the format: name1=value1&name2=value2
+ */
+$.fn.fieldSerialize = function(successful) {
+ var a = [];
+ this.each(function() {
+ var n = this.name;
+ if (!n) {
+ return;
+ }
+ var v = $.fieldValue(this, successful);
+ if (v && v.constructor == Array) {
+ for (var i=0,max=v.length; i < max; i++) {
+ a.push({name: n, value: v[i]});
+ }
+ }
+ else if (v !== null && typeof v != 'undefined') {
+ a.push({name: this.name, value: v});
+ }
+ });
+ //hand off to jQuery.param for proper encoding
+ return $.param(a);
+};
+
+/**
+ * Returns the value(s) of the element in the matched set. For example, consider the following form:
+ *
+ *
+ *
+ * var v = $('input[type=text]').fieldValue();
+ * // if no values are entered into the text inputs
+ * v == ['','']
+ * // if values entered into the text inputs are 'foo' and 'bar'
+ * v == ['foo','bar']
+ *
+ * var v = $('input[type=checkbox]').fieldValue();
+ * // if neither checkbox is checked
+ * v === undefined
+ * // if both checkboxes are checked
+ * v == ['B1', 'B2']
+ *
+ * var v = $('input[type=radio]').fieldValue();
+ * // if neither radio is checked
+ * v === undefined
+ * // if first radio is checked
+ * v == ['C1']
+ *
+ * The successful argument controls whether or not the field element must be 'successful'
+ * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
+ * The default value of the successful argument is true. If this value is false the value(s)
+ * for each element is returned.
+ *
+ * Note: This method *always* returns an array. If no valid value can be determined the
+ * array will be empty, otherwise it will contain one or more values.
+ */
+$.fn.fieldValue = function(successful) {
+ for (var val=[], i=0, max=this.length; i < max; i++) {
+ var el = this[i];
+ var v = $.fieldValue(el, successful);
+ if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
+ continue;
+ }
+ if (v.constructor == Array) {
+ $.merge(val, v);
+ }
+ else {
+ val.push(v);
+ }
+ }
+ return val;
+};
+
+/**
+ * Returns the value of the field element.
+ */
+$.fieldValue = function(el, successful) {
+ var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
+ if (successful === undefined) {
+ successful = true;
+ }
+
+ if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
+ (t == 'checkbox' || t == 'radio') && !el.checked ||
+ (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
+ tag == 'select' && el.selectedIndex == -1)) {
+ return null;
+ }
+
+ if (tag == 'select') {
+ var index = el.selectedIndex;
+ if (index < 0) {
+ return null;
+ }
+ var a = [], ops = el.options;
+ var one = (t == 'select-one');
+ var max = (one ? index+1 : ops.length);
+ for(var i=(one ? index : 0); i < max; i++) {
+ var op = ops[i];
+ if (op.selected) {
+ var v = op.value;
+ if (!v) { // extra pain for IE...
+ v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
+ }
+ if (one) {
+ return v;
+ }
+ a.push(v);
+ }
+ }
+ return a;
+ }
+ return $(el).val();
+};
+
+/**
+ * Clears the form data. Takes the following actions on the form's input fields:
+ * - input text fields will have their 'value' property set to the empty string
+ * - select elements will have their 'selectedIndex' property set to -1
+ * - checkbox and radio inputs will have their 'checked' property set to false
+ * - inputs of type submit, button, reset, and hidden will *not* be effected
+ * - button elements will *not* be effected
+ */
+$.fn.clearForm = function(includeHidden) {
+ return this.each(function() {
+ $('input,select,textarea', this).clearFields(includeHidden);
+ });
+};
+
+/**
+ * Clears the selected form elements.
+ */
+$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
+ var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
+ return this.each(function() {
+ var t = this.type, tag = this.tagName.toLowerCase();
+ if (re.test(t) || tag == 'textarea') {
+ this.value = '';
+ }
+ else if (t == 'checkbox' || t == 'radio') {
+ this.checked = false;
+ }
+ else if (tag == 'select') {
+ this.selectedIndex = -1;
+ }
+ else if (t == "file") {
+ if (/MSIE/.test(navigator.userAgent)) {
+ $(this).replaceWith($(this).clone(true));
+ } else {
+ $(this).val('');
+ }
+ }
+ else if (includeHidden) {
+ // includeHidden can be the value true, or it can be a selector string
+ // indicating a special test; for example:
+ // $('#myForm').clearForm('.special:hidden')
+ // the above would clean hidden inputs that have the class of 'special'
+ if ( (includeHidden === true && /hidden/.test(t)) ||
+ (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) {
+ this.value = '';
+ }
+ }
+ });
+};
+
+/**
+ * Resets the form data. Causes all form elements to be reset to their original value.
+ */
+$.fn.resetForm = function() {
+ return this.each(function() {
+ // guard against an input with the name of 'reset'
+ // note that IE reports the reset function as an 'object'
+ if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
+ this.reset();
+ }
+ });
+};
+
+/**
+ * Enables or disables any matching elements.
+ */
+$.fn.enable = function(b) {
+ if (b === undefined) {
+ b = true;
+ }
+ return this.each(function() {
+ this.disabled = !b;
+ });
+};
+
+/**
+ * Checks/unchecks any matching checkboxes or radio buttons and
+ * selects/deselects and matching option elements.
+ */
+$.fn.selected = function(select) {
+ if (select === undefined) {
+ select = true;
+ }
+ return this.each(function() {
+ var t = this.type;
+ if (t == 'checkbox' || t == 'radio') {
+ this.checked = select;
+ }
+ else if (this.tagName.toLowerCase() == 'option') {
+ var $sel = $(this).parent('select');
+ if (select && $sel[0] && $sel[0].type == 'select-one') {
+ // deselect all other options
+ $sel.find('option').selected(false);
+ }
+ this.selected = select;
+ }
+ });
+};
+
+// expose debug var
+$.fn.ajaxSubmit.debug = false;
+
+// helper fn for console logging
+function log() {
+ if (!$.fn.ajaxSubmit.debug) {
+ return;
+ }
+ var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
+ if (window.console && window.console.log) {
+ window.console.log(msg);
+ }
+ else if (window.opera && window.opera.postError) {
+ window.opera.postError(msg);
+ }
+}
+
+}));
\ No newline at end of file
diff --git a/template/acp/js/functions.js b/template/acp/js/functions.js
new file mode 100644
index 0000000..57d1b69
--- /dev/null
+++ b/template/acp/js/functions.js
@@ -0,0 +1,64 @@
+function loading(show)
+{
+ if(show)
+ $('#loadinginfo').css('display', 'block');
+ else
+ $('#loadinginfo').css('display', 'none');
+}
+
+function cashback_output(id, type)
+{
+ if(type == 'mm')
+ bootbox.dialog('Вы подтверждаете, что вывели деньги на эл. кошелек?',
+ [{
+ "label" : "Подтверждаю",
+ callback: function() {
+ cashback_output_go(id)
+ }
+ },{
+ "label" : "Отмена"
+ }]
+ );
+ else
+ bootbox.dialog('Будет выполнен запрос через шлюз',
+ [{
+ "label" : "Продолжить",
+ callback: function() {
+ cashback_output_go(id)
+ }
+ },{
+ "label" : "Отмена"
+ }]
+ );
+}
+
+function cashback_output_go(id)
+{
+ $.getJSON(home+'cashback/id/'+id, function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ {
+ bootbox.dialog(val,
+ [{
+ "label" : "Продолжить",
+ callback: function() {
+ location.reload()
+ }
+ }]
+ );
+ }
+ });
+ });
+
+ return false
+}
\ No newline at end of file
diff --git a/template/acp/js/jquery.js b/template/acp/js/jquery.js
new file mode 100644
index 0000000..da41706
--- /dev/null
+++ b/template/acp/js/jquery.js
@@ -0,0 +1,6 @@
+/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license
+//@ sourceMappingURL=jquery-1.10.2.min.map
+*/
+(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML=" ",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML=" ","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML=" ",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML=" a ",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t
+}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""," "],legend:[1,""," "],area:[1,""," "],param:[1,""," "],thead:[1,""],tr:[2,""],col:[2,""],td:[3,""],_default:x.support.htmlSerialize?[0,"",""]:[1,"X","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle);
+u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write(""),t.close(),n=un(e,t),Pt.detach()),Gt[e]=n),n}function un(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[0],"display");return n.remove(),r}x.each(["height","width"],function(e,n){x.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(x.css(e,"display"))?x.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,i),i):0)}}}),x.support.opacity||(x.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=x.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===x.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),x(function(){x.support.reliableMarginRight||(x.cssHooks.marginRight={get:function(e,n){return n?x.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!x.support.pixelPosition&&x.fn.position&&x.each(["top","left"],function(e,n){x.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?x(e).position()[n]+"px":r):t}}})}),x.expr&&x.expr.filters&&(x.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!x.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||x.css(e,"display"))},x.expr.filters.visible=function(e){return!x.expr.filters.hidden(e)}),x.each({margin:"",padding:"",border:"Width"},function(e,t){x.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(x.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Ct.test(e))}).map(function(e,t){var n=x(this).val();return null==n?null:x.isArray(n)?x.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),x.param=function(e,n){var r,i=[],o=function(e,t){t=x.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=x.ajaxSettings&&x.ajaxSettings.traditional),x.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==x.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}x.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){x.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),x.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var mn,yn,vn=x.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Cn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Nn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=x.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=o.href}catch(Ln){yn=a.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(T)||[];if(x.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(l){var u;return o[l]=!0,x.each(e[l]||[],function(e,l){var c=l(n,r,i);return"string"!=typeof c||a||o[c]?a?!(u=c):t:(n.dataTypes.unshift(c),s(c),!1)}),u}return s(n.dataTypes[0])||!o["*"]&&s("*")}function _n(e,n){var r,i,o=x.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&x.extend(!0,e,r),e}x.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,l=e.indexOf(" ");return l>=0&&(i=e.slice(l,e.length),e=e.slice(0,l)),x.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&x.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?x("").append(x.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Cn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?_n(_n(e,x.ajaxSettings),t):_n(x.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,l,u,c,p=x.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),g=x.Callbacks("once memory"),m=p.statusCode||{},y={},v={},b=0,w="canceled",C={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return b||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)m[t]=[m[t],e[t]];else C.always(e[C.status]);return this},abort:function(e){var t=e||w;return u&&u.abort(t),k(0,t),this}};if(h.promise(C).complete=g.add,C.success=C.done,C.error=C.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=x.trim(p.dataType||"*").toLowerCase().match(T)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(mn[3]||("http:"===mn[1]?"80":"443")))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=x.param(p.data,p.traditional)),qn(An,p,n,C),2===b)return C;l=p.global,l&&0===x.active++&&x.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Nn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(x.lastModified[o]&&C.setRequestHeader("If-Modified-Since",x.lastModified[o]),x.etag[o]&&C.setRequestHeader("If-None-Match",x.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&C.setRequestHeader("Content-Type",p.contentType),C.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)C.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,C,p)===!1||2===b))return C.abort();w="abort";for(i in{success:1,error:1,complete:1})C[i](p[i]);if(u=qn(jn,p,n,C)){C.readyState=1,l&&d.trigger("ajaxSend",[C,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){C.abort("timeout")},p.timeout));try{b=1,u.send(y,k)}catch(N){if(!(2>b))throw N;k(-1,N)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,N=n;2!==b&&(b=2,s&&clearTimeout(s),u=t,a=i||"",C.readyState=e>0?4:0,c=e>=200&&300>e||304===e,r&&(w=Mn(p,C,r)),w=On(p,w,C,c),c?(p.ifModified&&(T=C.getResponseHeader("Last-Modified"),T&&(x.lastModified[o]=T),T=C.getResponseHeader("etag"),T&&(x.etag[o]=T)),204===e||"HEAD"===p.type?N="nocontent":304===e?N="notmodified":(N=w.state,y=w.data,v=w.error,c=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),C.status=e,C.statusText=(n||N)+"",c?h.resolveWith(f,[y,N,C]):h.rejectWith(f,[C,N,v]),C.statusCode(m),m=t,l&&d.trigger(c?"ajaxSuccess":"ajaxError",[C,p,c?y:v]),g.fireWith(f,[C,N]),l&&(d.trigger("ajaxComplete",[C,p]),--x.active||x.event.trigger("ajaxStop")))}return C},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,n){return x.get(e,t,n,"script")}}),x.each(["get","post"],function(e,n){x[n]=function(e,r,i,o){return x.isFunction(r)&&(o=o||i,i=r,r=t),x.ajax({url:e,type:n,dataType:o,data:r,success:i})}});function Mn(e,n,r){var i,o,a,s,l=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in l)if(l[s]&&l[s].test(o)){u.unshift(s);break}if(u[0]in r)a=u[0];else{for(s in r){if(!u[0]||e.converters[s+" "+u[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==u[0]&&u.unshift(a),r[a]):t}function On(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(p){return{state:"parsererror",error:a?p:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),x.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=a.head||x("head")[0]||a.documentElement;return{send:function(t,i){n=a.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Fn=[],Bn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Fn.pop()||x.expando+"_"+vn++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,l=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return l||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=x.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,l?n[l]=n[l].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||x.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Fn.push(o)),s&&x.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}x.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=x.ajaxSettings.xhr(),x.support.cors=!!Rn&&"withCredentials"in Rn,Rn=x.support.ajax=!!Rn,Rn&&x.ajaxTransport(function(n){if(!n.crossDomain||x.support.cors){var r;return{send:function(i,o){var a,s,l=n.xhr();if(n.username?l.open(n.type,n.url,n.async,n.username,n.password):l.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)l[s]=n.xhrFields[s];n.mimeType&&l.overrideMimeType&&l.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)l.setRequestHeader(s,i[s])}catch(u){}l.send(n.hasContent&&n.data||null),r=function(e,i){var s,u,c,p;try{if(r&&(i||4===l.readyState))if(r=t,a&&(l.onreadystatechange=x.noop,$n&&delete Pn[a]),i)4!==l.readyState&&l.abort();else{p={},s=l.status,u=l.getAllResponseHeaders(),"string"==typeof l.responseText&&(p.text=l.responseText);try{c=l.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,u)},n.async?4===l.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},x(e).unload($n)),Pn[a]=r),l.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+w+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Yn.exec(t),o=i&&i[3]||(x.cssNumber[e]?"":"px"),a=(x.cssNumber[e]||"px"!==o&&+r)&&Yn.exec(x.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,x.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=x.now()}function Zn(e,t,n){var r,i=(Qn[t]||[]).concat(Qn["*"]),o=0,a=i.length;for(;a>o;o++)if(r=i[o].call(n,t,e))return r}function er(e,t,n){var r,i,o=0,a=Gn.length,s=x.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;for(;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(tr(c,u.opts.specialEasing);a>o;o++)if(r=Gn[o].call(u,e,c,u.opts))return r;return x.map(c,Zn,u),x.isFunction(u.opts.start)&&u.opts.start.call(e,u),x.fx.timer(x.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function tr(e,t){var n,r,i,o,a;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=x.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(er,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,l,u=this,c={},p=e.style,f=e.nodeType&&nn(e),d=x._data(e,"fxshow");n.queue||(s=x._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,u.always(function(){u.always(function(){s.unqueued--,x.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(x.support.inlineBlockNeedsLayout&&"inline"!==ln(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",x.support.shrinkWrapBlocks||u.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Vn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(f?"hide":"show"))continue;c[r]=d&&d[r]||x.style(e,r)}if(!x.isEmptyObject(c)){d?"hidden"in d&&(f=d.hidden):d=x._data(e,"fxshow",{}),o&&(d.hidden=!f),f?x(e).show():u.done(function(){x(e).hide()}),u.done(function(){var t;x._removeData(e,"fxshow");for(t in c)x.style(e,t,c[t])});for(r in c)a=Zn(f?d[r]:0,r,u),r in d||(d[r]=a.start,f&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}x.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),a=function(){var t=er(this,x.extend({},e),o);(i||x._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=x.timers,a=x._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=x._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,a=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=rr.prototype.init,x.fx.tick=function(){var e,n=x.timers,r=0;for(Xn=x.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||x.fx.stop(),Xn=t},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){Un||(Un=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(Un),Un=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){x.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,x.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},x.offset={setOffset:function(e,t,n){var r=x.css(e,"position");"static"===r&&(e.style.position="relative");var i=x(e),o=i.offset(),a=x.css(e,"top"),s=x.css(e,"left"),l=("absolute"===r||"fixed"===r)&&x.inArray("auto",[a,s])>-1,u={},c={},p,f;l?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),x.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(u.top=t.top-o.top+p),null!=t.left&&(u.left=t.left-o.left+f),"using"in t?t.using.call(e,u):i.css(u)}},x.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===x.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(n=e.offset()),n.top+=x.css(e[0],"borderTopWidth",!0),n.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-x.css(r,"marginTop",!0),left:t.left-n.left-x.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);x.fn[e]=function(i){return x.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?x(a).scrollLeft():o,r?o:x(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return x.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}x.each({Height:"height",Width:"width"},function(e,n){x.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){x.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return x.access(this,function(n,r,i){var o;return x.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?x.css(n,r,s):x.style(n,r,i,s)},n,a?i:t,a,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=x:(e.jQuery=e.$=x,"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}))})(window);
diff --git a/template/acp/js/letter.js b/template/acp/js/letter.js
new file mode 100644
index 0000000..a2124f2
--- /dev/null
+++ b/template/acp/js/letter.js
@@ -0,0 +1,49 @@
+$('#letter').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ {
+ bootbox.dialog('
Внимание Рассылка выполнена.
Список почт, на которые не удалось отправить сообщение: '+val,
+ [{
+ "label" : "Продолжить",
+ callback : function(){location.reload()}
+ }]
+ );
+ }
+ });
+
+ loading(0)
+ }
+});
+
+function checked_all()
+{
+ if(letter_all)
+ {
+ for(i=0; i < document.letter.length; i++)
+ {
+ if(document.letter.elements[i].type == 'checkbox')
+ document.letter.elements[i].checked = false;
+ }
+
+ letter_all = false;
+ }else{
+ for(i=0; i < document.letter.length; i++)
+ {
+ if(document.letter.elements[i].type == 'checkbox')
+ document.letter.elements[i].checked = true;
+ }
+
+ letter_all = true;
+ }
+}
diff --git a/template/acp/js/logs.js b/template/acp/js/logs.js
new file mode 100644
index 0000000..461ae29
--- /dev/null
+++ b/template/acp/js/logs.js
@@ -0,0 +1,87 @@
+function logs_sys_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'logs/subsection/search'+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function logs_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'logs/section/search'+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+ });
+
+ loading(0);
+ }
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/news.js b/template/acp/js/news.js
new file mode 100644
index 0000000..1e3c2aa
--- /dev/null
+++ b/template/acp/js/news.js
@@ -0,0 +1,120 @@
+$('#news').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+
+ loading(0)
+ }
+});
+
+function news_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'news/subsection/search'+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function news_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить новость?',
+ [{
+ "label" : "Удалить",
+ callback : function(){news_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function news_delete_go(id)
+{
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'news/section/delete/id/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function bbcode(bbbegin, bbend, ta)
+{
+ form = document.getElementById(ta);
+ begin = form.value.substr(0, form.selectionStart);
+ end = form.value.substr(form.selectionEnd);
+ sel = form.value.substr(form.selectionStart, form.selectionEnd-form.selectionStart);
+ var text = form.firstChild;
+ form.value = begin+bbbegin+sel+bbend+end;
+ selPos = bbbegin.length+begin.length+sel.length+bbend.length;
+ form.setSelectionRange(begin.length, selPos);
+
+ return false;
+}
\ No newline at end of file
diff --git a/template/acp/js/notice.js b/template/acp/js/notice.js
new file mode 100644
index 0000000..92aa0f4
--- /dev/null
+++ b/template/acp/js/notice.js
@@ -0,0 +1,118 @@
+$('#notice').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+
+ loading(0)
+ }
+});
+
+function notice_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'notice/subsection/search'+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function change_notice_type()
+{
+ if($('#type').val() == 'unit')
+ {
+ $('#notice_unit').css('display', 'block');
+ $('#notice_server').css('display', 'none');
+ }else{
+ $('#notice_unit').css('display', 'none');
+ $('#notice_server').css('display', 'block');
+ }
+}
+
+function notice_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить уведомление?',
+ [{
+ "label" : "Удалить",
+ callback : function(){notice_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function notice_delete_go(id)
+{
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'notice/section/delete/id/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/pages.js b/template/acp/js/pages.js
new file mode 100644
index 0000000..21e5fb1
--- /dev/null
+++ b/template/acp/js/pages.js
@@ -0,0 +1,62 @@
+$('#page').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+
+ loading(0)
+ }
+});
+
+function page_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить страницу?',
+ [{
+ "label" : "Удалить",
+ callback : function(){page_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function page_delete_go(id)
+{
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'pages/section/delete/id/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/promo.js b/template/acp/js/promo.js
new file mode 100644
index 0000000..cd3c9a3
--- /dev/null
+++ b/template/acp/js/promo.js
@@ -0,0 +1,148 @@
+$('#promo').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+
+ loading(0)
+ }
+});
+
+function promo_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'promo/subsection/search'+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function promo_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить акцию?',
+ [{
+ "label" : "Удалить",
+ callback : function(){promo_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function promo_delete_go(id)
+{
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'promo/section/delete/id/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function promo_use_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить лог использования акции?',
+ [{
+ "label" : "Удалить",
+ callback : function(){promo_use_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function promo_use_delete_go(id)
+{
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'promo/section/stats/delete/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/servers.js b/template/acp/js/servers.js
new file mode 100644
index 0000000..3548690
--- /dev/null
+++ b/template/acp/js/servers.js
@@ -0,0 +1,224 @@
+$('#server').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+
+ loading(0)
+ }
+});
+
+function servers_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'servers/subsection/search'+url_search+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+
+ if(i == 'url')
+ url_search = val;
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function servers_overdue(id, time)
+{
+ bootbox.dialog('
Установка даты:
',
+ [{
+ "label" : "Установить",
+ callback : function(){servers_overdue_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function servers_block(id, time)
+{
+ bootbox.dialog('
Установка даты:
',
+ [{
+ "label" : "Заблокировать",
+ callback : function(){servers_block_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function datepick(input, time)
+{
+ if($('#'+input).val() != '')
+ time = $('#'+input).val();
+
+ $('#'+input).datetimepicker({value: time, format: 'd/m/Y H:i'});
+}
+
+function servers_overdue_go(id)
+{
+ $.ajax({
+ type: 'POST',
+ url: home+'servers/type/overdue/id/'+id+'/go',
+ data: 'time='+$('#date_overdue').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+ }
+ });
+}
+
+function servers_block_go(id)
+{
+ $.ajax({
+ type: 'POST',
+ url: home+'servers/type/block/id/'+id+'/go',
+ data: 'time='+$('#date_block').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+ }
+ });
+}
+
+function servers_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить сервер?',
+ [{
+ "label" : "Удалить",
+ callback : function(){servers_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function servers_delete_go(id)
+{
+
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'servers/section/delete/id/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function change_tarif(id)
+{
+
+ loading(1);
+
+ $.ajax({
+ url: home+'servers/type/tarif/id/'+id+'/tarif/'+$('#tarif').val()+'/go',
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/system.js b/template/acp/js/system.js
new file mode 100644
index 0000000..e6e90d3
--- /dev/null
+++ b/template/acp/js/system.js
@@ -0,0 +1,72 @@
+function system_load(go)
+{
+ if(go)
+ loading(1);
+
+ $.getJSON(home+'system/go/',
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ $('#'+i).html(val);
+ });
+
+ loading(0);
+
+ if(!go)
+ setTimeout(function() {system_load(false)}, 3000);
+ });
+}
+
+function system_restart(service)
+{
+ switch(service)
+ {
+ case 'apache2':
+ type = 'apache2';
+ break;
+ case 'nginx':
+ type = 'nginx';
+ break;
+ case 'mysql':
+ type = 'mysql';
+ break;
+ case 'unit':
+ type = 'локацию';
+ break;
+ }
+
+ bootbox.dialog('
Внимание Вы уверены что хотите перезагруить
'+type+' ',
+ [{
+ "label" : "Перезагрузить",
+ callback: function(){system_restart_go(service)}
+ },{
+ "label" : "Отмена"
+ }]);
+
+ return false;
+}
+
+function system_restart_go(id, service)
+{
+ loading(1);
+
+ $.getJSON(home+'system/service/'+service,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ system_load(true);
+ });
+
+ loading(0);
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/tarifs.js b/template/acp/js/tarifs.js
new file mode 100644
index 0000000..31a910b
--- /dev/null
+++ b/template/acp/js/tarifs.js
@@ -0,0 +1,141 @@
+$('#tarif').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.href=home+'tarifs/id/'+val
+ });
+
+ loading(0)
+ }
+});
+
+function tarifs_ports(ports)
+{
+ $('#ports').val(ports);
+}
+
+function tarifs_sort(sort)
+{
+ switch(sort)
+ {
+ case 'id':
+ if(sort_id == 'asc')
+ sort_id = 'desc';
+ else
+ sort_id = 'asc';
+
+ sorting = sort_id;
+
+ break;
+
+ case 'unit':
+ sort_unit = sort_unit == 'asc' ? 'desc' : 'asc';
+ sorting = sort_unit;
+
+ break;
+
+ case 'game':
+ sort_game = sort_game == 'asc' ? 'desc' : 'asc';
+ sorting = sort_game;
+
+ }
+
+ location.href=home+'tarifs/sort/'+sort+'/sorting/'+sorting;
+}
+
+function tarifs_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'tarifs/subsection/search'+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function tarifs_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить тариф?',
+ [{
+ "label" : "Удалить",
+ callback : function(){tarifs_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function tarifs_delete_go(id)
+{
+
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'tarifs/section/delete/id/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/units.js b/template/acp/js/units.js
new file mode 100644
index 0000000..b704c5c
--- /dev/null
+++ b/template/acp/js/units.js
@@ -0,0 +1,142 @@
+$('#unit').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.href=home+'units/id/'+val;
+ });
+
+ loading(0)
+ }
+});
+
+function units_load(id, go)
+{
+ if(go)
+ loading(1);
+
+ $.getJSON(home+'units/section/loading/id/'+id,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ $('#'+i+'_'+id).html(val);
+ });
+
+ loading(0);
+
+ if(!go)
+ setTimeout(function() {units_load(id, false)}, 3000);
+ });
+}
+
+function units_restart(id, service)
+{
+ switch(service)
+ {
+ case 'apache2':
+ type = 'apache2';
+ break;
+ case 'nginx':
+ type = 'nginx';
+ break;
+ case 'mysql':
+ type = 'mysql';
+ break;
+ case 'unit':
+ type = 'локацию';
+ break;
+ }
+
+ bootbox.dialog('
Внимание Вы уверены что хотите перезагруить
'+type+' ',
+ [{
+ "label" : "Перезагрузить",
+ callback: function(){units_restart_go(id, service)}
+ },{
+ "label" : "Отмена"
+ }]);
+
+ return false;
+}
+
+function units_restart_go(id, service)
+{
+ loading(1);
+
+ $.getJSON(home+'units/section/loading/id/'+id+'/service/'+service,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ units_load(id, true);
+ });
+
+ loading(0);
+ });
+}
+
+function units_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить локацию?.
'
+ +'При
"Удалить полностью" - удаляются все услуги и их логи, а также тарифы.',
+ [{
+ "label" : "Удалить",
+ callback : function(){units_delete_go(id, false)}
+ },{
+ "label" : "Удалить полностью",
+ callback : function(){units_delete_go(id, true)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function units_delete_go(id, all)
+{
+
+ loading(1);
+
+ if(all) go = '/delete/all'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'units/section/delete/id/'+id+go,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/users.js b/template/acp/js/users.js
new file mode 100644
index 0000000..c4b7d93
--- /dev/null
+++ b/template/acp/js/users.js
@@ -0,0 +1,179 @@
+$('#user').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ {
+ bootbox.dialog('
Внимание Внесенные изменения сохранены.',
+ [{
+ "label" : "Продолжить",
+ callback : function(){location.reload()}
+ }]
+ );
+ }
+ });
+
+ loading(0)
+ }
+});
+
+function users_sort(sort)
+{
+ switch(sort)
+ {
+ case 'id':
+ if(sort_id == 'asc')
+ sort_id = 'desc';
+ else
+ sort_id = 'asc';
+
+ sorting = sort_id;
+
+ break;
+
+ case 'balance':
+ sort_balance = sort_balance == 'asc' ? 'desc' : 'asc';
+ sorting = sort_balance;
+
+ break;
+
+ case 'group':
+ sort_group = sort_group == 'asc' ? 'desc' : 'asc';
+ sorting = sort_group;
+
+ }
+
+ location.href=home+'users/sort/'+sort+'/sorting/'+sorting;
+}
+
+function users_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'users/subsection/search'+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function users_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить этого пользователя?
'
+ +'При
"Удалить полностью" - удаляются все логи, услуги поступают в поток удаления.',
+ [{
+ "label" : "Удалить",
+ callback : function(){users_delete_go(id, false)}
+ },{
+ "label" : "Удалить полностью",
+ callback : function(){users_delete_go(id, true)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function users_delete_go(id, all)
+{
+
+ loading(1);
+
+ if(all) go = '/delete/all'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'users/section/delete/id/'+id+go,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function users_delete_signup(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить подачу регистрации?',
+ [{
+ "label" : "Удалить",
+ callback : function(){users_delete_signup_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function users_delete_signup_go(id)
+{
+
+ loading(1);
+
+ $.getJSON(home+'users/section/signup/delete/signup/id/'+id,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 's')
+ location.reload();
+ });
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/web.js b/template/acp/js/web.js
new file mode 100644
index 0000000..01af6b7
--- /dev/null
+++ b/template/acp/js/web.js
@@ -0,0 +1,87 @@
+function web_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'web/subsection/search'+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function web_delete(server, type)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить услугу?',
+ [{
+ "label" : "Удалить",
+ callback : function(){web_delete_go(server, type)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function web_delete_go(server, type)
+{
+ loading(1);
+
+ $.getJSON('/servers/id/'+server+'/section/web/subsection/'+type+'/action/delete/go', function(arr)
+ {
+ $.each(arr, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить",
+ }]
+ );
+
+ if(i == 'i')
+ bootbox.dialog('
Внимание '+val,
+ [{
+ "label" : "Продолжить",
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+
+ loading(0)
+ });
+}
\ No newline at end of file
diff --git a/template/acp/js/wiki.js b/template/acp/js/wiki.js
new file mode 100644
index 0000000..d88d571
--- /dev/null
+++ b/template/acp/js/wiki.js
@@ -0,0 +1,162 @@
+$('#wiki').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+
+ loading(0)
+ }
+});
+
+function wiki_search(go)
+{
+ if($('#search').val() == '')
+ {
+ $('#search_error').css('display', 'none');
+
+ return;
+ }
+
+ loading(1);
+
+ if(go) go = '/go'; else go = '';
+
+ $.ajax({
+ type: 'POST',
+ url: home+'wiki/subsection/search'+go,
+ data: 'text='+$('#search').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ if(val != '')
+ {
+ $('#search_error').css('display', 'inline-block');
+ $('#search_error').html(val);
+ }else
+ $('#search_error').css('display', 'none');
+ }
+
+ if(i == 's')
+ {
+ $('#search_error').css('display', 'none');
+ $('#search_result').html(val);
+ }
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function wiki_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить ответ?',
+ [{
+ "label" : "Удалить",
+ callback : function(){wiki_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function wiki_delete_go(id)
+{
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'wiki/section/delete/id/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function wiki_cat_delete(id)
+{
+ bootbox.dialog('
Внимание Вы уверены, что хотите удалить категорию?',
+ [{
+ "label" : "Удалить",
+ callback : function(){wiki_cat_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ }]
+ );
+
+ return false;
+}
+
+function wiki_cat_delete_go(id)
+{
+ loading(1);
+
+ $.ajax({
+ type: 'POST',
+ url: home+'wiki/section/delete/type/cat/id/'+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "Продолжить"
+ }]
+ );
+
+ if(i == 's')
+ location.reload()
+ });
+
+ loading(0);
+ }
+ });
+}
+
+function bbcode(bbbegin, bbend)
+{
+ form = document.getElementById('text');
+ begin = form.value.substr(0, form.selectionStart);
+ end = form.value.substr(form.selectionEnd);
+ sel = form.value.substr(form.selectionStart, form.selectionEnd-form.selectionStart);
+ var text = form.firstChild;
+ form.value = begin+bbbegin+sel+bbend+end;
+ selPos = bbbegin.length+begin.length+sel.length+bbend.length;
+ form.setSelectionRange(begin.length, selPos);
+
+ return false;
+}
\ No newline at end of file
diff --git a/template/acp/main.html b/template/acp/main.html
new file mode 100644
index 0000000..affcb97
--- /dev/null
+++ b/template/acp/main.html
@@ -0,0 +1,99 @@
+
+
+
+ Пользователей в базе:
+ [users] шт.
+
+
+ Серверов в базе:
+ [servers] шт.
+
+
+
+
+
+
+
Заявки на вывод денежных средств
+
+
+
+
+
+
Количество слот на серверах
+
+
+
+
+ Игра
+ Серверов
+ Слот
+
+
+
+
+ Counter-Strike: 1.6
+ [cs] шт.
+ [slots_cs] шт.
+
+
+ Counter-Strike: Source v34
+ [cssold] шт.
+ [slots_cssold] шт.
+
+
+ Counter-Strike: Source
+ [css] шт.
+ [slots_css] шт.
+
+
+ Counter-Strike: Global Offensive
+ [csgo] шт.
+ [slots_csgo] шт.
+
+
+ GTA: SA-MP
+ [samp] шт.
+ [slots_samp] шт.
+
+
+ GTA: CR-MP
+ [crmp] шт.
+ [slots_crmp] шт.
+
+
+ GTA: MTA
+ [mta] шт.
+ [slots_mta] шт.
+
+
+ Minecraft
+ [mc] шт.
+ [slots_mc] шт.
+
+
+
+
+
+
+
Состояние персонала
+
+
+
+
+
+ Имя
+ Группа
+ Адрес
+ Браузер
+ Состояние
+ Активность
+
+
+
+ [staff]
+
+
diff --git a/template/acp/pages.html b/template/acp/pages.html
new file mode 100644
index 0000000..486c2a8
--- /dev/null
+++ b/template/acp/pages.html
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/addons/addcat.html b/template/acp/sections/addons/addcat.html
new file mode 100644
index 0000000..b7d5f95
--- /dev/null
+++ b/template/acp/sections/addons/addcat.html
@@ -0,0 +1,54 @@
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/addons/addpl.html b/template/acp/sections/addons/addpl.html
new file mode 100644
index 0000000..2a166ae
--- /dev/null
+++ b/template/acp/sections/addons/addpl.html
@@ -0,0 +1,179 @@
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/addons/cats.html b/template/acp/sections/addons/cats.html
new file mode 100644
index 0000000..9758f97
--- /dev/null
+++ b/template/acp/sections/addons/cats.html
@@ -0,0 +1,17 @@
+
+
+
+ #
+ Название
+ Игра
+ Плагинов
+ Положение
+
+
+
+
+ [list]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/addons/index.html b/template/acp/sections/addons/index.html
new file mode 100644
index 0000000..405150c
--- /dev/null
+++ b/template/acp/sections/addons/index.html
@@ -0,0 +1,38 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+
+ Название
+ Категория
+ Статус
+ Игра
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/addons/menu.html b/template/acp/sections/addons/menu.html
new file mode 100644
index 0000000..29fa6c7
--- /dev/null
+++ b/template/acp/sections/addons/menu.html
@@ -0,0 +1,9 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/addons/plugin.html b/template/acp/sections/addons/plugin.html
new file mode 100644
index 0000000..e92e0eb
--- /dev/null
+++ b/template/acp/sections/addons/plugin.html
@@ -0,0 +1,145 @@
+
+ Основные настройки
+
+
+
+
+
+ Редактируемые файлы
+
+
+
+
+
+ Удаление текста из файлов
+
+
+
+
+
+ Добавление текста в файлы после установки плагина
+
+
+
+
+
+ Добавление текста в файлы после удаления плагина
+
+
+
+
+
+ Удаление файлов после установки плагина
+
+
+
+
+
+
+
Новые версии плагина
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/addons/update.html b/template/acp/sections/addons/update.html
new file mode 100644
index 0000000..c95b270
--- /dev/null
+++ b/template/acp/sections/addons/update.html
@@ -0,0 +1,130 @@
+
Назад к плагину
+
+
+
+
+ Основные настройки
+
+
+
+
+
+ Редактируемые файлы
+
+
+
+
+
+ Удаление текста из файлов
+
+
+
+
+
+ Добавление текста в файлы после установки плагина
+
+
+
+
+
+ Добавление текста в файлы после удаления плагина
+
+
+
+
+
+ Удаление файлов после установки плагина
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/addons/updmp.html b/template/acp/sections/addons/updmp.html
new file mode 100644
index 0000000..5ed40f7
--- /dev/null
+++ b/template/acp/sections/addons/updmp.html
@@ -0,0 +1,44 @@
+
+
+
+
+
Список карт выбранной локации и игры
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/boost/index.html b/template/acp/sections/boost/index.html
new file mode 100644
index 0000000..f7b2754
--- /dev/null
+++ b/template/acp/sections/boost/index.html
@@ -0,0 +1,84 @@
+
Статистика за все время
+
+
+
+
+ Количесто купленных кругов:
+ [all_num] шт.
+
+
+ Затраченная сумма:
+ [all_sum] [cur].
+
+
+
+
+
+
+
Статистика за предыдущий месяц [month_old]
+
+
+
+
+ Количесто купленных кругов:
+ [old_num] шт.
+
+
+ Затраченная сумма:
+ [old_sum] [cur].
+
+
+
+
+
+
+
Статистика за текущий месяц [month_now]
+
+
+
+
+ Количесто купленных кругов:
+ [now_num] шт.
+
+
+ Затраченная сумма:
+ [now_sum] [cur].
+
+
+
+
+
+
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+
+ Операция
+ Пользователь
+ Сервер
+ Дата
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/boost/menu.html b/template/acp/sections/boost/menu.html
new file mode 100644
index 0000000..0342045
--- /dev/null
+++ b/template/acp/sections/boost/menu.html
@@ -0,0 +1,6 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/control/index.html b/template/acp/sections/control/index.html
new file mode 100644
index 0000000..8bd5d22
--- /dev/null
+++ b/template/acp/sections/control/index.html
@@ -0,0 +1,35 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+ Номер / Владелец
+ Адрес / Статус
+ Добавлен / Аренда
+ Лимит / Цена
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/control/menu.html b/template/acp/sections/control/menu.html
new file mode 100644
index 0000000..314695b
--- /dev/null
+++ b/template/acp/sections/control/menu.html
@@ -0,0 +1,6 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/control/server.html b/template/acp/sections/control/server.html
new file mode 100644
index 0000000..003d734
--- /dev/null
+++ b/template/acp/sections/control/server.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/hosting/menu.html b/template/acp/sections/hosting/menu.html
new file mode 100644
index 0000000..55d67be
--- /dev/null
+++ b/template/acp/sections/hosting/menu.html
@@ -0,0 +1,6 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/jobs/add.html b/template/acp/sections/jobs/add.html
new file mode 100644
index 0000000..ea12794
--- /dev/null
+++ b/template/acp/sections/jobs/add.html
@@ -0,0 +1,58 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/jobs/edit.html b/template/acp/sections/jobs/edit.html
new file mode 100644
index 0000000..5eebad8
--- /dev/null
+++ b/template/acp/sections/jobs/edit.html
@@ -0,0 +1,54 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/jobs/index.html b/template/acp/sections/jobs/index.html
new file mode 100644
index 0000000..2a05bb2
--- /dev/null
+++ b/template/acp/sections/jobs/index.html
@@ -0,0 +1,38 @@
+
+
+
+ ID
+ Название
+ Должность
+ Описание
+ Статус
+ Создан
+
+
+
+
+
+ [list]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/jobs/menu.html b/template/acp/sections/jobs/menu.html
new file mode 100644
index 0000000..16901fd
--- /dev/null
+++ b/template/acp/sections/jobs/menu.html
@@ -0,0 +1,7 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/jobs/request.html b/template/acp/sections/jobs/request.html
new file mode 100644
index 0000000..ee7deb7
--- /dev/null
+++ b/template/acp/sections/jobs/request.html
@@ -0,0 +1,38 @@
+
+
+
+ ID
+ Пользователь
+ Ответ
+ Контакты
+ Вакансия
+ Создан
+
+
+
+
+
+ [list]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/jobs/request_edit.html b/template/acp/sections/jobs/request_edit.html
new file mode 100644
index 0000000..9e61e6f
--- /dev/null
+++ b/template/acp/sections/jobs/request_edit.html
@@ -0,0 +1,52 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/letter/index.html b/template/acp/sections/letter/index.html
new file mode 100644
index 0000000..9e82d69
--- /dev/null
+++ b/template/acp/sections/letter/index.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+ Пользователь
+ Почта
+ Баланс
+ Активность
+
+
+
+ [list]
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/letter/menu.html b/template/acp/sections/letter/menu.html
new file mode 100644
index 0000000..023bfa3
--- /dev/null
+++ b/template/acp/sections/letter/menu.html
@@ -0,0 +1,5 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/logs/index.html b/template/acp/sections/logs/index.html
new file mode 100644
index 0000000..dc0d913
--- /dev/null
+++ b/template/acp/sections/logs/index.html
@@ -0,0 +1,33 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+
+ Операция
+ Пользователь
+ Сервер
+ Дата
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/logs/logs.html b/template/acp/sections/logs/logs.html
new file mode 100644
index 0000000..e92722f
--- /dev/null
+++ b/template/acp/sections/logs/logs.html
@@ -0,0 +1,33 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+
+ Операция
+ Пользователь
+ Сумма
+ Дата
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/logs/menu.html b/template/acp/sections/logs/menu.html
new file mode 100644
index 0000000..82ea3a9
--- /dev/null
+++ b/template/acp/sections/logs/menu.html
@@ -0,0 +1,10 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/news/add.html b/template/acp/sections/news/add.html
new file mode 100644
index 0000000..762e292
--- /dev/null
+++ b/template/acp/sections/news/add.html
@@ -0,0 +1,43 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/news/index.html b/template/acp/sections/news/index.html
new file mode 100644
index 0000000..e89602f
--- /dev/null
+++ b/template/acp/sections/news/index.html
@@ -0,0 +1,34 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+
+ Заголовок
+ Теги
+ Просмотров
+ Дата
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/news/menu.html b/template/acp/sections/news/menu.html
new file mode 100644
index 0000000..d902d0f
--- /dev/null
+++ b/template/acp/sections/news/menu.html
@@ -0,0 +1,6 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/news/news.html b/template/acp/sections/news/news.html
new file mode 100644
index 0000000..9df23ad
--- /dev/null
+++ b/template/acp/sections/news/news.html
@@ -0,0 +1,43 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/notice/add.html b/template/acp/sections/notice/add.html
new file mode 100644
index 0000000..d334457
--- /dev/null
+++ b/template/acp/sections/notice/add.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/notice/index.html b/template/acp/sections/notice/index.html
new file mode 100644
index 0000000..50505c6
--- /dev/null
+++ b/template/acp/sections/notice/index.html
@@ -0,0 +1,32 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+
+ Уведомления
+
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/notice/menu.html b/template/acp/sections/notice/menu.html
new file mode 100644
index 0000000..588f5e0
--- /dev/null
+++ b/template/acp/sections/notice/menu.html
@@ -0,0 +1,7 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/notice/notice.html b/template/acp/sections/notice/notice.html
new file mode 100644
index 0000000..ee964fd
--- /dev/null
+++ b/template/acp/sections/notice/notice.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/pages/add.html b/template/acp/sections/pages/add.html
new file mode 100644
index 0000000..8a51f0f
--- /dev/null
+++ b/template/acp/sections/pages/add.html
@@ -0,0 +1,20 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/pages/index.html b/template/acp/sections/pages/index.html
new file mode 100644
index 0000000..d5856c9
--- /dev/null
+++ b/template/acp/sections/pages/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+ Название
+ Файл
+
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
\ No newline at end of file
diff --git a/template/acp/sections/pages/menu.html b/template/acp/sections/pages/menu.html
new file mode 100644
index 0000000..7a7ebf1
--- /dev/null
+++ b/template/acp/sections/pages/menu.html
@@ -0,0 +1,6 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/pages/page.html b/template/acp/sections/pages/page.html
new file mode 100644
index 0000000..8f46188
--- /dev/null
+++ b/template/acp/sections/pages/page.html
@@ -0,0 +1,20 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/promo/add.html b/template/acp/sections/promo/add.html
new file mode 100644
index 0000000..a262988
--- /dev/null
+++ b/template/acp/sections/promo/add.html
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
Информация по условиям
+
+
+
+
+
+ FPS: "fps":"1000" или "fps":"500:1000"
+ RAM: "ram":"50" или "fps":"50:80"
+ TickRate: "tickrate":"66" или "fps":"64:128"
+ Период: "time":"30" или "time":"30:90"
+ Слоты: "slots":"32" или "slots":"20:32" или "slots":"10-20"
+ Указывать несколько условий через запятые: "fps":"500:1000","slots":"10-32","time":"30"
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/promo/index.html b/template/acp/sections/promo/index.html
new file mode 100644
index 0000000..aca9fdb
--- /dev/null
+++ b/template/acp/sections/promo/index.html
@@ -0,0 +1,37 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+
+ Промо-код
+ Значение
+ Тариф
+ Акция
+ Тип
+ Лимит
+ Окончание
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/promo/menu.html b/template/acp/sections/promo/menu.html
new file mode 100644
index 0000000..0cc54df
--- /dev/null
+++ b/template/acp/sections/promo/menu.html
@@ -0,0 +1,8 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/promo/promo.html b/template/acp/sections/promo/promo.html
new file mode 100644
index 0000000..abae1f7
--- /dev/null
+++ b/template/acp/sections/promo/promo.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
Информация по условиям
+
+
+
+
+
+ FPS: "fps":"1000" или "fps":"500:1000"
+ RAM: "ram":"50" или "fps":"50:80"
+ TickRate: "tickrate":"66" или "fps":"64:128"
+ Период: "time":"30" или "time":"30:90"
+ Слоты: "slots":"32" или "slots":"20:32" или "slots":"10-20"
+ Указывать несколько условий через запятые: "fps":"500:1000","slots":"10-32","time":"30"
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/promo/stats.html b/template/acp/sections/promo/stats.html
new file mode 100644
index 0000000..612d92a
--- /dev/null
+++ b/template/acp/sections/promo/stats.html
@@ -0,0 +1,17 @@
+
+
+
+
+ Акция
+ Клиент
+ Операция
+ Дата
+
+
+
+
+ [list]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/servers/index.html b/template/acp/sections/servers/index.html
new file mode 100644
index 0000000..05f5793
--- /dev/null
+++ b/template/acp/sections/servers/index.html
@@ -0,0 +1,36 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+ Номер / Владелец
+ Название / Адрес
+ Локация / Тариф
+ Слоты / Статус
+ Игра / Аренда
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/servers/menu.html b/template/acp/sections/servers/menu.html
new file mode 100644
index 0000000..15b8524
--- /dev/null
+++ b/template/acp/sections/servers/menu.html
@@ -0,0 +1,13 @@
+
Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/servers/overdue.html b/template/acp/sections/servers/overdue.html
new file mode 100644
index 0000000..c95c96d
--- /dev/null
+++ b/template/acp/sections/servers/overdue.html
@@ -0,0 +1,14 @@
+
+
+
+ Номер / Владелец
+ Название / Адрес
+ Локация / Тариф
+ Слоты / Статус
+ Игра / Удаление
+
+
+
+ [list]
+
+
\ No newline at end of file
diff --git a/template/acp/sections/servers/server.html b/template/acp/sections/servers/server.html
new file mode 100644
index 0000000..6898746
--- /dev/null
+++ b/template/acp/sections/servers/server.html
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
Текущий тарифный план: [tarif]
+
+
+
+ Выберете новый тарифный план [tarifs]
+ Изменить
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/system/index.html b/template/acp/sections/system/index.html
new file mode 100644
index 0000000..090a16f
--- /dev/null
+++ b/template/acp/sections/system/index.html
@@ -0,0 +1,34 @@
+
+
+
+
+ CPU
+ RAM
+ HDD
+ Apache
+ Nginx
+ MySQL
+ Uptime
+ SSH
+
+
+
+
+ Панель управления
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/tarifs/add.html b/template/acp/sections/tarifs/add.html
new file mode 100644
index 0000000..53232a4
--- /dev/null
+++ b/template/acp/sections/tarifs/add.html
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+Информация по портам
+
+
+
+
+
+Информация по полям
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/tarifs/copy.html b/template/acp/sections/tarifs/copy.html
new file mode 100644
index 0000000..2e2857a
--- /dev/null
+++ b/template/acp/sections/tarifs/copy.html
@@ -0,0 +1,120 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/tarifs/index.html b/template/acp/sections/tarifs/index.html
new file mode 100644
index 0000000..50e89f8
--- /dev/null
+++ b/template/acp/sections/tarifs/index.html
@@ -0,0 +1,40 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+
+ Название
+ Локация
+ Слоты
+ Порты
+ Игра
+
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/tarifs/menu.html b/template/acp/sections/tarifs/menu.html
new file mode 100644
index 0000000..66abcf3
--- /dev/null
+++ b/template/acp/sections/tarifs/menu.html
@@ -0,0 +1,7 @@
+Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/tarifs/stats.html b/template/acp/sections/tarifs/stats.html
new file mode 100644
index 0000000..0da2e30
--- /dev/null
+++ b/template/acp/sections/tarifs/stats.html
@@ -0,0 +1,20 @@
+За все время всего: [all_money] [cur] / За [month] всего: [all_money_month] [cur]
+
+
+
+
+
+
+
+ Название
+ Локация
+ Всего/Просроченных
+ За все время
+ За [month]
+ Игра
+
+
+
+ [list]
+
+
\ No newline at end of file
diff --git a/template/acp/sections/tarifs/tarif.html b/template/acp/sections/tarifs/tarif.html
new file mode 100644
index 0000000..37d7ba0
--- /dev/null
+++ b/template/acp/sections/tarifs/tarif.html
@@ -0,0 +1,120 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/units/add.html b/template/acp/sections/units/add.html
new file mode 100644
index 0000000..670de57
--- /dev/null
+++ b/template/acp/sections/units/add.html
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/units/index.html b/template/acp/sections/units/index.html
new file mode 100644
index 0000000..f51aa84
--- /dev/null
+++ b/template/acp/sections/units/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+ Название
+ Адрес
+ Аренда
+ Домен
+
+
+
+
+ [list]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/units/loading.html b/template/acp/sections/units/loading.html
new file mode 100644
index 0000000..3a15bb5
--- /dev/null
+++ b/template/acp/sections/units/loading.html
@@ -0,0 +1,25 @@
+
+
+
+
+ Название
+ CPU
+ RAM
+ HDD
+ Apache
+ Nginx
+ MySQL
+ Uptime
+ SSH
+
+
+
+ [list]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/units/menu.html b/template/acp/sections/units/menu.html
new file mode 100644
index 0000000..495052c
--- /dev/null
+++ b/template/acp/sections/units/menu.html
@@ -0,0 +1,8 @@
+Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/units/stats.html b/template/acp/sections/units/stats.html
new file mode 100644
index 0000000..e87a6b3
--- /dev/null
+++ b/template/acp/sections/units/stats.html
@@ -0,0 +1,20 @@
+За все время всего: [all_money] [cur] / За [month] всего: [all_money_month] [cur]
+
+
+
+
+
+
+
+ Название
+ Аренда
+ Всего серверов
+ Просроченных
+ За все время
+ За [month]
+
+
+
+ [list]
+
+
\ No newline at end of file
diff --git a/template/acp/sections/units/unit.html b/template/acp/sections/units/unit.html
new file mode 100644
index 0000000..e4fd740
--- /dev/null
+++ b/template/acp/sections/units/unit.html
@@ -0,0 +1,82 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/users/index.html b/template/acp/sections/users/index.html
new file mode 100644
index 0000000..22d75fc
--- /dev/null
+++ b/template/acp/sections/users/index.html
@@ -0,0 +1,38 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+ #
+ Логин
+ Почта
+ Баланс
+ Группа
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/users/menu.html b/template/acp/sections/users/menu.html
new file mode 100644
index 0000000..aeb8210
--- /dev/null
+++ b/template/acp/sections/users/menu.html
@@ -0,0 +1,9 @@
+Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/users/offline.html b/template/acp/sections/users/offline.html
new file mode 100644
index 0000000..d393eb6
--- /dev/null
+++ b/template/acp/sections/users/offline.html
@@ -0,0 +1,17 @@
+
+
+
+
+ Логин
+ Почта
+ Группа
+ Последний визит
+
+
+
+
+ [list]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/users/online.html b/template/acp/sections/users/online.html
new file mode 100644
index 0000000..4292148
--- /dev/null
+++ b/template/acp/sections/users/online.html
@@ -0,0 +1,18 @@
+
+
+
+
+ Логин
+ Группа
+ Браузер
+ Адрес
+ Страна
+
+
+
+
+ [list]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/users/signup.html b/template/acp/sections/users/signup.html
new file mode 100644
index 0000000..c8eaa6a
--- /dev/null
+++ b/template/acp/sections/users/signup.html
@@ -0,0 +1,16 @@
+
+
+
+
+ Почта
+ Ключ
+ Время
+
+
+
+
+ [list]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/users/stats.html b/template/acp/sections/users/stats.html
new file mode 100644
index 0000000..bed19a3
--- /dev/null
+++ b/template/acp/sections/users/stats.html
@@ -0,0 +1,31 @@
+
+
+
+ Пользователей с положительным балансом:
+ [users] шт.
+
+
+ Cумма балансов пользователей:
+ [money] [cur]
+
+
+
+
+
+
+Пользователи с большим балансом
+
+
+
+
+
+ Логин
+ Почта
+ Баланс
+ Серверов
+
+
+
+ [list]
+
+
\ No newline at end of file
diff --git a/template/acp/sections/users/user.html b/template/acp/sections/users/user.html
new file mode 100644
index 0000000..c2bd951
--- /dev/null
+++ b/template/acp/sections/users/user.html
@@ -0,0 +1,177 @@
+Пользователь id_[id] / Регистрация: [date] / Рефералов: [part] шт. Последняя активность: [time]
+
+
+
+
+
+
+
+|support|
+
+
+Информация по уровням технической поддержки
+
+
+|_support|
+
+
+
+Сервера пользователя
+
+
+
+
+ #
+ Адрес
+ Тариф
+ Статус
+ Игра
+ Аренда
+
+
+
+ [serv_user]
+
+
+
+
+
+Финансовая статистика
+
+
+
+
+ Затрачено за [month]
+ [money_month] [cur]
+
+
+ Всего затрачено средств
+ [money_all] [cur]
+
+
+ Затрачено средств на продление
+ [money_extend] [cur]
+
+
+ Затрачено средств на аренду
+ [money_buy] [cur]
+
+
+
+
+
+
+Последние операции
+
+
+
+
+ Операция
+ Дата
+
+
+
+ [logs]
+
+
+
+
+
+Последние авторизации
+
+
+
+
+ Браузер
+ Адрес
+ Страна
+ Дата
+
+
+
+ [auth]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/web/index.html b/template/acp/sections/web/index.html
new file mode 100644
index 0000000..4010761
--- /dev/null
+++ b/template/acp/sections/web/index.html
@@ -0,0 +1,34 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+ Номер / Услуга
+ Владелец / Сервер
+ Адрес / Расположение
+ Логин / Пароль
+ Дата создания
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/web/menu.html b/template/acp/sections/web/menu.html
new file mode 100644
index 0000000..a2a2846
--- /dev/null
+++ b/template/acp/sections/web/menu.html
@@ -0,0 +1,11 @@
+Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/wiki/add.html b/template/acp/sections/wiki/add.html
new file mode 100644
index 0000000..f6ffbba
--- /dev/null
+++ b/template/acp/sections/wiki/add.html
@@ -0,0 +1,36 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/wiki/addcat.html b/template/acp/sections/wiki/addcat.html
new file mode 100644
index 0000000..d955dfd
--- /dev/null
+++ b/template/acp/sections/wiki/addcat.html
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/wiki/cat.html b/template/acp/sections/wiki/cat.html
new file mode 100644
index 0000000..1fc8a9a
--- /dev/null
+++ b/template/acp/sections/wiki/cat.html
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/wiki/cats.html b/template/acp/sections/wiki/cats.html
new file mode 100644
index 0000000..be95ba4
--- /dev/null
+++ b/template/acp/sections/wiki/cats.html
@@ -0,0 +1,16 @@
+
+
+
+ #
+ Название
+ Ответов
+ Положение
+
+
+
+
+ [list]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/wiki/index.html b/template/acp/sections/wiki/index.html
new file mode 100644
index 0000000..980e7a3
--- /dev/null
+++ b/template/acp/sections/wiki/index.html
@@ -0,0 +1,33 @@
+
+
+ Поиск
+
+
+
+
+
+
+
+
+
+
+ Вопрос
+ Категория
+ Дата
+
+
+
+
+ [list]
+
+
+
+[pages]
+
+
+
+
\ No newline at end of file
diff --git a/template/acp/sections/wiki/menu.html b/template/acp/sections/wiki/menu.html
new file mode 100644
index 0000000..11e3db9
--- /dev/null
+++ b/template/acp/sections/wiki/menu.html
@@ -0,0 +1,8 @@
+Управление
+
+
\ No newline at end of file
diff --git a/template/acp/sections/wiki/wiki.html b/template/acp/sections/wiki/wiki.html
new file mode 100644
index 0000000..14ecfbf
--- /dev/null
+++ b/template/acp/sections/wiki/wiki.html
@@ -0,0 +1,36 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/template/agreement.html b/template/agreement.html
new file mode 100644
index 0000000..3bf0a5b
--- /dev/null
+++ b/template/agreement.html
@@ -0,0 +1,86 @@
+
+
+
Договор оферты
+
+
+
+
+
Договор оферты
+
+
+
1. Описание услуг
+
+
1.1. Все игровые серверы ,предоставляемые клиентам, являются публичными или приватным и работают в зависимости от тарифного плана, выбранного клиентом при заказе. К серверам предоставляется полноценный виртуальный доступ (панель управления и FTP-доступ). Мы используем операционную систему Debian (Linux), что гарантирует стабильную и бесперебойную работу игровых серверов.
+
1.2 Все дополнительные услуги, предоставляемые нашей организацией, действуют согласно их описания и входят в общие правила договора.
+
1.3. Заказывая игровой сервер, клиент принимает пользовательское соглашение, а так же общие правила договора. И несет полную ответственность за свои действия.
+
+
2. Пользовательское соглашение
+
+
2.1. Клиент осуществляет заказ игрового сервера на странице оформления заказа.
+
2.2. В форме заказа клиент самостоятельно определяет все доступные параметры игрового сервера и тариф.
+
2.3. После проверки данных клиент должен выбрать способ оплаты заказа и нажать кнопку «Оплатить заказ».
+
2.4. Далее клиент оплачивает свой заказ в режиме реального времени, соответствуя условиям выбранного способа платежа.
+
2.5. Сразу, после зачисления денежных средств на счет исполнителя, сервер в автоматическом режиме создается на аккаунте клиента. Все данные для полного виртуального доступа (панель управления, FTP-доступ) предоставляются в разделе «Список серверов».
+
2.6. Полный доступ к серверу предоставляется на срок, который был выбран при оплате сервера. После окончания оплаченного срока клиенту будет предложено оплатить продление заказа.
+
2.7. В случае окончания оплаченного срока и дальнейшей неуплаты - сервер будет заблокирован и полностью удален по истечению недели с момента окончания срока аренды.
+
2.8. После оплаты своего заказа клиент имеет полное право обращаться за помощью в техническую поддержку, по каким либо вопросам, непосредственно связанным с работоспособностью сервера.
+
2.9. Сразу после оплаты игрового сервера клиент полностью соглашается со всеми пунктами договора и принимает данное пользовательское соглашение.
+
+
3. Форма обращения
+
+
3.1. Обращение к администрации и технической поддержке осуществляется на русском языке. Использование транслита и других языков не допускается.
+
+
+
При обращение клиент обязан:
+
+
• соблюдать нормы формального общения.
+
• подробно описать суть проблемы, с указанием адреса сервера.
+
• указать после каких действий на сервере была обнаружена проблема.
+
• указать время возникшей проблемы.
+
+
Время работы технической поддержки.
+
+
+
Техническая поддержка работает в будние дни с 10-00 до 18-00 по Московскому времени с перерывами на обед.(с 13-00 до 14-00), а так-же в выходные дни с 11-00 до 21-00 (Среднее время ответа до 1 часа, возможны задержки в связи с загруженностью сотрудников). Ответы в ночное время осущестляются по мере наличия сотрудников (В ночное время, среднее время ожидания ответа 3-4 часа). Праздничные дни согласно законодательству РФ - Выходные.
+
+
Запрещается:
+
• использование нецензурной лексики, брань.
+
• неуважительное отношение к персоналу сайта или его содержимому.
+
• требовать, предъявлять требования, по вопросам, нарушающим правила настоящего договора.
+
+
В случае нарушения одного или нескольких правил клиент получает предупреждение в соответствие с пунктом 3.2:
+
+
3.2 В случае нарушения пункта правил 3.1. клиент может получить:
+
3.2.1. Устное предупреждение или отказ от технической поддержки за неуважительное отношение к персоналу сайта.
+
3.2.2. Блокировку аккаунта сроком на 1 сутки за повторное нарушения п.п.3.1.
+
3.2.3. Блокировку аккаунта сроком на 2-е суток и более, без возможности возврата неиспользованных средств за неоднократные нарушения и оскорбления администрации сайта.
+
+
4. Допустимая нагрузка
+
+
4.1. У каждого сервера существует лимит нагрузки на ядро системы (% cpu) и ОЗУ (% mem). Допустимое значение нагрузки распределяется по тарифам.
+
4.2. Kлиент в праве сам выбирать какой тариф ему больше подходит, однако обязан придерживаться допустимым нормам нагрузки на ядро (п.п. 4.1).
+
4.3. Все вопросы, связанные с нагрузкой и блокировкой серверов, решаются через центр поддержки путем переговоров исполнителя с клиентом.
+
+
5. Отказ от предоставленных услуг
+
+
5.1. При нарушении правил и взлома сервера (увеличение слотов, увеличение денежных средств и.т.д) клиенту будет предоставлено устное предупреждение с отключением сервера до востребование клиентам. Дальнейшее не подчинение отключается сервера без возврата средств.
+
5.2. В случае отказа от предоставленных Клиенту услуг аренды игрового сервера, возврат неиспользованных средств осуществляется только при наличии объективных причин, по которым Клиент отказывается пользоваться уже оказанными ему услугами.
+
5.3. Сумма выплаты зависит от количества неиспользованных дней аренды. Выплата производятся на банковскую карту Клиента.
+
5.4. Возврат неиспользованных средств производится по заявке клиента, направленной через сайт в техническую поддержку, после чего заявка рассматривается администрацией сроком до 3х рабочий дней и выносится решение о выплате средств.
+
5.5. Возврат средств, за уже оказанные услуги, не производится.
+
5.6. Администрация имеет полное право отказать в возврате денежных средств, ввиду не объективных причин клиента (см. 5.1, 5.2. пункт правил).
+
+
+
+
\ No newline at end of file
diff --git a/template/all.html b/template/all.html
new file mode 100644
index 0000000..cf1d271
--- /dev/null
+++ b/template/all.html
@@ -0,0 +1,221 @@
+
+
+
+ [title]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Вверх
+
+
+ [nav]
+
+
+
+
+
Меню
+
+
+ [other_menu]
+
+
Пользователей онлайн
+ [online_users]
+
+
+
+ Онлайн игроков: [aop]
+
+
+
+
+
+
+
+
+
+
+ [main]
+
+
+
+
+ [nav]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/api.html b/template/api.html
new file mode 100644
index 0000000..6670b07
--- /dev/null
+++ b/template/api.html
@@ -0,0 +1,96 @@
+|auth|
+
+
+
+
+
Описание:
+
+
+
+
+Большинство наших клиентов работают с хостингом через панель управления. Нашим клиентам, которым требуется автоматизировать работу с хостингом без входа в панель управления, мы можем предложить API.
+
+
+С его помощью можно делать все тоже самое, что и через панель — создавать и удалять сервера, обновлять, запускать и останавливать, перезагружать и переустанавливать, а так же менять карту на сервере.
+
+
+
+
+
+
+
+
+
Примеры использования:
+
+
+
+
+[home]api/key/_ключ_/action/start
+[home]api/key/_ключ_/action/restart
+[home]api/key/_ключ_/action/stop
+[home]api/key/_ключ_/action/reinstall
+[home]api/key/_ключ_/action/update
+[home]api/key/_ключ_/action/change
+[home]api/key/_ключ_/action/change/change/de_dust2
+[home]api/key/_ключ_/action/console
+[home]api/key/_ключ_/action/console/command/status
+[home]api/key/_ключ_/action/load
+[home]api/key/_ключ_/action/data
+
+
+
+
+
+
+
Команды:
+
+
+
+start — Запустить сервер
+restart — Перезапустить сервер
+stop — Выключить сервер
+reinstall — Переустановить
+update — Обновить сервер
+change — Получить список карт
+change/change/_карта_ — Сменить карту
+console — Логи консоли
+console/command/_команда_ — Отправить команду в консоль
+load — Получить данные о нагрузке
+data — Получить информацию о сервере
+
+
+
+
+
+
+
Описание полученных строк:
+
+
+
+
+id — ID сервера в панели управление
+address — Адрес сервера (IP:PORT)
+unit — Локация сервера
+tarif — Тариф сервера
+game — Игра
+name — Название сервера (hostname)
+slots — Игровых слотов
+online — Игроков онлайн
+players — Вывод игроков сервера
+status — Статус сервера (Если сервер включён выводит название карты)
+img — Картинка карты (Если сервер включён)
+time_end — Кол-во оставшихся дней
+time — Дата окончание аренды
+date — Дата создания
+pack — Сборка/Build сервера
+
+
+
+
|_auth|
+
+|!auth|
+
API интерфейс
+
+
Для просмотра этой страницы, вам необходимо авторизоваться .
+
+|_!auth|
\ No newline at end of file
diff --git a/template/chat.html b/template/chat.html
new file mode 100644
index 0000000..a514226
--- /dev/null
+++ b/template/chat.html
@@ -0,0 +1,23 @@
+
+
+
+
[date]
+
+ |me| |_me|
+ |!me||adm| |_adm||_!me|
+ |me| |_me|
+ |!me| |_!me|
+
+
|me|Я |_me| |!me|[name]|_!me|
+
[msg]
|date|
(ред.) |_date|
+ |img|
+
+ |_img|
+
+
+
\ No newline at end of file
diff --git a/template/contacts.html b/template/contacts.html
new file mode 100644
index 0000000..018bb63
--- /dev/null
+++ b/template/contacts.html
@@ -0,0 +1,79 @@
+
+
+
+
+
Контактная информация
+
+
+
Адрес: 107564, Москва, Краснобогатырская улица, 2с1
+
+
+
+
E-mail: 696e666f40656e67696e6567702e7275
+
Тел.: +7(495)0010203
+
+
+
+
+
Мы в социальных сетях
+
+
Телеграм канал: @tg_channel
+
+
Обратите пожалуйста внимание что, 13 апреля 2018 года Таганский суд Москвы вынес решение в пользу Роскомнадзора, тем самым позволив начать блокировку мессенджера на территории Российской Федерации.
+
+
+
Вконтакте: @vk_group_id
+
WhatsApp: @phone_number
+
Viber: @phone_number
+
YouTube: @channel_name
+
+
+
+
+
Техническая поддержка
+
+
+
Время работы: Круглосуточно
+
+
Техническая поддержка осуществляется только через тикет систему в панели управления. Перед созданием нового тикета рекомендуем заглянуть в нашу базу знаний , где Вы сможете найти ответы на большинство ваших вопросов
+
+
+
diff --git a/template/css/bootbox.css b/template/css/bootbox.css
new file mode 100644
index 0000000..c683693
--- /dev/null
+++ b/template/css/bootbox.css
@@ -0,0 +1,123 @@
+.fade
+{
+ opacity:0;
+ -webkit-transition:opacity .15s linear;
+ -moz-transition:opacity .15s linear;
+ -o-transition:opacity .15s linear;
+ transition:opacity .15s linear
+}
+.fade.in{
+ opacity:1
+}
+.modal-backdrop
+{
+ position:fixed;
+ top:0;
+ right:0;
+ bottom:0;
+ left:0;
+ z-index:1040;
+ background-color:#000
+}
+.modal-backdrop.fade{
+ opacity:0
+}
+.modal-backdrop,.modal-backdrop.fade.in
+{
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+ background-color: #90D3F4;
+}
+.modal
+{
+ position:fixed;
+ top:40%;
+ left:45%;
+ z-index:1050;
+ width:560px;
+ margin-left:-280px;
+ background-color:#fff;
+ border:1px solid #EEE;
+ *border:1px solid #EEE;
+ outline:0;
+ -webkit-background-clip:padding-box;
+ -moz-background-clip:padding-box;
+ background-clip:padding-box
+}
+.modal.fade
+{
+ top:-25%;
+ -webkit-transition:opacity .3s linear,top .3s ease-out;
+ -moz-transition:opacity .3s linear,top .3s ease-out;
+ -o-transition:opacity .3s linear,top .3s ease-out;
+ transition:opacity .3s linear,top .3s ease-out
+}
+.modal.fade.in
+{
+ top:25%;
+ left: 50%;
+}
+.modal-header
+{
+ padding:9px 15px;
+ border-bottom:1px solid #eee
+}
+.modal-header .close
+{
+ margin-top:2px
+}
+.modal-header h3
+{
+ margin:0;
+ line-height:30px
+}
+.modal-body
+{
+ position:relative;
+ max-height:400px;
+ padding:15px;
+ overflow-y:auto
+}
+.modal-form
+{
+ margin-bottom:0
+}
+.modal-footer
+{
+ padding:14px 15px 15px;
+ margin-bottom:0;
+ text-align:right;
+ background-color:#f5f5f5;
+ border-top:1px solid #ddd;
+ -webkit-border-radius:0 0 6px 6px;
+ -moz-border-radius:0 0 6px 6px;
+ border-radius:0 0 6px 6px;
+ *zoom:1;
+ -webkit-box-shadow:inset 0 1px 0 #fff;
+ -moz-box-shadow:inset 0 1px 0 #fff;
+ box-shadow:inset 0 1px 0 #fff
+}
+.modal-footer:before,.modal-footer:after
+{
+ display:table;
+ line-height:0;
+ content:""
+}
+.modal-footer:after
+{
+ clear:both
+}
+.modal-footer .btn+.btn
+{
+ margin-bottom:0;
+ margin-left:5px
+}
+.modal-footer .btn-group .btn+.btn
+{
+ margin-left:-1px
+}
+.modal-footer .btn-block+.btn-block
+{
+ margin-left:0
+}
+
diff --git a/template/css/chat.css b/template/css/chat.css
new file mode 100644
index 0000000..88f412a
--- /dev/null
+++ b/template/css/chat.css
@@ -0,0 +1,234 @@
+.chat_main_head span.chat_sender > a
+{
+ display: none;
+}
+.chat_main_head:hover span.chat_sender > a
+{
+ display: initial;
+}
+.chat_data a
+{
+
+ text-decoration: underline;
+}
+.chat_data_foot
+{
+ padding: 5px 5px 0 5px;
+ font-size: 10px;
+}
+.chat_data_foot > div
+{
+ display: inline-block;
+ width: 100%;
+ margin: 5px 0;
+}
+.chat_data_foot > div > div
+{
+ width: 88px;
+ float: left;
+ margin-right: 5px;
+}
+.chat_data_foot > div > div > a .image
+{
+ position: relative;
+ height: 80px;
+ width: 80px;
+ border: 4px solid #fff;
+ box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
+ background-color: #fff;
+ float: left;
+ overflow: hidden;
+ background-size: contain !important;
+ background-repeat: no-repeat !important;
+ text-align: center;
+}
+.chat_msg
+{
+ position: relative;
+ display: block;
+ margin-bottom: 15px;
+}
+.chat_msg_ava
+{
+ height: 57px;
+ width: 57px;
+ border: 4px solid #fff;
+ box-shadow: 0px 0px 1px 1px #DDD;
+ overflow: hidden;
+ margin-top: 5px;
+ border-radius: 50%;
+ float: left;
+}
+.chat_msg > .chat_main
+{
+ margin-left: 80px;
+}
+.chat_msg > .chat_main
+{
+ padding-right: 5px;
+ display: block;
+}
+.chat_main_head
+{
+ padding: 0 5px;
+ line-height: 22px;
+}
+.chat_main_head > span.chat_sender
+{
+ color: #597da3;
+ font-size: 11px;
+ font-family: 'Roboto Medium';
+}
+.chat_date
+{
+ color: #888;
+ float: right;
+ font-size: 10px;
+ margin-top: 2px;
+}
+.chat_body
+{
+ position: relative;
+}
+.chat_msg > .chat_main .chat_body span.chat_sprite
+{
+ background: url(../images/chat_sprite.png) no-repeat 0px 0px;
+ width: 25px;
+ height: 17px;
+ display: block;
+ position: absolute;
+ left: -11px;
+ top: 8px;
+}
+.chat_data
+{
+ background: #fff;
+ border: 1px solid rgba(196, 196, 196, 0.47);
+ padding: 10px;
+ color: #000;
+ font-size: 12px;
+ z-index: 0;
+ display: block;
+ font-family: 'Exo 2', sans-serif;
+ font-style: italic;
+ font-weight: 300;
+ -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.05);
+ -moz-box-shadow: 0 1px 1px rgba(0,0,0,0.05);
+ box-shadow: 0 1px 1px rgba(0,0,0,0.05);
+ word-wrap: break-word;
+}
+.chat_msg_ava img
+{
+ width: 110%;
+ min-height: 112%;
+ margin-top: -3px;
+ margin-left: -2px;
+}
+
+#dialog {
+ height: 500px;
+ overflow-y: scroll;
+}
+
+.emoji {
+ display: inline-block;
+ background: url(../images/emoji.png) no-repeat 0px 0px;
+ height: 16px;
+ width: 16px;
+}
+.emoji_1 {
+ background-position: 0px 0px;
+}
+.emoji_2 {
+ background-position: 0px -17px;
+}
+.emoji_3 {
+ background-position: 0px -34px;
+}
+.emoji_4 {
+ background-position: 0px -51px;
+}
+.emoji_5 {
+ background-position: 0px -68px;
+}
+.emoji_6 {
+ background-position: 0px -85px;
+}
+.emoji_7 {
+ background-position: 0px -102px;
+}
+.emoji_8 {
+ background-position: 0px -119px;
+}
+.emoji_9 {
+ background-position: 0px -136px;
+}
+.emoji_10 {
+ background-position: 0px -153px;
+}
+.emoji_11 {
+ background-position: 0px -170px;
+}
+.emoji_12 {
+ background-position: 0px -187px;
+}
+.emoji_13 {
+ background-position: 0px -204px;
+}
+.emoji_14 {
+ background-position: 0px -221px;
+}
+.emoji_15 {
+ background-position: 0px -238px;
+}
+.emoji_16 {
+ background-position: 0px -255px;
+}
+.emoji_17 {
+ background-position: 0px -272px;
+}
+.emoji_18 {
+ background-position: 0px -289px;
+}
+.emoji_19 {
+ background-position: 0px -306px;
+}
+.emoji_20 {
+ background-position: 0px -323px;
+}
+.emoji_21 {
+ background-position: 0px -340px;
+}
+.emoji_22 {
+ background-position: 0px -357px;
+}
+.emoji_23 {
+ background-position: 0px -374px;
+}
+.emoji_24 {
+ background-position: 0px -391px;
+}
+.emoji_25 {
+ background-position: 0px -408px;
+}
+.emoji_26 {
+ background-position: 0px -425px;
+}
+.emoji_27 {
+ background-position: 0px -442px;
+}
+.emoji_28 {
+ background-position: 0px -459px;
+}
+.emoji_29 {
+ background-position: 0px -476px;
+}
+.emoji_30 {
+ background-position: 0px -493px;
+}
+.emoji_31 {
+ background-position: 0px -510px;
+}
+.emoji_32 {
+ background-position: 0px -527px;
+}
\ No newline at end of file
diff --git a/template/css/chatm.css b/template/css/chatm.css
new file mode 100644
index 0000000..574e521
--- /dev/null
+++ b/template/css/chatm.css
@@ -0,0 +1,202 @@
+/* Mini chat by Zalkarbek */
+fieldset {
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+
+hr {
+ background: #e9e9e9;
+ border: 0;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ height: 1px;
+ margin: 0;
+ min-height: 1px;
+}
+
+.clearfix { *zoom: 1; } /* For IE 6/7 */
+.clearfix:before, .clearfix:after {
+ content: "";
+ display: table;
+}
+.clearfix:after { clear: both; }
+
+#bspanel-minichat {
+ bottom: 0;
+ font-size: 12px;
+ left: 24px;
+ position: fixed;
+ width: 330px;
+ z-index: 100;
+}
+
+#bspanel-minichat header {
+ background: #293239;
+ border-radius: 5px 5px 0 0;
+ color: #fff;
+ cursor: pointer;
+ padding: 13px 15px;
+}
+
+#bspanel-minichat h4:before {
+ background: #1a8a34;
+ border-radius: 50%;
+ content: "";
+ display: inline-block;
+ height: 8px;
+ margin: 0 8px 0 0;
+ width: 8px;
+}
+
+.users_online {
+ background: #1a8a34;
+ width: 9px;
+ height: 9px;
+ position: absolute;
+ left: 3px;
+ margin-top: 5px;
+ border-radius: 50%;
+ cursor: pointer;
+ box-shadow: 0 0 10px rgba(0,0,0, 0.20), 0 0 5px #1a8a346b;
+ animation: anim 0.5s infinite alternate;
+}
+
+#bspanel-minichat h4 {
+ font-size: 12px;
+}
+
+#bspanel-minichat h5 {
+ font-size: 10px;
+}
+
+#bspanel-minichat fieldset {
+ padding: 24px;
+}
+
+#bspanel-minichat input[type="text"] {
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ padding: 8px;
+ outline: none;
+ width: 234px;
+}
+
+.chat-message-counter {
+ background: #e62727;
+ border: 1px solid #fff;
+ border-radius: 50%;
+ display: none;
+ font-size: 12px;
+ font-weight: bold;
+ height: 28px;
+ left: 0;
+ line-height: 28px;
+ margin: -15px 0 0 -15px;
+ position: absolute;
+ text-align: center;
+ top: 0;
+ width: 28px;
+}
+
+.chat-close {
+ background: #1b2126;
+ border-radius: 50%;
+ color: #fff;
+ display: block;
+ float: right;
+ font-size: 10px;
+ height: 16px;
+ line-height: 16px;
+ margin: 12px 10px 0 0;
+ text-align: center;
+ width: 16px;
+}
+
+.chat {
+ background: #fff;
+}
+
+.chat-history {
+ height: 252px;
+ padding: 8px 24px;
+ overflow-y: scroll;
+ overflow-x: hidden;
+}
+
+.chat-message {
+ margin: 16px 0;
+}
+
+.chat-message img {
+ border-radius: 50%;
+ float: left;
+}
+
+.chat-message-content {
+ margin-left: 56px;
+}
+
+.chat-time {
+ float: right;
+ font-size: 10px;
+}
+
+.chat-feedback {
+ font-style: italic;
+ margin: 0 0 0 80px;
+}
+
+.float {
+ position: fixed;
+ width: 60px;
+ height: 60px;
+ bottom: 40px;
+ left: 40px;
+ background-color:#25d366;
+ color:#FFF;
+ border-radius: 50px;
+ text-align: center;
+ font-size: 30px;
+ box-shadow: 2px 2px 3px #999;
+ z-index: 100;
+}
+
+.bhover:hover{
+ opacity: 0.7;
+}
+
+.img-block
+{
+ width: 108px;
+ float: left;
+ margin-right: 12px;
+}
+
+div > div > a .image
+{
+ position: relative;
+ height: 80px;
+ width: 80px;
+ border: 4px solid #24292c;
+ box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
+ background-color: #24292c;
+ float: left;
+ overflow: hidden;
+ background-size: contain !important;
+ background-repeat: no-repeat !important;
+ text-align: center;
+}
+
+.notice_chat__check {
+ background: #FF5722;
+ width: 8px;
+ height: 8px;
+ position: absolute;
+ margin-top: -4px;
+ margin-left: 5px;
+ border-radius: 50%;
+ box-shadow: 0 0 10px rgba(0,0,0, 0.20), 0 0 5px lime;
+ animation: notice_anim 0.5s infinite alternate;
+}
+/* Mini chat by Zalkarbek */
\ No newline at end of file
diff --git a/template/css/code.css b/template/css/code.css
new file mode 100644
index 0000000..7d8be18
--- /dev/null
+++ b/template/css/code.css
@@ -0,0 +1 @@
+.hljs{display:block;overflow-x:auto;padding:0.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}
\ No newline at end of file
diff --git a/template/css/codemirror.css b/template/css/codemirror.css
new file mode 100644
index 0000000..adb65f3
--- /dev/null
+++ b/template/css/codemirror.css
@@ -0,0 +1,347 @@
+/* BASICS */
+
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: monospace;
+ height: 450px;
+ color: black;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+ padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+ padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+ border-right: 1px solid #ddd;
+ background-color: #f7f7f7;
+ white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+ padding: 0 3px 0 5px;
+ min-width: 20px;
+ text-align: right;
+ color: #999;
+ white-space: nowrap;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror-cursor {
+ border-left: 1px solid black;
+ border-right: none;
+ width: 0;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+ border-left: 1px solid silver;
+}
+.cm-fat-cursor .CodeMirror-cursor {
+ width: auto;
+ border: 0 !important;
+ background: #7e7;
+}
+.cm-fat-cursor div.CodeMirror-cursors {
+ z-index: 1;
+}
+
+.cm-animate-fat-cursor {
+ width: auto;
+ border: 0;
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+ background-color: #7e7;
+}
+@-moz-keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+}
+@-webkit-keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+}
+@keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror-overwrite .CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-rulers {
+ position: absolute;
+ left: 0; right: 0; top: -50px; bottom: -20px;
+ overflow: hidden;
+}
+.CodeMirror-ruler {
+ border-left: 1px solid #ccc;
+ top: 0; bottom: 0;
+ position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+.CodeMirror-composing { border-bottom: 2px solid; }
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+ the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+ position: relative;
+ overflow: hidden;
+ background: white;
+}
+
+.CodeMirror-scroll {
+ overflow: scroll !important; /* Things will break if this is overridden */
+ /* 30px is the magic margin used to hide the element's real scrollbars */
+ /* See overflow: hidden in .CodeMirror */
+ margin-bottom: -30px; margin-right: -30px;
+ padding-bottom: 30px;
+ height: 100%;
+ outline: none; /* Prevent dragging from highlighting the element */
+ position: relative;
+}
+.CodeMirror-sizer {
+ position: relative;
+ border-right: 30px solid transparent;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+ before actual scrolling happens, thus preventing shaking and
+ flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ position: absolute;
+ z-index: 6;
+ display: none;
+}
+.CodeMirror-vscrollbar {
+ right: 0; top: 0;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+ bottom: 0; left: 0;
+ overflow-y: hidden;
+ overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+ right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+ left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+ position: absolute; left: 0; top: 0;
+ min-height: 100%;
+ z-index: 3;
+}
+.CodeMirror-gutter {
+ white-space: normal;
+ height: 100%;
+ display: inline-block;
+ vertical-align: top;
+ margin-bottom: -30px;
+ /* Hack to make IE7 behave */
+ *zoom:1;
+ *display:inline;
+}
+.CodeMirror-gutter-wrapper {
+ position: absolute;
+ z-index: 4;
+ background: none !important;
+ border: none !important;
+}
+.CodeMirror-gutter-background {
+ position: absolute;
+ top: 0; bottom: 0;
+ z-index: 4;
+}
+.CodeMirror-gutter-elt {
+ position: absolute;
+ cursor: default;
+ z-index: 4;
+}
+.CodeMirror-gutter-wrapper {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+}
+
+.CodeMirror-lines {
+ cursor: text;
+ min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+ /* Reset some styles that the rest of the page might have set */
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+ border-width: 0;
+ background: transparent;
+ font-family: inherit;
+ font-size: inherit;
+ margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ line-height: inherit;
+ color: inherit;
+ z-index: 2;
+ position: relative;
+ overflow: visible;
+ -webkit-tap-highlight-color: transparent;
+ -webkit-font-variant-ligatures: none;
+ font-variant-ligatures: none;
+}
+.CodeMirror-wrap pre {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+}
+
+.CodeMirror-linebackground {
+ position: absolute;
+ left: 0; right: 0; top: 0; bottom: 0;
+ z-index: 0;
+}
+
+.CodeMirror-linewidget {
+ position: relative;
+ z-index: 2;
+ overflow: auto;
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-code {
+ outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+.CodeMirror-measure {
+ position: absolute;
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+}
+
+.CodeMirror-cursor {
+ position: absolute;
+ pointer-events: none;
+}
+.CodeMirror-measure pre { position: static; }
+
+div.CodeMirror-cursors {
+ visibility: hidden;
+ position: relative;
+ z-index: 3;
+}
+div.CodeMirror-dragcursors {
+ visibility: visible;
+}
+
+.CodeMirror-focused div.CodeMirror-cursors {
+ visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
+.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+
+.cm-searching {
+ background: #ffa;
+ background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+ /* Hide the cursor when printing */
+ .CodeMirror div.CodeMirror-cursors {
+ visibility: hidden;
+ }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }
diff --git a/template/css/date.css b/template/css/date.css
new file mode 100644
index 0000000..cda843a
--- /dev/null
+++ b/template/css/date.css
@@ -0,0 +1,302 @@
+.dropdown-menu
+{
+ -webkit-border-radius: 0!important;
+ -moz-border-radius: 0!important;
+ border-radius: 0!important;
+ -webkit-box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+ -moz-box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+.datepicker {
+ padding: 4px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ direction: ltr;
+ /*.dow {
+ border-top: 1px solid #ddd !important;
+ }*/
+
+}
+.datepicker-inline {
+ width: 220px;
+}
+.datepicker.datepicker-rtl {
+ direction: rtl;
+}
+.datepicker.datepicker-rtl table tr td span {
+ float: right;
+}
+.datepicker-dropdown {
+ display: none;
+ position: absolute;
+ background-color: #FFF;
+}
+.datepicker-dropdown:before {
+ content: '';
+ display: inline-block;
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid #ccc;
+ border-bottom-color: rgba(0, 0, 0, 0.2);
+ position: absolute;
+ top: -7px;
+ left: 6px;
+}
+.datepicker-dropdown:after {
+ content: '';
+ display: inline-block;
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid #ffffff;
+ position: absolute;
+ top: -6px;
+ left: 7px;
+}
+.datepicker > div {
+ display: none;
+}
+.datepicker.days div.datepicker-days {
+ display: block;
+}
+.datepicker.months div.datepicker-months {
+ display: block;
+}
+.datepicker.years div.datepicker-years {
+ display: block;
+}
+.datepicker table {
+ margin: 0;
+}
+.datepicker td,
+.datepicker th {
+ text-align: center;
+ width: 20px;
+ height: 20px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ border: none;
+}
+.table-striped .datepicker table tr td,
+.table-striped .datepicker table tr th {
+ background-color: transparent;
+}
+.datepicker table tr td.day:hover {
+ background: #eeeeee;
+ cursor: pointer;
+}
+.datepicker table tr td.old,
+.datepicker table tr td.new {
+ color: #999999;
+}
+.datepicker table tr td.disabled,
+.datepicker table tr td.disabled:hover {
+ background: none;
+ color: #999999;
+ cursor: default;
+}
+.datepicker table tr td.today,
+.datepicker table tr td.today:hover,
+.datepicker table tr td.today.disabled,
+.datepicker table tr td.today.disabled:hover {
+ background-color: #fde19a;
+ background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
+ background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
+ background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
+ background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
+ background-image: linear-gradient(top, #fdd49a, #fdf59a);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
+ border-color: #fdf59a #fdf59a #fbed50;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ color: #000 !important;
+}
+.datepicker table tr td.today:hover,
+.datepicker table tr td.today:hover:hover,
+.datepicker table tr td.today.disabled:hover,
+.datepicker table tr td.today.disabled:hover:hover,
+.datepicker table tr td.today:active,
+.datepicker table tr td.today:hover:active,
+.datepicker table tr td.today.disabled:active,
+.datepicker table tr td.today.disabled:hover:active,
+.datepicker table tr td.today.active,
+.datepicker table tr td.today:hover.active,
+.datepicker table tr td.today.disabled.active,
+.datepicker table tr td.today.disabled:hover.active,
+.datepicker table tr td.today.disabled,
+.datepicker table tr td.today:hover.disabled,
+.datepicker table tr td.today.disabled.disabled,
+.datepicker table tr td.today.disabled:hover.disabled,
+.datepicker table tr td.today[disabled],
+.datepicker table tr td.today:hover[disabled],
+.datepicker table tr td.today.disabled[disabled],
+.datepicker table tr td.today.disabled:hover[disabled] {
+ background-color: #fdf59a;
+}
+.datepicker table tr td.today:active,
+.datepicker table tr td.today:hover:active,
+.datepicker table tr td.today.disabled:active,
+.datepicker table tr td.today.disabled:hover:active,
+.datepicker table tr td.today.active,
+.datepicker table tr td.today:hover.active,
+.datepicker table tr td.today.disabled.active,
+.datepicker table tr td.today.disabled:hover.active {
+ background-color: #fbf069 \9;
+}
+.datepicker table tr td.active,
+.datepicker table tr td.active:hover,
+.datepicker table tr td.active.disabled,
+.datepicker table tr td.active.disabled:hover {
+ background-color: #006dcc;
+ background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -o-linear-gradient(top, #0088cc, #0044cc);
+ background-image: linear-gradient(top, #0088cc, #0044cc);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
+ border-color: #0044cc #0044cc #002a80;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ color: #fff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.datepicker table tr td.active:hover,
+.datepicker table tr td.active:hover:hover,
+.datepicker table tr td.active.disabled:hover,
+.datepicker table tr td.active.disabled:hover:hover,
+.datepicker table tr td.active:active,
+.datepicker table tr td.active:hover:active,
+.datepicker table tr td.active.disabled:active,
+.datepicker table tr td.active.disabled:hover:active,
+.datepicker table tr td.active.active,
+.datepicker table tr td.active:hover.active,
+.datepicker table tr td.active.disabled.active,
+.datepicker table tr td.active.disabled:hover.active,
+.datepicker table tr td.active.disabled,
+.datepicker table tr td.active:hover.disabled,
+.datepicker table tr td.active.disabled.disabled,
+.datepicker table tr td.active.disabled:hover.disabled,
+.datepicker table tr td.active[disabled],
+.datepicker table tr td.active:hover[disabled],
+.datepicker table tr td.active.disabled[disabled],
+.datepicker table tr td.active.disabled:hover[disabled] {
+ background-color: #0044cc;
+}
+.datepicker table tr td.active:active,
+.datepicker table tr td.active:hover:active,
+.datepicker table tr td.active.disabled:active,
+.datepicker table tr td.active.disabled:hover:active,
+.datepicker table tr td.active.active,
+.datepicker table tr td.active:hover.active,
+.datepicker table tr td.active.disabled.active,
+.datepicker table tr td.active.disabled:hover.active {
+ background-color: #003399 \9;
+}
+.datepicker table tr td span {
+ display: block;
+ width: 23%;
+ height: 54px;
+ line-height: 54px;
+ float: left;
+ margin: 1%;
+ cursor: pointer;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.datepicker table tr td span:hover {
+ background: #eeeeee;
+}
+.datepicker table tr td span.disabled,
+.datepicker table tr td span.disabled:hover {
+ background: none;
+ color: #999999;
+ cursor: default;
+}
+.datepicker table tr td span.active,
+.datepicker table tr td span.active:hover,
+.datepicker table tr td span.active.disabled,
+.datepicker table tr td span.active.disabled:hover {
+ background-color: #006dcc;
+ background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -o-linear-gradient(top, #0088cc, #0044cc);
+ background-image: linear-gradient(top, #0088cc, #0044cc);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
+ border-color: #0044cc #0044cc #002a80;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ color: #fff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.datepicker table tr td span.active:hover,
+.datepicker table tr td span.active:hover:hover,
+.datepicker table tr td span.active.disabled:hover,
+.datepicker table tr td span.active.disabled:hover:hover,
+.datepicker table tr td span.active:active,
+.datepicker table tr td span.active:hover:active,
+.datepicker table tr td span.active.disabled:active,
+.datepicker table tr td span.active.disabled:hover:active,
+.datepicker table tr td span.active.active,
+.datepicker table tr td span.active:hover.active,
+.datepicker table tr td span.active.disabled.active,
+.datepicker table tr td span.active.disabled:hover.active,
+.datepicker table tr td span.active.disabled,
+.datepicker table tr td span.active:hover.disabled,
+.datepicker table tr td span.active.disabled.disabled,
+.datepicker table tr td span.active.disabled:hover.disabled,
+.datepicker table tr td span.active[disabled],
+.datepicker table tr td span.active:hover[disabled],
+.datepicker table tr td span.active.disabled[disabled],
+.datepicker table tr td span.active.disabled:hover[disabled] {
+ background-color: #0044cc;
+}
+.datepicker table tr td span.active:active,
+.datepicker table tr td span.active:hover:active,
+.datepicker table tr td span.active.disabled:active,
+.datepicker table tr td span.active.disabled:hover:active,
+.datepicker table tr td span.active.active,
+.datepicker table tr td span.active:hover.active,
+.datepicker table tr td span.active.disabled.active,
+.datepicker table tr td span.active.disabled:hover.active {
+ background-color: #003399 \9;
+}
+.datepicker table tr td span.old {
+ color: #999999;
+}
+.datepicker th.switch {
+ width: 145px;
+}
+.datepicker thead tr:first-child th,
+.datepicker tfoot tr:first-child th {
+ cursor: pointer;
+}
+.datepicker thead tr:first-child th:hover,
+.datepicker tfoot tr:first-child th:hover {
+ background: #eeeeee;
+}
+.datepicker .cw {
+ font-size: 10px;
+ width: 12px;
+ padding: 0 2px 0 5px;
+ vertical-align: middle;
+}
+.datepicker thead tr:first-child th.cw {
+ cursor: default;
+ background-color: transparent;
+}
+.input-append.date .add-on i,
+.input-prepend.date .add-on i {
+ display: block;
+ cursor: pointer;
+ width: 16px;
+ height: 16px;
+}
\ No newline at end of file
diff --git a/template/css/error.css b/template/css/error.css
new file mode 100644
index 0000000..e9d0176
--- /dev/null
+++ b/template/css/error.css
@@ -0,0 +1,102 @@
+*
+{
+ padding: 0;
+ margin: 0;
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ -ms-box-sizing: content-box;
+ -o-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+html
+{
+ font-family: 'Open Sans';
+ font-size: 14px;
+ background-color: #c73f33;
+ color: #000;
+}
+
+html, body
+{
+ height: 100%;
+}
+
+.error, .content
+{
+ width: 1000px;
+}
+
+.error
+{
+ width: 1000px;
+ height: 375px;
+ margin: 0 auto;
+}
+
+.error-403
+{
+ background: url('../images/error/403.jpg') center top no-repeat;
+}
+
+.error-404
+{
+ background: url('../images/error/404.jpg') center top no-repeat;
+}
+
+.fail-replenish
+{
+ background: url('../images/error/fail.jpg') center top no-repeat;
+}
+
+.info-403, .info-404, .info-fail
+{
+ padding: 5px;
+ text-align: center;
+ text-transform: uppercase;
+ border: 4px solid #000;
+ border-radius: 30px;
+ font-size: 33px;
+
+}
+
+.info-404
+{
+ font-size: 28px;
+}
+
+.info-fail
+{
+ font-size: 22px;
+}
+
+.content
+{
+ margin: 0 auto;
+}
+
+.content ul
+{
+ margin-top: 15px;
+ text-align: center;
+}
+
+.content ul li
+{
+ list-style-type: none;
+ display: inline-block;
+}
+
+.content ul li a
+{
+ color: #000;
+ display: inline-block;
+ margin: 10px;
+ font-size: 21px;
+ text-transform: uppercase;
+}
+
+.content ul li a:hover
+{
+ text-decoration: none;
+}
\ No newline at end of file
diff --git a/template/css/fonts.css b/template/css/fonts.css
new file mode 100644
index 0000000..b2a5fe2
--- /dev/null
+++ b/template/css/fonts.css
@@ -0,0 +1,2086 @@
+/*!
+ * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */
+/* FONT PATH
+ * -------------------------- */
+@font-face {
+ font-family: 'FontAwesome';
+ src: url('../fonts/fontawesome-webfont.eot?v=4.5.0');
+ src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+.fa {
+ display: inline-block;
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+/* makes the font 33% larger relative to the icon container */
+.fa-lg {
+ font-size: 1.33333333em;
+ line-height: 0.75em;
+ vertical-align: -15%;
+}
+.fa-2x {
+ font-size: 2em;
+}
+.fa-3x {
+ font-size: 3em;
+}
+.fa-4x {
+ font-size: 4em;
+}
+.fa-5x {
+ font-size: 5em;
+}
+.fa-fw {
+ width: 1.28571429em;
+ text-align: center;
+}
+.fa-ul {
+ padding-left: 0;
+ margin-left: 2.14285714em;
+ list-style-type: none;
+}
+.fa-ul > li {
+ position: relative;
+}
+.fa-li {
+ position: absolute;
+ left: -2.14285714em;
+ width: 2.14285714em;
+ top: 0.14285714em;
+ text-align: center;
+}
+.fa-li.fa-lg {
+ left: -1.85714286em;
+}
+.fa-border {
+ padding: .2em .25em .15em;
+ border: solid 0.08em #eeeeee;
+ border-radius: .1em;
+}
+.fa-pull-left {
+ float: left;
+}
+.fa-pull-right {
+ float: right;
+}
+.fa.fa-pull-left {
+ margin-right: .3em;
+}
+.fa.fa-pull-right {
+ margin-left: .3em;
+}
+/* Deprecated as of 4.4.0 */
+.pull-right {
+ float: right;
+}
+.pull-left {
+ float: left;
+}
+.fa.pull-left {
+ margin-right: .3em;
+}
+.fa.pull-right {
+ margin-left: .3em;
+}
+.fa-spin {
+ -webkit-animation: fa-spin 2s infinite linear;
+ animation: fa-spin 2s infinite linear;
+}
+.fa-pulse {
+ -webkit-animation: fa-spin 1s infinite steps(8);
+ animation: fa-spin 1s infinite steps(8);
+}
+@-webkit-keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+@keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+.fa-rotate-90 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
+ -webkit-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+.fa-rotate-180 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
+ -webkit-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+.fa-rotate-270 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
+ -webkit-transform: rotate(270deg);
+ -ms-transform: rotate(270deg);
+ transform: rotate(270deg);
+}
+.fa-flip-horizontal {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
+ -webkit-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, 1);
+}
+.fa-flip-vertical {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
+ -webkit-transform: scale(1, -1);
+ -ms-transform: scale(1, -1);
+ transform: scale(1, -1);
+}
+:root .fa-rotate-90,
+:root .fa-rotate-180,
+:root .fa-rotate-270,
+:root .fa-flip-horizontal,
+:root .fa-flip-vertical {
+ filter: none;
+}
+.fa-stack {
+ position: relative;
+ display: inline-block;
+ width: 2em;
+ height: 2em;
+ line-height: 2em;
+ vertical-align: middle;
+}
+.fa-stack-1x,
+.fa-stack-2x {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ text-align: center;
+}
+.fa-stack-1x {
+ line-height: inherit;
+}
+.fa-stack-2x {
+ font-size: 2em;
+}
+.fa-inverse {
+ color: #ffffff;
+}
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
+ readers do not read off random characters that represent icons */
+.fa-glass:before {
+ content: "\f000";
+}
+.fa-music:before {
+ content: "\f001";
+}
+.fa-search:before {
+ content: "\f002";
+}
+.fa-envelope-o:before {
+ content: "\f003";
+}
+.fa-heart:before {
+ content: "\f004";
+}
+.fa-star:before {
+ content: "\f005";
+}
+.fa-star-o:before {
+ content: "\f006";
+}
+.fa-user:before {
+ content: "\f007";
+}
+.fa-film:before {
+ content: "\f008";
+}
+.fa-th-large:before {
+ content: "\f009";
+}
+.fa-th:before {
+ content: "\f00a";
+}
+.fa-th-list:before {
+ content: "\f00b";
+}
+.fa-check:before {
+ content: "\f00c";
+}
+.fa-remove:before,
+.fa-close:before,
+.fa-times:before {
+ content: "\f00d";
+}
+.fa-search-plus:before {
+ content: "\f00e";
+}
+.fa-search-minus:before {
+ content: "\f010";
+}
+.fa-power-off:before {
+ content: "\f011";
+}
+.fa-signal:before {
+ content: "\f012";
+}
+.fa-gear:before,
+.fa-cog:before {
+ content: "\f013";
+}
+.fa-trash-o:before {
+ content: "\f014";
+}
+.fa-home:before {
+ content: "\f015";
+}
+.fa-file-o:before {
+ content: "\f016";
+}
+.fa-clock-o:before {
+ content: "\f017";
+}
+.fa-road:before {
+ content: "\f018";
+}
+.fa-download:before {
+ content: "\f019";
+}
+.fa-arrow-circle-o-down:before {
+ content: "\f01a";
+}
+.fa-arrow-circle-o-up:before {
+ content: "\f01b";
+}
+.fa-inbox:before {
+ content: "\f01c";
+}
+.fa-play-circle-o:before {
+ content: "\f01d";
+}
+.fa-rotate-right:before,
+.fa-repeat:before {
+ content: "\f01e";
+}
+.fa-refresh:before {
+ content: "\f021";
+}
+.fa-list-alt:before {
+ content: "\f022";
+}
+.fa-lock:before {
+ content: "\f023";
+}
+.fa-flag:before {
+ content: "\f024";
+}
+.fa-headphones:before {
+ content: "\f025";
+}
+.fa-volume-off:before {
+ content: "\f026";
+}
+.fa-volume-down:before {
+ content: "\f027";
+}
+.fa-volume-up:before {
+ content: "\f028";
+}
+.fa-qrcode:before {
+ content: "\f029";
+}
+.fa-barcode:before {
+ content: "\f02a";
+}
+.fa-tag:before {
+ content: "\f02b";
+}
+.fa-tags:before {
+ content: "\f02c";
+}
+.fa-book:before {
+ content: "\f02d";
+}
+.fa-bookmark:before {
+ content: "\f02e";
+}
+.fa-print:before {
+ content: "\f02f";
+}
+.fa-camera:before {
+ content: "\f030";
+}
+.fa-font:before {
+ content: "\f031";
+}
+.fa-bold:before {
+ content: "\f032";
+}
+.fa-italic:before {
+ content: "\f033";
+}
+.fa-text-height:before {
+ content: "\f034";
+}
+.fa-text-width:before {
+ content: "\f035";
+}
+.fa-align-left:before {
+ content: "\f036";
+}
+.fa-align-center:before {
+ content: "\f037";
+}
+.fa-align-right:before {
+ content: "\f038";
+}
+.fa-align-justify:before {
+ content: "\f039";
+}
+.fa-list:before {
+ content: "\f03a";
+}
+.fa-dedent:before,
+.fa-outdent:before {
+ content: "\f03b";
+}
+.fa-indent:before {
+ content: "\f03c";
+}
+.fa-video-camera:before {
+ content: "\f03d";
+}
+.fa-photo:before,
+.fa-image:before,
+.fa-picture-o:before {
+ content: "\f03e";
+}
+.fa-pencil:before {
+ content: "\f040";
+}
+.fa-map-marker:before {
+ content: "\f041";
+}
+.fa-adjust:before {
+ content: "\f042";
+}
+.fa-tint:before {
+ content: "\f043";
+}
+.fa-edit:before,
+.fa-pencil-square-o:before {
+ content: "\f044";
+}
+.fa-share-square-o:before {
+ content: "\f045";
+}
+.fa-check-square-o:before {
+ content: "\f046";
+}
+.fa-arrows:before {
+ content: "\f047";
+}
+.fa-step-backward:before {
+ content: "\f048";
+}
+.fa-fast-backward:before {
+ content: "\f049";
+}
+.fa-backward:before {
+ content: "\f04a";
+}
+.fa-play:before {
+ content: "\f04b";
+}
+.fa-pause:before {
+ content: "\f04c";
+}
+.fa-stop:before {
+ content: "\f04d";
+}
+.fa-forward:before {
+ content: "\f04e";
+}
+.fa-fast-forward:before {
+ content: "\f050";
+}
+.fa-step-forward:before {
+ content: "\f051";
+}
+.fa-eject:before {
+ content: "\f052";
+}
+.fa-chevron-left:before {
+ content: "\f053";
+}
+.fa-chevron-right:before {
+ content: "\f054";
+}
+.fa-plus-circle:before {
+ content: "\f055";
+}
+.fa-minus-circle:before {
+ content: "\f056";
+}
+.fa-times-circle:before {
+ content: "\f057";
+}
+.fa-check-circle:before {
+ content: "\f058";
+}
+.fa-question-circle:before {
+ content: "\f059";
+}
+.fa-info-circle:before {
+ content: "\f05a";
+}
+.fa-crosshairs:before {
+ content: "\f05b";
+}
+.fa-times-circle-o:before {
+ content: "\f05c";
+}
+.fa-check-circle-o:before {
+ content: "\f05d";
+}
+.fa-ban:before {
+ content: "\f05e";
+}
+.fa-arrow-left:before {
+ content: "\f060";
+}
+.fa-arrow-right:before {
+ content: "\f061";
+}
+.fa-arrow-up:before {
+ content: "\f062";
+}
+.fa-arrow-down:before {
+ content: "\f063";
+}
+.fa-mail-forward:before,
+.fa-share:before {
+ content: "\f064";
+}
+.fa-expand:before {
+ content: "\f065";
+}
+.fa-compress:before {
+ content: "\f066";
+}
+.fa-plus:before {
+ content: "\f067";
+}
+.fa-minus:before {
+ content: "\f068";
+}
+.fa-asterisk:before {
+ content: "\f069";
+}
+.fa-exclamation-circle:before {
+ content: "\f06a";
+}
+.fa-gift:before {
+ content: "\f06b";
+}
+.fa-leaf:before {
+ content: "\f06c";
+}
+.fa-fire:before {
+ content: "\f06d";
+}
+.fa-eye:before {
+ content: "\f06e";
+}
+.fa-eye-slash:before {
+ content: "\f070";
+}
+.fa-warning:before,
+.fa-exclamation-triangle:before {
+ content: "\f071";
+}
+.fa-plane:before {
+ content: "\f072";
+}
+.fa-calendar:before {
+ content: "\f073";
+}
+.fa-random:before {
+ content: "\f074";
+}
+.fa-comment:before {
+ content: "\f075";
+}
+.fa-magnet:before {
+ content: "\f076";
+}
+.fa-chevron-up:before {
+ content: "\f077";
+}
+.fa-chevron-down:before {
+ content: "\f078";
+}
+.fa-retweet:before {
+ content: "\f079";
+}
+.fa-shopping-cart:before {
+ content: "\f07a";
+}
+.fa-folder:before {
+ content: "\f07b";
+}
+.fa-folder-open:before {
+ content: "\f07c";
+}
+.fa-arrows-v:before {
+ content: "\f07d";
+}
+.fa-arrows-h:before {
+ content: "\f07e";
+}
+.fa-bar-chart-o:before,
+.fa-bar-chart:before {
+ content: "\f080";
+}
+.fa-twitter-square:before {
+ content: "\f081";
+}
+.fa-facebook-square:before {
+ content: "\f082";
+}
+.fa-camera-retro:before {
+ content: "\f083";
+}
+.fa-key:before {
+ content: "\f084";
+}
+.fa-gears:before,
+.fa-cogs:before {
+ content: "\f085";
+}
+.fa-comments:before {
+ content: "\f086";
+}
+.fa-thumbs-o-up:before {
+ content: "\f087";
+}
+.fa-thumbs-o-down:before {
+ content: "\f088";
+}
+.fa-star-half:before {
+ content: "\f089";
+}
+.fa-heart-o:before {
+ content: "\f08a";
+}
+.fa-sign-out:before {
+ content: "\f08b";
+}
+.fa-linkedin-square:before {
+ content: "\f08c";
+}
+.fa-thumb-tack:before {
+ content: "\f08d";
+}
+.fa-external-link:before {
+ content: "\f08e";
+}
+.fa-sign-in:before {
+ content: "\f090";
+}
+.fa-trophy:before {
+ content: "\f091";
+}
+.fa-github-square:before {
+ content: "\f092";
+}
+.fa-upload:before {
+ content: "\f093";
+}
+.fa-lemon-o:before {
+ content: "\f094";
+}
+.fa-phone:before {
+ content: "\f095";
+}
+.fa-square-o:before {
+ content: "\f096";
+}
+.fa-bookmark-o:before {
+ content: "\f097";
+}
+.fa-phone-square:before {
+ content: "\f098";
+}
+.fa-twitter:before {
+ content: "\f099";
+}
+.fa-facebook-f:before,
+.fa-facebook:before {
+ content: "\f09a";
+}
+.fa-github:before {
+ content: "\f09b";
+}
+.fa-unlock:before {
+ content: "\f09c";
+}
+.fa-credit-card:before {
+ content: "\f09d";
+}
+.fa-feed:before,
+.fa-rss:before {
+ content: "\f09e";
+}
+.fa-hdd-o:before {
+ content: "\f0a0";
+}
+.fa-bullhorn:before {
+ content: "\f0a1";
+}
+.fa-bell:before {
+ content: "\f0f3";
+}
+.fa-certificate:before {
+ content: "\f0a3";
+}
+.fa-hand-o-right:before {
+ content: "\f0a4";
+}
+.fa-hand-o-left:before {
+ content: "\f0a5";
+}
+.fa-hand-o-up:before {
+ content: "\f0a6";
+}
+.fa-hand-o-down:before {
+ content: "\f0a7";
+}
+.fa-arrow-circle-left:before {
+ content: "\f0a8";
+}
+.fa-arrow-circle-right:before {
+ content: "\f0a9";
+}
+.fa-arrow-circle-up:before {
+ content: "\f0aa";
+}
+.fa-arrow-circle-down:before {
+ content: "\f0ab";
+}
+.fa-globe:before {
+ content: "\f0ac";
+}
+.fa-wrench:before {
+ content: "\f0ad";
+}
+.fa-tasks:before {
+ content: "\f0ae";
+}
+.fa-filter:before {
+ content: "\f0b0";
+}
+.fa-briefcase:before {
+ content: "\f0b1";
+}
+.fa-arrows-alt:before {
+ content: "\f0b2";
+}
+.fa-group:before,
+.fa-users:before {
+ content: "\f0c0";
+}
+.fa-chain:before,
+.fa-link:before {
+ content: "\f0c1";
+}
+.fa-cloud:before {
+ content: "\f0c2";
+}
+.fa-flask:before {
+ content: "\f0c3";
+}
+.fa-cut:before,
+.fa-scissors:before {
+ content: "\f0c4";
+}
+.fa-copy:before,
+.fa-files-o:before {
+ content: "\f0c5";
+}
+.fa-paperclip:before {
+ content: "\f0c6";
+}
+.fa-save:before,
+.fa-floppy-o:before {
+ content: "\f0c7";
+}
+.fa-square:before {
+ content: "\f0c8";
+}
+.fa-navicon:before,
+.fa-reorder:before,
+.fa-bars:before {
+ content: "\f0c9";
+}
+.fa-list-ul:before {
+ content: "\f0ca";
+}
+.fa-list-ol:before {
+ content: "\f0cb";
+}
+.fa-strikethrough:before {
+ content: "\f0cc";
+}
+.fa-underline:before {
+ content: "\f0cd";
+}
+.fa-table:before {
+ content: "\f0ce";
+}
+.fa-magic:before {
+ content: "\f0d0";
+}
+.fa-truck:before {
+ content: "\f0d1";
+}
+.fa-pinterest:before {
+ content: "\f0d2";
+}
+.fa-pinterest-square:before {
+ content: "\f0d3";
+}
+.fa-google-plus-square:before {
+ content: "\f0d4";
+}
+.fa-google-plus:before {
+ content: "\f0d5";
+}
+.fa-money:before {
+ content: "\f0d6";
+}
+.fa-caret-down:before {
+ content: "\f0d7";
+}
+.fa-caret-up:before {
+ content: "\f0d8";
+}
+.fa-caret-left:before {
+ content: "\f0d9";
+}
+.fa-caret-right:before {
+ content: "\f0da";
+}
+.fa-columns:before {
+ content: "\f0db";
+}
+.fa-unsorted:before,
+.fa-sort:before {
+ content: "\f0dc";
+}
+.fa-sort-down:before,
+.fa-sort-desc:before {
+ content: "\f0dd";
+}
+.fa-sort-up:before,
+.fa-sort-asc:before {
+ content: "\f0de";
+}
+.fa-envelope:before {
+ content: "\f0e0";
+}
+.fa-linkedin:before {
+ content: "\f0e1";
+}
+.fa-rotate-left:before,
+.fa-undo:before {
+ content: "\f0e2";
+}
+.fa-legal:before,
+.fa-gavel:before {
+ content: "\f0e3";
+}
+.fa-dashboard:before,
+.fa-tachometer:before {
+ content: "\f0e4";
+}
+.fa-comment-o:before {
+ content: "\f0e5";
+}
+.fa-comments-o:before {
+ content: "\f0e6";
+}
+.fa-flash:before,
+.fa-bolt:before {
+ content: "\f0e7";
+}
+.fa-sitemap:before {
+ content: "\f0e8";
+}
+.fa-umbrella:before {
+ content: "\f0e9";
+}
+.fa-paste:before,
+.fa-clipboard:before {
+ content: "\f0ea";
+}
+.fa-lightbulb-o:before {
+ content: "\f0eb";
+}
+.fa-exchange:before {
+ content: "\f0ec";
+}
+.fa-cloud-download:before {
+ content: "\f0ed";
+}
+.fa-cloud-upload:before {
+ content: "\f0ee";
+}
+.fa-user-md:before {
+ content: "\f0f0";
+}
+.fa-stethoscope:before {
+ content: "\f0f1";
+}
+.fa-suitcase:before {
+ content: "\f0f2";
+}
+.fa-bell-o:before {
+ content: "\f0a2";
+}
+.fa-coffee:before {
+ content: "\f0f4";
+}
+.fa-cutlery:before {
+ content: "\f0f5";
+}
+.fa-file-text-o:before {
+ content: "\f0f6";
+}
+.fa-building-o:before {
+ content: "\f0f7";
+}
+.fa-hospital-o:before {
+ content: "\f0f8";
+}
+.fa-ambulance:before {
+ content: "\f0f9";
+}
+.fa-medkit:before {
+ content: "\f0fa";
+}
+.fa-fighter-jet:before {
+ content: "\f0fb";
+}
+.fa-beer:before {
+ content: "\f0fc";
+}
+.fa-h-square:before {
+ content: "\f0fd";
+}
+.fa-plus-square:before {
+ content: "\f0fe";
+}
+.fa-angle-double-left:before {
+ content: "\f100";
+}
+.fa-angle-double-right:before {
+ content: "\f101";
+}
+.fa-angle-double-up:before {
+ content: "\f102";
+}
+.fa-angle-double-down:before {
+ content: "\f103";
+}
+.fa-angle-left:before {
+ content: "\f104";
+}
+.fa-angle-right:before {
+ content: "\f105";
+}
+.fa-angle-up:before {
+ content: "\f106";
+}
+.fa-angle-down:before {
+ content: "\f107";
+}
+.fa-desktop:before {
+ content: "\f108";
+}
+.fa-laptop:before {
+ content: "\f109";
+}
+.fa-tablet:before {
+ content: "\f10a";
+}
+.fa-mobile-phone:before,
+.fa-mobile:before {
+ content: "\f10b";
+}
+.fa-circle-o:before {
+ content: "\f10c";
+}
+.fa-quote-left:before {
+ content: "\f10d";
+}
+.fa-quote-right:before {
+ content: "\f10e";
+}
+.fa-spinner:before {
+ content: "\f110";
+}
+.fa-circle:before {
+ content: "\f111";
+}
+.fa-mail-reply:before,
+.fa-reply:before {
+ content: "\f112";
+}
+.fa-github-alt:before {
+ content: "\f113";
+}
+.fa-folder-o:before {
+ content: "\f114";
+}
+.fa-folder-open-o:before {
+ content: "\f115";
+}
+.fa-smile-o:before {
+ content: "\f118";
+}
+.fa-frown-o:before {
+ content: "\f119";
+}
+.fa-meh-o:before {
+ content: "\f11a";
+}
+.fa-gamepad:before {
+ content: "\f11b";
+}
+.fa-keyboard-o:before {
+ content: "\f11c";
+}
+.fa-flag-o:before {
+ content: "\f11d";
+}
+.fa-flag-checkered:before {
+ content: "\f11e";
+}
+.fa-terminal:before {
+ content: "\f120";
+}
+.fa-code:before {
+ content: "\f121";
+}
+.fa-mail-reply-all:before,
+.fa-reply-all:before {
+ content: "\f122";
+}
+.fa-star-half-empty:before,
+.fa-star-half-full:before,
+.fa-star-half-o:before {
+ content: "\f123";
+}
+.fa-location-arrow:before {
+ content: "\f124";
+}
+.fa-crop:before {
+ content: "\f125";
+}
+.fa-code-fork:before {
+ content: "\f126";
+}
+.fa-unlink:before,
+.fa-chain-broken:before {
+ content: "\f127";
+}
+.fa-question:before {
+ content: "\f128";
+}
+.fa-info:before {
+ content: "\f129";
+}
+.fa-exclamation:before {
+ content: "\f12a";
+}
+.fa-superscript:before {
+ content: "\f12b";
+}
+.fa-subscript:before {
+ content: "\f12c";
+}
+.fa-eraser:before {
+ content: "\f12d";
+}
+.fa-puzzle-piece:before {
+ content: "\f12e";
+}
+.fa-microphone:before {
+ content: "\f130";
+}
+.fa-microphone-slash:before {
+ content: "\f131";
+}
+.fa-shield:before {
+ content: "\f132";
+}
+.fa-calendar-o:before {
+ content: "\f133";
+}
+.fa-fire-extinguisher:before {
+ content: "\f134";
+}
+.fa-rocket:before {
+ content: "\f135";
+}
+.fa-maxcdn:before {
+ content: "\f136";
+}
+.fa-chevron-circle-left:before {
+ content: "\f137";
+}
+.fa-chevron-circle-right:before {
+ content: "\f138";
+}
+.fa-chevron-circle-up:before {
+ content: "\f139";
+}
+.fa-chevron-circle-down:before {
+ content: "\f13a";
+}
+.fa-html5:before {
+ content: "\f13b";
+}
+.fa-css3:before {
+ content: "\f13c";
+}
+.fa-anchor:before {
+ content: "\f13d";
+}
+.fa-unlock-alt:before {
+ content: "\f13e";
+}
+.fa-bullseye:before {
+ content: "\f140";
+}
+.fa-ellipsis-h:before {
+ content: "\f141";
+}
+.fa-ellipsis-v:before {
+ content: "\f142";
+}
+.fa-rss-square:before {
+ content: "\f143";
+}
+.fa-play-circle:before {
+ content: "\f144";
+}
+.fa-ticket:before {
+ content: "\f145";
+}
+.fa-minus-square:before {
+ content: "\f146";
+}
+.fa-minus-square-o:before {
+ content: "\f147";
+}
+.fa-level-up:before {
+ content: "\f148";
+}
+.fa-level-down:before {
+ content: "\f149";
+}
+.fa-check-square:before {
+ content: "\f14a";
+}
+.fa-pencil-square:before {
+ content: "\f14b";
+}
+.fa-external-link-square:before {
+ content: "\f14c";
+}
+.fa-share-square:before {
+ content: "\f14d";
+}
+.fa-compass:before {
+ content: "\f14e";
+}
+.fa-toggle-down:before,
+.fa-caret-square-o-down:before {
+ content: "\f150";
+}
+.fa-toggle-up:before,
+.fa-caret-square-o-up:before {
+ content: "\f151";
+}
+.fa-toggle-right:before,
+.fa-caret-square-o-right:before {
+ content: "\f152";
+}
+.fa-euro:before,
+.fa-eur:before {
+ content: "\f153";
+}
+.fa-gbp:before {
+ content: "\f154";
+}
+.fa-dollar:before,
+.fa-usd:before {
+ content: "\f155";
+}
+.fa-rupee:before,
+.fa-inr:before {
+ content: "\f156";
+}
+.fa-cny:before,
+.fa-rmb:before,
+.fa-yen:before,
+.fa-jpy:before {
+ content: "\f157";
+}
+.fa-ruble:before,
+.fa-rouble:before,
+.fa-rub:before {
+ content: "\f158";
+}
+.fa-won:before,
+.fa-krw:before {
+ content: "\f159";
+}
+.fa-bitcoin:before,
+.fa-btc:before {
+ content: "\f15a";
+}
+.fa-file:before {
+ content: "\f15b";
+}
+.fa-file-text:before {
+ content: "\f15c";
+}
+.fa-sort-alpha-asc:before {
+ content: "\f15d";
+}
+.fa-sort-alpha-desc:before {
+ content: "\f15e";
+}
+.fa-sort-amount-asc:before {
+ content: "\f160";
+}
+.fa-sort-amount-desc:before {
+ content: "\f161";
+}
+.fa-sort-numeric-asc:before {
+ content: "\f162";
+}
+.fa-sort-numeric-desc:before {
+ content: "\f163";
+}
+.fa-thumbs-up:before {
+ content: "\f164";
+}
+.fa-thumbs-down:before {
+ content: "\f165";
+}
+.fa-youtube-square:before {
+ content: "\f166";
+}
+.fa-youtube:before {
+ content: "\f167";
+}
+.fa-xing:before {
+ content: "\f168";
+}
+.fa-xing-square:before {
+ content: "\f169";
+}
+.fa-youtube-play:before {
+ content: "\f16a";
+}
+.fa-dropbox:before {
+ content: "\f16b";
+}
+.fa-stack-overflow:before {
+ content: "\f16c";
+}
+.fa-instagram:before {
+ content: "\f16d";
+}
+.fa-flickr:before {
+ content: "\f16e";
+}
+.fa-adn:before {
+ content: "\f170";
+}
+.fa-bitbucket:before {
+ content: "\f171";
+}
+.fa-bitbucket-square:before {
+ content: "\f172";
+}
+.fa-tumblr:before {
+ content: "\f173";
+}
+.fa-tumblr-square:before {
+ content: "\f174";
+}
+.fa-long-arrow-down:before {
+ content: "\f175";
+}
+.fa-long-arrow-up:before {
+ content: "\f176";
+}
+.fa-long-arrow-left:before {
+ content: "\f177";
+}
+.fa-long-arrow-right:before {
+ content: "\f178";
+}
+.fa-apple:before {
+ content: "\f179";
+}
+.fa-windows:before {
+ content: "\f17a";
+}
+.fa-android:before {
+ content: "\f17b";
+}
+.fa-linux:before {
+ content: "\f17c";
+}
+.fa-dribbble:before {
+ content: "\f17d";
+}
+.fa-skype:before {
+ content: "\f17e";
+}
+.fa-foursquare:before {
+ content: "\f180";
+}
+.fa-trello:before {
+ content: "\f181";
+}
+.fa-female:before {
+ content: "\f182";
+}
+.fa-male:before {
+ content: "\f183";
+}
+.fa-gittip:before,
+.fa-gratipay:before {
+ content: "\f184";
+}
+.fa-sun-o:before {
+ content: "\f185";
+}
+.fa-moon-o:before {
+ content: "\f186";
+}
+.fa-archive:before {
+ content: "\f187";
+}
+.fa-bug:before {
+ content: "\f188";
+}
+.fa-vk:before {
+ content: "\f189";
+}
+.fa-weibo:before {
+ content: "\f18a";
+}
+.fa-renren:before {
+ content: "\f18b";
+}
+.fa-pagelines:before {
+ content: "\f18c";
+}
+.fa-stack-exchange:before {
+ content: "\f18d";
+}
+.fa-arrow-circle-o-right:before {
+ content: "\f18e";
+}
+.fa-arrow-circle-o-left:before {
+ content: "\f190";
+}
+.fa-toggle-left:before,
+.fa-caret-square-o-left:before {
+ content: "\f191";
+}
+.fa-dot-circle-o:before {
+ content: "\f192";
+}
+.fa-wheelchair:before {
+ content: "\f193";
+}
+.fa-vimeo-square:before {
+ content: "\f194";
+}
+.fa-turkish-lira:before,
+.fa-try:before {
+ content: "\f195";
+}
+.fa-plus-square-o:before {
+ content: "\f196";
+}
+.fa-space-shuttle:before {
+ content: "\f197";
+}
+.fa-slack:before {
+ content: "\f198";
+}
+.fa-envelope-square:before {
+ content: "\f199";
+}
+.fa-wordpress:before {
+ content: "\f19a";
+}
+.fa-openid:before {
+ content: "\f19b";
+}
+.fa-institution:before,
+.fa-bank:before,
+.fa-university:before {
+ content: "\f19c";
+}
+.fa-mortar-board:before,
+.fa-graduation-cap:before {
+ content: "\f19d";
+}
+.fa-yahoo:before {
+ content: "\f19e";
+}
+.fa-google:before {
+ content: "\f1a0";
+}
+.fa-reddit:before {
+ content: "\f1a1";
+}
+.fa-reddit-square:before {
+ content: "\f1a2";
+}
+.fa-stumbleupon-circle:before {
+ content: "\f1a3";
+}
+.fa-stumbleupon:before {
+ content: "\f1a4";
+}
+.fa-delicious:before {
+ content: "\f1a5";
+}
+.fa-digg:before {
+ content: "\f1a6";
+}
+.fa-pied-piper:before {
+ content: "\f1a7";
+}
+.fa-pied-piper-alt:before {
+ content: "\f1a8";
+}
+.fa-drupal:before {
+ content: "\f1a9";
+}
+.fa-joomla:before {
+ content: "\f1aa";
+}
+.fa-language:before {
+ content: "\f1ab";
+}
+.fa-fax:before {
+ content: "\f1ac";
+}
+.fa-building:before {
+ content: "\f1ad";
+}
+.fa-child:before {
+ content: "\f1ae";
+}
+.fa-paw:before {
+ content: "\f1b0";
+}
+.fa-spoon:before {
+ content: "\f1b1";
+}
+.fa-cube:before {
+ content: "\f1b2";
+}
+.fa-cubes:before {
+ content: "\f1b3";
+}
+.fa-behance:before {
+ content: "\f1b4";
+}
+.fa-behance-square:before {
+ content: "\f1b5";
+}
+.fa-steam:before {
+ content: "\f1b6";
+}
+.fa-steam-square:before {
+ content: "\f1b7";
+}
+.fa-recycle:before {
+ content: "\f1b8";
+}
+.fa-automobile:before,
+.fa-car:before {
+ content: "\f1b9";
+}
+.fa-cab:before,
+.fa-taxi:before {
+ content: "\f1ba";
+}
+.fa-tree:before {
+ content: "\f1bb";
+}
+.fa-spotify:before {
+ content: "\f1bc";
+}
+.fa-deviantart:before {
+ content: "\f1bd";
+}
+.fa-soundcloud:before {
+ content: "\f1be";
+}
+.fa-database:before {
+ content: "\f1c0";
+}
+.fa-file-pdf-o:before {
+ content: "\f1c1";
+}
+.fa-file-word-o:before {
+ content: "\f1c2";
+}
+.fa-file-excel-o:before {
+ content: "\f1c3";
+}
+.fa-file-powerpoint-o:before {
+ content: "\f1c4";
+}
+.fa-file-photo-o:before,
+.fa-file-picture-o:before,
+.fa-file-image-o:before {
+ content: "\f1c5";
+}
+.fa-file-zip-o:before,
+.fa-file-archive-o:before {
+ content: "\f1c6";
+}
+.fa-file-sound-o:before,
+.fa-file-audio-o:before {
+ content: "\f1c7";
+}
+.fa-file-movie-o:before,
+.fa-file-video-o:before {
+ content: "\f1c8";
+}
+.fa-file-code-o:before {
+ content: "\f1c9";
+}
+.fa-vine:before {
+ content: "\f1ca";
+}
+.fa-codepen:before {
+ content: "\f1cb";
+}
+.fa-jsfiddle:before {
+ content: "\f1cc";
+}
+.fa-life-bouy:before,
+.fa-life-buoy:before,
+.fa-life-saver:before,
+.fa-support:before,
+.fa-life-ring:before {
+ content: "\f1cd";
+}
+.fa-circle-o-notch:before {
+ content: "\f1ce";
+}
+.fa-ra:before,
+.fa-rebel:before {
+ content: "\f1d0";
+}
+.fa-ge:before,
+.fa-empire:before {
+ content: "\f1d1";
+}
+.fa-git-square:before {
+ content: "\f1d2";
+}
+.fa-git:before {
+ content: "\f1d3";
+}
+.fa-y-combinator-square:before,
+.fa-yc-square:before,
+.fa-hacker-news:before {
+ content: "\f1d4";
+}
+.fa-tencent-weibo:before {
+ content: "\f1d5";
+}
+.fa-qq:before {
+ content: "\f1d6";
+}
+.fa-wechat:before,
+.fa-weixin:before {
+ content: "\f1d7";
+}
+.fa-send:before,
+.fa-paper-plane:before {
+ content: "\f1d8";
+}
+.fa-send-o:before,
+.fa-paper-plane-o:before {
+ content: "\f1d9";
+}
+.fa-history:before {
+ content: "\f1da";
+}
+.fa-circle-thin:before {
+ content: "\f1db";
+}
+.fa-header:before {
+ content: "\f1dc";
+}
+.fa-paragraph:before {
+ content: "\f1dd";
+}
+.fa-sliders:before {
+ content: "\f1de";
+}
+.fa-share-alt:before {
+ content: "\f1e0";
+}
+.fa-share-alt-square:before {
+ content: "\f1e1";
+}
+.fa-bomb:before {
+ content: "\f1e2";
+}
+.fa-soccer-ball-o:before,
+.fa-futbol-o:before {
+ content: "\f1e3";
+}
+.fa-tty:before {
+ content: "\f1e4";
+}
+.fa-binoculars:before {
+ content: "\f1e5";
+}
+.fa-plug:before {
+ content: "\f1e6";
+}
+.fa-slideshare:before {
+ content: "\f1e7";
+}
+.fa-twitch:before {
+ content: "\f1e8";
+}
+.fa-yelp:before {
+ content: "\f1e9";
+}
+.fa-newspaper-o:before {
+ content: "\f1ea";
+}
+.fa-wifi:before {
+ content: "\f1eb";
+}
+.fa-calculator:before {
+ content: "\f1ec";
+}
+.fa-paypal:before {
+ content: "\f1ed";
+}
+.fa-google-wallet:before {
+ content: "\f1ee";
+}
+.fa-cc-visa:before {
+ content: "\f1f0";
+}
+.fa-cc-mastercard:before {
+ content: "\f1f1";
+}
+.fa-cc-discover:before {
+ content: "\f1f2";
+}
+.fa-cc-amex:before {
+ content: "\f1f3";
+}
+.fa-cc-paypal:before {
+ content: "\f1f4";
+}
+.fa-cc-stripe:before {
+ content: "\f1f5";
+}
+.fa-bell-slash:before {
+ content: "\f1f6";
+}
+.fa-bell-slash-o:before {
+ content: "\f1f7";
+}
+.fa-trash:before {
+ content: "\f1f8";
+}
+.fa-copyright:before {
+ content: "\f1f9";
+}
+.fa-at:before {
+ content: "\f1fa";
+}
+.fa-eyedropper:before {
+ content: "\f1fb";
+}
+.fa-paint-brush:before {
+ content: "\f1fc";
+}
+.fa-birthday-cake:before {
+ content: "\f1fd";
+}
+.fa-area-chart:before {
+ content: "\f1fe";
+}
+.fa-pie-chart:before {
+ content: "\f200";
+}
+.fa-line-chart:before {
+ content: "\f201";
+}
+.fa-lastfm:before {
+ content: "\f202";
+}
+.fa-lastfm-square:before {
+ content: "\f203";
+}
+.fa-toggle-off:before {
+ content: "\f204";
+}
+.fa-toggle-on:before {
+ content: "\f205";
+}
+.fa-bicycle:before {
+ content: "\f206";
+}
+.fa-bus:before {
+ content: "\f207";
+}
+.fa-ioxhost:before {
+ content: "\f208";
+}
+.fa-angellist:before {
+ content: "\f209";
+}
+.fa-cc:before {
+ content: "\f20a";
+}
+.fa-shekel:before,
+.fa-sheqel:before,
+.fa-ils:before {
+ content: "\f20b";
+}
+.fa-meanpath:before {
+ content: "\f20c";
+}
+.fa-buysellads:before {
+ content: "\f20d";
+}
+.fa-connectdevelop:before {
+ content: "\f20e";
+}
+.fa-dashcube:before {
+ content: "\f210";
+}
+.fa-forumbee:before {
+ content: "\f211";
+}
+.fa-leanpub:before {
+ content: "\f212";
+}
+.fa-sellsy:before {
+ content: "\f213";
+}
+.fa-shirtsinbulk:before {
+ content: "\f214";
+}
+.fa-simplybuilt:before {
+ content: "\f215";
+}
+.fa-skyatlas:before {
+ content: "\f216";
+}
+.fa-cart-plus:before {
+ content: "\f217";
+}
+.fa-cart-arrow-down:before {
+ content: "\f218";
+}
+.fa-diamond:before {
+ content: "\f219";
+}
+.fa-ship:before {
+ content: "\f21a";
+}
+.fa-user-secret:before {
+ content: "\f21b";
+}
+.fa-motorcycle:before {
+ content: "\f21c";
+}
+.fa-street-view:before {
+ content: "\f21d";
+}
+.fa-heartbeat:before {
+ content: "\f21e";
+}
+.fa-venus:before {
+ content: "\f221";
+}
+.fa-mars:before {
+ content: "\f222";
+}
+.fa-mercury:before {
+ content: "\f223";
+}
+.fa-intersex:before,
+.fa-transgender:before {
+ content: "\f224";
+}
+.fa-transgender-alt:before {
+ content: "\f225";
+}
+.fa-venus-double:before {
+ content: "\f226";
+}
+.fa-mars-double:before {
+ content: "\f227";
+}
+.fa-venus-mars:before {
+ content: "\f228";
+}
+.fa-mars-stroke:before {
+ content: "\f229";
+}
+.fa-mars-stroke-v:before {
+ content: "\f22a";
+}
+.fa-mars-stroke-h:before {
+ content: "\f22b";
+}
+.fa-neuter:before {
+ content: "\f22c";
+}
+.fa-genderless:before {
+ content: "\f22d";
+}
+.fa-facebook-official:before {
+ content: "\f230";
+}
+.fa-pinterest-p:before {
+ content: "\f231";
+}
+.fa-whatsapp:before {
+ content: "\f232";
+}
+.fa-server:before {
+ content: "\f233";
+}
+.fa-user-plus:before {
+ content: "\f234";
+}
+.fa-user-times:before {
+ content: "\f235";
+}
+.fa-hotel:before,
+.fa-bed:before {
+ content: "\f236";
+}
+.fa-viacoin:before {
+ content: "\f237";
+}
+.fa-train:before {
+ content: "\f238";
+}
+.fa-subway:before {
+ content: "\f239";
+}
+.fa-medium:before {
+ content: "\f23a";
+}
+.fa-yc:before,
+.fa-y-combinator:before {
+ content: "\f23b";
+}
+.fa-optin-monster:before {
+ content: "\f23c";
+}
+.fa-opencart:before {
+ content: "\f23d";
+}
+.fa-expeditedssl:before {
+ content: "\f23e";
+}
+.fa-battery-4:before,
+.fa-battery-full:before {
+ content: "\f240";
+}
+.fa-battery-3:before,
+.fa-battery-three-quarters:before {
+ content: "\f241";
+}
+.fa-battery-2:before,
+.fa-battery-half:before {
+ content: "\f242";
+}
+.fa-battery-1:before,
+.fa-battery-quarter:before {
+ content: "\f243";
+}
+.fa-battery-0:before,
+.fa-battery-empty:before {
+ content: "\f244";
+}
+.fa-mouse-pointer:before {
+ content: "\f245";
+}
+.fa-i-cursor:before {
+ content: "\f246";
+}
+.fa-object-group:before {
+ content: "\f247";
+}
+.fa-object-ungroup:before {
+ content: "\f248";
+}
+.fa-sticky-note:before {
+ content: "\f249";
+}
+.fa-sticky-note-o:before {
+ content: "\f24a";
+}
+.fa-cc-jcb:before {
+ content: "\f24b";
+}
+.fa-cc-diners-club:before {
+ content: "\f24c";
+}
+.fa-clone:before {
+ content: "\f24d";
+}
+.fa-balance-scale:before {
+ content: "\f24e";
+}
+.fa-hourglass-o:before {
+ content: "\f250";
+}
+.fa-hourglass-1:before,
+.fa-hourglass-start:before {
+ content: "\f251";
+}
+.fa-hourglass-2:before,
+.fa-hourglass-half:before {
+ content: "\f252";
+}
+.fa-hourglass-3:before,
+.fa-hourglass-end:before {
+ content: "\f253";
+}
+.fa-hourglass:before {
+ content: "\f254";
+}
+.fa-hand-grab-o:before,
+.fa-hand-rock-o:before {
+ content: "\f255";
+}
+.fa-hand-stop-o:before,
+.fa-hand-paper-o:before {
+ content: "\f256";
+}
+.fa-hand-scissors-o:before {
+ content: "\f257";
+}
+.fa-hand-lizard-o:before {
+ content: "\f258";
+}
+.fa-hand-spock-o:before {
+ content: "\f259";
+}
+.fa-hand-pointer-o:before {
+ content: "\f25a";
+}
+.fa-hand-peace-o:before {
+ content: "\f25b";
+}
+.fa-trademark:before {
+ content: "\f25c";
+}
+.fa-registered:before {
+ content: "\f25d";
+}
+.fa-creative-commons:before {
+ content: "\f25e";
+}
+.fa-gg:before {
+ content: "\f260";
+}
+.fa-gg-circle:before {
+ content: "\f261";
+}
+.fa-tripadvisor:before {
+ content: "\f262";
+}
+.fa-odnoklassniki:before {
+ content: "\f263";
+}
+.fa-odnoklassniki-square:before {
+ content: "\f264";
+}
+.fa-get-pocket:before {
+ content: "\f265";
+}
+.fa-wikipedia-w:before {
+ content: "\f266";
+}
+.fa-safari:before {
+ content: "\f267";
+}
+.fa-chrome:before {
+ content: "\f268";
+}
+.fa-firefox:before {
+ content: "\f269";
+}
+.fa-opera:before {
+ content: "\f26a";
+}
+.fa-internet-explorer:before {
+ content: "\f26b";
+}
+.fa-tv:before,
+.fa-television:before {
+ content: "\f26c";
+}
+.fa-contao:before {
+ content: "\f26d";
+}
+.fa-500px:before {
+ content: "\f26e";
+}
+.fa-amazon:before {
+ content: "\f270";
+}
+.fa-calendar-plus-o:before {
+ content: "\f271";
+}
+.fa-calendar-minus-o:before {
+ content: "\f272";
+}
+.fa-calendar-times-o:before {
+ content: "\f273";
+}
+.fa-calendar-check-o:before {
+ content: "\f274";
+}
+.fa-industry:before {
+ content: "\f275";
+}
+.fa-map-pin:before {
+ content: "\f276";
+}
+.fa-map-signs:before {
+ content: "\f277";
+}
+.fa-map-o:before {
+ content: "\f278";
+}
+.fa-map:before {
+ content: "\f279";
+}
+.fa-commenting:before {
+ content: "\f27a";
+}
+.fa-commenting-o:before {
+ content: "\f27b";
+}
+.fa-houzz:before {
+ content: "\f27c";
+}
+.fa-vimeo:before {
+ content: "\f27d";
+}
+.fa-black-tie:before {
+ content: "\f27e";
+}
+.fa-fonticons:before {
+ content: "\f280";
+}
+.fa-reddit-alien:before {
+ content: "\f281";
+}
+.fa-edge:before {
+ content: "\f282";
+}
+.fa-credit-card-alt:before {
+ content: "\f283";
+}
+.fa-codiepie:before {
+ content: "\f284";
+}
+.fa-modx:before {
+ content: "\f285";
+}
+.fa-fort-awesome:before {
+ content: "\f286";
+}
+.fa-usb:before {
+ content: "\f287";
+}
+.fa-product-hunt:before {
+ content: "\f288";
+}
+.fa-mixcloud:before {
+ content: "\f289";
+}
+.fa-scribd:before {
+ content: "\f28a";
+}
+.fa-pause-circle:before {
+ content: "\f28b";
+}
+.fa-pause-circle-o:before {
+ content: "\f28c";
+}
+.fa-stop-circle:before {
+ content: "\f28d";
+}
+.fa-stop-circle-o:before {
+ content: "\f28e";
+}
+.fa-shopping-bag:before {
+ content: "\f290";
+}
+.fa-shopping-basket:before {
+ content: "\f291";
+}
+.fa-hashtag:before {
+ content: "\f292";
+}
+.fa-bluetooth:before {
+ content: "\f293";
+}
+.fa-bluetooth-b:before {
+ content: "\f294";
+}
+.fa-percent:before {
+ content: "\f295";
+}
diff --git a/template/css/fotorama.css b/template/css/fotorama.css
new file mode 100644
index 0000000..fd9b526
--- /dev/null
+++ b/template/css/fotorama.css
@@ -0,0 +1,4 @@
+/*!
+ * Fotorama 4.6.4 | http://fotorama.io/license/
+ */
+.fotorama__arr:focus:after,.fotorama__fullscreen-icon:focus:after,.fotorama__html,.fotorama__img,.fotorama__nav__frame:focus .fotorama__dot:after,.fotorama__nav__frame:focus .fotorama__thumb:after,.fotorama__stage__frame,.fotorama__stage__shaft,.fotorama__video iframe{position:absolute;width:100%;height:100%;top:0;right:0;left:0;bottom:0}.fotorama--fullscreen,.fotorama__img{max-width:99999px!important;max-height:99999px!important;min-width:0!important;min-height:0!important;border-radius:0!important;box-shadow:none!important;padding:0!important}.fotorama__wrap .fotorama__grab{cursor:move;cursor:-webkit-grab;cursor:-o-grab;cursor:-ms-grab;cursor:grab}.fotorama__grabbing *{cursor:move;cursor:-webkit-grabbing;cursor:-o-grabbing;cursor:-ms-grabbing;cursor:grabbing}.fotorama__spinner{position:absolute!important;top:50%!important;left:50%!important}.fotorama__wrap--css3 .fotorama__arr,.fotorama__wrap--css3 .fotorama__fullscreen-icon,.fotorama__wrap--css3 .fotorama__nav__shaft,.fotorama__wrap--css3 .fotorama__stage__shaft,.fotorama__wrap--css3 .fotorama__thumb-border,.fotorama__wrap--css3 .fotorama__video-close,.fotorama__wrap--css3 .fotorama__video-play{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.fotorama__caption,.fotorama__nav:after,.fotorama__nav:before,.fotorama__stage:after,.fotorama__stage:before,.fotorama__wrap--css3 .fotorama__html,.fotorama__wrap--css3 .fotorama__nav,.fotorama__wrap--css3 .fotorama__spinner,.fotorama__wrap--css3 .fotorama__stage,.fotorama__wrap--css3 .fotorama__stage .fotorama__img,.fotorama__wrap--css3 .fotorama__stage__frame{-webkit-transform:translateZ(0);transform:translateZ(0)}.fotorama__arr:focus,.fotorama__fullscreen-icon:focus,.fotorama__nav__frame{outline:0}.fotorama__arr:focus:after,.fotorama__fullscreen-icon:focus:after,.fotorama__nav__frame:focus .fotorama__dot:after,.fotorama__nav__frame:focus .fotorama__thumb:after{content:'';border-radius:inherit;background-color:rgba(0,175,234,.5)}.fotorama__wrap--video .fotorama__stage,.fotorama__wrap--video .fotorama__stage__frame--video,.fotorama__wrap--video .fotorama__stage__frame--video .fotorama__html,.fotorama__wrap--video .fotorama__stage__frame--video .fotorama__img,.fotorama__wrap--video .fotorama__stage__shaft{-webkit-transform:none!important;transform:none!important}.fotorama__wrap--css3 .fotorama__nav__shaft,.fotorama__wrap--css3 .fotorama__stage__shaft,.fotorama__wrap--css3 .fotorama__thumb-border{transition-property:-webkit-transform,width;transition-property:transform,width;transition-timing-function:cubic-bezier(0.1,0,.25,1);transition-duration:0ms}.fotorama__arr,.fotorama__fullscreen-icon,.fotorama__no-select,.fotorama__video-close,.fotorama__video-play,.fotorama__wrap{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fotorama__select{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.fotorama__nav,.fotorama__nav__frame{margin:auto;padding:0}.fotorama__caption__wrap,.fotorama__nav__frame,.fotorama__nav__shaft{-moz-box-orient:vertical;display:inline-block;vertical-align:middle;*display:inline;*zoom:1}.fotorama__nav__frame,.fotorama__thumb-border{box-sizing:content-box}.fotorama__caption__wrap{box-sizing:border-box}.fotorama--hidden,.fotorama__load{position:absolute;left:-99999px;top:-99999px;z-index:-1}.fotorama__arr,.fotorama__fullscreen-icon,.fotorama__nav,.fotorama__nav__frame,.fotorama__nav__shaft,.fotorama__stage__frame,.fotorama__stage__shaft,.fotorama__video-close,.fotorama__video-play{-webkit-tap-highlight-color:transparent}.fotorama__arr,.fotorama__fullscreen-icon,.fotorama__video-close,.fotorama__video-play{background:url(../images/fotorama.png) no-repeat}@media (-webkit-min-device-pixel-ratio:1.5),(min-resolution:2dppx){.fotorama__arr,.fotorama__fullscreen-icon,.fotorama__video-close,.fotorama__video-play{background:url(../images/fotorama@2x.png) 0 0/96px 160px no-repeat}}.fotorama__thumb{background-color:#7f7f7f;background-color:rgba(127,127,127,.2)}@media print{.fotorama__arr,.fotorama__fullscreen-icon,.fotorama__thumb-border,.fotorama__video-close,.fotorama__video-play{background:none!important}}.fotorama{min-width:1px;overflow:hidden}.fotorama:not(.fotorama--unobtrusive)>*:not(:first-child){display:none}.fullscreen{width:100%!important;height:100%!important;max-width:100%!important;max-height:100%!important;margin:0!important;padding:0!important;overflow:hidden!important;background:#000}.fotorama--fullscreen{position:absolute!important;top:0!important;left:0!important;right:0!important;bottom:0!important;float:none!important;z-index:2147483647!important;background:#000;width:100%!important;height:100%!important;margin:0!important}.fotorama--fullscreen .fotorama__nav,.fotorama--fullscreen .fotorama__stage{background:#000}.fotorama__wrap{-webkit-text-size-adjust:100%;position:relative;direction:ltr;z-index:0}.fotorama__wrap--rtl .fotorama__stage__frame{direction:rtl}.fotorama__nav,.fotorama__stage{overflow:hidden;position:relative;max-width:100%}.fotorama__wrap--pan-y{-ms-touch-action:pan-y}.fotorama__wrap .fotorama__pointer{cursor:pointer}.fotorama__wrap--slide .fotorama__stage__frame{opacity:1!important}.fotorama__stage__frame{overflow:hidden}.fotorama__stage__frame.fotorama__active{z-index:8}.fotorama__wrap--fade .fotorama__stage__frame{display:none}.fotorama__wrap--fade .fotorama__fade-front,.fotorama__wrap--fade .fotorama__fade-rear,.fotorama__wrap--fade .fotorama__stage__frame.fotorama__active{display:block;left:0;top:0}.fotorama__wrap--fade .fotorama__fade-front{z-index:8}.fotorama__wrap--fade .fotorama__fade-rear{z-index:7}.fotorama__wrap--fade .fotorama__fade-rear.fotorama__active{z-index:9}.fotorama__wrap--fade .fotorama__stage .fotorama__shadow{display:none}.fotorama__img{-ms-filter:"alpha(Opacity=0)";filter:alpha(opacity=0);opacity:0;border:none!important}.fotorama__error .fotorama__img,.fotorama__loaded .fotorama__img{-ms-filter:"alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1}.fotorama--fullscreen .fotorama__loaded--full .fotorama__img,.fotorama__img--full{display:none}.fotorama--fullscreen .fotorama__loaded--full .fotorama__img--full{display:block}.fotorama__wrap--only-active .fotorama__nav,.fotorama__wrap--only-active .fotorama__stage{max-width:99999px!important}.fotorama__wrap--only-active .fotorama__stage__frame{visibility:hidden}.fotorama__wrap--only-active .fotorama__stage__frame.fotorama__active{visibility:visible}.fotorama__nav{font-size:0;line-height:0;text-align:center;display:none;white-space:nowrap;z-index:5}.fotorama__nav__shaft{position:relative;left:0;top:0;text-align:left}.fotorama__nav__frame{position:relative;cursor:pointer}.fotorama__nav--dots{display:block}.fotorama__nav--dots .fotorama__nav__frame{width:18px;height:30px}.fotorama__nav--dots .fotorama__nav__frame--thumb,.fotorama__nav--dots .fotorama__thumb-border{display:none}.fotorama__nav--thumbs{display:block}.fotorama__nav--thumbs .fotorama__nav__frame{padding-left:0!important}.fotorama__nav--thumbs .fotorama__nav__frame:last-child{padding-right:0!important}.fotorama__nav--thumbs .fotorama__nav__frame--dot{display:none}.fotorama__dot{display:block;width:4px;height:4px;position:relative;top:12px;left:6px;border-radius:6px;border:1px solid #7f7f7f}.fotorama__nav__frame:focus .fotorama__dot:after{padding:1px;top:-1px;left:-1px}.fotorama__nav__frame.fotorama__active .fotorama__dot{width:0;height:0;border-width:3px}.fotorama__nav__frame.fotorama__active .fotorama__dot:after{padding:3px;top:-3px;left:-3px}.fotorama__thumb{overflow:hidden;position:relative;width:100%;height:100%}.fotorama__nav__frame:focus .fotorama__thumb{z-index:2}.fotorama__thumb-border{position:absolute;z-index:9;top:0;left:0;border-style:solid;border-color:#00afea;background-image:linear-gradient(to bottom right,rgba(255,255,255,.25),rgba(64,64,64,.1))}.fotorama__caption{position:absolute;z-index:12;bottom:0;left:0;right:0;font-family:'Helvetica Neue',Arial,sans-serif;font-size:14px;line-height:1.5;color:#000}.fotorama__caption a{text-decoration:none;color:#000;border-bottom:1px solid;border-color:rgba(0,0,0,.5)}.fotorama__caption a:hover{color:#333;border-color:rgba(51,51,51,.5)}.fotorama__wrap--rtl .fotorama__caption{left:auto;right:0}.fotorama__wrap--no-captions .fotorama__caption,.fotorama__wrap--video .fotorama__caption{display:none}.fotorama__caption__wrap{background-color:#fff;background-color:rgba(255,255,255,.9);padding:5px 10px}@-webkit-keyframes spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.fotorama__wrap--css3 .fotorama__spinner{-webkit-animation:spinner 24s infinite linear;animation:spinner 24s infinite linear}.fotorama__wrap--css3 .fotorama__html,.fotorama__wrap--css3 .fotorama__stage .fotorama__img{transition-property:opacity;transition-timing-function:linear;transition-duration:.3s}.fotorama__wrap--video .fotorama__stage__frame--video .fotorama__html,.fotorama__wrap--video .fotorama__stage__frame--video .fotorama__img{-ms-filter:"alpha(Opacity=0)";filter:alpha(opacity=0);opacity:0}.fotorama__select{cursor:auto}.fotorama__video{top:32px;right:0;bottom:0;left:0;position:absolute;z-index:10}@-moz-document url-prefix(){.fotorama__active{box-shadow:0 0 0 transparent}}.fotorama__arr,.fotorama__fullscreen-icon,.fotorama__video-close,.fotorama__video-play{position:absolute;z-index:11;cursor:pointer}.fotorama__arr{position:absolute;width:32px;height:32px;top:50%;margin-top:-16px}.fotorama__arr--prev{left:2px;background-position:0 0}.fotorama__arr--next{right:2px;background-position:-32px 0}.fotorama__arr--disabled{pointer-events:none;cursor:default;*display:none;opacity:.1}.fotorama__fullscreen-icon{width:32px;height:32px;top:2px;right:2px;background-position:0 -32px;z-index:20}.fotorama__arr:focus,.fotorama__fullscreen-icon:focus{border-radius:50%}.fotorama--fullscreen .fotorama__fullscreen-icon{background-position:-32px -32px}.fotorama__video-play{width:96px;height:96px;left:50%;top:50%;margin-left:-48px;margin-top:-48px;background-position:0 -64px;opacity:0}.fotorama__wrap--css2 .fotorama__video-play,.fotorama__wrap--video .fotorama__stage .fotorama__video-play{display:none}.fotorama__error .fotorama__video-play,.fotorama__loaded .fotorama__video-play,.fotorama__nav__frame .fotorama__video-play{opacity:1;display:block}.fotorama__nav__frame .fotorama__video-play{width:32px;height:32px;margin-left:-16px;margin-top:-16px;background-position:-64px -32px}.fotorama__video-close{width:32px;height:32px;top:0;right:0;background-position:-64px 0;z-index:20;opacity:0}.fotorama__wrap--css2 .fotorama__video-close{display:none}.fotorama__wrap--css3 .fotorama__video-close{-webkit-transform:translate3d(32px,-32px,0);transform:translate3d(32px,-32px,0)}.fotorama__wrap--video .fotorama__video-close{display:block;opacity:1}.fotorama__wrap--css3.fotorama__wrap--video .fotorama__video-close{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.fotorama__wrap--no-controls.fotorama__wrap--toggle-arrows .fotorama__arr,.fotorama__wrap--no-controls.fotorama__wrap--toggle-arrows .fotorama__fullscreen-icon{opacity:0}.fotorama__wrap--no-controls.fotorama__wrap--toggle-arrows .fotorama__arr:focus,.fotorama__wrap--no-controls.fotorama__wrap--toggle-arrows .fotorama__fullscreen-icon:focus{opacity:1}.fotorama__wrap--video .fotorama__arr,.fotorama__wrap--video .fotorama__fullscreen-icon{opacity:0!important}.fotorama__wrap--css2.fotorama__wrap--no-controls.fotorama__wrap--toggle-arrows .fotorama__arr,.fotorama__wrap--css2.fotorama__wrap--no-controls.fotorama__wrap--toggle-arrows .fotorama__fullscreen-icon{display:none}.fotorama__wrap--css2.fotorama__wrap--no-controls.fotorama__wrap--toggle-arrows .fotorama__arr:focus,.fotorama__wrap--css2.fotorama__wrap--no-controls.fotorama__wrap--toggle-arrows .fotorama__fullscreen-icon:focus{display:block}.fotorama__wrap--css2.fotorama__wrap--video .fotorama__arr,.fotorama__wrap--css2.fotorama__wrap--video .fotorama__fullscreen-icon{display:none!important}.fotorama__wrap--css3.fotorama__wrap--no-controls.fotorama__wrap--slide.fotorama__wrap--toggle-arrows .fotorama__fullscreen-icon:not(:focus){-webkit-transform:translate3d(32px,-32px,0);transform:translate3d(32px,-32px,0)}.fotorama__wrap--css3.fotorama__wrap--no-controls.fotorama__wrap--slide.fotorama__wrap--toggle-arrows .fotorama__arr--prev:not(:focus){-webkit-transform:translate3d(-48px,0,0);transform:translate3d(-48px,0,0)}.fotorama__wrap--css3.fotorama__wrap--no-controls.fotorama__wrap--slide.fotorama__wrap--toggle-arrows .fotorama__arr--next:not(:focus){-webkit-transform:translate3d(48px,0,0);transform:translate3d(48px,0,0)}.fotorama__wrap--css3.fotorama__wrap--video .fotorama__fullscreen-icon{-webkit-transform:translate3d(32px,-32px,0)!important;transform:translate3d(32px,-32px,0)!important}.fotorama__wrap--css3.fotorama__wrap--video .fotorama__arr--prev{-webkit-transform:translate3d(-48px,0,0)!important;transform:translate3d(-48px,0,0)!important}.fotorama__wrap--css3.fotorama__wrap--video .fotorama__arr--next{-webkit-transform:translate3d(48px,0,0)!important;transform:translate3d(48px,0,0)!important}.fotorama__wrap--css3 .fotorama__arr:not(:focus),.fotorama__wrap--css3 .fotorama__fullscreen-icon:not(:focus),.fotorama__wrap--css3 .fotorama__video-close:not(:focus),.fotorama__wrap--css3 .fotorama__video-play:not(:focus){transition-property:-webkit-transform,opacity;transition-property:transform,opacity;transition-duration:.3s}.fotorama__nav:after,.fotorama__nav:before,.fotorama__stage:after,.fotorama__stage:before{content:"";display:block;position:absolute;text-decoration:none;top:0;bottom:0;width:10px;height:auto;z-index:10;pointer-events:none;background-repeat:no-repeat;background-size:1px 100%,5px 100%}.fotorama__nav:before,.fotorama__stage:before{background-image:linear-gradient(transparent,rgba(0,0,0,.2) 25%,rgba(0,0,0,.3) 75%,transparent),radial-gradient(farthest-side at 0 50%,rgba(0,0,0,.4),transparent);background-position:0 0,0 0;left:-10px}.fotorama__nav.fotorama__shadows--left:before,.fotorama__stage.fotorama__shadows--left:before{left:0}.fotorama__nav:after,.fotorama__stage:after{background-image:linear-gradient(transparent,rgba(0,0,0,.2) 25%,rgba(0,0,0,.3) 75%,transparent),radial-gradient(farthest-side at 100% 50%,rgba(0,0,0,.4),transparent);background-position:100% 0,100% 0;right:-10px}.fotorama__nav.fotorama__shadows--right:after,.fotorama__stage.fotorama__shadows--right:after{right:0}.fotorama--fullscreen .fotorama__nav:after,.fotorama--fullscreen .fotorama__nav:before,.fotorama--fullscreen .fotorama__stage:after,.fotorama--fullscreen .fotorama__stage:before,.fotorama__wrap--fade .fotorama__stage:after,.fotorama__wrap--fade .fotorama__stage:before,.fotorama__wrap--no-shadows .fotorama__nav:after,.fotorama__wrap--no-shadows .fotorama__nav:before,.fotorama__wrap--no-shadows .fotorama__stage:after,.fotorama__wrap--no-shadows .fotorama__stage:before{display:none}
\ No newline at end of file
diff --git a/template/css/google-translate.css b/template/css/google-translate.css
new file mode 100644
index 0000000..bfb62d6
--- /dev/null
+++ b/template/css/google-translate.css
@@ -0,0 +1,50 @@
+/* Фиксируем позицию body, которую меняет панель гугла */
+
+body {
+ top: 0 !important;
+ position: static !important;
+ min-height: 100vh !important;
+}
+
+
+/* Прячем панель гугла */
+
+.skiptranslate {
+ display: none !important;
+ position: initial !important;
+ min-height: 100% !important;
+ top: 40px !important;
+}
+
+/* Убираем подсветку ссылок */
+
+.goog-text-highlight {
+ background-color: inherit;
+ box-shadow: none;
+ box-sizing: inherit;
+}
+
+/* language */
+
+.language {
+ position: fixed;
+ left: 10px;
+ top: 50%;
+ transform: translateY(-50%);
+ z-index: 999;
+ display: flex;
+ flex-direction: column;
+}
+
+.language__img {
+ margin: 2px;
+ cursor: pointer;
+ opacity: .5;
+}
+
+.language__img:hover,
+.language__img_active {
+ opacity: 1;
+}
+
+/* Стили для демонстрации */
\ No newline at end of file
diff --git a/template/css/help.css b/template/css/help.css
new file mode 100644
index 0000000..321cd95
--- /dev/null
+++ b/template/css/help.css
@@ -0,0 +1,271 @@
+#drop-files
+{
+ position: relative;
+ width: 99%;
+ height: 56px;
+ margin: 0 auto;
+ background-color: #ffffff;
+ border: 2px dashed #dcdcdc;
+ text-align: center;
+ font: 15px 'Open Sans';
+}
+#drop-files p
+{
+ clear: none;
+ padding: 0;
+ margin: 0;
+}
+#uploaded-holder
+{
+ display: none;
+ position: relative;
+ margin: 0 auto 10px;
+ width: 100%;
+}
+#dropped-files
+{
+ display: block;
+ margin: 0 auto;
+ width: 100%
+}
+#upload-button
+{
+ z-index: 9999;
+ display: none;
+ margin: 5px 0;
+}
+.drop-button, .success-button
+{
+ display: inline-block;
+ padding: 0px;
+ width: 100%;
+ font-size: 13px;
+ bottom: 0;
+ text-align: center;
+ text-decoration: none;
+ color: #ffffff !important
+}
+.drop-button
+{
+ background-color: #b07474
+}
+.success-button
+{
+ background-color: #74b084
+}
+#dropped-files .image, #uploaded-files .image
+{
+ position: relative;
+ height: 100px;
+ width: 100px;
+ border: 4px solid #fff;
+ box-shadow: 0px 0px 10px rgba(0,0,0,0.1);
+ background-color: #fff;
+ float: left;
+ overflow: hidden;
+ background-size: contain !important;
+ background-repeat: no-repeat !important;
+ text-align: center;
+}
+#upload-button .ss-upload
+{
+ font-size: 0.7em;
+}
+
+#upload-button span
+{
+ position: relative;
+ text-align: center;
+ background: white;
+ font-size: 15px;
+ padding: 5px;
+ margin-right: 8px;
+}
+#loading
+{
+ width: 100%;
+ position: relative;
+ margin: 5px 0;
+}
+#loading-bar
+{
+ width: 100%;
+ height: 16px;
+ background: #fff;
+ box-shadow: 0 0 15px rgba(0,0,0,0.1);
+ border-radius: 5px;
+ padding: 2px;
+}
+.loading-color
+{
+ width: 0%;
+ height: 100%;
+ -webkit-transition: all 0.1s ease-in;
+ -moz-transition: all 0.1s ease-in;
+ -ms-transition: all 0.1s ease-in;
+ -o-transition: all 0.1s ease-in;
+ transition: all 0.1s ease-in;
+ border-radius: inherit;
+ background-color: #4edbf1;
+}
+#loading-content
+{
+ position: relative;
+ font-size: 13px;
+ font-weight: bold;
+ text-align: center;
+ width: 100%;
+}
+#file-name-holder
+{
+ width: 100%;
+ float: left;
+}
+#file-name-holder h1
+{
+ padding: 5px 0;
+ font-size: 13px;
+ margin: 0;
+}
+
+#drop-files input
+{
+ border: 0;
+ background-color: #ffffff;
+ width: initial;
+}
+#uploaded-files .img-block:nth-child(8), #dropped-files .img-block:nth-child(9)
+{
+ margin-right: 0px;
+}
+.img-block
+{
+ width: 108px;
+ float: left;
+ margin-right: 12px
+}
+.help_main_head span.help_sender > a
+{
+ display: none;
+}
+.help_main_head:hover span.help_sender > a
+{
+ display: initial;
+}
+.help_data a
+{
+ text-decoration: underline;
+}
+.help_data_foot
+{
+ padding: 5px 5px 0 5px;
+ font-size: 10px;
+}
+.help_data_foot > div
+{
+ display: inline-block;
+ width: 100%;
+ margin: 5px 0;
+}
+.help_data_foot > div > div
+{
+ width: 88px;
+ float: left;
+ margin-right: 5px;
+}
+.help_data_foot > div > div > a .image
+{
+ position: relative;
+ height: 80px;
+ width: 80px;
+ border: 4px solid #fff;
+ box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
+ background-color: #fff;
+ float: left;
+ overflow: hidden;
+ background-size: contain !important;
+ background-repeat: no-repeat !important;
+ text-align: center;
+}
+.help_msg
+{
+ position: relative;
+ display: block;
+ margin-bottom: 15px;
+}
+.help_msg_ava
+{
+ height: 57px;
+ width: 57px;
+ border: 4px solid #fff;
+ box-shadow: 0px 0px 1px 1px #DDD;
+ overflow: hidden;
+ margin-top: 5px;
+ border-radius: 50%;
+ float: right;
+}
+.help_msg > .help_main
+{
+ margin-right: 73px;
+}
+.help_msg > .help_main
+{
+ padding-right: 5px;
+ display: block;
+}
+.help_main_head
+{
+ padding: 0 5px;
+ line-height: 22px;
+}
+.help_main_head > span.help_sender
+{
+ color: #597da3;
+ font-size: 11px;
+ font-family: 'Roboto Medium';
+}
+.help_date
+{
+ color: #888;
+ float: right;
+ font-size: 10px;
+ margin-top: 2px;
+}
+.help_body
+{
+ position: relative;
+}
+.help_msg > .help_main .help_body span.help_sprite
+{
+ background: url(../images/help_sprite.png) no-repeat 0px 0px;
+ width: 25px;
+ height: 17px;
+ display: block;
+ position: absolute;
+ right: -22px;
+ top: 8px;
+}
+.help_data
+{
+ background: #fff;
+ border: 1px solid rgba(196, 196, 196, 0.47);
+ padding: 10px;
+ color: #555;
+ font-size: 12px;
+ z-index: 0;
+ display: block;
+ font-family: 'Exo 2', sans-serif;
+ font-style: italic;
+ font-weight: 300;
+ -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.05);
+ -moz-box-shadow: 0 1px 1px rgba(0,0,0,0.05);
+ box-shadow: 0 1px 1px rgba(0,0,0,0.05);
+ word-wrap: break-word;
+}
+.help_msg_ava img
+{
+ width: 110%;
+ min-height: 112%;
+ margin-top: -3px;
+ margin-left: -2px;
+}
diff --git a/template/css/img.css b/template/css/img.css
new file mode 100644
index 0000000..2293931
--- /dev/null
+++ b/template/css/img.css
@@ -0,0 +1,320 @@
+#fancybox-loading
+{
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ height: 40px;
+ width: 40px;
+ margin-top: -20px;
+ margin-left: -20px;
+ cursor: pointer;
+ overflow: hidden;
+ background: transparent;
+ z-index: 1104;
+ display: none;
+}
+* html #fancybox-loading
+{
+ position: absolute;
+ margin-top: 0;
+}
+#fancybox-loading div
+{
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 40px;
+ height: 480px;
+ background: transparent url('../images/icons/box/fancy_loading.png') no-repeat;
+}
+#fancybox-overlay
+{
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ background: #000;
+ z-index: 1100;
+ display: none;
+}
+* html #fancybox-overlay
+{
+ position: absolute;
+ width: 100%;
+}
+#fancybox-tmp
+{
+ padding: 0;
+ margin: 0;
+ border: 0;
+ overflow: auto;
+ display: none;
+}
+#fancybox-wrap
+{
+ position: absolute;
+ top: 0;
+ left: 0;
+ margin: 0;
+ padding: 20px;
+ z-index: 1101;
+ display: none;
+}
+#fancybox-outer
+{
+ position: relative;
+ width: 100%;
+ height: 100%;
+ background: #FFF;
+}
+#fancybox-inner
+{
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: 0;
+ outline: none;
+ overflow: hidden;
+}
+#fancybox-hide-sel-frame
+{
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: transparent;
+}
+#fancybox-close
+{
+ position: absolute;
+ top: -15px;
+ right: -15px;
+ width: 32px;
+ height: 32px;
+ background: url('../images/icons/box/fancy_close.png') top left no-repeat;
+ cursor: pointer;
+ z-index: 1103;
+ display: none;
+}
+#fancybox_error
+{
+ color: #444;
+ font: normal 12px/20px Arial;
+}
+#fancybox-content
+{
+ height: auto;
+ width: auto;
+ padding: 0;
+ margin: 0;
+}
+#fancybox-img
+{
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ border: none;
+ outline: none;
+ line-height: 0;
+ vertical-align: top;
+ -ms-interpolation-mode: bicubic;
+}
+#fancybox-frame
+{
+ position: relative;
+ width: 100%;
+ height: 100%;
+ border: none;
+ display: block;
+}
+#fancybox-title
+{
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ font-family: Arial;
+ font-size: 12px;
+ z-index: 1102;
+}
+.fancybox-title-inside
+{
+ padding: 10px 0;
+ text-align: center;
+ color: #333;
+}
+.fancybox-title-outside
+{
+ padding-top: 5px;
+ color: #FFF;
+ text-align: center;
+ font-weight: bold;
+}
+.fancybox-title-over
+{
+ color: #FFF;
+ text-align: left;
+}
+#fancybox-title-over
+{
+ padding: 10px;
+ background: url('../images/icons/box/fancy_title_over.png');
+ display: block;
+}
+#fancybox-title-wrap
+{
+ display: inline-block;
+}
+#fancybox-title-wrap span
+{
+ height: 32px;
+ float: left;
+}
+#fancybox-title-left
+{
+ padding-left: 15px;
+ background: transparent url('../images/icons/box/fancy_title_left.png') repeat-x;
+}
+#fancybox-title-main
+{
+ font-weight: bold;
+ line-height: 29px;
+ background: transparent url('../images/icons/box/fancy_title_main.png') repeat-x;
+ color: #FFF;
+}
+
+#fancybox-title-right
+{
+ padding-left: 15px;
+ background: transparent url('../images/icons/box/fancy_title_right.png') repeat-x;
+}
+#fancybox-left, #fancybox-right
+{
+ position: absolute;
+ bottom: 0px;
+ height: 100%;
+ width: 35%;
+ cursor: pointer;
+ outline: none;
+ background-image: url('../images/icons/box/blank.gif');
+ z-index: 1102;
+ display: none;
+}
+#fancybox-left
+{
+ left: 0px;
+}
+#fancybox-right
+{
+ right: 0px;
+}
+#fancybox-left-ico, #fancybox-right-ico
+{
+ position: absolute;
+ top: 50%;
+ left: -9999px;
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ cursor: pointer;
+ z-index: 1102;
+ display: block;
+}
+#fancybox-left-ico
+{
+ background: transparent url('../images/icons/box/fancy_nav_left.png') no-repeat;
+}
+#fancybox-right-ico
+{
+ background: transparent url('../images/icons/box/fancy_nav_right.png') no-repeat;
+}
+#fancybox-left:hover, #fancybox-right:hover
+{
+ visibility: visible;
+}
+#fancybox-left:hover span
+{
+ left: 20px;
+}
+#fancybox-right:hover span
+{
+ left: auto;
+ right: 20px;
+}
+div.fancy-bg
+{
+ position: absolute;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ z-index: 1001;
+}
+div#fancy-bg-n
+{
+ top: -20px;
+ left: 0;
+ width: 100%;
+ height: 20px;
+ background: transparent url('../images/icons/box/fancy_shadow_n.png') repeat-x;
+}
+div#fancy-bg-ne
+{
+ top: -20px;
+ right: -20px;
+ width: 20px;
+ height: 20px;
+ background: transparent url('../images/icons/box/fancy_shadow_ne.png') no-repeat;
+}
+div#fancy-bg-e
+{
+ top: 0;
+ right: -20px;
+ height: 100%;
+ width: 20px;
+ background: transparent url('../images/icons/box/fancy_shadow_e.png') repeat-y;
+}
+div#fancy-bg-se
+{
+ bottom: -20px;
+ right: -20px;
+ width: 20px;
+ height: 20px;
+ background: transparent url('../images/icons/box/fancy_shadow_se.png') no-repeat;
+}
+div#fancy-bg-s
+{
+ bottom: -20px;
+ left: 0;
+ width: 100%;
+ height: 20px;
+ background: transparent url('../images/icons/box/fancy_shadow_s.png') repeat-x;
+}
+div#fancy-bg-sw
+{
+ bottom: -20px;
+ left: -20px;
+ width: 20px;
+ height: 20px;
+ background: transparent url('../images/icons/box/fancy_shadow_sw.png') no-repeat;
+}
+div#fancy-bg-w
+{
+ top: 0;
+ left: -20px;
+ height: 100%;
+ width: 20px;
+ background: transparent url('../images/icons/box/fancy_shadow_w.png') repeat-y;
+}
+div#fancy-bg-nw
+{
+ top: -20px;
+ left: -20px;
+ width: 20px;
+ height: 20px;
+ background: transparent url('../images/icons/box/fancy_shadow_nw.png') no-repeat;
+}
\ No newline at end of file
diff --git a/template/css/load.css b/template/css/load.css
new file mode 100644
index 0000000..e69de29
diff --git a/template/css/pointer.css b/template/css/pointer.css
new file mode 100644
index 0000000..fb4d3ff
--- /dev/null
+++ b/template/css/pointer.css
@@ -0,0 +1,47 @@
+.radio {
+ vertical-align: top;
+ width: 17px;
+ height: 17px;
+ margin: 0 3px 0 0;
+}
+.radio + label {
+ cursor: pointer;
+}
+.radio:not(checked) {
+ position: absolute;
+ opacity: 0;
+}
+.radio:not(checked) + label {
+ position: relative;
+ padding: 0 0 0 35px;
+}
+.radio:not(checked) + label:before {
+ content: '';
+ position: absolute;
+ top: -3px;
+ left: 0;
+ width: 22px;
+ height: 22px;
+ border: 1px solid #CDD1DA;
+ border-radius: 50%;
+ background: #FFF;
+}
+.radio:not(checked) + label:after {
+ content: '';
+ position: absolute;
+ top: 1px;
+ left: 4px;
+ width: 16px;
+ height: 16px;
+ border-radius: 50%;
+ background: #9FD468;
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.5);
+ opacity: 0;
+ transition: all .2s;
+}
+.radio:checked + label:after {
+ opacity: 1;
+}
+.radio:focus + label:before {
+ box-shadow: 0 0 0 3px rgba(255,255,0,.5);
+}
\ No newline at end of file
diff --git a/template/css/replenish.css b/template/css/replenish.css
new file mode 100644
index 0000000..8894172
--- /dev/null
+++ b/template/css/replenish.css
@@ -0,0 +1,87 @@
+*
+{
+ padding: 0;
+ margin: 0;
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ -ms-box-sizing: content-box;
+ -o-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+html
+{
+ font-family: 'Open Sans';
+ font-size: 14px;
+ background-color: #17a918;
+ color: #000;
+}
+
+html, body
+{
+ height: 100%;
+}
+
+.success, .content
+{
+ width: 1000px;
+}
+
+.success
+{
+ width: 1000px;
+ height: 375px;
+ margin: 0 auto;
+}
+
+.success-replenish
+{
+ background: url('../images/error/success.jpg') center top no-repeat;
+}
+
+.info-success
+{
+ padding: 5px;
+ text-align: center;
+ text-transform: uppercase;
+ border: 4px solid #000;
+ border-radius: 30px;
+ font-size: 33px;
+
+}
+
+.info-success
+{
+ font-size: 28px;
+}
+
+.content
+{
+ margin: 0 auto;
+}
+
+.content ul
+{
+ margin-top: 15px;
+ text-align: center;
+}
+
+.content ul li
+{
+ list-style-type: none;
+ display: inline-block;
+}
+
+.content ul li a
+{
+ color: #000;
+ display: inline-block;
+ margin: 10px;
+ font-size: 21px;
+ text-transform: uppercase;
+}
+
+.content ul li a:hover
+{
+ text-decoration: none;
+}
\ No newline at end of file
diff --git a/template/css/style.css b/template/css/style.css
new file mode 100644
index 0000000..4829612
--- /dev/null
+++ b/template/css/style.css
@@ -0,0 +1,1602 @@
+@font-face
+{
+ font-family: 'Open Sans';
+ src: local('Open Sans Light'), local('OpenSans-Light'), url('../fonts/opensanslight.woff2') format('woff2'), url('../fonts/opensanslight.woff') format('woff'), url('../fonts/opensanslight.ttf') format('truetype');
+ font-weight: 300;
+ font-style: normal
+}
+@font-face
+{
+ font-family: 'Open Sans';
+ src: local('Open Sans Bold'), local('OpenSans-Bold'), url('../fonts/opensansbold.woff2') format('woff2'), url('../fonts/opensansbold.woff') format('woff'), url('../fonts/opensansbold.ttf') format('truetype');
+ font-weight: 800;
+ font-style: normal
+}
+@font-face
+{
+ font-family: 'links';
+ src: local('Airborne Pilot'), url('../fonts/airbornepilot.woff2') format('woff2'), url('../fonts/airbornepilot.woff') format('woff'), url('../fonts/airbornepilot.ttf') format('truetype');
+ font-weight: 400;
+ font-style: normal
+}
+@font-face
+{
+ font-family: 'nav';
+ src: local('Airborne'), url('../fonts/airborne.woff2') format('woff2'), url('../fonts/airborne.woff') format('woff'), url('../fonts/airborne.ttf') format('truetype');
+ font-weight: 400;
+ font-style: normal
+}
+@font-face
+{
+ font-family: 'menu';
+ src: local('Arimo'), url('../fonts/arimo.woff2') format('woff2'), url('../fonts/arimo.woff') format('woff'), url('../fonts/arimo.ttf') format('truetype');
+ font-weight: 400;
+ font-style: normal
+}
+@font-face
+{
+ font-family: 'logo';
+ src: local('Cantarell Regular'), local('Cantarell-Regular'), url('../fonts/cantarellregular.woff2') format('woff2'), url('../fonts/cantarellregular.woff') format('woff'), url('../fonts/cantarellregular.ttf') format('truetype');
+ font-weight: 400;
+ font-style: normal
+}
+.miniscreen
+{
+ display: none
+}
+
+@media screen and (max-width: 1199px)
+{
+ .all
+ {
+ width: 1000px !important;
+ }
+ .main
+ {
+ width: 1000px !important;
+ }
+ .block
+ {
+ display: none !important;
+ }
+
+ .miniscreen
+ {
+ display: inline-block !important;
+ }
+
+ .server_list table
+ {
+ width: 100% !important;
+ }
+ .server_img
+ {
+ display: none !important;
+ }
+}
+
+*
+{
+ padding: 0;
+ margin: 0;
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ -ms-box-sizing: content-box;
+ -o-box-sizing: content-box;
+ box-sizing: content-box
+}
+
+html
+{
+ background: #EEE url('../images/bg.png');
+ font-family: 'Open Sans';
+ font-size: 13px
+}
+html, body, .all
+{
+ height: 100%
+}
+a
+{
+ color: #333;
+ text-decoration: none
+}
+a:hover
+{
+ color: #222
+}
+table
+{
+ border-collapse: collapse;
+ width: 100%;
+ font-size: 12px
+}
+input, select {
+ /* width: 100%; */
+ position: relative;
+ bottom: -1px;
+}
+h1, h2, h3, h4, h5
+{
+ display: inline-block;
+ font-weight: bold
+}
+.modal-body > h3
+{
+ display: block
+}
+h1
+{
+ font-size: 2em
+}
+h2
+{
+ font-size: 1.5em
+}
+h3
+{
+ font-size: 1.17em
+}
+h4
+{
+ font-size: 0.9em
+}
+h5
+{
+ font-size: 0.83em
+}
+#loadinginfo {
+ display: none;
+ /* background-color: #fff; */
+ height: 15px;
+ width: 100%;
+ left: 90%;
+ top: 0px;
+ position: fixed;
+}
+
+.left
+{
+ float: left
+}
+.right
+{
+ float: right
+}
+.center
+{
+ text-align: center
+}
+.text-left
+{
+ text-align: left
+}
+.text-right
+{
+ text-align: right
+}
+.margin-top
+{
+ margin-top: 5px
+}
+.margin-bottom
+{
+ margin-bottom: 3px
+}
+.margin-left
+{
+ margin-left: 5px
+}
+.margin-right
+{
+ margin-right: 5px
+}
+.vtop
+{
+ vertical-align: top
+}
+.vbot
+{
+ vertical-align: bottom
+}
+.vbottom
+{
+ vertical-align: bottom
+}
+.border-bottom
+{
+ border-bottom: 1px solid #DCDCDC
+}
+.underline
+{
+ border-bottom: 1px dashed;
+ padding-bottom: 2px !important
+}
+
+.none
+{
+ display: none
+}
+
+.hide
+{
+ display: none
+}
+
+.pointer
+{
+ cursor: pointer !important
+}
+
+.informer
+{
+ padding: 8px
+}
+
+.informer.red
+{
+ color: #a94442;
+ background-color: #f2dede;
+ border-color: #ebccd1
+}
+.informer.blue
+{
+ color: #31708f;
+ background-color: #d9edf7;
+ border-color: #bce8f1
+}
+.informer.green
+{
+ color: #3c763d;
+ background-color: #dff0d8;
+ border-color: #d6e9c6
+}
+.informer.gray
+{
+ color: #8a6d3b;
+ background-color: #fcf8e3;
+ border-color: #faebcc
+}
+
+button, .btn
+{
+ opacity: 0.9;
+ border: 0;
+ background-color: #7f97b1;
+ color: #FFF !important;
+ outline: 0;
+ cursor: pointer;
+ display: inline-block;
+ text-align: center;
+ font: 11px/16px 'Open Sans';
+ text-transform: uppercase;
+ box-sizing: border-box;
+ padding: 1px 5px
+}
+button:hover, .btn:hover
+{
+ opacity: 1
+}
+.btn-switch > .lcs_wrap
+{
+ display: block !important
+}
+.btn-max
+{
+ width: 100%;
+ box-sizing: border-box
+}
+.btn-fix
+{
+ width: 90px
+}
+.btn-short
+{
+ padding: 1px 5px
+}
+.btn-shift
+{
+ padding: 1px 6px
+}
+.btn-success
+{
+ background-color: #82b17f
+}
+.btn-error
+{
+ background-color: #b17f7f
+}
+.btn-info
+{
+ background-color: #33a9de
+}
+.btn-black
+{
+ background-color: #000
+}
+.btn-gray
+{
+ background-color: #DDD;
+ color: #222 !important
+}
+.btn-gray:hover
+{
+ background-color: #CCC !important;
+ color: #222 !important
+}
+.btn-block
+{
+ background-color: #000000;
+ opacity: 0.1;
+ cursor: no-drop
+}
+.btn-none
+{
+ cursor: auto;
+ background: none !important;
+ color: #222 !important
+}
+.btn-full
+{
+ width: 99%
+}
+
+.all
+{
+ width: 1200px;
+ margin: 0 auto
+}
+
+.top
+{
+ width: 100%;
+ max-width: 1200px;
+ display: inline-block;
+ padding: 10px;
+ background-color: #fff;
+ border: 1px solid #e1e1e1;
+ margin-bottom: 10px;
+ box-sizing: border-box
+}
+.top a
+{
+ font: 12px 'Open Sans';
+ font-weight: bold;
+ color: #000;
+ padding: 0 5px;
+ text-transform: uppercase
+}
+.top a:hover
+{
+ color: #000;
+ border-bottom: 1px solid
+}
+.header
+{
+ width: 100%;
+ display: inline-block;
+ height: 54px;
+ background-color: #FFF;
+ border: 1px solid #EBEBEB;
+ border-bottom: 0;
+ box-sizing: border-box
+}
+.logo
+{
+ font: 30px/30px 'logo';
+ color: #191919;
+ display: inline-block;
+ padding: 12px
+}
+.user
+{
+ padding: 5px
+}
+.recsign a
+{
+ display: block;
+ color: #191919;
+ text-transform: uppercase;
+ font: 12px/12px 'Open Sans';
+ font-weight: bold;
+ padding: 5px
+}
+.recsign a:hover
+{
+ text-decoration: underline
+}
+.user_lk
+{
+ margin: 11px 7px
+}
+.user_lk a
+{
+ padding: 5px;
+ text-transform: uppercase;
+ font: 11px 'Open Sans';
+ font-weight: bold;
+ color: #191919
+}
+.user_lk a:hover
+{
+ text-decoration: underline
+}
+.nav
+{
+ height: 30px;
+ box-sizing: border-box;
+ padding: 9px;
+ background-color: #f0f2f2;
+ color: #888;
+ font: 11px 'menu';
+ display: inline-block;
+ width: 100%;
+ border-left: 1px solid #EBEBEB;
+ border-right: 1px solid #EBEBEB
+}
+.nav i
+{
+ padding: 0 3px
+}
+.nav a
+{
+ color: #191919
+}
+.nav a:hover
+{
+ text-decoration: underline
+}
+.wrapper
+{
+ display: table;
+ height: 100%;
+ border: 1px solid #E1E1E1;
+ border-top: 0
+}
+.main
+{
+ background-color: #fff;
+ width: 1000px;
+ max-width: 1000px;
+ height: 100%;
+ box-sizing: border-box;
+ padding: 10px;
+ display: table-cell
+}
+.block
+{
+ background-color: #FFFFFF;
+ width: 200px;
+ min-width: 200px;
+ vertical-align: top;
+ display: table-cell;
+ border-right: 1px solid #E1E1E1
+}
+.block .title
+{
+ text-align: right;
+ padding: 7px;
+ font: 13px 'Open Sans';
+ text-align: center
+}
+.block .title::after
+{
+ content: '';
+ border-bottom: 1px solid #191919;
+ display: block;
+ margin: 5px auto;
+ width: 70%
+}
+.block ul
+{
+ list-style: none
+}
+.block ul li
+{
+ border-bottom: 1px solid rgb(225, 230, 234)
+}
+.block ul li a
+{
+ color: #191919;
+ font: 12px 'menu';
+ padding: 8px 6px;
+ display: block;
+ transition: all 0.23s ease-in-out;
+ -moz-transition: all 0.23s ease-in-out;
+ -webkit-transition: all 0.23s ease-in-out;
+ -o-transition: all 0.23s ease-in-out
+}
+.block ul li a:hover, .block ul li .active
+{
+ background-color: #F0F2F5
+}
+.block ul li a > i:nth-child(1)
+{
+ margin-right: 4px;
+ width: 20px;
+ text-align: center
+}
+.footer
+{
+ width: 100%;
+ display: inline-block;
+ height: 80px;
+ background-color: #FFF;
+ color: #DDD;
+ margin-bottom: 15px;
+ border: 1px solid #EBEBEB;
+ border-top: 0;
+ box-sizing: border-box
+}
+.footer > .menu
+{
+ text-align: left;
+ margin: 10px;
+ display: inline-block
+}
+.footer > .menu a
+{
+ color: #808CA0;
+ padding: 0 5px
+}
+.infoegp
+{
+ color: #555;
+ padding: 15px 15px 0 0;
+ text-align: center
+}
+.copyright
+{
+ color: #777;
+ margin: 15px;
+ font: 14px 'Arial'
+}
+.social
+{
+ font-size: 25px;
+ color: #000
+}
+.social .fa-vk
+{
+ color: #597da3;
+ margin-right: 5px
+}
+
+.service
+{
+ text-align: center;
+ width: 200px
+}
+
+.service
+{
+ text-align: center;
+ width: 200px;
+ float: left;
+ margin: 10px 16px;
+ background-color: #f0f2f2;
+ border: 1px solid #e1e1e1;
+ padding: 4px
+}
+.service > .line
+{
+ border-top: 1px solid #222;
+ width: 200px;
+ position: relative;
+ top: 12px
+}
+.service > .title
+{
+ font: 11px 'nav';
+ background-color: #f0f2f2;
+ display: inline-block;
+ position: relative;
+ padding: 5px;
+ z-index: 10
+}
+.service img
+{
+ opacity: 0.8
+}
+.service a
+{
+ border: 1px solid #cecece;
+ padding: 5px 36px;
+ text-transform: uppercase;
+ font-size: 10px;
+ font-weight: bold;
+ margin: 5px 0;
+ display: inline-block
+}
+.service a:hover
+{
+ background-color: #eee
+}
+
+blockquote
+{
+ padding: 5px;
+ border-left: 1px solid #597da3;
+ background-color: #e8ebf0
+}
+
+/* form */
+.form
+{
+ margin: 0 auto
+}
+.form td
+{
+ padding: 5px
+}
+.form img, .form label
+{
+ cursor: pointer
+}
+.form label
+{
+ display: block;
+ margin-bottom: 3px;
+ font: 9px 'Open Sans';
+ font-weight: bold
+}
+.form input, .form select, .form textarea
+{
+ border: 1px dashed #CCC;
+ padding: 5px;
+ width: 100%;
+ outline: 0;
+ box-sizing: border-box;
+ font: 12px 'Tahoma';
+ background-color: #FFF
+}
+.form textarea
+{
+ resize: vertical;
+ width: 100%
+}
+.form input:focus, .form select:focus, .form textarea:focus
+{
+ border-color: #222
+}
+.form #captha
+{
+ text-transform: uppercase
+}
+
+.server_list {
+ width: 100%;
+ display: inline-block;
+ padding: 5px 5px 0 5px;
+ box-sizing: border-box;
+ font: 14px 'Arial';
+}
+.service_block_mon {
+ float: left;
+ width: 113px;
+ height: 150px;
+ border-radius: 5px;
+ margin: 0 0 6px 5px;
+ background-color: #f0f2f2;
+}
+.server_list table td
+{
+ padding: 2px;
+ border-bottom: 1px solid #E8EBF0
+}
+.server_list table
+{
+ width: 80%
+}
+.server_list table tr:last-child td
+{
+ padding-bottom: 10px
+}
+.server_list .btn
+{
+ position: relative;
+ bottom: -5px
+}
+.server_img
+{
+ display: inline-block;
+ float: left;
+ margin-right: 10px;
+ width: 160px;
+ height: 120px
+}
+.server_img img
+{
+ width: 150px;
+ height: 113px
+}
+.server_name
+{
+ max-width: 210px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap
+}
+.server_nav
+{
+ margin: 10px 0;
+ width: initial
+}
+.server_nav i
+{
+ font-size: 24px;
+ display: block;
+ text-shadow: none
+}
+.server_nav td
+{
+ border-bottom: 1px solid #ccc;
+ padding: 3px;
+ text-align: center;
+ font: 7px/20px menu;
+ text-transform: uppercase;
+ text-shadow: 1px 0 1px rgba(25, 25, 25, 0.34)
+}
+.server_nav td a
+{
+ color: #555;
+ background-color: #fcfcfc;
+ box-shadow: 0 0 2px #ccc;
+ padding: 5px;
+ width: 60px;
+ height: 35px;
+ display: block;
+ transition: all 0.23s ease-in-out;
+ -moz-transition: all 0.23s ease-in-out;
+ -webkit-transition: all 0.23s ease-in-out;
+ -o-transition: all 0.23s ease-in-out
+}
+.server_nav td a:hover,
+.server_nav .active
+{
+ color: #2196F3
+}
+.server_console pre
+{
+ width: 99%;
+ height: 350px;
+ overflow-y: scroll;
+ overflow-x: hidden;
+ border-radius: 0;
+ padding: 5px;
+ margin-bottom: 10px;
+ background-color: #191919;
+ color: #FFF
+}
+.server_console table td
+{
+ border: 0
+}
+#console_update
+{
+ font-size: 26px
+}
+.progress
+{
+ width: 185px
+}
+.progress > span
+{
+ position: relative;
+ float: left;
+ margin: 0 -1px;
+ min-width: 30px;
+ height: 16px;
+ line-height: 12px;
+ border-radius: 10px;
+ text-align: right;
+ border: 1px solid #FFF;
+ background: #DDD url(../images/progress.jpg) no-repeat;
+ -webkit-box-shadow: inset 0 1px rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2);
+ box-shadow: inset 0 1px rgba(255, 255, 255, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2);
+ transition: all 0.20s ease-in-out;
+ -moz-transition: all 0.20s ease-in-out;
+ -webkit-transition: all 0.20s ease-in-out;
+ -o-transition: all 0.20s ease-in-out
+}
+.progress > span > span
+{
+ padding: 0 3px;
+ font: 10px 'menu';
+ color: #FFF
+}
+.progress > span:before
+{
+ content: '';
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 1;
+ height: 18px;
+ background: url("../images/progress.png") 0 0 repeat-x;
+ border-radius: 10px
+}
+.change_map
+{
+ display: none;
+ position: absolute;
+ width: 408px;
+ max-height: 370px;
+ overflow: hidden;
+ overflow-y: scroll;
+ background-color: #F8F8F8;
+ box-shadow: 1px 4px 20px #9B9B9B;
+ z-index: 999
+}
+.change_close {
+ display: none;
+ cursor: pointer;
+ position: absolute;
+ padding: 5px 10px;
+ background-color: #F24141;
+ color: #FFF;
+ z-index: 1000;
+ margin-left: 360px;
+ /* border-top: 2px solid #000; */
+}
+.change_close:hover
+{
+ background-color: #323E53
+}
+.change_map div
+{
+ cursor: pointer;
+ float: left;
+ padding: 4px;
+ border: 1px solid #EEE
+}
+.change_map div > i
+{
+ width: 20px;
+ float: right;
+ height: 20px;
+ line-height: 20px;
+ text-align: center;
+ background-color: #222;
+ color: #ffffff;
+ display: block;
+ box-sizing: border-box;
+ position: absolute;
+ margin-left: 100px
+}
+.change_map div:hover span
+{
+ background-color: #323E53
+}
+.change_map span
+{
+ display: block;
+ text-align: center;
+ padding: 5px;
+ background-color: #3E4E68;
+ color: #F5F5F5;
+ font-size: 12px;
+ max-width: 110px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap
+}
+.table_pad th
+{
+ padding: 5px;
+ border-bottom: 1px solid #DCDCDC;
+ background-color: #f0f2f2;
+ color: #191919;
+ font-weight: normal
+}
+.table_pad td
+{
+ padding: 5px;
+ border-bottom: 1px solid #DCDCDC
+}
+.table_pad tr:nth-last-child(1) td
+{
+ border: 0
+}
+.input_pad input, .input_pad select, .input_pad textarea {
+ padding: 5px;
+ border: 1px solid #E8EBF0;
+ box-sizing: border-box;
+ outline: 0;
+ width: 100%;
+}
+.input_pad textarea
+{
+ resize: vertical;
+ width: 100%
+}
+.adminflags
+{
+ display: table-cell !important
+}
+.adminflags input
+{
+ width: 164px
+}
+.adminflags div
+{
+ position: absolute;
+ margin: -23px 145px 0;
+ padding: 0 5px;
+ cursor: pointer
+}
+.hblock_content {
+ background-color: #7f97b1;
+ padding: 4px 10px 2px 10px;
+ color: #fff;
+ border-bottom: 2px solid #6e849a;
+ display: block;
+ font: 10px 'menu';
+ text-transform: uppercase;
+}
+.block_content
+{
+ border: 2px solid #f0f2f5;
+ border-left-style: solid;
+ border-right-style: solid;
+ padding: 5px;
+ background-color: #ffffff;
+ box-shadow: 0px 3px 8px 0px rgba(33, 38, 45, 0.22)
+}
+.help_question
+{
+ background-color: #fff;
+ padding: 10px;
+ border-bottom: 1px dashed #CCC
+}
+.space
+{
+ margin-top: 10px;
+ display: inline-block;
+ width: 100%;
+ clear: both
+}
+.height200
+{
+ height: 200px;
+ overflow-y: scroll
+}
+.width50p
+{
+ width: 49%;
+ display: inline-block
+}
+.width250
+{
+ width: 250px
+}
+.width350
+{
+ width: 350px
+}
+.width500
+{
+ width: 500px
+}
+.width550
+{
+ width: 550px
+}
+
+.spoiler_body
+{
+ display: none
+}
+.spoiler_links
+{
+ padding-left: 15px;
+ font-size: 14px;
+ padding-bottom: 5px;
+ border-bottom: 1px solid #DDD;
+ display: block
+}
+.spoiler_body
+{
+ display: none;
+ padding: 5px
+}
+.spoilerblock
+{
+ padding: 7px;
+ border-bottom: 1px solid #ddd
+}
+.icon
+{
+ font-weight: bold;
+ color: #CCC;
+ display: inline-block;
+ float: right;
+ position: relative;
+
+}
+.plugin_block
+{
+ border-bottom: 1px solid #DDD;
+ padding-bottom: 5px;
+ display: table;
+ width: 100%
+}
+.plugin_info
+{
+ display: table-cell;
+ width: 90%;
+ box-sizing: border-box;
+ padding: 5px;
+ border-bottom: 1px dashed #DDD
+}
+.plugin_action
+{
+ display: table-cell;
+ width: 10%;
+ box-sizing: border-box;
+ padding: 5px;
+ text-align: center;
+ border-bottom: 1px dashed #DDD
+}
+.plugin_update
+{
+ cursor: pointer;
+ font-size: 12px;
+ color: #BCA744;
+ padding-left: 5px
+}
+.plugin_images
+{
+ display: table-row
+}
+.plugin_images a
+{
+ margin: 3px 3px 0 0;
+ display: inline-block;
+ box-sizing: border-box
+}
+.plugin_images img
+{
+ height: 50px;
+ opacity: 0.7
+}
+.plugin_images img:hover
+{
+ opacity: 1
+}
+.plugin_status_stable, .plugin_status_unstable, .plugin_status_testing
+{
+ font-size: 12px
+}
+.plugin_status_stable
+{
+ color: #74B084
+}
+.plugin_status_unstable
+{
+ color: #B07474
+}
+.plugin_status_testing
+{
+ color: #6E9FBC
+}
+#search_block
+{
+ display: none
+}
+.maps_types a > div
+{
+ cursor: pointer;
+ display: inline-block;
+ padding: 5px 5px 0 5px;
+ border: 1px solid #DCDCDC;
+ margin: 5px 5px 0 0;
+ box-sizing: border-box
+}
+.maps_types a:nth-child(8) > div
+{
+ margin-right: 0
+}
+.maps_types a > div > div
+{
+ text-align: center;
+ position: absolute;
+ margin-top: 30px;
+ background-color: rgba(0, 0, 0, 0.25);
+ width: 100px;
+ height: 20px;
+ line-height: 20px;
+ color: #FFF
+}
+.maps_types a > div > img
+{
+ width: 100px
+}
+.maps_types a > div:hover
+{
+ border-color: #999
+}
+.maps > div
+{
+ display: inline-block;
+ padding: 5px;
+ border: 1px solid #DDD;
+ margin: 4px
+}
+.maps > div:nth-child(6n)
+{
+ margin-right: 0
+}
+.maps > div > div
+{
+ display: block;
+ cursor: pointer
+}
+.maps > div > div i
+{
+ width: 20px;
+ float: right;
+ height: 20px;
+ line-height: 20px;
+ text-align: center;
+ background-color: #222;
+ color: #ffffff;
+ display: block;
+ box-sizing: border-box;
+ position: absolute;
+ margin-left: 120px
+}
+.maps div > span
+{
+ display: block;
+ text-align: center;
+ font-size: 13px;
+ max-width: 130px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ background-color: #222;
+ color: #ffffff;
+ padding: 2px 5px
+}
+.pages
+{
+ display: inline-block;
+ width: 100%;
+ text-align: center
+}
+.pages > div
+{
+ margin-top: 10px
+}
+.pages a
+{
+ display: inline-block;
+ transition: all 0.1s ease-in-out;
+ -moz-transition: all 0.1s ease-in-out;
+ -webkit-transition: all 0.1s ease-in-out;
+ -o-transition: all 0.1s ease-in-out;
+ background-color: #597da3;
+ color: #F8F8F8;
+ font-size: 13px;
+ font-family: nav;
+ padding: 2px 6px;
+ position: relative;
+ height: 15px;
+ margin-right: 2px
+}
+.pages a:hover, .pages a.active
+{
+ background-color: #222
+}
+.filetp
+{
+ padding-top: 15px
+}
+.filetp_block
+{
+ height: 500px;
+ overflow-y: scroll;
+ overflow-x: hidden
+}
+.filetp_block table td:nth-child(1) p
+{
+ max-width: 210px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap
+}
+#filetp_block > table
+{
+ width: 98%;
+ margin-top: 10px
+}
+#filetp_block > table td
+{
+ border-bottom: 3px solid #f5f5f5;
+ background-color: #dcdcdc;
+ color: #777;
+ padding: 3px
+}
+.filetp table:nth-child(1) tr:nth-child(1) td
+{
+ border-top: 1px solid #DCDCDC
+}
+#filetp_edit
+{
+ padding-top: 10px
+}
+#filetp td
+{
+ font: 14px 'menu';
+ background-color: #FFF
+}
+#filetp td p
+{
+ padding: 5px
+}
+#filetp td:nth-child(1)
+{
+ padding: 0
+}
+#filetp tr:nth-child(2n) td
+{
+ background-color: #fbfbfb
+}
+#filetp tr:hover td
+{
+ background-color: #ededed
+}
+.filetp_find
+{
+ width: 250px;
+ float: right
+}
+.filetp_find input
+{
+ font-size: 14px;
+ height: 18px;
+ padding: 5px;
+ border: 1px solid #8ca1b9;
+ box-sizing: border-box;
+ outline: 0
+}
+#infopath
+{
+ border-top: 3px solid #f5f5f5;
+ background-color: #dcdcdc;
+ color: #777;
+ padding: 3px
+}
+.CodeMirror
+{
+ max-width: 960px
+}
+.spoiler
+{
+ cursor: pointer;
+ display: block;
+ padding: 5px;
+ border: 1px solid #EEE;
+ background-color: #EAEAEA
+}
+.spoiler_main
+{
+ display: none;
+ border: 1px solid #EEE;
+ padding: 5px;
+ border-top: 0;
+ background-color: #f4f4f4
+}
+
+#help_notice
+{
+ display: none;
+ float: right;
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ text-align: center;
+ background-color: #597da3;
+ color: #FFF;
+ font-size: 6px;
+ line-height: 12px;
+ margin-top: -1px
+}
+
+#help_notice_block
+{
+ display: none;
+ position: fixed;
+ bottom: 0;
+ padding: 3px;
+ width: 200px
+}
+
+#help_notice_block > a > div
+{
+ background-color: #597da3;
+ color: #FFF;
+ padding: 5px;
+ margin-top: 3px
+}
+
+#help_notice_block > a > div > span
+{
+ font-size: 12px
+}
+
+#help_notice_block > a > div > p
+{
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ max-width: 210px;
+ overflow: hidden
+}
+
+#help_notice_block > a > div > span > i
+{
+ font-size: 10px;
+ margin-top: 2px
+}
+
+.infoblock
+{
+ background-color: #F7F7F7;
+ padding: 10px;
+ border-left: 5px solid #e8ebf0;
+ color: #585757;
+ margin-top: 10px
+}
+
+.help_category
+{
+ padding: 8px;
+ background-color: #FFF;
+ display: block;
+ margin-bottom: 3px
+}
+.help_question
+{
+ padding: 8px;
+ background-color: #fff;
+ margin-bottom: 8px
+}
+.help_question > a
+{
+ text-decoration: underline;
+ margin-bottom: 5px;
+ display: block
+}
+.help_question > a:hover
+{
+ text-decoration: none
+}
+.help_question > div > a
+{
+ padding: 5px;
+ background-color: #f0f2f2;
+ display: inline-block;
+ border-radius: 5px;
+ margin-right: 5px;
+ border: 1px solid #e1e1e1;
+ font: 11px/5px 'menu'
+}
+.help_question > div > a:hover
+{
+ background-color: #c2cdde
+}
+
+.news_block
+{
+ padding: 8px;
+ background-color: #fff;
+ margin-bottom: 8px
+}
+.news_block > h1,
+.news_block > h3
+{
+ margin-bottom: 5px;
+ width: 100%;
+ font-size: 14px
+}
+.news_block > h1 > a,
+.news_block > h3 > a
+{
+ text-decoration: underline;
+ margin-bottom: 5px;
+ display: block
+}
+.news_block > h1 span,
+.news_block > h3 span
+{
+ font-size: 13px;
+ font-weight: normal
+}
+.news_block > h1 span:nth-child(2),
+.news_block > h3 span:nth-child(2)
+{
+ margin-right: 10px
+}
+.news_block > h1 > a:hover,
+.news_block > h3 > a:hover
+{
+ text-decoration: none
+}
+.news_block > .tags
+{
+ font-size: 12px;
+ margin-top: 10px;
+ border-top: 1px dashed #CCC
+}
+
+.box-shadow
+{
+ box-shadow: 0 0 5px #CCC
+}
+
+.dblock
+{
+ display: block
+}
+#sum_info
+{
+ font-weight: bold
+}
+
+.loader
+{
+ z-index: 99;
+ position: fixed;
+ top: 10px;
+ width: 100%
+}
+
+.loader > span
+{
+ display: inline-block;
+ background-color: #dbdbdb;
+ width: 5px;
+ height: 5px;
+ border-radius: 5px;
+ -o-border-radius: 5px;
+ -ms-border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ margin: 0px;
+ position: fixed;
+ top: 5px;
+ left: 90%;
+ transition: 2.8s all cubic-bezier(0.030, 0.615, 0.995, 0.415);
+ -o-transition: 2.8s all cubic-bezier(0.030, 0.615, 0.995, 0.415);
+ -ms-transition: 2.8s all cubic-bezier(0.030, 0.615, 0.995, 0.415);
+ -moz-transition: 2.8s all cubic-bezier(0.030, 0.615, 0.995, 0.415);
+ -webkit-transition: 2.8s all cubic-bezier(0.030, 0.615, 0.995, 0.415);
+ z-index: 101
+}
+
+.loader > span.jmp
+{
+ transition: none !important;
+ -o-transition: none !important;
+ -ms-transition: none !important;
+ -moz-transition: none !important;
+ -webkit-transition: none !important
+}
+
+.loader span.l-1 { background-color: #e74c3c;}
+.loader span.l-2 { background-color: #e67e22;}
+.loader span.l-3 { background-color: #f1c40f;}
+.loader span.l-4 { background-color: #2ecc71;}
+.loader span.l-5 { background-color: #3498db;}
+.loader span.l-6 { background-color: #9b59b6;}
+
+.outinfo
+{
+ text-align: center;
+ top: 47%;
+ position: absolute;
+ background-color: #191919;
+ padding: 15px;
+ font-size: 20px;
+ color: #fff;
+ border: 2px dashed #ffffff;
+ border-left: 0;
+ border-right: 0;
+ width: 100%
+}
+
+.pretext
+{
+ white-space: pre-wrap;
+ white-space: -moz-pre-wrap;
+ white-space: -pre-wrap;
+ white-space: -o-pre-wrap;
+ word-wrap: break-word
+}
+/*Стилизация кнопки ВВЕРХ */
+#back-top{
+ position:fixed;
+ bottom:30px;
+ left:50%;
+ margin-left:200px
+}
+
+#back-top a{
+ width:64px;
+ display:block;
+ text-align:center;
+ font:11px/100% Arial, Helvetica, sans-serif;
+ text-transform:uppercase;
+ text-decoration:none;
+ color:#bbb;
+ /* background color transition */
+ -webkit-transition:1s;
+ -moz-transition:1s;
+ transition:1s;
+}
+
+#back-top a:hover{color:#000;}
+
+/* графическая стрелка ВВЕРХ */
+#back-top span {
+ width: 64px;
+ height: 50px;
+ display: block;
+ background: url(../images/up-arrow.png) no-repeat center center;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+ -webkit-transition: 1s;
+ -moz-transition: 1s;
+ transition: 1s;
+}
+
+#back-top a:hover span{background-color:#eeeeee26}
+
+.partnerList {
+ text-align: justify;
+ text-justify: newspaper;
+}
+.partnerList li {
+ display: inline-block;
+ width: 130px;
+ border: 1px solid #dfdfdf;
+ border-radius: 3px;
+ padding: 15px;
+}
+.partnerList li a {
+ text-decoration: none;
+ color: #444;
+ font-weight: 700;
+}
+.partnerList li a:hover {
+ text-decoration: underline;
+}
+.partnerList li img {
+ padding-bottom: 5px;
+ border-bottom: 1px solid #dfdfdf;
+}
+.partnerList li p {
+ font-size: 14px;
+ text-align: center;
+ margin-right: 0 !important;
+}
+.partnerList:after {
+ width: 100%;
+ height: 0px;
+ visibility: hidden;
+ overflow: hidden;
+ content: '';
+ display: inline-block;;
+}
+.sup {
+ font-size: inherit
+}
+.online_host_players {
+ text-align: center;
+ font-family: monospace;
+ font-size: smaller;
+}
\ No newline at end of file
diff --git a/template/css/switch.css b/template/css/switch.css
new file mode 100644
index 0000000..4dc8112
--- /dev/null
+++ b/template/css/switch.css
@@ -0,0 +1,106 @@
+.lcs_wrap
+{
+ display: inline-block;
+ direction: ltr;
+ height: 26px;
+ vertical-align: middle
+}
+.lcs_wrap input
+{
+ display: none;
+}
+.lcs_switch
+{
+ display: inline-block;
+ position: relative;
+ width: 65px;
+ height: 20px;
+ background: #ddd;
+ overflow: hidden;
+ cursor: pointer;
+ -webkit-transition: all .2s ease-in-out;
+ -ms-transition: all .2s ease-in-out;
+ transition: all .2s ease-in-out
+}
+.lcs_cursor
+{
+ display: inline-block;
+ position: absolute;
+ top: 1px;
+ width: 18px;
+ height: 18px;
+ background: #ffffff;
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2), 0 3px 4px 0 rgba(0, 0, 0, 0.1);
+ z-index: 10;
+ -webkit-transition: all .2s linear;
+ -ms-transition: all .2s linear;
+ transition: all .2s linear
+}
+.lcs_label
+{
+ font-family: "Trebuchet MS", Helvetica, sans-serif;
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: 12px;
+ color: #fff;
+ font-weight: bold;
+ position: absolute;
+ width: 26px;
+ top: 5px;
+ overflow: hidden;
+ text-align: center;
+ opacity: 0;
+ -webkit-transition: all .2s ease-in-out .1s;
+ -ms-transition: all .2s ease-in-out .1s;
+ transition: all .2s ease-in-out .1s
+}
+.lcs_label.lcs_label_on
+{
+ left: -70px;
+ z-index: 6;
+}
+.lcs_label.lcs_label_off
+{
+ right: -70px;
+ z-index: 5;
+}
+
+/* on */
+.lcs_switch.lcs_on
+{
+ background: #8eb98c;
+ box-shadow: 0 0 2px #1f571c inset
+}
+.lcs_switch.lcs_on .lcs_cursor
+{
+ left: 46px
+}
+.lcs_switch.lcs_on .lcs_label_on
+{
+ left: 10px;
+ opacity: 1
+}
+
+/* off */
+.lcs_switch.lcs_off
+{
+ background: #b2b2b2;
+ box-shadow: 0px 0px 2px #a4a4a4 inset;
+}
+.lcs_switch.lcs_off .lcs_cursor
+{
+ left: 2px
+}
+.lcs_switch.lcs_off .lcs_label_off
+{
+ right: 10px;
+ opacity: 1;
+}
+
+/* disabled */
+.lcs_switch.lcs_disabled
+{
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ cursor: default
+}
\ No newline at end of file
diff --git a/template/css/user.css b/template/css/user.css
new file mode 100644
index 0000000..8d1e833
--- /dev/null
+++ b/template/css/user.css
@@ -0,0 +1,69 @@
+#drop-files
+{
+ position: relative;
+ width: 99%;
+ height: 56px;
+ margin: 0 auto;
+ background-color: #ffffff;
+ border: 2px dashed #dcdcdc;
+ text-align: center;
+ font: 15px 'Open Sans';
+}
+#drop-files p
+{
+ clear: none;
+ padding: 0;
+ margin: 0;
+}
+#uploaded-holder
+{
+ display: none;
+ position: relative;
+ margin: 0 auto 10px;
+ width: 100%;
+}
+#dropped-files
+{
+ display: block;
+ margin: 0 auto;
+ width: 100%
+}
+#upload-button
+{
+ z-index: 9999;
+ display: none;
+ margin: 5px 0;
+}
+#loading-content
+{
+ position: relative;
+ font-size: 13px;
+ font-weight: bold;
+ text-align: center;
+ width: 100%;
+}
+#file-name-holder
+{
+ width: 100%;
+ float: left;
+}
+#file-name-holder h1
+{
+ padding: 5px 0;
+ font-size: 13px;
+ margin: 0;
+}
+
+#drop-files input
+{
+ border: 0;
+ background-color: #ffffff;
+ width: initial;
+}
+
+.image
+{
+ width: 140px;
+ height: 140px;
+ background-repeat: no-repeat !important
+}
\ No newline at end of file
diff --git a/template/fonts/FontAwesome.otf b/template/fonts/FontAwesome.otf
new file mode 100644
index 0000000..3ed7f8b
Binary files /dev/null and b/template/fonts/FontAwesome.otf differ
diff --git a/template/fonts/airborne.ttf b/template/fonts/airborne.ttf
new file mode 100644
index 0000000..96dff79
Binary files /dev/null and b/template/fonts/airborne.ttf differ
diff --git a/template/fonts/airborne.woff b/template/fonts/airborne.woff
new file mode 100644
index 0000000..16fed32
Binary files /dev/null and b/template/fonts/airborne.woff differ
diff --git a/template/fonts/airborne.woff2 b/template/fonts/airborne.woff2
new file mode 100644
index 0000000..b7ce4ad
Binary files /dev/null and b/template/fonts/airborne.woff2 differ
diff --git a/template/fonts/airbornepilot.ttf b/template/fonts/airbornepilot.ttf
new file mode 100644
index 0000000..2d89632
Binary files /dev/null and b/template/fonts/airbornepilot.ttf differ
diff --git a/template/fonts/airbornepilot.woff b/template/fonts/airbornepilot.woff
new file mode 100644
index 0000000..e32339f
Binary files /dev/null and b/template/fonts/airbornepilot.woff differ
diff --git a/template/fonts/airbornepilot.woff2 b/template/fonts/airbornepilot.woff2
new file mode 100644
index 0000000..4d1cdb1
Binary files /dev/null and b/template/fonts/airbornepilot.woff2 differ
diff --git a/template/fonts/arimo.ttf b/template/fonts/arimo.ttf
new file mode 100644
index 0000000..9947980
Binary files /dev/null and b/template/fonts/arimo.ttf differ
diff --git a/template/fonts/arimo.woff b/template/fonts/arimo.woff
new file mode 100644
index 0000000..eeb68d2
Binary files /dev/null and b/template/fonts/arimo.woff differ
diff --git a/template/fonts/arimo.woff2 b/template/fonts/arimo.woff2
new file mode 100644
index 0000000..2abcf40
Binary files /dev/null and b/template/fonts/arimo.woff2 differ
diff --git a/template/fonts/cantarellbold.ttf b/template/fonts/cantarellbold.ttf
new file mode 100644
index 0000000..7cc74ec
Binary files /dev/null and b/template/fonts/cantarellbold.ttf differ
diff --git a/template/fonts/cantarellbold.woff b/template/fonts/cantarellbold.woff
new file mode 100644
index 0000000..ae6513f
Binary files /dev/null and b/template/fonts/cantarellbold.woff differ
diff --git a/template/fonts/cantarellbold.woff2 b/template/fonts/cantarellbold.woff2
new file mode 100644
index 0000000..078275c
Binary files /dev/null and b/template/fonts/cantarellbold.woff2 differ
diff --git a/template/fonts/cantarellregular.ttf b/template/fonts/cantarellregular.ttf
new file mode 100644
index 0000000..df22811
Binary files /dev/null and b/template/fonts/cantarellregular.ttf differ
diff --git a/template/fonts/cantarellregular.woff b/template/fonts/cantarellregular.woff
new file mode 100644
index 0000000..d225ccb
Binary files /dev/null and b/template/fonts/cantarellregular.woff differ
diff --git a/template/fonts/cantarellregular.woff2 b/template/fonts/cantarellregular.woff2
new file mode 100644
index 0000000..171d610
Binary files /dev/null and b/template/fonts/cantarellregular.woff2 differ
diff --git a/template/fonts/fontawesome-webfont.eot b/template/fonts/fontawesome-webfont.eot
new file mode 100644
index 0000000..9b6afae
Binary files /dev/null and b/template/fonts/fontawesome-webfont.eot differ
diff --git a/template/fonts/fontawesome-webfont.svg b/template/fonts/fontawesome-webfont.svg
new file mode 100644
index 0000000..d05688e
--- /dev/null
+++ b/template/fonts/fontawesome-webfont.svg
@@ -0,0 +1,655 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/fonts/fontawesome-webfont.ttf b/template/fonts/fontawesome-webfont.ttf
new file mode 100644
index 0000000..26dea79
Binary files /dev/null and b/template/fonts/fontawesome-webfont.ttf differ
diff --git a/template/fonts/fontawesome-webfont.woff b/template/fonts/fontawesome-webfont.woff
new file mode 100644
index 0000000..dc35ce3
Binary files /dev/null and b/template/fonts/fontawesome-webfont.woff differ
diff --git a/template/fonts/fontawesome-webfont.woff2 b/template/fonts/fontawesome-webfont.woff2
new file mode 100644
index 0000000..500e517
Binary files /dev/null and b/template/fonts/fontawesome-webfont.woff2 differ
diff --git a/template/fonts/opensans.ttf b/template/fonts/opensans.ttf
new file mode 100644
index 0000000..486b38c
Binary files /dev/null and b/template/fonts/opensans.ttf differ
diff --git a/template/fonts/opensans.woff b/template/fonts/opensans.woff
new file mode 100644
index 0000000..df0c92a
Binary files /dev/null and b/template/fonts/opensans.woff differ
diff --git a/template/fonts/opensans.woff2 b/template/fonts/opensans.woff2
new file mode 100644
index 0000000..90cc12e
Binary files /dev/null and b/template/fonts/opensans.woff2 differ
diff --git a/template/fonts/opensansbold.ttf b/template/fonts/opensansbold.ttf
new file mode 100644
index 0000000..d679304
Binary files /dev/null and b/template/fonts/opensansbold.ttf differ
diff --git a/template/fonts/opensansbold.woff b/template/fonts/opensansbold.woff
new file mode 100644
index 0000000..700d06a
Binary files /dev/null and b/template/fonts/opensansbold.woff differ
diff --git a/template/fonts/opensansbold.woff2 b/template/fonts/opensansbold.woff2
new file mode 100644
index 0000000..148ed5e
Binary files /dev/null and b/template/fonts/opensansbold.woff2 differ
diff --git a/template/fonts/opensanslight.ttf b/template/fonts/opensanslight.ttf
new file mode 100644
index 0000000..b0ae56d
Binary files /dev/null and b/template/fonts/opensanslight.ttf differ
diff --git a/template/fonts/opensanslight.woff b/template/fonts/opensanslight.woff
new file mode 100644
index 0000000..68b9eae
Binary files /dev/null and b/template/fonts/opensanslight.woff differ
diff --git a/template/fonts/opensanslight.woff2 b/template/fonts/opensanslight.woff2
new file mode 100644
index 0000000..b1de2c4
Binary files /dev/null and b/template/fonts/opensanslight.woff2 differ
diff --git a/template/images/avatar.png b/template/images/avatar.png
new file mode 100644
index 0000000..168dba8
Binary files /dev/null and b/template/images/avatar.png differ
diff --git a/template/images/bg.jpg b/template/images/bg.jpg
new file mode 100644
index 0000000..96f5092
Binary files /dev/null and b/template/images/bg.jpg differ
diff --git a/template/images/bg.png b/template/images/bg.png
new file mode 100644
index 0000000..75cbf7b
Binary files /dev/null and b/template/images/bg.png differ
diff --git a/template/images/country/AD.png b/template/images/country/AD.png
new file mode 100644
index 0000000..bdbe434
Binary files /dev/null and b/template/images/country/AD.png differ
diff --git a/template/images/country/AE.png b/template/images/country/AE.png
new file mode 100644
index 0000000..501ebc6
Binary files /dev/null and b/template/images/country/AE.png differ
diff --git a/template/images/country/AF.png b/template/images/country/AF.png
new file mode 100644
index 0000000..61e9518
Binary files /dev/null and b/template/images/country/AF.png differ
diff --git a/template/images/country/AG.png b/template/images/country/AG.png
new file mode 100644
index 0000000..cb6b940
Binary files /dev/null and b/template/images/country/AG.png differ
diff --git a/template/images/country/AI.png b/template/images/country/AI.png
new file mode 100644
index 0000000..3206b92
Binary files /dev/null and b/template/images/country/AI.png differ
diff --git a/template/images/country/AL.png b/template/images/country/AL.png
new file mode 100644
index 0000000..5bd4986
Binary files /dev/null and b/template/images/country/AL.png differ
diff --git a/template/images/country/AM.png b/template/images/country/AM.png
new file mode 100644
index 0000000..83df09a
Binary files /dev/null and b/template/images/country/AM.png differ
diff --git a/template/images/country/AN.png b/template/images/country/AN.png
new file mode 100644
index 0000000..0fc1c3b
Binary files /dev/null and b/template/images/country/AN.png differ
diff --git a/template/images/country/AO.png b/template/images/country/AO.png
new file mode 100644
index 0000000..fa426da
Binary files /dev/null and b/template/images/country/AO.png differ
diff --git a/template/images/country/AQ.png b/template/images/country/AQ.png
new file mode 100644
index 0000000..cfa0b9f
Binary files /dev/null and b/template/images/country/AQ.png differ
diff --git a/template/images/country/AR.png b/template/images/country/AR.png
new file mode 100644
index 0000000..e1fa97b
Binary files /dev/null and b/template/images/country/AR.png differ
diff --git a/template/images/country/AS.png b/template/images/country/AS.png
new file mode 100644
index 0000000..43b5aec
Binary files /dev/null and b/template/images/country/AS.png differ
diff --git a/template/images/country/AT.png b/template/images/country/AT.png
new file mode 100644
index 0000000..794bd2f
Binary files /dev/null and b/template/images/country/AT.png differ
diff --git a/template/images/country/AU.png b/template/images/country/AU.png
new file mode 100644
index 0000000..11f4d1c
Binary files /dev/null and b/template/images/country/AU.png differ
diff --git a/template/images/country/AW.png b/template/images/country/AW.png
new file mode 100644
index 0000000..b21c2ea
Binary files /dev/null and b/template/images/country/AW.png differ
diff --git a/template/images/country/AX.png b/template/images/country/AX.png
new file mode 100644
index 0000000..46a1802
Binary files /dev/null and b/template/images/country/AX.png differ
diff --git a/template/images/country/AZ.png b/template/images/country/AZ.png
new file mode 100644
index 0000000..878e559
Binary files /dev/null and b/template/images/country/AZ.png differ
diff --git a/template/images/country/BA.png b/template/images/country/BA.png
new file mode 100644
index 0000000..b484edb
Binary files /dev/null and b/template/images/country/BA.png differ
diff --git a/template/images/country/BB.png b/template/images/country/BB.png
new file mode 100644
index 0000000..be3d149
Binary files /dev/null and b/template/images/country/BB.png differ
diff --git a/template/images/country/BD.png b/template/images/country/BD.png
new file mode 100644
index 0000000..b6c440a
Binary files /dev/null and b/template/images/country/BD.png differ
diff --git a/template/images/country/BE.png b/template/images/country/BE.png
new file mode 100644
index 0000000..5e47a3a
Binary files /dev/null and b/template/images/country/BE.png differ
diff --git a/template/images/country/BF.png b/template/images/country/BF.png
new file mode 100644
index 0000000..9e0643c
Binary files /dev/null and b/template/images/country/BF.png differ
diff --git a/template/images/country/BG.png b/template/images/country/BG.png
new file mode 100644
index 0000000..775550c
Binary files /dev/null and b/template/images/country/BG.png differ
diff --git a/template/images/country/BH.png b/template/images/country/BH.png
new file mode 100644
index 0000000..b3eb851
Binary files /dev/null and b/template/images/country/BH.png differ
diff --git a/template/images/country/BI.png b/template/images/country/BI.png
new file mode 100644
index 0000000..394a829
Binary files /dev/null and b/template/images/country/BI.png differ
diff --git a/template/images/country/BJ.png b/template/images/country/BJ.png
new file mode 100644
index 0000000..688797f
Binary files /dev/null and b/template/images/country/BJ.png differ
diff --git a/template/images/country/BL.png b/template/images/country/BL.png
new file mode 100644
index 0000000..79e24d2
Binary files /dev/null and b/template/images/country/BL.png differ
diff --git a/template/images/country/BM.png b/template/images/country/BM.png
new file mode 100644
index 0000000..132e990
Binary files /dev/null and b/template/images/country/BM.png differ
diff --git a/template/images/country/BN.png b/template/images/country/BN.png
new file mode 100644
index 0000000..62882e8
Binary files /dev/null and b/template/images/country/BN.png differ
diff --git a/template/images/country/BO.png b/template/images/country/BO.png
new file mode 100644
index 0000000..63d8e76
Binary files /dev/null and b/template/images/country/BO.png differ
diff --git a/template/images/country/BR.png b/template/images/country/BR.png
new file mode 100644
index 0000000..42e1849
Binary files /dev/null and b/template/images/country/BR.png differ
diff --git a/template/images/country/BS.png b/template/images/country/BS.png
new file mode 100644
index 0000000..aabafab
Binary files /dev/null and b/template/images/country/BS.png differ
diff --git a/template/images/country/BT.png b/template/images/country/BT.png
new file mode 100644
index 0000000..026fde3
Binary files /dev/null and b/template/images/country/BT.png differ
diff --git a/template/images/country/BW.png b/template/images/country/BW.png
new file mode 100644
index 0000000..b4a502b
Binary files /dev/null and b/template/images/country/BW.png differ
diff --git a/template/images/country/BY.png b/template/images/country/BY.png
new file mode 100644
index 0000000..0376c98
Binary files /dev/null and b/template/images/country/BY.png differ
diff --git a/template/images/country/BZ.png b/template/images/country/BZ.png
new file mode 100644
index 0000000..0291200
Binary files /dev/null and b/template/images/country/BZ.png differ
diff --git a/template/images/country/CA.png b/template/images/country/CA.png
new file mode 100644
index 0000000..0906eca
Binary files /dev/null and b/template/images/country/CA.png differ
diff --git a/template/images/country/CC.png b/template/images/country/CC.png
new file mode 100644
index 0000000..d599e12
Binary files /dev/null and b/template/images/country/CC.png differ
diff --git a/template/images/country/CD.png b/template/images/country/CD.png
new file mode 100644
index 0000000..de00b2f
Binary files /dev/null and b/template/images/country/CD.png differ
diff --git a/template/images/country/CF.png b/template/images/country/CF.png
new file mode 100644
index 0000000..32cb360
Binary files /dev/null and b/template/images/country/CF.png differ
diff --git a/template/images/country/CG.png b/template/images/country/CG.png
new file mode 100644
index 0000000..f35021f
Binary files /dev/null and b/template/images/country/CG.png differ
diff --git a/template/images/country/CH.png b/template/images/country/CH.png
new file mode 100644
index 0000000..de304f3
Binary files /dev/null and b/template/images/country/CH.png differ
diff --git a/template/images/country/CI.png b/template/images/country/CI.png
new file mode 100644
index 0000000..e519e0b
Binary files /dev/null and b/template/images/country/CI.png differ
diff --git a/template/images/country/CK.png b/template/images/country/CK.png
new file mode 100644
index 0000000..e07ed83
Binary files /dev/null and b/template/images/country/CK.png differ
diff --git a/template/images/country/CL.png b/template/images/country/CL.png
new file mode 100644
index 0000000..57bf33e
Binary files /dev/null and b/template/images/country/CL.png differ
diff --git a/template/images/country/CM.png b/template/images/country/CM.png
new file mode 100644
index 0000000..7b21abc
Binary files /dev/null and b/template/images/country/CM.png differ
diff --git a/template/images/country/CN.png b/template/images/country/CN.png
new file mode 100644
index 0000000..8c35f25
Binary files /dev/null and b/template/images/country/CN.png differ
diff --git a/template/images/country/CO.png b/template/images/country/CO.png
new file mode 100644
index 0000000..25142bc
Binary files /dev/null and b/template/images/country/CO.png differ
diff --git a/template/images/country/CR.png b/template/images/country/CR.png
new file mode 100644
index 0000000..17eeb62
Binary files /dev/null and b/template/images/country/CR.png differ
diff --git a/template/images/country/CU.png b/template/images/country/CU.png
new file mode 100644
index 0000000..cd80f6d
Binary files /dev/null and b/template/images/country/CU.png differ
diff --git a/template/images/country/CV.png b/template/images/country/CV.png
new file mode 100644
index 0000000..f0c6fb0
Binary files /dev/null and b/template/images/country/CV.png differ
diff --git a/template/images/country/CW.png b/template/images/country/CW.png
new file mode 100644
index 0000000..ebcbaf1
Binary files /dev/null and b/template/images/country/CW.png differ
diff --git a/template/images/country/CX.png b/template/images/country/CX.png
new file mode 100644
index 0000000..6d068d8
Binary files /dev/null and b/template/images/country/CX.png differ
diff --git a/template/images/country/CY.png b/template/images/country/CY.png
new file mode 100644
index 0000000..3aff6b8
Binary files /dev/null and b/template/images/country/CY.png differ
diff --git a/template/images/country/CZ.png b/template/images/country/CZ.png
new file mode 100644
index 0000000..1bbbf0d
Binary files /dev/null and b/template/images/country/CZ.png differ
diff --git a/template/images/country/DE.png b/template/images/country/DE.png
new file mode 100644
index 0000000..d282989
Binary files /dev/null and b/template/images/country/DE.png differ
diff --git a/template/images/country/DJ.png b/template/images/country/DJ.png
new file mode 100644
index 0000000..78660a1
Binary files /dev/null and b/template/images/country/DJ.png differ
diff --git a/template/images/country/DK.png b/template/images/country/DK.png
new file mode 100644
index 0000000..a81768c
Binary files /dev/null and b/template/images/country/DK.png differ
diff --git a/template/images/country/DM.png b/template/images/country/DM.png
new file mode 100644
index 0000000..93700d7
Binary files /dev/null and b/template/images/country/DM.png differ
diff --git a/template/images/country/DO.png b/template/images/country/DO.png
new file mode 100644
index 0000000..4b2c207
Binary files /dev/null and b/template/images/country/DO.png differ
diff --git a/template/images/country/DZ.png b/template/images/country/DZ.png
new file mode 100644
index 0000000..65a50f3
Binary files /dev/null and b/template/images/country/DZ.png differ
diff --git a/template/images/country/EC.png b/template/images/country/EC.png
new file mode 100644
index 0000000..204c631
Binary files /dev/null and b/template/images/country/EC.png differ
diff --git a/template/images/country/EE.png b/template/images/country/EE.png
new file mode 100644
index 0000000..6c19913
Binary files /dev/null and b/template/images/country/EE.png differ
diff --git a/template/images/country/EG.png b/template/images/country/EG.png
new file mode 100644
index 0000000..96c93ed
Binary files /dev/null and b/template/images/country/EG.png differ
diff --git a/template/images/country/EH.png b/template/images/country/EH.png
new file mode 100644
index 0000000..2a53959
Binary files /dev/null and b/template/images/country/EH.png differ
diff --git a/template/images/country/ER.png b/template/images/country/ER.png
new file mode 100644
index 0000000..273ef15
Binary files /dev/null and b/template/images/country/ER.png differ
diff --git a/template/images/country/ES.png b/template/images/country/ES.png
new file mode 100644
index 0000000..df2315f
Binary files /dev/null and b/template/images/country/ES.png differ
diff --git a/template/images/country/ET.png b/template/images/country/ET.png
new file mode 100644
index 0000000..c66f7ac
Binary files /dev/null and b/template/images/country/ET.png differ
diff --git a/template/images/country/EU.png b/template/images/country/EU.png
new file mode 100644
index 0000000..7652407
Binary files /dev/null and b/template/images/country/EU.png differ
diff --git a/template/images/country/FI.png b/template/images/country/FI.png
new file mode 100644
index 0000000..627ad2e
Binary files /dev/null and b/template/images/country/FI.png differ
diff --git a/template/images/country/FJ.png b/template/images/country/FJ.png
new file mode 100644
index 0000000..a90ae00
Binary files /dev/null and b/template/images/country/FJ.png differ
diff --git a/template/images/country/FK.png b/template/images/country/FK.png
new file mode 100644
index 0000000..8c4e4a7
Binary files /dev/null and b/template/images/country/FK.png differ
diff --git a/template/images/country/FM.png b/template/images/country/FM.png
new file mode 100644
index 0000000..bf420a8
Binary files /dev/null and b/template/images/country/FM.png differ
diff --git a/template/images/country/FO.png b/template/images/country/FO.png
new file mode 100644
index 0000000..4264083
Binary files /dev/null and b/template/images/country/FO.png differ
diff --git a/template/images/country/FR.png b/template/images/country/FR.png
new file mode 100644
index 0000000..b18c5ff
Binary files /dev/null and b/template/images/country/FR.png differ
diff --git a/template/images/country/GA.png b/template/images/country/GA.png
new file mode 100644
index 0000000..2b198db
Binary files /dev/null and b/template/images/country/GA.png differ
diff --git a/template/images/country/GB.png b/template/images/country/GB.png
new file mode 100644
index 0000000..a1d267e
Binary files /dev/null and b/template/images/country/GB.png differ
diff --git a/template/images/country/GD.png b/template/images/country/GD.png
new file mode 100644
index 0000000..5ec83c4
Binary files /dev/null and b/template/images/country/GD.png differ
diff --git a/template/images/country/GE.png b/template/images/country/GE.png
new file mode 100644
index 0000000..b6b33ec
Binary files /dev/null and b/template/images/country/GE.png differ
diff --git a/template/images/country/GG.png b/template/images/country/GG.png
new file mode 100644
index 0000000..f2c1b71
Binary files /dev/null and b/template/images/country/GG.png differ
diff --git a/template/images/country/GH.png b/template/images/country/GH.png
new file mode 100644
index 0000000..3ba2af7
Binary files /dev/null and b/template/images/country/GH.png differ
diff --git a/template/images/country/GI.png b/template/images/country/GI.png
new file mode 100644
index 0000000..7a79f3e
Binary files /dev/null and b/template/images/country/GI.png differ
diff --git a/template/images/country/GL.png b/template/images/country/GL.png
new file mode 100644
index 0000000..6f27d69
Binary files /dev/null and b/template/images/country/GL.png differ
diff --git a/template/images/country/GM.png b/template/images/country/GM.png
new file mode 100644
index 0000000..b056657
Binary files /dev/null and b/template/images/country/GM.png differ
diff --git a/template/images/country/GN.png b/template/images/country/GN.png
new file mode 100644
index 0000000..4b4a25a
Binary files /dev/null and b/template/images/country/GN.png differ
diff --git a/template/images/country/GQ.png b/template/images/country/GQ.png
new file mode 100644
index 0000000..22e85d7
Binary files /dev/null and b/template/images/country/GQ.png differ
diff --git a/template/images/country/GR.png b/template/images/country/GR.png
new file mode 100644
index 0000000..9321a1e
Binary files /dev/null and b/template/images/country/GR.png differ
diff --git a/template/images/country/GS.png b/template/images/country/GS.png
new file mode 100644
index 0000000..fbd3fd7
Binary files /dev/null and b/template/images/country/GS.png differ
diff --git a/template/images/country/GT.png b/template/images/country/GT.png
new file mode 100644
index 0000000..dacd490
Binary files /dev/null and b/template/images/country/GT.png differ
diff --git a/template/images/country/GU.png b/template/images/country/GU.png
new file mode 100644
index 0000000..fa0cc49
Binary files /dev/null and b/template/images/country/GU.png differ
diff --git a/template/images/country/GW.png b/template/images/country/GW.png
new file mode 100644
index 0000000..4f48bec
Binary files /dev/null and b/template/images/country/GW.png differ
diff --git a/template/images/country/GY.png b/template/images/country/GY.png
new file mode 100644
index 0000000..17a1692
Binary files /dev/null and b/template/images/country/GY.png differ
diff --git a/template/images/country/HK.png b/template/images/country/HK.png
new file mode 100644
index 0000000..a2bd2ea
Binary files /dev/null and b/template/images/country/HK.png differ
diff --git a/template/images/country/HN.png b/template/images/country/HN.png
new file mode 100644
index 0000000..4aadec8
Binary files /dev/null and b/template/images/country/HN.png differ
diff --git a/template/images/country/HR.png b/template/images/country/HR.png
new file mode 100644
index 0000000..d0b5266
Binary files /dev/null and b/template/images/country/HR.png differ
diff --git a/template/images/country/HT.png b/template/images/country/HT.png
new file mode 100644
index 0000000..d20cf24
Binary files /dev/null and b/template/images/country/HT.png differ
diff --git a/template/images/country/HU.png b/template/images/country/HU.png
new file mode 100644
index 0000000..ae5c6c9
Binary files /dev/null and b/template/images/country/HU.png differ
diff --git a/template/images/country/IC.png b/template/images/country/IC.png
new file mode 100644
index 0000000..c1d8d46
Binary files /dev/null and b/template/images/country/IC.png differ
diff --git a/template/images/country/ID.png b/template/images/country/ID.png
new file mode 100644
index 0000000..9881b87
Binary files /dev/null and b/template/images/country/ID.png differ
diff --git a/template/images/country/IE.png b/template/images/country/IE.png
new file mode 100644
index 0000000..ec9ba0f
Binary files /dev/null and b/template/images/country/IE.png differ
diff --git a/template/images/country/IL.png b/template/images/country/IL.png
new file mode 100644
index 0000000..13e3503
Binary files /dev/null and b/template/images/country/IL.png differ
diff --git a/template/images/country/IM.png b/template/images/country/IM.png
new file mode 100644
index 0000000..06ee5c4
Binary files /dev/null and b/template/images/country/IM.png differ
diff --git a/template/images/country/IN.png b/template/images/country/IN.png
new file mode 100644
index 0000000..e5dd9d2
Binary files /dev/null and b/template/images/country/IN.png differ
diff --git a/template/images/country/IQ.png b/template/images/country/IQ.png
new file mode 100644
index 0000000..1f28ebd
Binary files /dev/null and b/template/images/country/IQ.png differ
diff --git a/template/images/country/IR.png b/template/images/country/IR.png
new file mode 100644
index 0000000..5388dfb
Binary files /dev/null and b/template/images/country/IR.png differ
diff --git a/template/images/country/IS.png b/template/images/country/IS.png
new file mode 100644
index 0000000..d535f7f
Binary files /dev/null and b/template/images/country/IS.png differ
diff --git a/template/images/country/IT.png b/template/images/country/IT.png
new file mode 100644
index 0000000..42d1677
Binary files /dev/null and b/template/images/country/IT.png differ
diff --git a/template/images/country/JE.png b/template/images/country/JE.png
new file mode 100644
index 0000000..407e319
Binary files /dev/null and b/template/images/country/JE.png differ
diff --git a/template/images/country/JM.png b/template/images/country/JM.png
new file mode 100644
index 0000000..6237052
Binary files /dev/null and b/template/images/country/JM.png differ
diff --git a/template/images/country/JO.png b/template/images/country/JO.png
new file mode 100644
index 0000000..8e11b7b
Binary files /dev/null and b/template/images/country/JO.png differ
diff --git a/template/images/country/JP.png b/template/images/country/JP.png
new file mode 100644
index 0000000..a742140
Binary files /dev/null and b/template/images/country/JP.png differ
diff --git a/template/images/country/KE.png b/template/images/country/KE.png
new file mode 100644
index 0000000..e1ac097
Binary files /dev/null and b/template/images/country/KE.png differ
diff --git a/template/images/country/KG.png b/template/images/country/KG.png
new file mode 100644
index 0000000..2de2061
Binary files /dev/null and b/template/images/country/KG.png differ
diff --git a/template/images/country/KH.png b/template/images/country/KH.png
new file mode 100644
index 0000000..4f80d8c
Binary files /dev/null and b/template/images/country/KH.png differ
diff --git a/template/images/country/KI.png b/template/images/country/KI.png
new file mode 100644
index 0000000..1990c8d
Binary files /dev/null and b/template/images/country/KI.png differ
diff --git a/template/images/country/KM.png b/template/images/country/KM.png
new file mode 100644
index 0000000..5eedfeb
Binary files /dev/null and b/template/images/country/KM.png differ
diff --git a/template/images/country/KN.png b/template/images/country/KN.png
new file mode 100644
index 0000000..be930df
Binary files /dev/null and b/template/images/country/KN.png differ
diff --git a/template/images/country/KP.png b/template/images/country/KP.png
new file mode 100644
index 0000000..9716adc
Binary files /dev/null and b/template/images/country/KP.png differ
diff --git a/template/images/country/KR.png b/template/images/country/KR.png
new file mode 100644
index 0000000..7878247
Binary files /dev/null and b/template/images/country/KR.png differ
diff --git a/template/images/country/KW.png b/template/images/country/KW.png
new file mode 100644
index 0000000..af0191c
Binary files /dev/null and b/template/images/country/KW.png differ
diff --git a/template/images/country/KY.png b/template/images/country/KY.png
new file mode 100644
index 0000000..c787eb0
Binary files /dev/null and b/template/images/country/KY.png differ
diff --git a/template/images/country/KZ.png b/template/images/country/KZ.png
new file mode 100644
index 0000000..5fe5a82
Binary files /dev/null and b/template/images/country/KZ.png differ
diff --git a/template/images/country/LA.png b/template/images/country/LA.png
new file mode 100644
index 0000000..d68c937
Binary files /dev/null and b/template/images/country/LA.png differ
diff --git a/template/images/country/LB.png b/template/images/country/LB.png
new file mode 100644
index 0000000..627a3aa
Binary files /dev/null and b/template/images/country/LB.png differ
diff --git a/template/images/country/LC.png b/template/images/country/LC.png
new file mode 100644
index 0000000..8e93f66
Binary files /dev/null and b/template/images/country/LC.png differ
diff --git a/template/images/country/LI.png b/template/images/country/LI.png
new file mode 100644
index 0000000..7d65fba
Binary files /dev/null and b/template/images/country/LI.png differ
diff --git a/template/images/country/LK.png b/template/images/country/LK.png
new file mode 100644
index 0000000..e926ca3
Binary files /dev/null and b/template/images/country/LK.png differ
diff --git a/template/images/country/LR.png b/template/images/country/LR.png
new file mode 100644
index 0000000..4918491
Binary files /dev/null and b/template/images/country/LR.png differ
diff --git a/template/images/country/LS.png b/template/images/country/LS.png
new file mode 100644
index 0000000..c8a5493
Binary files /dev/null and b/template/images/country/LS.png differ
diff --git a/template/images/country/LT.png b/template/images/country/LT.png
new file mode 100644
index 0000000..f12a14b
Binary files /dev/null and b/template/images/country/LT.png differ
diff --git a/template/images/country/LU.png b/template/images/country/LU.png
new file mode 100644
index 0000000..59b08cc
Binary files /dev/null and b/template/images/country/LU.png differ
diff --git a/template/images/country/LV.png b/template/images/country/LV.png
new file mode 100644
index 0000000..c31180d
Binary files /dev/null and b/template/images/country/LV.png differ
diff --git a/template/images/country/LY.png b/template/images/country/LY.png
new file mode 100644
index 0000000..6847120
Binary files /dev/null and b/template/images/country/LY.png differ
diff --git a/template/images/country/MA.png b/template/images/country/MA.png
new file mode 100644
index 0000000..67fe432
Binary files /dev/null and b/template/images/country/MA.png differ
diff --git a/template/images/country/MC.png b/template/images/country/MC.png
new file mode 100644
index 0000000..9881b87
Binary files /dev/null and b/template/images/country/MC.png differ
diff --git a/template/images/country/MD.png b/template/images/country/MD.png
new file mode 100644
index 0000000..4ebe0d6
Binary files /dev/null and b/template/images/country/MD.png differ
diff --git a/template/images/country/ME.png b/template/images/country/ME.png
new file mode 100644
index 0000000..96e74b0
Binary files /dev/null and b/template/images/country/ME.png differ
diff --git a/template/images/country/MF.png b/template/images/country/MF.png
new file mode 100644
index 0000000..4aafb1f
Binary files /dev/null and b/template/images/country/MF.png differ
diff --git a/template/images/country/MG.png b/template/images/country/MG.png
new file mode 100644
index 0000000..c55a2f4
Binary files /dev/null and b/template/images/country/MG.png differ
diff --git a/template/images/country/MH.png b/template/images/country/MH.png
new file mode 100644
index 0000000..7ffbb41
Binary files /dev/null and b/template/images/country/MH.png differ
diff --git a/template/images/country/MK.png b/template/images/country/MK.png
new file mode 100644
index 0000000..1f6b1c7
Binary files /dev/null and b/template/images/country/MK.png differ
diff --git a/template/images/country/ML.png b/template/images/country/ML.png
new file mode 100644
index 0000000..530e5c5
Binary files /dev/null and b/template/images/country/ML.png differ
diff --git a/template/images/country/MM.png b/template/images/country/MM.png
new file mode 100644
index 0000000..102f811
Binary files /dev/null and b/template/images/country/MM.png differ
diff --git a/template/images/country/MN.png b/template/images/country/MN.png
new file mode 100644
index 0000000..8589e8d
Binary files /dev/null and b/template/images/country/MN.png differ
diff --git a/template/images/country/MO.png b/template/images/country/MO.png
new file mode 100644
index 0000000..ff2e117
Binary files /dev/null and b/template/images/country/MO.png differ
diff --git a/template/images/country/MP.png b/template/images/country/MP.png
new file mode 100644
index 0000000..0a80d84
Binary files /dev/null and b/template/images/country/MP.png differ
diff --git a/template/images/country/MQ.png b/template/images/country/MQ.png
new file mode 100644
index 0000000..5d78e64
Binary files /dev/null and b/template/images/country/MQ.png differ
diff --git a/template/images/country/MR.png b/template/images/country/MR.png
new file mode 100644
index 0000000..f25680e
Binary files /dev/null and b/template/images/country/MR.png differ
diff --git a/template/images/country/MS.png b/template/images/country/MS.png
new file mode 100644
index 0000000..1689556
Binary files /dev/null and b/template/images/country/MS.png differ
diff --git a/template/images/country/MT.png b/template/images/country/MT.png
new file mode 100644
index 0000000..e77d8b2
Binary files /dev/null and b/template/images/country/MT.png differ
diff --git a/template/images/country/MU.png b/template/images/country/MU.png
new file mode 100644
index 0000000..d7d7e1c
Binary files /dev/null and b/template/images/country/MU.png differ
diff --git a/template/images/country/MV.png b/template/images/country/MV.png
new file mode 100644
index 0000000..94d63cf
Binary files /dev/null and b/template/images/country/MV.png differ
diff --git a/template/images/country/MW.png b/template/images/country/MW.png
new file mode 100644
index 0000000..9b70c79
Binary files /dev/null and b/template/images/country/MW.png differ
diff --git a/template/images/country/MX.png b/template/images/country/MX.png
new file mode 100644
index 0000000..d05b37a
Binary files /dev/null and b/template/images/country/MX.png differ
diff --git a/template/images/country/MY.png b/template/images/country/MY.png
new file mode 100644
index 0000000..9b7b458
Binary files /dev/null and b/template/images/country/MY.png differ
diff --git a/template/images/country/MZ.png b/template/images/country/MZ.png
new file mode 100644
index 0000000..a919a16
Binary files /dev/null and b/template/images/country/MZ.png differ
diff --git a/template/images/country/NA.png b/template/images/country/NA.png
new file mode 100644
index 0000000..a053026
Binary files /dev/null and b/template/images/country/NA.png differ
diff --git a/template/images/country/NC.png b/template/images/country/NC.png
new file mode 100644
index 0000000..d04c592
Binary files /dev/null and b/template/images/country/NC.png differ
diff --git a/template/images/country/NE.png b/template/images/country/NE.png
new file mode 100644
index 0000000..94c9512
Binary files /dev/null and b/template/images/country/NE.png differ
diff --git a/template/images/country/NF.png b/template/images/country/NF.png
new file mode 100644
index 0000000..d09be06
Binary files /dev/null and b/template/images/country/NF.png differ
diff --git a/template/images/country/NG.png b/template/images/country/NG.png
new file mode 100644
index 0000000..4265810
Binary files /dev/null and b/template/images/country/NG.png differ
diff --git a/template/images/country/NI.png b/template/images/country/NI.png
new file mode 100644
index 0000000..3b542e4
Binary files /dev/null and b/template/images/country/NI.png differ
diff --git a/template/images/country/NL.png b/template/images/country/NL.png
new file mode 100644
index 0000000..e1ede53
Binary files /dev/null and b/template/images/country/NL.png differ
diff --git a/template/images/country/NO.png b/template/images/country/NO.png
new file mode 100644
index 0000000..9f79424
Binary files /dev/null and b/template/images/country/NO.png differ
diff --git a/template/images/country/NP.png b/template/images/country/NP.png
new file mode 100644
index 0000000..23c3c6d
Binary files /dev/null and b/template/images/country/NP.png differ
diff --git a/template/images/country/NR.png b/template/images/country/NR.png
new file mode 100644
index 0000000..ee4d01e
Binary files /dev/null and b/template/images/country/NR.png differ
diff --git a/template/images/country/NU.png b/template/images/country/NU.png
new file mode 100644
index 0000000..354a67c
Binary files /dev/null and b/template/images/country/NU.png differ
diff --git a/template/images/country/NZ.png b/template/images/country/NZ.png
new file mode 100644
index 0000000..c44ca79
Binary files /dev/null and b/template/images/country/NZ.png differ
diff --git a/template/images/country/OM.png b/template/images/country/OM.png
new file mode 100644
index 0000000..0e12bf9
Binary files /dev/null and b/template/images/country/OM.png differ
diff --git a/template/images/country/PA.png b/template/images/country/PA.png
new file mode 100644
index 0000000..87f2a4b
Binary files /dev/null and b/template/images/country/PA.png differ
diff --git a/template/images/country/PE.png b/template/images/country/PE.png
new file mode 100644
index 0000000..792a9ce
Binary files /dev/null and b/template/images/country/PE.png differ
diff --git a/template/images/country/PF.png b/template/images/country/PF.png
new file mode 100644
index 0000000..5f9a5a5
Binary files /dev/null and b/template/images/country/PF.png differ
diff --git a/template/images/country/PG.png b/template/images/country/PG.png
new file mode 100644
index 0000000..a7d1d23
Binary files /dev/null and b/template/images/country/PG.png differ
diff --git a/template/images/country/PH.png b/template/images/country/PH.png
new file mode 100644
index 0000000..fd9d8fb
Binary files /dev/null and b/template/images/country/PH.png differ
diff --git a/template/images/country/PK.png b/template/images/country/PK.png
new file mode 100644
index 0000000..2f2550c
Binary files /dev/null and b/template/images/country/PK.png differ
diff --git a/template/images/country/PL.png b/template/images/country/PL.png
new file mode 100644
index 0000000..e30222e
Binary files /dev/null and b/template/images/country/PL.png differ
diff --git a/template/images/country/PN.png b/template/images/country/PN.png
new file mode 100644
index 0000000..18b8e38
Binary files /dev/null and b/template/images/country/PN.png differ
diff --git a/template/images/country/PR.png b/template/images/country/PR.png
new file mode 100644
index 0000000..bac124c
Binary files /dev/null and b/template/images/country/PR.png differ
diff --git a/template/images/country/PS.png b/template/images/country/PS.png
new file mode 100644
index 0000000..7145cbe
Binary files /dev/null and b/template/images/country/PS.png differ
diff --git a/template/images/country/PT.png b/template/images/country/PT.png
new file mode 100644
index 0000000..eab3f39
Binary files /dev/null and b/template/images/country/PT.png differ
diff --git a/template/images/country/PW.png b/template/images/country/PW.png
new file mode 100644
index 0000000..a5b01c3
Binary files /dev/null and b/template/images/country/PW.png differ
diff --git a/template/images/country/PY.png b/template/images/country/PY.png
new file mode 100644
index 0000000..ab391d7
Binary files /dev/null and b/template/images/country/PY.png differ
diff --git a/template/images/country/QA.png b/template/images/country/QA.png
new file mode 100644
index 0000000..c3f3d57
Binary files /dev/null and b/template/images/country/QA.png differ
diff --git a/template/images/country/RO.png b/template/images/country/RO.png
new file mode 100644
index 0000000..79a7d55
Binary files /dev/null and b/template/images/country/RO.png differ
diff --git a/template/images/country/RS.png b/template/images/country/RS.png
new file mode 100644
index 0000000..5a6f9a3
Binary files /dev/null and b/template/images/country/RS.png differ
diff --git a/template/images/country/RU.png b/template/images/country/RU.png
new file mode 100644
index 0000000..0d74832
Binary files /dev/null and b/template/images/country/RU.png differ
diff --git a/template/images/country/RW.png b/template/images/country/RW.png
new file mode 100644
index 0000000..64d72c9
Binary files /dev/null and b/template/images/country/RW.png differ
diff --git a/template/images/country/SA.png b/template/images/country/SA.png
new file mode 100644
index 0000000..b9f78ad
Binary files /dev/null and b/template/images/country/SA.png differ
diff --git a/template/images/country/SB.png b/template/images/country/SB.png
new file mode 100644
index 0000000..bfd2624
Binary files /dev/null and b/template/images/country/SB.png differ
diff --git a/template/images/country/SC.png b/template/images/country/SC.png
new file mode 100644
index 0000000..0ff3930
Binary files /dev/null and b/template/images/country/SC.png differ
diff --git a/template/images/country/SD.png b/template/images/country/SD.png
new file mode 100644
index 0000000..25e8425
Binary files /dev/null and b/template/images/country/SD.png differ
diff --git a/template/images/country/SE.png b/template/images/country/SE.png
new file mode 100644
index 0000000..f9ad787
Binary files /dev/null and b/template/images/country/SE.png differ
diff --git a/template/images/country/SG.png b/template/images/country/SG.png
new file mode 100644
index 0000000..b274a5d
Binary files /dev/null and b/template/images/country/SG.png differ
diff --git a/template/images/country/SH.png b/template/images/country/SH.png
new file mode 100644
index 0000000..8640c10
Binary files /dev/null and b/template/images/country/SH.png differ
diff --git a/template/images/country/SI.png b/template/images/country/SI.png
new file mode 100644
index 0000000..5b257f2
Binary files /dev/null and b/template/images/country/SI.png differ
diff --git a/template/images/country/SK.png b/template/images/country/SK.png
new file mode 100644
index 0000000..1409b5f
Binary files /dev/null and b/template/images/country/SK.png differ
diff --git a/template/images/country/SL.png b/template/images/country/SL.png
new file mode 100644
index 0000000..23695b3
Binary files /dev/null and b/template/images/country/SL.png differ
diff --git a/template/images/country/SM.png b/template/images/country/SM.png
new file mode 100644
index 0000000..64e5428
Binary files /dev/null and b/template/images/country/SM.png differ
diff --git a/template/images/country/SN.png b/template/images/country/SN.png
new file mode 100644
index 0000000..08ba0f5
Binary files /dev/null and b/template/images/country/SN.png differ
diff --git a/template/images/country/SO.png b/template/images/country/SO.png
new file mode 100644
index 0000000..021f6ca
Binary files /dev/null and b/template/images/country/SO.png differ
diff --git a/template/images/country/SR.png b/template/images/country/SR.png
new file mode 100644
index 0000000..2b114de
Binary files /dev/null and b/template/images/country/SR.png differ
diff --git a/template/images/country/SS.png b/template/images/country/SS.png
new file mode 100644
index 0000000..8c6616c
Binary files /dev/null and b/template/images/country/SS.png differ
diff --git a/template/images/country/ST.png b/template/images/country/ST.png
new file mode 100644
index 0000000..8eab5c5
Binary files /dev/null and b/template/images/country/ST.png differ
diff --git a/template/images/country/SV.png b/template/images/country/SV.png
new file mode 100644
index 0000000..82fc634
Binary files /dev/null and b/template/images/country/SV.png differ
diff --git a/template/images/country/SY.png b/template/images/country/SY.png
new file mode 100644
index 0000000..82ab48c
Binary files /dev/null and b/template/images/country/SY.png differ
diff --git a/template/images/country/SZ.png b/template/images/country/SZ.png
new file mode 100644
index 0000000..555a279
Binary files /dev/null and b/template/images/country/SZ.png differ
diff --git a/template/images/country/TC.png b/template/images/country/TC.png
new file mode 100644
index 0000000..5edc6e9
Binary files /dev/null and b/template/images/country/TC.png differ
diff --git a/template/images/country/TD.png b/template/images/country/TD.png
new file mode 100644
index 0000000..d382728
Binary files /dev/null and b/template/images/country/TD.png differ
diff --git a/template/images/country/TF.png b/template/images/country/TF.png
new file mode 100644
index 0000000..0cb1a01
Binary files /dev/null and b/template/images/country/TF.png differ
diff --git a/template/images/country/TG.png b/template/images/country/TG.png
new file mode 100644
index 0000000..52e3a51
Binary files /dev/null and b/template/images/country/TG.png differ
diff --git a/template/images/country/TH.png b/template/images/country/TH.png
new file mode 100644
index 0000000..485709a
Binary files /dev/null and b/template/images/country/TH.png differ
diff --git a/template/images/country/TJ.png b/template/images/country/TJ.png
new file mode 100644
index 0000000..2731fd5
Binary files /dev/null and b/template/images/country/TJ.png differ
diff --git a/template/images/country/TK.png b/template/images/country/TK.png
new file mode 100644
index 0000000..78c1a29
Binary files /dev/null and b/template/images/country/TK.png differ
diff --git a/template/images/country/TL.png b/template/images/country/TL.png
new file mode 100644
index 0000000..7e17f16
Binary files /dev/null and b/template/images/country/TL.png differ
diff --git a/template/images/country/TM.png b/template/images/country/TM.png
new file mode 100644
index 0000000..4a31703
Binary files /dev/null and b/template/images/country/TM.png differ
diff --git a/template/images/country/TN.png b/template/images/country/TN.png
new file mode 100644
index 0000000..fd2b160
Binary files /dev/null and b/template/images/country/TN.png differ
diff --git a/template/images/country/TO.png b/template/images/country/TO.png
new file mode 100644
index 0000000..ea666d2
Binary files /dev/null and b/template/images/country/TO.png differ
diff --git a/template/images/country/TR.png b/template/images/country/TR.png
new file mode 100644
index 0000000..d5a384a
Binary files /dev/null and b/template/images/country/TR.png differ
diff --git a/template/images/country/TT.png b/template/images/country/TT.png
new file mode 100644
index 0000000..e929812
Binary files /dev/null and b/template/images/country/TT.png differ
diff --git a/template/images/country/TV.png b/template/images/country/TV.png
new file mode 100644
index 0000000..40a5323
Binary files /dev/null and b/template/images/country/TV.png differ
diff --git a/template/images/country/TW.png b/template/images/country/TW.png
new file mode 100644
index 0000000..cb691b3
Binary files /dev/null and b/template/images/country/TW.png differ
diff --git a/template/images/country/TZ.png b/template/images/country/TZ.png
new file mode 100644
index 0000000..5b7395e
Binary files /dev/null and b/template/images/country/TZ.png differ
diff --git a/template/images/country/UA.png b/template/images/country/UA.png
new file mode 100644
index 0000000..70db400
Binary files /dev/null and b/template/images/country/UA.png differ
diff --git a/template/images/country/UG.png b/template/images/country/UG.png
new file mode 100644
index 0000000..d07682e
Binary files /dev/null and b/template/images/country/UG.png differ
diff --git a/template/images/country/US.png b/template/images/country/US.png
new file mode 100644
index 0000000..5706b57
Binary files /dev/null and b/template/images/country/US.png differ
diff --git a/template/images/country/UY.png b/template/images/country/UY.png
new file mode 100644
index 0000000..419d11f
Binary files /dev/null and b/template/images/country/UY.png differ
diff --git a/template/images/country/UZ.png b/template/images/country/UZ.png
new file mode 100644
index 0000000..b88e27e
Binary files /dev/null and b/template/images/country/UZ.png differ
diff --git a/template/images/country/VA.png b/template/images/country/VA.png
new file mode 100644
index 0000000..da76146
Binary files /dev/null and b/template/images/country/VA.png differ
diff --git a/template/images/country/VC.png b/template/images/country/VC.png
new file mode 100644
index 0000000..28b71cf
Binary files /dev/null and b/template/images/country/VC.png differ
diff --git a/template/images/country/VE.png b/template/images/country/VE.png
new file mode 100644
index 0000000..9e51f9a
Binary files /dev/null and b/template/images/country/VE.png differ
diff --git a/template/images/country/VG.png b/template/images/country/VG.png
new file mode 100644
index 0000000..d337635
Binary files /dev/null and b/template/images/country/VG.png differ
diff --git a/template/images/country/VI.png b/template/images/country/VI.png
new file mode 100644
index 0000000..5fa3126
Binary files /dev/null and b/template/images/country/VI.png differ
diff --git a/template/images/country/VN.png b/template/images/country/VN.png
new file mode 100644
index 0000000..06b9498
Binary files /dev/null and b/template/images/country/VN.png differ
diff --git a/template/images/country/VU.png b/template/images/country/VU.png
new file mode 100644
index 0000000..e898000
Binary files /dev/null and b/template/images/country/VU.png differ
diff --git a/template/images/country/WF.png b/template/images/country/WF.png
new file mode 100644
index 0000000..79bf057
Binary files /dev/null and b/template/images/country/WF.png differ
diff --git a/template/images/country/WS.png b/template/images/country/WS.png
new file mode 100644
index 0000000..45fe25f
Binary files /dev/null and b/template/images/country/WS.png differ
diff --git a/template/images/country/YE.png b/template/images/country/YE.png
new file mode 100644
index 0000000..19a9e90
Binary files /dev/null and b/template/images/country/YE.png differ
diff --git a/template/images/country/YT.png b/template/images/country/YT.png
new file mode 100644
index 0000000..877ddf8
Binary files /dev/null and b/template/images/country/YT.png differ
diff --git a/template/images/country/ZA.png b/template/images/country/ZA.png
new file mode 100644
index 0000000..2c2eff8
Binary files /dev/null and b/template/images/country/ZA.png differ
diff --git a/template/images/country/ZM.png b/template/images/country/ZM.png
new file mode 100644
index 0000000..cd3d06a
Binary files /dev/null and b/template/images/country/ZM.png differ
diff --git a/template/images/country/ZW.png b/template/images/country/ZW.png
new file mode 100644
index 0000000..a40454d
Binary files /dev/null and b/template/images/country/ZW.png differ
diff --git a/template/images/country/none.png b/template/images/country/none.png
new file mode 100644
index 0000000..6d3afa5
Binary files /dev/null and b/template/images/country/none.png differ
diff --git a/template/images/emoji.png b/template/images/emoji.png
new file mode 100644
index 0000000..a985352
Binary files /dev/null and b/template/images/emoji.png differ
diff --git a/template/images/error/403.png b/template/images/error/403.png
new file mode 100644
index 0000000..bfa3729
Binary files /dev/null and b/template/images/error/403.png differ
diff --git a/template/images/error/404.png b/template/images/error/404.png
new file mode 100644
index 0000000..bfcb58c
Binary files /dev/null and b/template/images/error/404.png differ
diff --git a/template/images/error/fail.jpg b/template/images/error/fail.jpg
new file mode 100644
index 0000000..081d23b
Binary files /dev/null and b/template/images/error/fail.jpg differ
diff --git a/template/images/error/success.jpg b/template/images/error/success.jpg
new file mode 100644
index 0000000..0505b84
Binary files /dev/null and b/template/images/error/success.jpg differ
diff --git a/template/images/gscp.png b/template/images/gscp.png
new file mode 100644
index 0000000..4a2092e
Binary files /dev/null and b/template/images/gscp.png differ
diff --git a/template/images/help_sprite.png b/template/images/help_sprite.png
new file mode 100644
index 0000000..1f065dc
Binary files /dev/null and b/template/images/help_sprite.png differ
diff --git a/template/images/icons/box/blank.gif b/template/images/icons/box/blank.gif
new file mode 100644
index 0000000..35d42e8
Binary files /dev/null and b/template/images/icons/box/blank.gif differ
diff --git a/template/images/icons/box/fancy_close.png b/template/images/icons/box/fancy_close.png
new file mode 100644
index 0000000..0703530
Binary files /dev/null and b/template/images/icons/box/fancy_close.png differ
diff --git a/template/images/icons/box/fancy_loading.png b/template/images/icons/box/fancy_loading.png
new file mode 100644
index 0000000..2503017
Binary files /dev/null and b/template/images/icons/box/fancy_loading.png differ
diff --git a/template/images/icons/box/fancy_nav_left.png b/template/images/icons/box/fancy_nav_left.png
new file mode 100644
index 0000000..ebaa6a4
Binary files /dev/null and b/template/images/icons/box/fancy_nav_left.png differ
diff --git a/template/images/icons/box/fancy_nav_right.png b/template/images/icons/box/fancy_nav_right.png
new file mode 100644
index 0000000..873294e
Binary files /dev/null and b/template/images/icons/box/fancy_nav_right.png differ
diff --git a/template/images/icons/box/fancy_shadow_e.png b/template/images/icons/box/fancy_shadow_e.png
new file mode 100644
index 0000000..2eda089
Binary files /dev/null and b/template/images/icons/box/fancy_shadow_e.png differ
diff --git a/template/images/icons/box/fancy_shadow_n.png b/template/images/icons/box/fancy_shadow_n.png
new file mode 100644
index 0000000..69aa10e
Binary files /dev/null and b/template/images/icons/box/fancy_shadow_n.png differ
diff --git a/template/images/icons/box/fancy_shadow_ne.png b/template/images/icons/box/fancy_shadow_ne.png
new file mode 100644
index 0000000..79f6980
Binary files /dev/null and b/template/images/icons/box/fancy_shadow_ne.png differ
diff --git a/template/images/icons/box/fancy_shadow_nw.png b/template/images/icons/box/fancy_shadow_nw.png
new file mode 100644
index 0000000..7182cd9
Binary files /dev/null and b/template/images/icons/box/fancy_shadow_nw.png differ
diff --git a/template/images/icons/box/fancy_shadow_s.png b/template/images/icons/box/fancy_shadow_s.png
new file mode 100644
index 0000000..d8858bf
Binary files /dev/null and b/template/images/icons/box/fancy_shadow_s.png differ
diff --git a/template/images/icons/box/fancy_shadow_se.png b/template/images/icons/box/fancy_shadow_se.png
new file mode 100644
index 0000000..541e3ff
Binary files /dev/null and b/template/images/icons/box/fancy_shadow_se.png differ
diff --git a/template/images/icons/box/fancy_shadow_sw.png b/template/images/icons/box/fancy_shadow_sw.png
new file mode 100644
index 0000000..b451689
Binary files /dev/null and b/template/images/icons/box/fancy_shadow_sw.png differ
diff --git a/template/images/icons/box/fancy_shadow_w.png b/template/images/icons/box/fancy_shadow_w.png
new file mode 100644
index 0000000..8a4e4a8
Binary files /dev/null and b/template/images/icons/box/fancy_shadow_w.png differ
diff --git a/template/images/icons/box/fancy_title_left.png b/template/images/icons/box/fancy_title_left.png
new file mode 100644
index 0000000..6049223
Binary files /dev/null and b/template/images/icons/box/fancy_title_left.png differ
diff --git a/template/images/icons/box/fancy_title_main.png b/template/images/icons/box/fancy_title_main.png
new file mode 100644
index 0000000..8044271
Binary files /dev/null and b/template/images/icons/box/fancy_title_main.png differ
diff --git a/template/images/icons/box/fancy_title_over.png b/template/images/icons/box/fancy_title_over.png
new file mode 100644
index 0000000..d9f458f
Binary files /dev/null and b/template/images/icons/box/fancy_title_over.png differ
diff --git a/template/images/icons/box/fancy_title_right.png b/template/images/icons/box/fancy_title_right.png
new file mode 100644
index 0000000..e36d9db
Binary files /dev/null and b/template/images/icons/box/fancy_title_right.png differ
diff --git a/template/images/icons/vk.png b/template/images/icons/vk.png
new file mode 100644
index 0000000..afb4879
Binary files /dev/null and b/template/images/icons/vk.png differ
diff --git a/template/images/lang/lang__ar.png b/template/images/lang/lang__ar.png
new file mode 100644
index 0000000..05c9daa
Binary files /dev/null and b/template/images/lang/lang__ar.png differ
diff --git a/template/images/lang/lang__de.png b/template/images/lang/lang__de.png
new file mode 100644
index 0000000..181f552
Binary files /dev/null and b/template/images/lang/lang__de.png differ
diff --git a/template/images/lang/lang__en.png b/template/images/lang/lang__en.png
new file mode 100644
index 0000000..b1c1a32
Binary files /dev/null and b/template/images/lang/lang__en.png differ
diff --git a/template/images/lang/lang__es.png b/template/images/lang/lang__es.png
new file mode 100644
index 0000000..b70c61c
Binary files /dev/null and b/template/images/lang/lang__es.png differ
diff --git a/template/images/lang/lang__fr.png b/template/images/lang/lang__fr.png
new file mode 100644
index 0000000..75116f5
Binary files /dev/null and b/template/images/lang/lang__fr.png differ
diff --git a/template/images/lang/lang__it.png b/template/images/lang/lang__it.png
new file mode 100644
index 0000000..aa9ad49
Binary files /dev/null and b/template/images/lang/lang__it.png differ
diff --git a/template/images/lang/lang__nl.png b/template/images/lang/lang__nl.png
new file mode 100644
index 0000000..2a47cf6
Binary files /dev/null and b/template/images/lang/lang__nl.png differ
diff --git a/template/images/lang/lang__pt.png b/template/images/lang/lang__pt.png
new file mode 100644
index 0000000..e7fbb10
Binary files /dev/null and b/template/images/lang/lang__pt.png differ
diff --git a/template/images/lang/lang__ru.png b/template/images/lang/lang__ru.png
new file mode 100644
index 0000000..b145fdc
Binary files /dev/null and b/template/images/lang/lang__ru.png differ
diff --git a/template/images/lang/lang__sv.png b/template/images/lang/lang__sv.png
new file mode 100644
index 0000000..bc48d11
Binary files /dev/null and b/template/images/lang/lang__sv.png differ
diff --git a/template/images/lang/lang__ua.png b/template/images/lang/lang__ua.png
new file mode 100644
index 0000000..777bda9
Binary files /dev/null and b/template/images/lang/lang__ua.png differ
diff --git a/template/images/lang/lang__zh.png b/template/images/lang/lang__zh.png
new file mode 100644
index 0000000..13d610b
Binary files /dev/null and b/template/images/lang/lang__zh.png differ
diff --git a/template/images/maps.png b/template/images/maps.png
new file mode 100644
index 0000000..1a657f9
Binary files /dev/null and b/template/images/maps.png differ
diff --git a/template/images/maps/de_dust2.jpg b/template/images/maps/de_dust2.jpg
new file mode 100644
index 0000000..5304d17
Binary files /dev/null and b/template/images/maps/de_dust2.jpg differ
diff --git a/template/images/partners/amd.png b/template/images/partners/amd.png
new file mode 100644
index 0000000..cb4eaa2
Binary files /dev/null and b/template/images/partners/amd.png differ
diff --git a/template/images/partners/hp.png b/template/images/partners/hp.png
new file mode 100644
index 0000000..295e5a7
Binary files /dev/null and b/template/images/partners/hp.png differ
diff --git a/template/images/partners/intel.png b/template/images/partners/intel.png
new file mode 100644
index 0000000..9e580ad
Binary files /dev/null and b/template/images/partners/intel.png differ
diff --git a/template/images/partners/ispsystem.jpg b/template/images/partners/ispsystem.jpg
new file mode 100644
index 0000000..e014cdf
Binary files /dev/null and b/template/images/partners/ispsystem.jpg differ
diff --git a/template/images/partners/paypal.jpg b/template/images/partners/paypal.jpg
new file mode 100644
index 0000000..5dede9c
Binary files /dev/null and b/template/images/partners/paypal.jpg differ
diff --git a/template/images/partners/qiwi.jpg b/template/images/partners/qiwi.jpg
new file mode 100644
index 0000000..8d07bfb
Binary files /dev/null and b/template/images/partners/qiwi.jpg differ
diff --git a/template/images/partners/supermicro.jpg b/template/images/partners/supermicro.jpg
new file mode 100644
index 0000000..511dfa3
Binary files /dev/null and b/template/images/partners/supermicro.jpg differ
diff --git a/template/images/partners/unitpay.jpg b/template/images/partners/unitpay.jpg
new file mode 100644
index 0000000..33d0d3e
Binary files /dev/null and b/template/images/partners/unitpay.jpg differ
diff --git a/template/images/partners/webmoney.jpg b/template/images/partners/webmoney.jpg
new file mode 100644
index 0000000..df46ee8
Binary files /dev/null and b/template/images/partners/webmoney.jpg differ
diff --git a/template/images/partners/yandex.jpg b/template/images/partners/yandex.jpg
new file mode 100644
index 0000000..9175faa
Binary files /dev/null and b/template/images/partners/yandex.jpg differ
diff --git a/template/images/progress.jpg b/template/images/progress.jpg
new file mode 100644
index 0000000..6892fd8
Binary files /dev/null and b/template/images/progress.jpg differ
diff --git a/template/images/replenish/beeline.jpg b/template/images/replenish/beeline.jpg
new file mode 100644
index 0000000..fd37175
Binary files /dev/null and b/template/images/replenish/beeline.jpg differ
diff --git a/template/images/replenish/liqpay.jpg b/template/images/replenish/liqpay.jpg
new file mode 100644
index 0000000..e56b28c
Binary files /dev/null and b/template/images/replenish/liqpay.jpg differ
diff --git a/template/images/replenish/mastercard.jpg b/template/images/replenish/mastercard.jpg
new file mode 100644
index 0000000..75eec17
Binary files /dev/null and b/template/images/replenish/mastercard.jpg differ
diff --git a/template/images/replenish/megafon.jpg b/template/images/replenish/megafon.jpg
new file mode 100644
index 0000000..101f43b
Binary files /dev/null and b/template/images/replenish/megafon.jpg differ
diff --git a/template/images/replenish/mobile.jpg b/template/images/replenish/mobile.jpg
new file mode 100644
index 0000000..0993d24
Binary files /dev/null and b/template/images/replenish/mobile.jpg differ
diff --git a/template/images/replenish/mts.jpg b/template/images/replenish/mts.jpg
new file mode 100644
index 0000000..2bdab2e
Binary files /dev/null and b/template/images/replenish/mts.jpg differ
diff --git a/template/images/replenish/qiwi.jpg b/template/images/replenish/qiwi.jpg
new file mode 100644
index 0000000..a528209
Binary files /dev/null and b/template/images/replenish/qiwi.jpg differ
diff --git a/template/images/replenish/visa.jpg b/template/images/replenish/visa.jpg
new file mode 100644
index 0000000..f3f0fbf
Binary files /dev/null and b/template/images/replenish/visa.jpg differ
diff --git a/template/images/replenish/webmoney.jpg b/template/images/replenish/webmoney.jpg
new file mode 100644
index 0000000..ef532d0
Binary files /dev/null and b/template/images/replenish/webmoney.jpg differ
diff --git a/template/images/replenish/yandex.jpg b/template/images/replenish/yandex.jpg
new file mode 100644
index 0000000..030b5bd
Binary files /dev/null and b/template/images/replenish/yandex.jpg differ
diff --git a/template/images/services/crmp.png b/template/images/services/crmp.png
new file mode 100644
index 0000000..dfc95ed
Binary files /dev/null and b/template/images/services/crmp.png differ
diff --git a/template/images/services/cs.png b/template/images/services/cs.png
new file mode 100644
index 0000000..52427dc
Binary files /dev/null and b/template/images/services/cs.png differ
diff --git a/template/images/services/cscz.png b/template/images/services/cscz.png
new file mode 100644
index 0000000..2653831
Binary files /dev/null and b/template/images/services/cscz.png differ
diff --git a/template/images/services/csgo.png b/template/images/services/csgo.png
new file mode 100644
index 0000000..1241070
Binary files /dev/null and b/template/images/services/csgo.png differ
diff --git a/template/images/services/css.png b/template/images/services/css.png
new file mode 100644
index 0000000..2dae6bd
Binary files /dev/null and b/template/images/services/css.png differ
diff --git a/template/images/services/cssold.png b/template/images/services/cssold.png
new file mode 100644
index 0000000..6ba94b3
Binary files /dev/null and b/template/images/services/cssold.png differ
diff --git a/template/images/services/mc.png b/template/images/services/mc.png
new file mode 100644
index 0000000..204afc2
Binary files /dev/null and b/template/images/services/mc.png differ
diff --git a/template/images/services/mta.png b/template/images/services/mta.png
new file mode 100644
index 0000000..9cef038
Binary files /dev/null and b/template/images/services/mta.png differ
diff --git a/template/images/services/samp.png b/template/images/services/samp.png
new file mode 100644
index 0000000..7e4316c
Binary files /dev/null and b/template/images/services/samp.png differ
diff --git a/template/images/status/blocked.jpg b/template/images/status/blocked.jpg
new file mode 100644
index 0000000..8b39eb0
Binary files /dev/null and b/template/images/status/blocked.jpg differ
diff --git a/template/images/status/change.gif b/template/images/status/change.gif
new file mode 100644
index 0000000..0ff8c60
Binary files /dev/null and b/template/images/status/change.gif differ
diff --git a/template/images/status/install.gif b/template/images/status/install.gif
new file mode 100644
index 0000000..0a186f1
Binary files /dev/null and b/template/images/status/install.gif differ
diff --git a/template/images/status/none.jpg b/template/images/status/none.jpg
new file mode 100644
index 0000000..5a74994
Binary files /dev/null and b/template/images/status/none.jpg differ
diff --git a/template/images/status/off.jpg b/template/images/status/off.jpg
new file mode 100644
index 0000000..74ab949
Binary files /dev/null and b/template/images/status/off.jpg differ
diff --git a/template/images/status/overdue.jpg b/template/images/status/overdue.jpg
new file mode 100644
index 0000000..1d09547
Binary files /dev/null and b/template/images/status/overdue.jpg differ
diff --git a/template/images/status/recovery.gif b/template/images/status/recovery.gif
new file mode 100644
index 0000000..7ce6a8c
Binary files /dev/null and b/template/images/status/recovery.gif differ
diff --git a/template/images/status/reinstall.gif b/template/images/status/reinstall.gif
new file mode 100644
index 0000000..ffedafb
Binary files /dev/null and b/template/images/status/reinstall.gif differ
diff --git a/template/images/status/restart.gif b/template/images/status/restart.gif
new file mode 100644
index 0000000..938886e
Binary files /dev/null and b/template/images/status/restart.gif differ
diff --git a/template/images/status/start.gif b/template/images/status/start.gif
new file mode 100644
index 0000000..6f41aa7
Binary files /dev/null and b/template/images/status/start.gif differ
diff --git a/template/images/status/update.gif b/template/images/status/update.gif
new file mode 100644
index 0000000..a1f229e
Binary files /dev/null and b/template/images/status/update.gif differ
diff --git a/template/images/up-arrow.png b/template/images/up-arrow.png
new file mode 100644
index 0000000..b0868ab
Binary files /dev/null and b/template/images/up-arrow.png differ
diff --git a/template/index.html b/template/index.html
new file mode 100644
index 0000000..e2f3121
--- /dev/null
+++ b/template/index.html
@@ -0,0 +1,35 @@
+
Аренда игровых серверов
+
+
+
+
+
+
Counter-Strike: Source v34
+
+
Заказать
+
+
+
+
+
Counter-Strike: Source
+
+
Заказать
+
+
+
+
+
+
+
Последние новости
+
+[news]
\ No newline at end of file
diff --git a/template/informer.html b/template/informer.html
new file mode 100644
index 0000000..0ab4d6a
--- /dev/null
+++ b/template/informer.html
@@ -0,0 +1 @@
+
[text]
\ No newline at end of file
diff --git a/template/jobs/index.html b/template/jobs/index.html
new file mode 100644
index 0000000..835de0b
--- /dev/null
+++ b/template/jobs/index.html
@@ -0,0 +1,38 @@
+
+
+[jobs]
diff --git a/template/jobs/jobs.html b/template/jobs/jobs.html
new file mode 100644
index 0000000..acc616e
--- /dev/null
+++ b/template/jobs/jobs.html
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
[desc]
+
+ |answer|
Ответ от администрации: [text] |_answer|
+
+
Подать заявку
+
+
+
\ No newline at end of file
diff --git a/template/jobs/list.html b/template/jobs/list.html
new file mode 100644
index 0000000..415c69e
--- /dev/null
+++ b/template/jobs/list.html
@@ -0,0 +1,7 @@
+
[job]
+
+
+
\ No newline at end of file
diff --git a/template/js/ace-elements.js b/template/js/ace-elements.js
new file mode 100644
index 0000000..30fd1fe
--- /dev/null
+++ b/template/js/ace-elements.js
@@ -0,0 +1 @@
+jQuery(function(){if(!("ace" in window)){window.ace={}}window.ace.click_event=$.fn.tap?"tap":"click"});(function(e,c){var d="multiple" in document.createElement("INPUT");var j="FileList" in window;var b="FileReader" in window;var f=function(l,m){var k=this;this.settings=e.extend({},e.fn.ace_file_input.defaults,m);this.$element=e(l);this.element=l;this.disabled=false;this.can_reset=true;this.$element.on("change.ace_inner_call",function(o,n){if(n===true){return}return a.call(k)});this.$element.wrap('
');this.apply_settings()};f.error={FILE_LOAD_FAILED:1,IMAGE_LOAD_FAILED:2,THUMBNAIL_FAILED:3};f.prototype.apply_settings=function(){var l=this;var k=!!this.settings.icon_remove;this.multi=this.$element.attr("multiple")&&d;this.well_style=this.settings.style=="well";if(this.well_style){this.$element.parent().addClass("ace-file-multiple")}else{this.$element.parent().removeClass("ace-file-multiple")}this.$element.parent().find(":not(input[type=file])").remove();this.$element.after('
'+(this.settings.no_icon?' ':"")+" "+(k?'
':""));this.$label=this.$element.next();this.$label.on("click",function(){if(!this.disabled&&!l.element.disabled&&!l.$element.attr("readonly")){l.$element.click()}});if(k){this.$label.next("a").on(ace.click_event,function(){if(!l.can_reset){return false}var m=true;if(l.settings.before_remove){m=l.settings.before_remove.call(l.element)}if(!m){return false}return l.reset_input()})}if(this.settings.droppable&&j){g.call(this)}};f.prototype.show_file_list=function(k){var n=typeof k==="undefined"?this.$element.data("ace_input_files"):k;if(!n||n.length==0){return}if(this.well_style){this.$label.find("span").remove();if(!this.settings.btn_change){this.$label.addClass("hide-placeholder")}}this.$label.attr("data-title",this.settings.btn_change).addClass("selected");for(var p=0;p
');var r=e.trim(n[p].type);var o=b&&this.settings.thumbnail&&((r.length>0&&r.match("image"))||(r.length==0&&m=="icon-picture"));if(o){var s=this;e.when(i.call(this,n[p])).fail(function(t){if(s.settings.preview_error){s.settings.preview_error.call(s,l,t.code)}})}}}return true};f.prototype.reset_input=function(){this.$label.attr({"data-title":this.settings.btn_choose,"class":""}).find("span:first").attr({"data-title":this.settings.no_file,"class":""}).find('[class*="icon-"]').attr("class",this.settings.no_icon).prev("img").remove();if(!this.settings.no_icon){this.$label.find('[class*="icon-"]').remove()}this.$label.find("span").not(":first").remove();if(this.$element.data("ace_input_files")){this.$element.removeData("ace_input_files");this.$element.removeData("ace_input_method")}this.reset_input_field();return false};f.prototype.reset_input_field=function(){this.$element.wrap("").closest("form").get(0).reset();this.$element.unwrap()};f.prototype.enable_reset=function(k){this.can_reset=k};f.prototype.disable=function(){this.disabled=true;this.$element.attr("disabled","disabled").addClass("disabled")};f.prototype.enable=function(){this.disabled=false;this.$element.removeAttr("disabled").removeClass("disabled")};f.prototype.files=function(){return e(this).data("ace_input_files")||null};f.prototype.method=function(){return e(this).data("ace_input_method")||""};f.prototype.update_settings=function(k){this.settings=e.extend({},this.settings,k);this.apply_settings()};var g=function(){var l=this;var k=this.element.parentNode;e(k).on("dragenter",function(m){m.preventDefault();m.stopPropagation()}).on("dragover",function(m){m.preventDefault();m.stopPropagation()}).on("drop",function(q){q.preventDefault();q.stopPropagation();var p=q.originalEvent.dataTransfer;var o=p.files;if(!l.multi&&o.length>1){var n=[];n.push(o[0]);o=n}var m=true;if(l.settings.before_change){m=l.settings.before_change.call(l.element,o,true)}if(!m||m.length==0){return false}if(m instanceof Array||(j&&m instanceof FileList)){o=m}l.$element.data("ace_input_files",o);l.$element.data("ace_input_method","drop");l.show_file_list(o);l.$element.triggerHandler("change",[true]);return true})};var a=function(){var l=true;if(this.settings.before_change){l=this.settings.before_change.call(this.element,this.element.files||[this.element.value],false)}if(!l||l.length==0){if(!this.$element.data("ace_input_files")){this.reset_input_field()}return false}var m=!j?null:((l instanceof Array||l instanceof FileList)?l:this.element.files);this.$element.data("ace_input_method","select");if(m&&m.length>0){this.$element.data("ace_input_files",m)}else{var k=e.trim(this.element.value);if(k&&k.length>0){m=[];m.push(k);this.$element.data("ace_input_files",m)}}if(!m||m.length==0){return false}this.show_file_list(m);return true};var i=function(o){var n=this;var l=n.$label.find("span:last");var m=new e.Deferred;var k=new FileReader();k.onload=function(q){l.prepend(" ");var p=l.find("img:last").get(0);e(p).one("load",function(){var t=50;if(n.settings.thumbnail=="large"){t=150}else{if(n.settings.thumbnail=="fit"){t=l.width()}}l.addClass(t>50?"large":"");var s=h(p,t,o.type);if(s==null){e(this).remove();m.reject({code:f.error.THUMBNAIL_FAILED});return}var r=s.w,u=s.h;if(n.settings.thumbnail=="small"){r=u=t}e(p).css({"background-image":"url("+s.src+")",width:r,height:u}).data("thumb",s.src).attr({src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg=="}).show();m.resolve()}).one("error",function(){l.find("img").remove();m.reject({code:f.error.IMAGE_LOAD_FAILED})});p.src=q.target.result};k.onerror=function(p){m.reject({code:f.error.FILE_LOAD_FAILED})};k.readAsDataURL(o);return m.promise()};var h=function(n,s,q){var r=n.width,o=n.height;if(r>s||o>s){if(r>o){o=parseInt(s/r*o);r=s}else{r=parseInt(s/o*r);o=s}}var m;try{var l=document.createElement("canvas");l.width=r;l.height=o;var k=l.getContext("2d");k.drawImage(n,0,0,n.width,n.height,0,0,r,o);m=l.toDataURL()}catch(p){m=null}if(!(/^data\:image\/(png|jpe?g|gif);base64,[0-9A-Za-z\+\/\=]+$/.test(m))){m=null}if(!m){return null}return{src:m,w:r,h:o}};e.fn.ace_file_input=function(m,n){var l;var k=this.each(function(){var q=e(this);var p=q.data("ace_file_input");var o=typeof m==="object"&&m;if(!p){q.data("ace_file_input",(p=new f(this,o)))}if(typeof m==="string"){l=p[m](n)}});return(l===c)?k:l};e.fn.ace_file_input.defaults={style:false,no_file:"No File ...",no_icon:"icon-upload-alt",btn_choose:"Choose",btn_change:"Change",icon_remove:"icon-remove",droppable:false,thumbnail:false,before_change:null,before_remove:null,preview_error:null}})(window.jQuery);(function(a,b){a.fn.ace_spinner=function(c){this.each(function(){var f=c.icon_up||"icon-chevron-up";var i=c.icon_down||"icon-chevron-down";var e=c.btn_up_class||"";var g=c.btn_down_class||"";var d=c.max||999;d=(""+d).length;var j=a(this).addClass("spinner-input").css("width",(d*10)+"px").wrap('').after('
').closest(".ace-spinner").spinner(c).wrapInner("
");a(this).on("mousewheel DOMMouseScroll",function(k){var l=k.originalEvent.detail<0||k.originalEvent.wheelDelta>0?1:-1;j.spinner("step",l>0);j.spinner("triggerChangedEvent");return false});var h=a(this);j.on("changed",function(){h.trigger("change")})});return this}})(window.jQuery);(function(a,b){a.fn.ace_wizard=function(c){this.each(function(){var h=a(this);var d=h.find("li");var e=d.length;var f=parseFloat((100/e).toFixed(1))+"%";d.css({"min-width":f,"max-width":f});h.show().wizard();var g=h.siblings(".wizard-actions").eq(0);var i=h.data("wizard");i.$prevBtn.remove();i.$nextBtn.remove();i.$prevBtn=g.find(".btn-prev").eq(0).on(ace.click_event,function(){h.wizard("previous")}).attr("disabled","disabled");i.$nextBtn=g.find(".btn-next").eq(0).on(ace.click_event,function(){h.wizard("next")}).removeAttr("disabled");i.nextText=i.$nextBtn.text()});return this}})(window.jQuery);(function(a,b){a.fn.ace_colorpicker=function(c){var d=a.extend({pull_right:false,caret:true},c);this.each(function(){var g=a(this);var e="";var f="";a(this).hide().find("option").each(function(){var h="colorpick-btn";if(this.selected){h+=" selected";f=this.value}e+='
'}).end().on("change.ace_inner_call",function(){a(this).next().find(".btn-colorpicker").css("background-color",this.value)}).after('
").next().find(".dropdown-menu").on(ace.click_event,function(j){var h=a(j.target);if(!h.is(".colorpick-btn")){return false}h.closest("ul").find(".selected").removeClass("selected");h.addClass("selected");var i=h.data("color");g.val(i).change();j.preventDefault();return true})});return this}})(window.jQuery);(function(a,b){a.fn.ace_tree=function(d){var c={"open-icon":"icon-folder-open","close-icon":"icon-folder-close",selectable:true,"selected-icon":"icon-ok","unselected-icon":"tree-dot"};c=a.extend({},c,d);this.each(function(){var e=a(this);e.html('
'+(c["unselected-icon"]==null?"":'
')+'
');e.addClass(c.selectable==true?"tree-selectable":"tree-unselectable");e.tree(c)});return this}})(window.jQuery);(function(a,b){a.fn.ace_wysiwyg=function(c,h){var d=a.extend({speech_button:true,wysiwyg:{}},c);var e=["#ac725e","#d06b64","#f83a22","#fa573c","#ff7537","#ffad46","#42d692","#16a765","#7bd148","#b3dc6c","#fbe983","#fad165","#92e1c0","#9fe1e7","#9fc6e7","#4986e7","#9a9cff","#b99aff","#c2c2c2","#cabdbf","#cca6ac","#f691b2","#cd74e6","#a47ae2","#444444"];var g={font:{values:["Arial","Courier","Comic Sans MS","Helvetica","Open Sans","Tahoma","Verdana"],icon:"icon-font",title:"Font"},fontSize:{values:{5:"Huge",3:"Normal",1:"Small"},icon:"icon-text-height",title:"Font Size"},bold:{icon:"icon-bold",title:"Bold (Ctrl/Cmd+B)"},italic:{icon:"icon-italic",title:"Italic (Ctrl/Cmd+I)"},strikethrough:{icon:"icon-strikethrough",title:"Strikethrough"},underline:{icon:"icon-underline",title:"Underline"},insertunorderedlist:{icon:"icon-list-ul",title:"Bullet list"},insertorderedlist:{icon:"icon-list-ol",title:"Number list"},outdent:{icon:"icon-indent-left",title:"Reduce indent (Shift+Tab)"},indent:{icon:"icon-indent-right",title:"Indent (Tab)"},justifyleft:{icon:"icon-align-left",title:"Align Left (Ctrl/Cmd+L)"},justifycenter:{icon:"icon-align-center",title:"Center (Ctrl/Cmd+E)"},justifyright:{icon:"icon-align-right",title:"Align Right (Ctrl/Cmd+R)"},justifyfull:{icon:"icon-align-justify",title:"Justify (Ctrl/Cmd+J)"},createLink:{icon:"icon-link",title:"Hyperlink",button_text:"Add",placeholder:"URL"},unlink:{icon:"icon-unlink",title:"Remove Hyperlink"},insertImage:{icon:"icon-picture",title:"Insert picture",button_text:'
Choose Image …',placeholder:"Image URL"},foreColor:{values:e,title:"Change Color"},backColor:{values:e,title:"Change Background Color"},undo:{icon:"icon-undo",title:"Undo (Ctrl/Cmd+Z)"},redo:{icon:"icon-repeat",title:"Redo (Ctrl/Cmd+Y)"}};var f=d.toolbar||["font",null,"fontSize",null,"bold","italic","strikethrough","underline",null,"insertunorderedlist","insertorderedlist","outdent","indent",null,"justifyleft","justifycenter","justifyright","justifyfull",null,"createLink","unlink",null,"insertImage",null,"foreColor",null,"undo","redo"];this.each(function(){var q='
";if(d.toolbar_place){q=d.toolbar_place.call(this,q)}else{q=a(this).before(q).prev()}q.find("a[title]").tooltip({animation:false});q.find(".dropdown-menu input:not([type=file])").on(ace.click_event,function(){return false}).on("change",function(){a(this).closest(".dropdown-menu").siblings(".dropdown-toggle").dropdown("toggle")}).on("keydown",function(s){if(s.which==27){this.value="";a(this).change()}});q.find("input[type=file]").prev().on(ace.click_event,function(s){a(this).next().click()});q.find(".wysiwyg_colorpicker").each(function(){a(this).ace_colorpicker({pull_right:true,caret:false}).change(function(){a(this).nextAll("input").eq(0).val(this.value).change()}).next().find(".btn-colorpicker").tooltip({title:this.title,animation:false})});var k;if(d.speech_button&&"onwebkitspeechchange" in (k=document.createElement("input"))){var i=a(this).offset();q.append(k);a(k).attr({type:"text","data-edit":"inserttext","x-webkit-speech":""}).addClass("wysiwyg-speech-input").css({position:"absolute"}).offset({top:i.top,left:i.left+a(this).innerWidth()-35})}else{k=null}var n=a.extend({},{activeToolbarClass:"active",toolbarSelector:q},d.wysiwyg||{});a(this).wysiwyg(n)});return this}})(window.jQuery);
\ No newline at end of file
diff --git a/template/js/ace.js b/template/js/ace.js
new file mode 100644
index 0000000..b5ee1b8
--- /dev/null
+++ b/template/js/ace.js
@@ -0,0 +1 @@
+jQuery(function(){handle_side_menu();enable_search_ahead();general_things();widget_boxes()});function handle_side_menu(){$("#menu-toggler").on(ace.click_event,function(){$("#sidebar").toggleClass("display");$(this).toggleClass("display");return false});var b=$("#sidebar").hasClass("menu-min");$("#sidebar-collapse").on(ace.click_event,function(){$("#sidebar").toggleClass("menu-min");$(this).find('[class*="icon-"]:eq(0)').toggleClass("icon-double-angle-right");b=$("#sidebar").hasClass("menu-min");if(b){$(".open > .submenu").removeClass("open")}});var a="ontouchend" in document;$(".nav-list").on(ace.click_event,function(g){var f=$(g.target).closest("a");if(!f||f.length==0){return}if(!f.hasClass("dropdown-toggle")){if(b&&ace.click_event=="tap"&&f.get(0).parentNode.parentNode==this){var h=f.find(".menu-text").get(0);if(g.target!=h&&!$.contains(h,g.target)){return false}}return}var d=f.next().get(0);if(!$(d).is(":visible")){var c=$(d.parentNode).closest("ul");if(b&&c.hasClass("nav-list")){return}c.find("> .open > .submenu").each(function(){if(this!=d&&!$(this.parentNode).hasClass("active")){$(this).slideUp(200).parent().removeClass("open")}})}else{}if(b&&$(d.parentNode.parentNode).hasClass("nav-list")){return false}$(d).slideToggle(200).parent().toggleClass("open");return false})}function enable_search_ahead(){$("#nav-search-input").typeahead({source:["Alabama","Alaska","Arizona","Arkansas","California","Colorado","Connecticut","Delaware","Florida","Georgia","Hawaii","Idaho","Illinois","Indiana","Iowa","Kansas","Kentucky","Louisiana","Maine","Maryland","Massachusetts","Michigan","Minnesota","Mississippi","Missouri","Montana","Nebraska","Nevada","New Hampshire","New Jersey","New Mexico","New York","North Dakota","North Carolina","Ohio","Oklahoma","Oregon","Pennsylvania","Rhode Island","South Carolina","South Dakota","Tennessee","Texas","Utah","Vermont","Virginia","Washington","West Virginia","Wisconsin","Wyoming"],updater:function(a){$("#nav-search-input").focus();return a}})}function general_things(){$('.ace-nav [class*="icon-animated-"]').closest("a").on("click",function(){var b=$(this).find('[class*="icon-animated-"]').eq(0);var a=b.attr("class").match(/icon\-animated\-([\d\w]+)/);b.removeClass(a[0]);$(this).off("click")});$(".nav-list .badge[title],.nav-list .label[title]").tooltip({placement:"right"});$("#ace-settings-btn").on(ace.click_event,function(){$(this).toggleClass("open");$("#ace-settings-box").toggleClass("open")});$("#ace-settings-header").removeAttr("checked").on("click",function(){if(!this.checked){if($("#ace-settings-sidebar").get(0).checked){$("#ace-settings-sidebar").click()}}$(".navbar").toggleClass("navbar-fixed-top");$(document.body).toggleClass("navbar-fixed")});$("#ace-settings-sidebar").removeAttr("checked").on("click",function(){if(this.checked){if(!$("#ace-settings-header").get(0).checked){$("#ace-settings-header").click()}}else{if($("#ace-settings-breadcrumbs").get(0).checked){$("#ace-settings-breadcrumbs").click()}}$("#sidebar").toggleClass("fixed")});$("#ace-settings-breadcrumbs").removeAttr("checked").on("click",function(){if(this.checked){if(!$("#ace-settings-sidebar").get(0).checked){$("#ace-settings-sidebar").click()}}$("#breadcrumbs").toggleClass("fixed");$(document.body).toggleClass("breadcrumbs-fixed")});$("#ace-settings-rtl").removeAttr("checked").on("click",function(){switch_direction()});$("#btn-scroll-up").on(ace.click_event,function(){var a=Math.max(100,parseInt($("html").scrollTop()/3));$("html,body").animate({scrollTop:0},a);return false});$("#skin-colorpicker").ace_colorpicker().on("change",function(){var b=$(this).find("option:selected").data("class");var a=$(document.body);a.removeClass("skin-1 skin-2 skin-3");if(b!="default"){a.addClass(b)}if(b=="skin-1"){$(".ace-nav > li.grey").addClass("dark")}else{$(".ace-nav > li.grey").removeClass("dark")}if(b=="skin-2"){$(".ace-nav > li").addClass("no-border margin-1");$(".ace-nav > li:not(:last-child)").addClass("light-pink").find('> a > [class*="icon-"]').addClass("pink").end().eq(0).find(".badge").addClass("badge-warning")}else{$(".ace-nav > li").removeClass("no-border margin-1");$(".ace-nav > li:not(:last-child)").removeClass("light-pink").find('> a > [class*="icon-"]').removeClass("pink").end().eq(0).find(".badge").removeClass("badge-warning")}if(b=="skin-3"){$(".ace-nav > li.grey").addClass("red").find(".badge").addClass("badge-yellow")}else{$(".ace-nav > li.grey").removeClass("red").find(".badge").removeClass("badge-yellow")}})}function widget_boxes(){$(".page-content").delegate(".widget-toolbar > [data-action]","click",function(k){k.preventDefault();var j=$(this);var l=j.data("action");var a=j.closest(".widget-box");if(a.hasClass("ui-sortable-helper")){return}if(l=="collapse"){var d=a.find(".widget-body");var i=j.find("[class*=icon-]").eq(0);var e=i.attr("class").match(/icon\-(.*)\-(up|down)/);var b="icon-"+e[1]+"-down";var f="icon-"+e[1]+"-up";var h=d.find(".widget-body-inner");if(h.length==0){d=d.wrapInner('
').find(":first-child").eq(0)}else{d=h.eq(0)}var c=300;var g=200;if(a.hasClass("collapsed")){if(i){i.addClass(f).removeClass(b)}a.removeClass("collapsed");d.slideUp(0,function(){d.slideDown(c)})}else{if(i){i.addClass(b).removeClass(f)}d.slideUp(g,function(){a.addClass("collapsed")})}}else{if(l=="close"){var n=parseInt(j.data("close-speed"))||300;a.hide(n,function(){a.remove()})}else{if(l=="reload"){j.blur();var m=false;if(a.css("position")=="static"){m=true;a.addClass("position-relative")}a.append('
');setTimeout(function(){a.find(".widget-box-layer").remove();if(m){a.removeClass("position-relative")}},parseInt(Math.random()*1000+1000))}else{if(l=="settings"){}}}}})}function switch_direction(){var c=$(document.body);c.toggleClass("rtl").find(".dropdown-menu:not(.datepicker-dropdown,.colorpicker)").toggleClass("pull-right").end().find('.pull-right:not(.dropdown-menu,blockquote,.dropdown-submenu,.profile-skills .pull-right,.control-group .controls > [class*="span"]:first-child)').removeClass("pull-right").addClass("tmp-rtl-pull-right").end().find(".pull-left:not(.dropdown-submenu,.profile-skills .pull-left)").removeClass("pull-left").addClass("pull-right").end().find(".tmp-rtl-pull-right").removeClass("tmp-rtl-pull-right").addClass("pull-left").end().find(".chzn-container").toggleClass("chzn-rtl").end().find('.control-group .controls > [class*="span"]:first-child').toggleClass("pull-right").end();function a(g,f){c.find("."+g).removeClass(g).addClass("tmp-rtl-"+g).end().find("."+f).removeClass(f).addClass(g).end().find(".tmp-rtl-"+g).removeClass("tmp-rtl-"+g).addClass(f)}function b(g,f,h){h.each(function(){var j=$(this);var i=j.css(f);j.css(f,j.css(g));j.css(g,i)})}a("align-left","align-right");a("arrowed","arrowed-right");a("arrowed-in","arrowed-in-right");var d=$("#piechart-placeholder");if(d.size()>0){var e=$(document.body).hasClass("rtl")?"nw":"ne";d.data("draw").call(d.get(0),d,d.data("chart"),e)}};
\ No newline at end of file
diff --git a/template/js/bootbox.js b/template/js/bootbox.js
new file mode 100644
index 0000000..e78f8d7
--- /dev/null
+++ b/template/js/bootbox.js
@@ -0,0 +1,6 @@
+/**
+ * bootbox.js v3.3.0
+ *
+ * http://bootboxjs.com/license.txt
+ */
+var bootbox=window.bootbox||function(a,b){function c(a,b){return"undefined"==typeof b&&(b=d),"string"==typeof m[b][a]?m[b][a]:b!=e?c(a,e):a}var d="en",e="en",f=!0,g="static",h="javascript:;",i="",j={},k={},l={};l.setLocale=function(a){for(var b in m)if(b==a)return d=a,void 0;throw new Error("Invalid locale: "+a)},l.addLocale=function(a,b){"undefined"==typeof m[a]&&(m[a]={});for(var c in b)m[a][c]=b[c]},l.setIcons=function(a){k=a,("object"!=typeof k||null===k)&&(k={})},l.setBtnClasses=function(a){j=a,("object"!=typeof j||null===j)&&(j={})},l.alert=function(){var a="",b=c("OK"),d=null;switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"function"==typeof arguments[1]?d=arguments[1]:b=arguments[1];break;case 3:a=arguments[0],b=arguments[1],d=arguments[2];break;default:throw new Error("Incorrect number of arguments: expected 1-3")}return l.dialog(a,{label:b,icon:k.OK,"class":j.OK,callback:d},{onEscape:d||!0})},l.confirm=function(){var a="",b=c("CANCEL"),d=c("CONFIRM"),e=null;switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"function"==typeof arguments[1]?e=arguments[1]:b=arguments[1];break;case 3:a=arguments[0],b=arguments[1],"function"==typeof arguments[2]?e=arguments[2]:d=arguments[2];break;case 4:a=arguments[0],b=arguments[1],d=arguments[2],e=arguments[3];break;default:throw new Error("Incorrect number of arguments: expected 1-4")}var f=function(){return"function"==typeof e?e(!1):void 0},g=function(){return"function"==typeof e?e(!0):void 0};return l.dialog(a,[{label:b,icon:k.CANCEL,"class":j.CANCEL,callback:f},{label:d,icon:k.CONFIRM,"class":j.CONFIRM,callback:g}],{onEscape:f})},l.prompt=function(){var a="",d=c("CANCEL"),e=c("CONFIRM"),f=null,g="";switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"function"==typeof arguments[1]?f=arguments[1]:d=arguments[1];break;case 3:a=arguments[0],d=arguments[1],"function"==typeof arguments[2]?f=arguments[2]:e=arguments[2];break;case 4:a=arguments[0],d=arguments[1],e=arguments[2],f=arguments[3];break;case 5:a=arguments[0],d=arguments[1],e=arguments[2],f=arguments[3],g=arguments[4];break;default:throw new Error("Incorrect number of arguments: expected 1-5")}var h=a,i=b("
");i.append("
");var m=function(){return"function"==typeof f?f(null):void 0},n=function(){return"function"==typeof f?f(i.find("input[type=text]").val()):void 0},o=l.dialog(i,[{label:d,icon:k.CANCEL,"class":j.CANCEL,callback:m},{label:e,icon:k.CONFIRM,"class":j.CONFIRM,callback:n}],{header:h,show:!1,onEscape:m});return o.on("shown",function(){i.find("input[type=text]").focus(),i.on("submit",function(a){a.preventDefault(),o.find(".btn-primary").click()})}),o.modal("show"),o},l.dialog=function(c,d,e){function j(){var a=null;"function"==typeof e.onEscape&&(a=e.onEscape()),a!==!1&&x.modal("hide")}var k="",l=[];e||(e={}),"undefined"==typeof d?d=[]:"undefined"==typeof d.length&&(d=[d]);for(var m=d.length;m--;){var n=null,o=null,p=null,q="",r=null;if("undefined"==typeof d[m].label&&"undefined"==typeof d[m]["class"]&&"undefined"==typeof d[m].callback){var s=0,t=null;for(var u in d[m])if(t=u,++s>1)break;1==s&&"function"==typeof d[m][u]&&(d[m].label=t,d[m].callback=d[m][u])}"function"==typeof d[m].callback&&(r=d[m].callback),d[m]["class"]?p=d[m]["class"]:m==d.length-1&&d.length<=2&&(p="btn-primary"),d[m].link!==!0&&(p="btn "+p),n=d[m].label?d[m].label:"Option "+(m+1),d[m].icon&&(q="
"),o=d[m].href?d[m].href:h,k="
"+q+n+" "+k,l[m]=r}var v=["
"];if(e.header){var w="";("undefined"==typeof e.headerCloseButton||e.headerCloseButton)&&(w="
× "),v.push("")}v.push("
"),k&&v.push(""),v.push("
");var x=b(v.join("\n")),y="undefined"==typeof e.animate?f:e.animate;y&&x.addClass("fade");var z="undefined"==typeof e.classes?i:e.classes;return z&&x.addClass(z),x.find(".modal-body").html(c),x.on("keyup.dismiss.modal",function(a){27===a.which&&e.onEscape&&j("escape")}),x.on("click","a.close",function(a){a.preventDefault(),j("close")}),x.on("shown",function(){x.find("a.btn-primary:first").focus()}),x.on("hidden",function(a){a.target===this&&x.remove()}),x.on("click",".modal-footer a",function(a){var c=b(this).data("handler"),e=l[c],f=null;("undefined"==typeof c||"undefined"==typeof d[c].href)&&(a.preventDefault(),"function"==typeof e&&(f=e(a)),f!==!1&&x.modal("hide"))}),b("body").append(x),x.modal({backdrop:"undefined"==typeof e.backdrop?g:e.backdrop,keyboard:!1,show:!1}),x.on("show",function(){b(a).off("focusin.modal")}),("undefined"==typeof e.show||e.show===!0)&&x.modal("show"),x},l.modal=function(){var a,c,d,e={onEscape:null,keyboard:!0,backdrop:g};switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"object"==typeof arguments[1]?d=arguments[1]:c=arguments[1];break;case 3:a=arguments[0],c=arguments[1],d=arguments[2];break;default:throw new Error("Incorrect number of arguments: expected 1-3")}return e.header=c,d="object"==typeof d?b.extend(e,d):e,l.dialog(a,[],d)},l.hideAll=function(){b(".bootbox").modal("hide")},l.animate=function(a){f=a},l.backdrop=function(a){g=a},l.classes=function(a){i=a};var m={br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},zh_CN:{OK:"OK",CANCEL:"å–消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"å–消",CONFIRM:"確èª"}};return l}(document,window.jQuery);window.bootbox=bootbox;
\ No newline at end of file
diff --git a/template/js/bootstrap.js b/template/js/bootstrap.js
new file mode 100644
index 0000000..f9cbdae
--- /dev/null
+++ b/template/js/bootstrap.js
@@ -0,0 +1,6 @@
+/*!
+* Bootstrap.js by @fat & @mdo
+* Copyright 2012 Twitter, Inc.
+* http://www.apache.org/licenses/LICENSE-2.0.txt
+*/
+!function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(".dropdown-backdrop").remove(),e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||("ontouchstart"in document.documentElement&&e('
').insertBefore(e(this)).on("click",r),s.toggleClass("open")),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f
').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'
'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length
"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:' ',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);
\ No newline at end of file
diff --git a/template/js/chat.js b/template/js/chat.js
new file mode 100644
index 0000000..bd48bad
--- /dev/null
+++ b/template/js/chat.js
@@ -0,0 +1,552 @@
+$('#send').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ {
+ chat_sound('send');
+ $('#text').val('');
+ $('#login').val('')
+ document.getElementById("text").removeAttribute("style");
+ $('#img-input').html('');
+ $('#uploaded-files').html('');
+ $('#uploaded-files').css('display', 'none');
+ $('#frm').html($('#frm').html());;
+ chat_dialog(false);
+ }
+ });
+
+ loading(0);
+
+ return false;
+ }
+});
+
+function chat_readers(go)
+{
+ $.get(home+'chat/section/read/id/1',
+ function(readers)
+ {
+ $('#chat_readers').html(readers);
+ $('#chat_readers_modal').html(readers);
+
+ if(!go)
+ setTimeout(function() {chat_readers(false)}, 3000);
+ });
+}
+
+function chat_writers(now)
+{
+ write = '';
+
+ if(now)
+ write = '/write/1';
+
+ $.get(home+'chat/section/write/id/1'+write,
+ function(writers)
+ {
+ $('#chat_writers').html(writers);
+
+ if(!now)
+ setTimeout(function() {chat_writers(false)}, 3000);
+ });
+}
+
+function chat_msg_send()
+{
+ if($('#text').val() == '')
+ {
+ chat_sound('err');
+ return false;
+ }
+
+ loading(1);
+
+ return true;
+}
+
+function chat_msg_edit(id, type)
+{
+ if(type == 'edit')
+ {
+ $.get(home+'chat/section/edit/id/'+id, function(data){
+ bootbox.dialog('Выбранное сообщение '+data+'
Спойлер
Подсветка
Цитата
Ссылка
'+data+'
',
+ [{
+ "label" : "Продолжить",
+ callback : function(){chat_msg_edit(id, 'edit_go')},
+ },{
+ "label" : "Закрыть",
+ }]
+ );
+ });
+ }else{
+ $.ajax({
+ type: 'POST',
+ url: home+'chat/section/edit/go',
+ data: 'edit='+$('#edit').val()+'&id='+id,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Внимание! '+val,
+ [{
+ "label" : "OK",
+ }]
+ );
+
+ if(i == 's')
+ {
+ chat_dialog(false);
+ loading(0)
+ }
+
+ });
+ }
+ });
+ }
+}
+
+function chat_msg_del(id, user)
+{
+ bootbox.dialog('
Выберите операцию ',
+ [{
+ "label" : "Удалить выбранное сообщение",
+ "class" : "btn-small btn-primary",
+ callback: function(){
+ $('#msg_'+id).css('display', 'none');
+ chat_msg_del_go(id, '?id=1')
+ }
+ },{
+ "label" : "Удалить все отправленное сообщение",
+ "class" : "btn-small btn-primary",
+ callback: function(){
+ $('.user_'+user).css('display', 'none');
+ chat_msg_del_go(user, '?user=1')
+ }
+ },{
+ "label" : "Отмена",
+ "class" : "btn-small btn-primary"
+ }]
+ );
+
+ return false;
+}
+
+function chat_msg_del_go(id, type)
+{
+
+ $.getJSON(home+'chat/section/delete/id/'+id+'/go/'+type, function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('
Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ chat_dialog(false);
+ });
+ });
+
+ return false;
+}
+
+function chat_dialog(go)
+{
+ var dialog_form = document.getElementById('dialog');
+
+ $.get(home+'chat/section/dialog', function(data){
+ $('#dialog').html(data)
+
+ $('.spoiler').click(function(){
+ $(this).parent().children('div.spoiler_main').toggle(0);
+ });
+
+ $('pre code').each(function(i, block){
+ hljs.highlightBlock(block);
+ });
+ });
+ notice_load(true);
+
+ if(chat_scroll_ativate)
+ setTimeout(function() {dialog_form.scrollTop = dialog_form.scrollHeight;}, 300);
+
+ if(chat_notice_ativate)
+ chat_dialog_info();
+
+ if(go)
+ {
+ spoilers = $('.spoiler_main');
+ update = true;
+ for(var i = 0; i < spoilers.length; i++)
+ {
+ if(spoilers[i].style.display == 'block')
+ {
+ setTimeout(function() {chat_dialog(true)}, 4000);
+
+ return false;
+ }
+ }
+ setTimeout(function() {chat_dialog(true)}, 4000);
+ }
+}
+
+function notice_load(go)
+{
+ $.get(home+'chat/section/notice/check', function(data)
+ {
+ if(data == 0)
+ document.getElementById('notice_chat').className = 'notice_chat__check';
+ else
+ document.getElementById('notice_chat').className = '';
+
+ if(go)
+ notice_load(go);
+ });
+}
+
+function chat_dialog_info(go)
+{
+ $.get(home+'chat/section/dialog/go', function(data){
+ if(data != chat_dialog_id && data != '')
+ {
+ chat_dialog_id = data;
+ chat_sound('notice')
+ }
+ });
+}
+
+function reply_chat(login)
+{
+ $('#login').val(login);
+ $('#text').val(login+', '+$('#text').val());
+
+ return false;
+}
+
+function chat_scroll_change()
+{
+ if(chat_scroll_ativate)
+ {
+ chat_scroll_ativate = false;
+ $('#chat_scroll').html('включить скроллинг');
+ }else{
+ chat_scroll_ativate = true;
+ $('#chat_scroll').html('отключить скроллинг');
+ }
+}
+
+function chat_notice_change()
+{
+ if(chat_notice_ativate)
+ {
+ chat_notice_ativate = false;
+ $('#chat_notice').html('включить уведомления');
+ }else{
+ chat_notice_ativate = true;
+ $('#chat_notice').html('отключить уведомления');
+ }
+}
+
+function chat_sound_change()
+{
+ if(chat_sound_ativate)
+ {
+ chat_sound_ativate = false;
+ $('#chat_sound').html('включить звуки');
+ }else{
+ chat_sound_ativate = true;
+ $('#chat_sound').html('отключить звуки');
+ }
+}
+
+function chat_emoji(emoji)
+{
+ form = document.getElementById('text');
+ begin = form.value.substr(0, form.selectionStart);
+ end = form.value.substr(form.selectionEnd);
+ var text = form.firstChild;
+ form.value = begin+'[emoji_'+emoji+']'+end;
+ selPos = '[emoji_'+emoji+']'.length+begin.length;
+ form.setSelectionRange(begin.length, selPos);
+
+ return false;
+}
+
+function chat_sound(track)
+{
+ if(!chat_sound_ativate)
+ return false;
+
+ var audio = new Audio();
+ audio.preload = 'auto';
+ audio.src = '/template/sections/chat/sound/chat_'+track+'.wav';
+ audio.play();
+}
+
+function clearFileUpload(id)
+{
+ fileField = document.getElementById(id);
+ parentNod = fileField.parentNode;
+ tmpForm = document.createElement("form");
+ parentNod.replaceChild(tmpForm,fileField);
+ tmpForm.appendChild(fileField);
+ tmpForm.reset();
+ parentNod.replaceChild(fileField,tmpForm);
+}
+
+function bbcode(bbbegin, bbend, eID)
+{
+ form = document.getElementById(eID);
+ begin = form.value.substr(0, form.selectionStart);
+ end = form.value.substr(form.selectionEnd);
+ sel = form.value.substr(form.selectionStart, form.selectionEnd-form.selectionStart);
+ var text = form.firstChild;
+ form.value = begin+bbbegin+sel+bbend+end;
+ selPos = bbbegin.length+begin.length+sel.length+bbend.length;
+ form.setSelectionRange(begin.length, selPos);
+
+ return false;
+}
+
+jQuery.event.props.push('dataTransfer');
+
+var maxFiles = 8;
+var dataArray = [];
+
+$('#uploaded-files').hide();
+
+$('#drop-files').on('drop', function(e)
+{
+ var files = e.dataTransfer.files;
+
+ if(files.length <= maxFiles)
+ {
+ for(i = 0; i < files.length; i++)
+ {
+ if(files[i].size < (1024*1024*1))
+ loadInView(files[i]);
+ else
+ bootbox.dialog('
Внимание! Слишком большой файл. Максимально 1024Kb.',
+ [{
+ "label" : "Хорошо"
+ }]
+ );
+ }
+ }else{
+ bootbox.dialog('
Внимание! Нельзя загружать больше '+maxFiles+' изображений.',
+ [{
+ "label" : "Хорошо"
+ }]
+ );
+ files.length = 0;
+ }
+
+ return false;
+});
+
+$('#img').on('change', function()
+{
+ var files = $(this)[0].files;
+
+ if(files.length <= maxFiles)
+ {
+ for(i = 0; i < files.length; i++)
+ {
+ if(files[i].size < (1024*1024*1))
+ loadInView(files[i]);
+ else
+ bootbox.dialog('
Внимание! Слишком большой файл. Максимально 1024Kb.',
+ [{
+ "label" : "Хорошо"
+ }]
+ );
+ }
+ }else{
+ // swal('Внимание!', 'Нельзя загружать больше '+maxFiles+' изображений.', 'warning');
+ bootbox.dialog('
Внимание! Нельзя загружать больше '+maxFiles+' изображений.',
+ [{
+ "label" : "Хорошо"
+ }]
+ );
+ files.length = 0;
+ }
+
+ clearFileUpload('img');
+});
+
+var upload = 0;
+var files = 0;
+
+function loadInView(file)
+{
+ if(files < 0)
+ files = 0;
+
+ $('#uploaded-holder').css('display', 'inline-block');
+
+ if(!file.type.match('image.*'))
+ {
+ $('#drop-files p').html('Файл не является изображением.');
+
+ return false;
+ }
+
+ files = files+1;
+
+ if(files <= maxFiles)
+ $('#upload-button').css({'display' : 'block'});
+ else{
+ bootbox.dialog('
Внимание! Нельзя загружать больше '+maxFiles+' изображений.',
+ [{
+ "label" : "Хорошо"
+ }]
+ );
+
+ files = files-1;
+
+ return false;
+ }
+
+ var fileReader = new FileReader();
+
+ fileReader.onload = (function(file){
+ return function(e){
+ dataArray.push({name : file.name, value : this.result, check : null, sel : null});
+ addImage((dataArray.length-1));
+ };
+ })(file);
+
+ fileReader.readAsDataURL(file);
+
+ return false;
+}
+
+function delImage(id)
+{
+ $(this).empty();
+ dataArray.splice(id, 1);
+ $('#dropped-files > .img-block').remove();
+ addImage(-1);
+
+ return false;
+}
+
+function addImage(ind)
+{
+ if(ind < 0 )
+ {
+ start = 0;
+ end = dataArray.length;
+ files = files-1;
+ }else{
+ start = ind;
+ end = ind+1;
+ }
+
+ if(dataArray.length == 0)
+ {
+ $('#upload-button').hide();
+ $('#uploaded-holder').hide();
+ }
+
+ for(i = start; i < end; i++)
+ if($('#dropped-files > .img-block').length <= maxFiles)
+ $('#dropped-files').append('
');
+
+ return false;
+}
+
+function restartFiles(go)
+{
+ $('#loading-bar .loading-color').css({'width' : '0%'});
+ $('#loading').css({'display' : 'none'});
+ $('#loading-content').html(' ');
+ $('#upload-button').hide();
+ $('#dropped-files > .img-block').remove();
+ $('#uploaded-holder').hide();
+ dataArray.length = 0;
+ if(go != 1)
+ files = upload;
+
+ return false;
+}
+
+$('#dropped-files #upload-button .delete').click(restartFiles);
+
+$('#upload-button .upload').click(function()
+{
+ $("#loading").show();
+
+ var totalPercent = 100 / dataArray.length;
+ var x = 0;
+
+ $('#loading-content').html('Загружен '+dataArray[0].name);
+
+ $.each(dataArray, function(index, file)
+ {
+ upload = upload + 1;
+
+ $.post(home+'chat/section/upload', dataArray[index], function(data)
+ {
+ var fileName = dataArray[index].name;
+ ++x;
+
+ $('#loading-bar .loading-color').css({'width' : totalPercent*(x)+'%'});
+
+ if(totalPercent*(x) == 100)
+ {
+ $('#loading-content').html('Загрузка завершена.');
+ setTimeout(restartFiles(1), 1000);
+ }else if(totalPercent*(x) < 100)
+ $('#loading-content').html('Загружается '+fileName);
+
+ var dataSplit = data.split(':');
+
+ if(dataSplit[1] == 'ok')
+ {
+ $('#uploaded-files').append('
');
+ $('#img-input').append('
');
+ }else
+ $('#uploaded-files').append(data);
+ });
+
+ if(upload == maxFiles)
+ $('#upload_block').hide();
+ });
+
+ $('#uploaded-files').show();
+
+ return false;
+});
+
+$('#drop-files').on('dragenter', function()
+{
+ $(this).css({'border-color' : '#74b084'});
+
+ return false;
+});
+
+$('#drop-files').on('drop', function()
+{
+ $(this).css({'border-color' : '#dcdcdc'});
+ files = files-1;
+
+ return false;
+});
\ No newline at end of file
diff --git a/template/js/code.js b/template/js/code.js
new file mode 100644
index 0000000..28cb0e4
--- /dev/null
+++ b/template/js/code.js
@@ -0,0 +1 @@
+!function(e){"undefined"!=typeof exports?e(exports):(window.hljs=e({}),"function"==typeof define&&define.amd&&define("hljs",[],function(){return window.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&").replace(//gm,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){return/no-?highlight|plain|text/.test(e)}function i(e){var n,t,r,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=/\blang(?:uage)?-([\w-]+)\b/.exec(i))return E(t[1])?t[1]:"no-highlight";for(i=i.split(/\s+/),n=0,r=i.length;r>n;n++)if(E(i[n])||a(i[n]))return i[n]}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3==i.nodeType?a+=i.nodeValue.length:1==i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset"}function u(e){f+=""+t(e)+">"}function c(e){("start"==e.event?o:u)(e.node)}for(var s=0,f="",l=[];e.length||r.length;){var g=i();if(f+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){l.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g==e&&g.length&&g[0].offset==s);l.reverse().forEach(o)}else"start"==g[0].event?l.push(g[0].node):l.pop(),c(g.splice(0,1)[0])}return f+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):Object.keys(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\b\w+\b/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var f=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=f.length?t(f.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){for(var t=0;t";return i+=e+'">',i+n+o}function p(){if(!L.k)return n(B);var e="",t=0;L.lR.lastIndex=0;for(var r=L.lR.exec(B);r;){e+=n(B.substr(t,r.index-t));var a=g(L,r);a?(y+=a[1],e+=h(a[0],n(r[0]))):e+=n(r[0]),t=L.lR.lastIndex,r=L.lR.exec(B)}return e+n(B.substr(t))}function d(){if(L.sL&&!x[L.sL])return n(B);var e=L.sL?f(L.sL,B,!0,M[L.sL]):l(B);return L.r>0&&(y+=e.r),"continuous"==L.subLanguageMode&&(M[L.sL]=e.top),h(e.language,e.value,!1,!0)}function b(){return void 0!==L.sL?d():p()}function v(e,t){var r=e.cN?h(e.cN,"",!0):"";e.rB?(k+=r,B=""):e.eB?(k+=n(t)+r,B=""):(k+=r,B=t),L=Object.create(e,{parent:{value:L}})}function m(e,t){if(B+=e,void 0===t)return k+=b(),0;var r=o(t,L);if(r)return k+=b(),v(r,t),r.rB?0:t.length;var a=u(L,t);if(a){var i=L;i.rE||i.eE||(B+=t),k+=b();do L.cN&&(k+=""),y+=L.r,L=L.parent;while(L!=a.parent);return i.eE&&(k+=n(t)),B="",a.starts&&v(a.starts,""),i.rE?0:t.length}if(c(t,L))throw new Error('Illegal lexeme "'+t+'" for mode "'+(L.cN||"")+'"');return B+=t,t.length||1}var N=E(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,L=i||N,M={},k="";for(R=L;R!=N;R=R.parent)R.cN&&(k=h(R.cN,"",!0)+k);var B="",y=0;try{for(var C,j,I=0;;){if(L.t.lastIndex=I,C=L.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}for(m(t.substr(I)),R=L;R.parent;R=R.parent)R.cN&&(k+="");return{r:y,value:k,language:e,top:L}}catch(O){if(-1!=O.message.indexOf("Illegal"))return{r:0,value:n(t)};throw O}}function l(e,t){t=t||w.languages||Object.keys(x);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(E(n)){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function g(e){return w.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,w.tabReplace)})),w.useBR&&(e=e.replace(/\n/g," ")),e}function h(e,n,t){var r=n?R[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n=i(e);if(!a(n)){var t;w.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/ /g,"\n")):t=e;var r=t.textContent,o=n?f(n,r,!0):l(r),s=u(t);if(s.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=o.value,o.value=c(s,u(p),r)}o.value=g(o.value),e.innerHTML=o.value,e.className=h(e.className,n,o.language),e.result={language:o.language,re:o.r},o.second_best&&(e.second_best={language:o.second_best.language,re:o.second_best.r})}}function d(e){w=o(w,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){R[e]=n})}function N(){return Object.keys(x)}function E(e){return x[e]||x[R[e]]}var w={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},x={},R={};return e.highlight=f,e.highlightAuto=l,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=E,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="\\b(0[xX][a-fA-F0-9]+|(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",bK:"TODO FIXME NOTE BUG XXX",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("javascript",function(e){return{aliases:["js"],k:{keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},c:[{cN:"pi",r:10,b:/^\s*['"]use (strict|asm)['"]/},e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE,{cN:"subst",b:"\\$\\{",e:"\\}"}]},e.CLCM,e.CBCM,{cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/,e:/>\s*[);\]]/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:[e.CLCM,e.CBCM],i:/["'\(]/}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+e.IR,r:0},{bK:"import",e:"[;$]",k:"import from as",c:[e.ASM,e.QSM]},{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]}]}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",a={cN:"function",b:c+"\\(",rB:!0,eE:!0,e:"\\("},r={cN:"rule",b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{cN:"value",eW:!0,eE:!0,c:[a,e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"hexcolor",b:"#[0-9A-Fa-f]+"},{cN:"important",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,r,{cN:"id",b:/\#[A-Za-z0-9_-]+/},{cN:"class",b:/\.[A-Za-z0-9_-]+/},{cN:"attr_selector",b:/\[/,e:/\]/,i:"$"},{cN:"pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"']+/},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[a,e.ASM,e.QSM,e.CSSNM]}]},{cN:"tag",b:c,r:0},{cN:"rules",b:"{",e:"}",i:/\S/,c:[e.CBCM,r]}]}});hljs.registerLanguage("cpp",function(t){var e={cN:"keyword",b:"[a-z\\d_]*_t"},r={keyword:"false int float while private char catch export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using true class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept nullptr static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong",built_in:"std string cin cout cerr clog stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf"};return{aliases:["c","cc","h","c++","h++","hpp"],k:r,i:"",c:[e,t.CLCM,t.CBCM,{cN:"string",v:[t.inherit(t.QSM,{b:'((u8?|U)|L)?"'}),{b:'(u8?|U)?R"',e:'"',c:[t.BE]},{b:"'\\\\?.",e:"'",i:"."}]},{cN:"number",b:"\\b(\\d+(\\.\\d*)?|\\.\\d+)(u|U|l|L|ul|UL|f|F)"},t.CNM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line pragma",c:[{b:/\\\n/,r:0},{b:'include\\s*[<"]',e:'[>"]',k:"include",i:"\\n"},t.CLCM]},{b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:r,c:["self",e]},{b:t.IR+"::",k:r},{bK:"new throw return else",r:0},{cN:"function",b:"("+t.IR+"\\s+)+"+t.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:r,c:[{b:t.IR+"\\s*\\(",rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:r,r:0,c:[t.CBCM]},t.CLCM,t.CBCM]}]}});hljs.registerLanguage("sql",function(e){var t=e.C("--","$");return{cI:!0,i:/[<>]/,c:[{cN:"operator",bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate savepoint release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke",e:/;/,eW:!0,k:{keyword:"abs absolute acos action add adddate addtime aes_decrypt aes_encrypt after aggregate all allocate alter analyze and any are as asc ascii asin assertion at atan atan2 atn2 authorization authors avg backup before begin benchmark between bin binlog bit_and bit_count bit_length bit_or bit_xor both by cache call cascade cascaded case cast catalog ceil ceiling chain change changed char_length character_length charindex charset check checksum checksum_agg choose close coalesce coercibility collate collation collationproperty column columns columns_updated commit compress concat concat_ws concurrent connect connection connection_id consistent constraint constraints continue contributors conv convert convert_tz corresponding cos cot count count_big crc32 create cross cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime data database databases datalength date_add date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts datetimeoffsetfromparts day dayname dayofmonth dayofweek dayofyear deallocate declare decode default deferrable deferred degrees delayed delete des_decrypt des_encrypt des_key_file desc describe descriptor diagnostics difference disconnect distinct distinctrow div do domain double drop dumpfile each else elt enclosed encode encrypt end end-exec engine engines eomonth errors escape escaped event eventdata events except exception exec execute exists exp explain export_set extended external extract fast fetch field fields find_in_set first first_value floor flush for force foreign format found found_rows from from_base64 from_days from_unixtime full function get get_format get_lock getdate getutcdate global go goto grant grants greatest group group_concat grouping grouping_id gtid_subset gtid_subtract handler having help hex high_priority hosts hour ident_current ident_incr ident_seed identified identity if ifnull ignore iif ilike immediate in index indicator inet6_aton inet6_ntoa inet_aton inet_ntoa infile initially inner innodb input insert install instr intersect into is is_free_lock is_ipv4 is_ipv4_compat is_ipv4_mapped is_not is_not_null is_used_lock isdate isnull isolation join key kill language last last_day last_insert_id last_value lcase lead leading least leaves left len lenght level like limit lines ln load load_file local localtime localtimestamp locate lock log log10 log2 logfile logs low_priority lower lpad ltrim make_set makedate maketime master master_pos_wait match matched max md5 medium merge microsecond mid min minute mod mode module month monthname mutex name_const names national natural nchar next no no_write_to_binlog not now nullif nvarchar oct octet_length of old_password on only open optimize option optionally or ord order outer outfile output pad parse partial partition password patindex percent_rank percentile_cont percentile_disc period_add period_diff pi plugin position pow power pragma precision prepare preserve primary prior privileges procedure procedure_analyze processlist profile profiles public publishingservername purge quarter query quick quote quotename radians rand read references regexp relative relaylog release release_lock rename repair repeat replace replicate reset restore restrict return returns reverse revoke right rlike rollback rollup round row row_count rows rpad rtrim savepoint schema scroll sec_to_time second section select serializable server session session_user set sha sha1 sha2 share show sign sin size slave sleep smalldatetimefromparts snapshot some soname soundex sounds_like space sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sql_variant_property sqlstate sqrt square start starting status std stddev stddev_pop stddev_samp stdev stdevp stop str str_to_date straight_join strcmp string stuff subdate substr substring subtime subtring_index sum switchoffset sysdate sysdatetime sysdatetimeoffset system_user sysutcdatetime table tables tablespace tan temporary terminated tertiary_weights then time time_format time_to_sec timediff timefromparts timestamp timestampadd timestampdiff timezone_hour timezone_minute to to_base64 to_days to_seconds todatetimeoffset trailing transaction translation trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse ucase uncompress uncompressed_length unhex unicode uninstall union unique unix_timestamp unknown unlock update upgrade upped upper usage use user user_resources using utc_date utc_time utc_timestamp uuid uuid_short validate_password_strength value values var var_pop var_samp variables variance varp version view warnings week weekday weekofyear weight_string when whenever where with work write xml xor year yearweek zon",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int integer interval number numeric real serial smallint varchar varying int8 serial8 text"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}});hljs.registerLanguage("xml",function(t){var e="[A-Za-z0-9\\._:-]+",s={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php",subLanguageMode:"continuous"},c={eW:!0,i:/,r:0,c:[s,{cN:"attribute",b:e,r:0},{b:"=",r:0,c:[{cN:"value",c:[s],v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s\/>]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:!0,c:[{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},t.C("",{r:10}),{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"",rE:!0,sL:"css"}},{cN:"tag",b:"",rE:!0,sL:""}},s,{cN:"pi",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"?",e:"/?>",c:[{cN:"title",b:/[^ \/><\n\t]+/,r:0},c]}]}});hljs.registerLanguage("php",function(e){var c={cN:"variable",b:"\\$+[a-zA-Z_-Гї][a-zA-Z0-9_-Гї]*"},a={cN:"preprocessor",b:/<\?(php)?|\?>/},i={cN:"string",c:[e.BE,a],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},n={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.CLCM,e.HCM,e.C("/\\*","\\*/",{c:[{cN:"doctag",b:"@[A-Za-z]+"},a]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[e.BE]},a,c,{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",c,e.CBCM,i,n]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},i,n]}});
\ No newline at end of file
diff --git a/template/js/date.js b/template/js/date.js
new file mode 100644
index 0000000..07a4285
--- /dev/null
+++ b/template/js/date.js
@@ -0,0 +1 @@
+!function(d){function f(){return new Date(Date.UTC.apply(Date,arguments))}function b(){var g=new Date();return f(g.getUTCFullYear(),g.getUTCMonth(),g.getUTCDate())}var a=function(h,g){var i=this;this.element=d(h);this.language=g.language||this.element.data("date-language")||"en";this.language=this.language in e?this.language:this.language.split("-")[0];this.language=this.language in e?this.language:"en";this.isRTL=e[this.language].rtl||false;this.format=c.parseFormat(g.format||this.element.data("date-format")||e[this.language].format||"mm/dd/yyyy");this.isInline=false;this.isInput=this.element.is("input");this.component=this.element.is(".date")?this.element.find(".add-on, .btn"):false;this.hasInput=this.component&&this.element.find("input").length;if(this.component&&this.component.length===0){this.component=false}this._attachEvents();this.forceParse=true;if("forceParse" in g){this.forceParse=g.forceParse}else{if("dateForceParse" in this.element.data()){this.forceParse=this.element.data("date-force-parse")}}this.picker=d(c.template).appendTo(this.isInline?this.element:"body").on({click:d.proxy(this.click,this),mousedown:d.proxy(this.mousedown,this)});if(this.isInline){this.picker.addClass("datepicker-inline")}else{this.picker.addClass("datepicker-dropdown dropdown-menu")}if(this.isRTL){this.picker.addClass("datepicker-rtl");this.picker.find(".prev i, .next i").toggleClass("fa-angle-left fa-angle-right")}d(document).on("mousedown",function(j){if(d(j.target).closest(".datepicker.datepicker-inline, .datepicker.datepicker-dropdown").length===0){i.hide()}});this.autoclose=false;if("autoclose" in g){this.autoclose=g.autoclose}else{if("dateAutoclose" in this.element.data()){this.autoclose=this.element.data("date-autoclose")}}this.keyboardNavigation=true;if("keyboardNavigation" in g){this.keyboardNavigation=g.keyboardNavigation}else{if("dateKeyboardNavigation" in this.element.data()){this.keyboardNavigation=this.element.data("date-keyboard-navigation")}}this.viewMode=this.startViewMode=0;switch(g.startView||this.element.data("date-start-view")){case 2:case"decade":this.viewMode=this.startViewMode=2;break;case 1:case"year":this.viewMode=this.startViewMode=1;break}this.minViewMode=g.minViewMode||this.element.data("date-min-view-mode")||0;if(typeof this.minViewMode==="string"){switch(this.minViewMode){case"months":this.minViewMode=1;break;case"years":this.minViewMode=2;break;default:this.minViewMode=0;break}}this.viewMode=this.startViewMode=Math.max(this.startViewMode,this.minViewMode);this.todayBtn=(g.todayBtn||this.element.data("date-today-btn")||false);this.todayHighlight=(g.todayHighlight||this.element.data("date-today-highlight")||false);this.calendarWeeks=false;if("calendarWeeks" in g){this.calendarWeeks=g.calendarWeeks}else{if("dateCalendarWeeks" in this.element.data()){this.calendarWeeks=this.element.data("date-calendar-weeks")}}if(this.calendarWeeks){this.picker.find("tfoot th.today").attr("colspan",function(j,k){return parseInt(k)+1})}this.weekStart=((g.weekStart||this.element.data("date-weekstart")||e[this.language].weekStart||0)%7);this.weekEnd=((this.weekStart+6)%7);this.startDate=-Infinity;this.endDate=Infinity;this.daysOfWeekDisabled=[];this.setStartDate(g.startDate||this.element.data("date-startdate"));this.setEndDate(g.endDate||this.element.data("date-enddate"));this.setDaysOfWeekDisabled(g.daysOfWeekDisabled||this.element.data("date-days-of-week-disabled"));this.fillDow();this.fillMonths();this.update();this.showMode();if(this.isInline){this.show()}};a.prototype={constructor:a,_events:[],_attachEvents:function(){this._detachEvents();if(this.isInput){this._events=[[this.element,{focus:d.proxy(this.show,this),keyup:d.proxy(this.update,this),keydown:d.proxy(this.keydown,this)}]]}else{if(this.component&&this.hasInput){this._events=[[this.element.find("input"),{focus:d.proxy(this.show,this),keyup:d.proxy(this.update,this),keydown:d.proxy(this.keydown,this)}],[this.component,{click:d.proxy(this.show,this)}]]}else{if(this.element.is("div")){this.isInline=true}else{this._events=[[this.element,{click:d.proxy(this.show,this)}]]}}}for(var g=0,h,j;gthis.endDate){this.viewDate=new Date(this.endDate)}else{this.viewDate=new Date(this.date)}}this.fill()},fillDow:function(){var h=this.weekStart,i="";if(this.calendarWeeks){var g=' ';i+=g;this.picker.find(".datepicker-days thead tr:first-child").prepend(g)}while(h'+e[this.language].daysMin[(h++)%7]+""}i+=" ";this.picker.find(".datepicker-days thead").append(i)},fillMonths:function(){var h="",g=0;while(g<12){h+=''+e[this.language].monthsShort[g++]+" "}this.picker.find(".datepicker-months td").html(h)},fill:function(){var y=new Date(this.viewDate),p=y.getUTCFullYear(),z=y.getUTCMonth(),s=this.startDate!==-Infinity?this.startDate.getUTCFullYear():-Infinity,w=this.startDate!==-Infinity?this.startDate.getUTCMonth():-Infinity,m=this.endDate!==Infinity?this.endDate.getUTCFullYear():Infinity,t=this.endDate!==Infinity?this.endDate.getUTCMonth():Infinity,n=this.date&&this.date.valueOf(),x=new Date();this.picker.find(".datepicker-days thead th.switch").text(e[this.language].months[z]+" "+p);this.picker.find("tfoot th.today").text(e[this.language].today).toggle(this.todayBtn!==false);this.updateNavArrows();this.fillMonths();var B=f(p,z-1,28,0,0,0,0),v=c.getDaysInMonth(B.getUTCFullYear(),B.getUTCMonth());B.setUTCDate(v);B.setUTCDate(v-(B.getUTCDay()-this.weekStart+7)%7);var g=new Date(B);g.setUTCDate(g.getUTCDate()+42);g=g.valueOf();var o=[];var r;while(B.valueOf()");if(this.calendarWeeks){var h=new Date(+B+(this.weekStart-B.getUTCDay()-7)%7*86400000),k=new Date(+h+(7+4-h.getUTCDay())%7*86400000),j=new Date(+(j=f(k.getUTCFullYear(),0,1))+(7+4-j.getUTCDay())%7*86400000),q=(k-j)/86400000/7+1;o.push(''+q+" ")}}r="";if(B.getUTCFullYear()p||(B.getUTCFullYear()==p&&B.getUTCMonth()>z)){r+=" new"}}if(this.todayHighlight&&B.getUTCFullYear()==x.getFullYear()&&B.getUTCMonth()==x.getMonth()&&B.getUTCDate()==x.getDate()){r+=" today"}if(n&&B.valueOf()==n){r+=" active"}if(B.valueOf()this.endDate||d.inArray(B.getUTCDay(),this.daysOfWeekDisabled)!==-1){r+=" disabled"}o.push(''+B.getUTCDate()+" ");if(B.getUTCDay()==this.weekEnd){o.push("")}B.setUTCDate(B.getUTCDate()+1)}this.picker.find(".datepicker-days tbody").empty().append(o.join(""));var C=this.date&&this.date.getUTCFullYear();var l=this.picker.find(".datepicker-months").find("th:eq(1)").text(p).end().find("span").removeClass("active");if(C&&C==p){l.eq(this.date.getUTCMonth()).addClass("active")}if(pm){l.addClass("disabled")}if(p==s){l.slice(0,w).addClass("disabled")}if(p==m){l.slice(t+1).addClass("disabled")}o="";p=parseInt(p/10,10)*10;var A=this.picker.find(".datepicker-years").find("th:eq(1)").text(p+"-"+(p+9)).end().find("td");p-=1;for(var u=-1;u<11;u++){o+='m?" disabled":"")+'">'+p+" ";p+=1}A.html(o)},updateNavArrows:function(){var i=new Date(this.viewDate),g=i.getUTCFullYear(),h=i.getUTCMonth();switch(this.viewMode){case 0:if(this.startDate!==-Infinity&&g<=this.startDate.getUTCFullYear()&&h<=this.startDate.getUTCMonth()){this.picker.find(".prev").css({visibility:"hidden"})}else{this.picker.find(".prev").css({visibility:"visible"})}if(this.endDate!==Infinity&&g>=this.endDate.getUTCFullYear()&&h>=this.endDate.getUTCMonth()){this.picker.find(".next").css({visibility:"hidden"})}else{this.picker.find(".next").css({visibility:"visible"})}break;case 1:case 2:if(this.startDate!==-Infinity&&g<=this.startDate.getUTCFullYear()){this.picker.find(".prev").css({visibility:"hidden"})}else{this.picker.find(".prev").css({visibility:"visible"})}if(this.endDate!==Infinity&&g>=this.endDate.getUTCFullYear()){this.picker.find(".next").css({visibility:"hidden"})}else{this.picker.find(".next").css({visibility:"visible"})}break}},click:function(m){m.preventDefault();var l=d(m.target).closest("span, td, th");if(l.length==1){switch(l[0].nodeName.toLowerCase()){case"th":switch(l[0].className){case"switch":this.showMode(1);break;case"prev":case"next":var i=c.modes[this.viewMode].navStep*(l[0].className=="prev"?-1:1);switch(this.viewMode){case 0:this.viewDate=this.moveMonth(this.viewDate,i);break;case 1:case 2:this.viewDate=this.moveYear(this.viewDate,i);break}this.fill();break;case"today":var h=new Date();h=f(h.getFullYear(),h.getMonth(),h.getDate(),0,0,0);this.showMode(-2);var n=this.todayBtn=="linked"?null:"view";this._setDate(h,n);break}break;case"span":if(!l.is(".disabled")){this.viewDate.setUTCDate(1);if(l.is(".month")){var g=1;var k=l.parent().find("span").index(l);var j=this.viewDate.getUTCFullYear();this.viewDate.setUTCMonth(k);this.element.trigger({type:"changeMonth",date:this.viewDate});if(this.minViewMode==1){this._setDate(f(j,k,g,0,0,0,0))}}else{var j=parseInt(l.text(),10)||0;var g=1;var k=0;this.viewDate.setUTCFullYear(j);this.element.trigger({type:"changeYear",date:this.viewDate});if(this.minViewMode==2){this._setDate(f(j,k,g,0,0,0,0))}}this.showMode(-1);this.fill()}break;case"td":if(l.is(".day")&&!l.is(".disabled")){var g=parseInt(l.text(),10)||1;var j=this.viewDate.getUTCFullYear(),k=this.viewDate.getUTCMonth();if(l.is(".old")){if(k===0){k=11;j-=1}else{k-=1}}else{if(l.is(".new")){if(k==11){k=0;j+=1}else{k+=1}}}this._setDate(f(j,k,g,0,0,0,0))}break}}},_setDate:function(g,i){if(!i||i=="date"){this.date=g}if(!i||i=="view"){this.viewDate=g}this.fill();this.setValue();this.element.trigger({type:"changeDate",date:this.date});var h;if(this.isInput){h=this.element}else{if(this.component){h=this.element.find("input")}}if(h){h.change();if(this.autoclose&&(!i||i=="date")){this.hide()}}},moveMonth:function(g,h){if(!h){return g}var l=new Date(g.valueOf()),p=l.getUTCDate(),m=l.getUTCMonth(),k=Math.abs(h),o,n;h=h>0?1:-1;if(k==1){n=h==-1?function(){return l.getUTCMonth()==m}:function(){return l.getUTCMonth()!=o};o=m+h;l.setUTCMonth(o);if(o<0||o>11){o=(o+12)%12}}else{for(var j=0;j=this.startDate&&g<=this.endDate},keydown:function(n){if(this.picker.is(":not(:visible)")){if(n.keyCode==27){this.show()}return}var j=false,i,h,m,g,l;switch(n.keyCode){case 27:this.hide();n.preventDefault();break;case 37:case 39:if(!this.keyboardNavigation){break}i=n.keyCode==37?-1:1;if(n.ctrlKey){g=this.moveYear(this.date,i);l=this.moveYear(this.viewDate,i)}else{if(n.shiftKey){g=this.moveMonth(this.date,i);l=this.moveMonth(this.viewDate,i)}else{g=new Date(this.date);g.setUTCDate(this.date.getUTCDate()+i);l=new Date(this.viewDate);l.setUTCDate(this.viewDate.getUTCDate()+i)}}if(this.dateWithinRange(g)){this.date=g;this.viewDate=l;this.setValue();this.update();n.preventDefault();j=true}break;case 38:case 40:if(!this.keyboardNavigation){break}i=n.keyCode==38?-1:1;if(n.ctrlKey){g=this.moveYear(this.date,i);l=this.moveYear(this.viewDate,i)}else{if(n.shiftKey){g=this.moveMonth(this.date,i);l=this.moveMonth(this.viewDate,i)}else{g=new Date(this.date);g.setUTCDate(this.date.getUTCDate()+i*7);l=new Date(this.viewDate);l.setUTCDate(this.viewDate.getUTCDate()+i*7)}}if(this.dateWithinRange(g)){this.date=g;this.viewDate=l;this.setValue();this.update();n.preventDefault();j=true}break;case 13:this.hide();n.preventDefault();break;case 9:this.hide();break}if(j){this.element.trigger({type:"changeDate",date:this.date});var k;if(this.isInput){k=this.element}else{if(this.component){k=this.element.find("input")}}if(k){k.change()}}},showMode:function(g){if(g){this.viewMode=Math.max(this.minViewMode,Math.min(2,this.viewMode+g))}this.picker.find(">div").hide().filter(".datepicker-"+c.modes[this.viewMode].clsName).css("display","block");this.updateNavArrows()}};d.fn.datepicker=function(h){var g=Array.apply(null,arguments);g.shift();return this.each(function(){var k=d(this),j=k.data("datepicker"),i=typeof h=="object"&&h;if(!j){k.data("datepicker",(j=new a(this,d.extend({},d.fn.datepicker.defaults,i))))}if(typeof h=="string"&&typeof j[h]=="function"){j[h].apply(j,g)}})};d.fn.datepicker.defaults={};d.fn.datepicker.Constructor=a;var e=d.fn.datepicker.dates={en:{days:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота","Воскресенье"],daysShort:["Вс","Пн","Вт","Ср","Чт","Пт","Сб","Вс"],daysMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб","Вс"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Сегодня"}};var c={modes:[{clsName:"days",navFnc:"Month",navStep:1},{clsName:"months",navFnc:"FullYear",navStep:1},{clsName:"years",navFnc:"FullYear",navStep:10}],isLeapYear:function(g){return(((g%4===0)&&(g%100!==0))||(g%400===0))},getDaysInMonth:function(g,h){return[31,(c.isLeapYear(g)?29:28),31,30,31,30,31,31,30,31,30,31][h]},validParts:/dd?|DD?|mm?|MM?|yy(?:yy)?/g,nonpunctuation:/[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,parseFormat:function(i){var g=i.replace(this.validParts,"\0").split("\0"),h=i.match(this.validParts);if(!g||!g.length||!h||h.length===0){throw new Error("Invalid date format.")}return{separators:g,parts:h}},parseDate:function(k,u,n){if(k instanceof Date){return k}if(/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(k)){var w=/([\-+]\d+)([dmwy])/,m=k.match(/([\-+]\d+)([dmwy])/g),g,l;k=new Date();for(var o=0;o ',contTemplate:' ',footTemplate:' '};c.template=''+c.headTemplate+" "+c.footTemplate+'
'+c.headTemplate+c.contTemplate+c.footTemplate+'
'+c.headTemplate+c.contTemplate+c.footTemplate+"
";d.fn.datepicker.DPGlobal=c}(window.jQuery);
\ No newline at end of file
diff --git a/template/js/form.js b/template/js/form.js
new file mode 100644
index 0000000..c67fc33
--- /dev/null
+++ b/template/js/form.js
@@ -0,0 +1,1277 @@
+/*!
+ * jQuery Form Plugin
+ * version: 3.51.0-2014.06.20
+ * Requires jQuery v1.5 or later
+ * Copyright (c) 2014 M. Alsup
+ * Examples and documentation at: http://malsup.com/jquery/form/
+ * Project repository: https://github.com/malsup/form
+ * Dual licensed under the MIT and GPL licenses.
+ * https://github.com/malsup/form#copyright-and-license
+ */
+/*global ActiveXObject */
+
+// AMD support
+(function (factory) {
+ "use strict";
+ if (typeof define === 'function' && define.amd) {
+ // using AMD; register as anon module
+ define(['jquery'], factory);
+ } else {
+ // no AMD; invoke directly
+ factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
+ }
+}
+
+(function($) {
+"use strict";
+
+/*
+ Usage Note:
+ -----------
+ Do not use both ajaxSubmit and ajaxForm on the same form. These
+ functions are mutually exclusive. Use ajaxSubmit if you want
+ to bind your own submit handler to the form. For example,
+
+ $(document).ready(function() {
+ $('#myForm').on('submit', function(e) {
+ e.preventDefault(); // <-- important
+ $(this).ajaxSubmit({
+ target: '#output'
+ });
+ });
+ });
+
+ Use ajaxForm when you want the plugin to manage all the event binding
+ for you. For example,
+
+ $(document).ready(function() {
+ $('#myForm').ajaxForm({
+ target: '#output'
+ });
+ });
+
+ You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
+ form does not have to exist when you invoke ajaxForm:
+
+ $('#myForm').ajaxForm({
+ delegation: true,
+ target: '#output'
+ });
+
+ When using ajaxForm, the ajaxSubmit function will be invoked for you
+ at the appropriate time.
+*/
+
+/**
+ * Feature detection
+ */
+var feature = {};
+feature.fileapi = $(" ").get(0).files !== undefined;
+feature.formdata = window.FormData !== undefined;
+
+var hasProp = !!$.fn.prop;
+
+// attr2 uses prop when it can but checks the return type for
+// an expected string. this accounts for the case where a form
+// contains inputs with names like "action" or "method"; in those
+// cases "prop" returns the element
+$.fn.attr2 = function() {
+ if ( ! hasProp ) {
+ return this.attr.apply(this, arguments);
+ }
+ var val = this.prop.apply(this, arguments);
+ if ( ( val && val.jquery ) || typeof val === 'string' ) {
+ return val;
+ }
+ return this.attr.apply(this, arguments);
+};
+
+/**
+ * ajaxSubmit() provides a mechanism for immediately submitting
+ * an HTML form using AJAX.
+ */
+$.fn.ajaxSubmit = function(options) {
+ /*jshint scripturl:true */
+
+ // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
+ if (!this.length) {
+ log('ajaxSubmit: skipping submit process - no element selected');
+ return this;
+ }
+
+ var method, action, url, $form = this;
+
+ if (typeof options == 'function') {
+ options = { success: options };
+ }
+ else if ( options === undefined ) {
+ options = {};
+ }
+
+ method = options.type || this.attr2('method');
+ action = options.url || this.attr2('action');
+
+ url = (typeof action === 'string') ? $.trim(action) : '';
+ url = url || window.location.href || '';
+ if (url) {
+ // clean url (don't include hash vaue)
+ url = (url.match(/^([^#]+)/)||[])[1];
+ }
+
+ options = $.extend(true, {
+ url: url,
+ success: $.ajaxSettings.success,
+ type: method || $.ajaxSettings.type,
+ iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
+ }, options);
+
+ // hook for manipulating the form data before it is extracted;
+ // convenient for use with rich editors like tinyMCE or FCKEditor
+ var veto = {};
+ this.trigger('form-pre-serialize', [this, options, veto]);
+ if (veto.veto) {
+ log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
+ return this;
+ }
+
+ // provide opportunity to alter form data before it is serialized
+ if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
+ log('ajaxSubmit: submit aborted via beforeSerialize callback');
+ return this;
+ }
+
+ var traditional = options.traditional;
+ if ( traditional === undefined ) {
+ traditional = $.ajaxSettings.traditional;
+ }
+
+ var elements = [];
+ var qx, a = this.formToArray(options.semantic, elements);
+ if (options.data) {
+ options.extraData = options.data;
+ qx = $.param(options.data, traditional);
+ }
+
+ // give pre-submit callback an opportunity to abort the submit
+ if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
+ log('ajaxSubmit: submit aborted via beforeSubmit callback');
+ return this;
+ }
+
+ // fire vetoable 'validate' event
+ this.trigger('form-submit-validate', [a, this, options, veto]);
+ if (veto.veto) {
+ log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
+ return this;
+ }
+
+ var q = $.param(a, traditional);
+ if (qx) {
+ q = ( q ? (q + '&' + qx) : qx );
+ }
+ if (options.type.toUpperCase() == 'GET') {
+ options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
+ options.data = null; // data is null for 'get'
+ }
+ else {
+ options.data = q; // data is the query string for 'post'
+ }
+
+ var callbacks = [];
+ if (options.resetForm) {
+ callbacks.push(function() { $form.resetForm(); });
+ }
+ if (options.clearForm) {
+ callbacks.push(function() { $form.clearForm(options.includeHidden); });
+ }
+
+ // perform a load on the target only if dataType is not provided
+ if (!options.dataType && options.target) {
+ var oldSuccess = options.success || function(){};
+ callbacks.push(function(data) {
+ var fn = options.replaceTarget ? 'replaceWith' : 'html';
+ $(options.target)[fn](data).each(oldSuccess, arguments);
+ });
+ }
+ else if (options.success) {
+ callbacks.push(options.success);
+ }
+
+ options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
+ var context = options.context || this ; // jQuery 1.4+ supports scope context
+ for (var i=0, max=callbacks.length; i < max; i++) {
+ callbacks[i].apply(context, [data, status, xhr || $form, $form]);
+ }
+ };
+
+ if (options.error) {
+ var oldError = options.error;
+ options.error = function(xhr, status, error) {
+ var context = options.context || this;
+ oldError.apply(context, [xhr, status, error, $form]);
+ };
+ }
+
+ if (options.complete) {
+ var oldComplete = options.complete;
+ options.complete = function(xhr, status) {
+ var context = options.context || this;
+ oldComplete.apply(context, [xhr, status, $form]);
+ };
+ }
+
+ // are there files to upload?
+
+ // [value] (issue #113), also see comment:
+ // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
+ var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; });
+
+ var hasFileInputs = fileInputs.length > 0;
+ var mp = 'multipart/form-data';
+ var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
+
+ var fileAPI = feature.fileapi && feature.formdata;
+ log("fileAPI :" + fileAPI);
+ var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
+
+ var jqxhr;
+
+ // options.iframe allows user to force iframe mode
+ // 06-NOV-09: now defaulting to iframe mode if file input is detected
+ if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
+ // hack to fix Safari hang (thanks to Tim Molendijk for this)
+ // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
+ if (options.closeKeepAlive) {
+ $.get(options.closeKeepAlive, function() {
+ jqxhr = fileUploadIframe(a);
+ });
+ }
+ else {
+ jqxhr = fileUploadIframe(a);
+ }
+ }
+ else if ((hasFileInputs || multipart) && fileAPI) {
+ jqxhr = fileUploadXhr(a);
+ }
+ else {
+ jqxhr = $.ajax(options);
+ }
+
+ $form.removeData('jqxhr').data('jqxhr', jqxhr);
+
+ // clear element array
+ for (var k=0; k < elements.length; k++) {
+ elements[k] = null;
+ }
+
+ // fire 'notify' event
+ this.trigger('form-submit-notify', [this, options]);
+ return this;
+
+ // utility fn for deep serialization
+ function deepSerialize(extraData){
+ var serialized = $.param(extraData, options.traditional).split('&');
+ var len = serialized.length;
+ var result = [];
+ var i, part;
+ for (i=0; i < len; i++) {
+ // #252; undo param space replacement
+ serialized[i] = serialized[i].replace(/\+/g,' ');
+ part = serialized[i].split('=');
+ // #278; use array instead of object storage, favoring array serializations
+ result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
+ }
+ return result;
+ }
+
+ // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
+ function fileUploadXhr(a) {
+ var formdata = new FormData();
+
+ for (var i=0; i < a.length; i++) {
+ formdata.append(a[i].name, a[i].value);
+ }
+
+ if (options.extraData) {
+ var serializedData = deepSerialize(options.extraData);
+ for (i=0; i < serializedData.length; i++) {
+ if (serializedData[i]) {
+ formdata.append(serializedData[i][0], serializedData[i][1]);
+ }
+ }
+ }
+
+ options.data = null;
+
+ var s = $.extend(true, {}, $.ajaxSettings, options, {
+ contentType: false,
+ processData: false,
+ cache: false,
+ type: method || 'POST'
+ });
+
+ if (options.uploadProgress) {
+ // workaround because jqXHR does not expose upload property
+ s.xhr = function() {
+ var xhr = $.ajaxSettings.xhr();
+ if (xhr.upload) {
+ xhr.upload.addEventListener('progress', function(event) {
+ var percent = 0;
+ var position = event.loaded || event.position; /*event.position is deprecated*/
+ var total = event.total;
+ if (event.lengthComputable) {
+ percent = Math.ceil(position / total * 100);
+ }
+ options.uploadProgress(event, position, total, percent);
+ }, false);
+ }
+ return xhr;
+ };
+ }
+
+ s.data = null;
+ var beforeSend = s.beforeSend;
+ s.beforeSend = function(xhr, o) {
+ //Send FormData() provided by user
+ if (options.formData) {
+ o.data = options.formData;
+ }
+ else {
+ o.data = formdata;
+ }
+ if(beforeSend) {
+ beforeSend.call(this, xhr, o);
+ }
+ };
+ return $.ajax(s);
+ }
+
+ // private function for handling file uploads (hat tip to YAHOO!)
+ function fileUploadIframe(a) {
+ var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
+ var deferred = $.Deferred();
+
+ // #341
+ deferred.abort = function(status) {
+ xhr.abort(status);
+ };
+
+ if (a) {
+ // ensure that every serialized input is still enabled
+ for (i=0; i < elements.length; i++) {
+ el = $(elements[i]);
+ if ( hasProp ) {
+ el.prop('disabled', false);
+ }
+ else {
+ el.removeAttr('disabled');
+ }
+ }
+ }
+
+ s = $.extend(true, {}, $.ajaxSettings, options);
+ s.context = s.context || s;
+ id = 'jqFormIO' + (new Date().getTime());
+ if (s.iframeTarget) {
+ $io = $(s.iframeTarget);
+ n = $io.attr2('name');
+ if (!n) {
+ $io.attr2('name', id);
+ }
+ else {
+ id = n;
+ }
+ }
+ else {
+ $io = $('');
+ $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
+ }
+ io = $io[0];
+
+
+ xhr = { // mock object
+ aborted: 0,
+ responseText: null,
+ responseXML: null,
+ status: 0,
+ statusText: 'n/a',
+ getAllResponseHeaders: function() {},
+ getResponseHeader: function() {},
+ setRequestHeader: function() {},
+ abort: function(status) {
+ var e = (status === 'timeout' ? 'timeout' : 'aborted');
+ log('aborting upload... ' + e);
+ this.aborted = 1;
+
+ try { // #214, #257
+ if (io.contentWindow.document.execCommand) {
+ io.contentWindow.document.execCommand('Stop');
+ }
+ }
+ catch(ignore) {}
+
+ $io.attr('src', s.iframeSrc); // abort op in progress
+ xhr.error = e;
+ if (s.error) {
+ s.error.call(s.context, xhr, e, status);
+ }
+ if (g) {
+ $.event.trigger("ajaxError", [xhr, s, e]);
+ }
+ if (s.complete) {
+ s.complete.call(s.context, xhr, e);
+ }
+ }
+ };
+
+ g = s.global;
+ // trigger ajax global events so that activity/block indicators work like normal
+ if (g && 0 === $.active++) {
+ $.event.trigger("ajaxStart");
+ }
+ if (g) {
+ $.event.trigger("ajaxSend", [xhr, s]);
+ }
+
+ if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
+ if (s.global) {
+ $.active--;
+ }
+ deferred.reject();
+ return deferred;
+ }
+ if (xhr.aborted) {
+ deferred.reject();
+ return deferred;
+ }
+
+ // add submitting element to data if we know it
+ sub = form.clk;
+ if (sub) {
+ n = sub.name;
+ if (n && !sub.disabled) {
+ s.extraData = s.extraData || {};
+ s.extraData[n] = sub.value;
+ if (sub.type == "image") {
+ s.extraData[n+'.x'] = form.clk_x;
+ s.extraData[n+'.y'] = form.clk_y;
+ }
+ }
+ }
+
+ var CLIENT_TIMEOUT_ABORT = 1;
+ var SERVER_ABORT = 2;
+
+ function getDoc(frame) {
+ /* it looks like contentWindow or contentDocument do not
+ * carry the protocol property in ie8, when running under ssl
+ * frame.document is the only valid response document, since
+ * the protocol is know but not on the other two objects. strange?
+ * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
+ */
+
+ var doc = null;
+
+ // IE8 cascading access check
+ try {
+ if (frame.contentWindow) {
+ doc = frame.contentWindow.document;
+ }
+ } catch(err) {
+ // IE8 access denied under ssl & missing protocol
+ log('cannot get iframe.contentWindow document: ' + err);
+ }
+
+ if (doc) { // successful getting content
+ return doc;
+ }
+
+ try { // simply checking may throw in ie8 under ssl or mismatched protocol
+ doc = frame.contentDocument ? frame.contentDocument : frame.document;
+ } catch(err) {
+ // last attempt
+ log('cannot get iframe.contentDocument: ' + err);
+ doc = frame.document;
+ }
+ return doc;
+ }
+
+ // Rails CSRF hack (thanks to Yvan Barthelemy)
+ var csrf_token = $('meta[name=csrf-token]').attr('content');
+ var csrf_param = $('meta[name=csrf-param]').attr('content');
+ if (csrf_param && csrf_token) {
+ s.extraData = s.extraData || {};
+ s.extraData[csrf_param] = csrf_token;
+ }
+
+ // take a breath so that pending repaints get some cpu time before the upload starts
+ function doSubmit() {
+ // make sure form attrs are set
+ var t = $form.attr2('target'),
+ a = $form.attr2('action'),
+ mp = 'multipart/form-data',
+ et = $form.attr('enctype') || $form.attr('encoding') || mp;
+
+ // update form attrs in IE friendly way
+ form.setAttribute('target',id);
+ if (!method || /post/i.test(method) ) {
+ form.setAttribute('method', 'POST');
+ }
+ if (a != s.url) {
+ form.setAttribute('action', s.url);
+ }
+
+ // ie borks in some cases when setting encoding
+ if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
+ $form.attr({
+ encoding: 'multipart/form-data',
+ enctype: 'multipart/form-data'
+ });
+ }
+
+ // support timout
+ if (s.timeout) {
+ timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
+ }
+
+ // look for server aborts
+ function checkState() {
+ try {
+ var state = getDoc(io).readyState;
+ log('state = ' + state);
+ if (state && state.toLowerCase() == 'uninitialized') {
+ setTimeout(checkState,50);
+ }
+ }
+ catch(e) {
+ log('Server abort: ' , e, ' (', e.name, ')');
+ cb(SERVER_ABORT);
+ if (timeoutHandle) {
+ clearTimeout(timeoutHandle);
+ }
+ timeoutHandle = undefined;
+ }
+ }
+
+ // add "extra" data to form if provided in options
+ var extraInputs = [];
+ try {
+ if (s.extraData) {
+ for (var n in s.extraData) {
+ if (s.extraData.hasOwnProperty(n)) {
+ // if using the $.param format that allows for multiple values with the same name
+ if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
+ extraInputs.push(
+ $(' ').val(s.extraData[n].value)
+ .appendTo(form)[0]);
+ } else {
+ extraInputs.push(
+ $(' ').val(s.extraData[n])
+ .appendTo(form)[0]);
+ }
+ }
+ }
+ }
+
+ if (!s.iframeTarget) {
+ // add iframe to doc and submit the form
+ $io.appendTo('body');
+ }
+ if (io.attachEvent) {
+ io.attachEvent('onload', cb);
+ }
+ else {
+ io.addEventListener('load', cb, false);
+ }
+ setTimeout(checkState,15);
+
+ try {
+ form.submit();
+ } catch(err) {
+ // just in case form has element with name/id of 'submit'
+ var submitFn = document.createElement('form').submit;
+ submitFn.apply(form);
+ }
+ }
+ finally {
+ // reset attrs and remove "extra" input elements
+ form.setAttribute('action',a);
+ form.setAttribute('enctype', et); // #380
+ if(t) {
+ form.setAttribute('target', t);
+ } else {
+ $form.removeAttr('target');
+ }
+ $(extraInputs).remove();
+ }
+ }
+
+ if (s.forceSync) {
+ doSubmit();
+ }
+ else {
+ setTimeout(doSubmit, 10); // this lets dom updates render
+ }
+
+ var data, doc, domCheckCount = 50, callbackProcessed;
+
+ function cb(e) {
+ if (xhr.aborted || callbackProcessed) {
+ return;
+ }
+
+ doc = getDoc(io);
+ if(!doc) {
+ log('cannot access response document');
+ e = SERVER_ABORT;
+ }
+ if (e === CLIENT_TIMEOUT_ABORT && xhr) {
+ xhr.abort('timeout');
+ deferred.reject(xhr, 'timeout');
+ return;
+ }
+ else if (e == SERVER_ABORT && xhr) {
+ xhr.abort('server abort');
+ deferred.reject(xhr, 'error', 'server abort');
+ return;
+ }
+
+ if (!doc || doc.location.href == s.iframeSrc) {
+ // response not received yet
+ if (!timedOut) {
+ return;
+ }
+ }
+ if (io.detachEvent) {
+ io.detachEvent('onload', cb);
+ }
+ else {
+ io.removeEventListener('load', cb, false);
+ }
+
+ var status = 'success', errMsg;
+ try {
+ if (timedOut) {
+ throw 'timeout';
+ }
+
+ var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
+ log('isXml='+isXml);
+ if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
+ if (--domCheckCount) {
+ // in some browsers (Opera) the iframe DOM is not always traversable when
+ // the onload callback fires, so we loop a bit to accommodate
+ log('requeing onLoad callback, DOM not available');
+ setTimeout(cb, 250);
+ return;
+ }
+ // let this fall through because server response could be an empty document
+ //log('Could not access iframe DOM after mutiple tries.');
+ //throw 'DOMException: not available';
+ }
+
+ //log('response detected');
+ var docRoot = doc.body ? doc.body : doc.documentElement;
+ xhr.responseText = docRoot ? docRoot.innerHTML : null;
+ xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
+ if (isXml) {
+ s.dataType = 'xml';
+ }
+ xhr.getResponseHeader = function(header){
+ var headers = {'content-type': s.dataType};
+ return headers[header.toLowerCase()];
+ };
+ // support for XHR 'status' & 'statusText' emulation :
+ if (docRoot) {
+ xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
+ xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
+ }
+
+ var dt = (s.dataType || '').toLowerCase();
+ var scr = /(json|script|text)/.test(dt);
+ if (scr || s.textarea) {
+ // see if user embedded response in textarea
+ var ta = doc.getElementsByTagName('textarea')[0];
+ if (ta) {
+ xhr.responseText = ta.value;
+ // support for XHR 'status' & 'statusText' emulation :
+ xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
+ xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
+ }
+ else if (scr) {
+ // account for browsers injecting pre around json response
+ var pre = doc.getElementsByTagName('pre')[0];
+ var b = doc.getElementsByTagName('body')[0];
+ if (pre) {
+ xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
+ }
+ else if (b) {
+ xhr.responseText = b.textContent ? b.textContent : b.innerText;
+ }
+ }
+ }
+ else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
+ xhr.responseXML = toXml(xhr.responseText);
+ }
+
+ try {
+ data = httpData(xhr, dt, s);
+ }
+ catch (err) {
+ status = 'parsererror';
+ xhr.error = errMsg = (err || status);
+ }
+ }
+ catch (err) {
+ log('error caught: ',err);
+ status = 'error';
+ xhr.error = errMsg = (err || status);
+ }
+
+ if (xhr.aborted) {
+ log('upload aborted');
+ status = null;
+ }
+
+ if (xhr.status) { // we've set xhr.status
+ status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
+ }
+
+ // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
+ if (status === 'success') {
+ if (s.success) {
+ s.success.call(s.context, data, 'success', xhr);
+ }
+ deferred.resolve(xhr.responseText, 'success', xhr);
+ if (g) {
+ $.event.trigger("ajaxSuccess", [xhr, s]);
+ }
+ }
+ else if (status) {
+ if (errMsg === undefined) {
+ errMsg = xhr.statusText;
+ }
+ if (s.error) {
+ s.error.call(s.context, xhr, status, errMsg);
+ }
+ deferred.reject(xhr, 'error', errMsg);
+ if (g) {
+ $.event.trigger("ajaxError", [xhr, s, errMsg]);
+ }
+ }
+
+ if (g) {
+ $.event.trigger("ajaxComplete", [xhr, s]);
+ }
+
+ if (g && ! --$.active) {
+ $.event.trigger("ajaxStop");
+ }
+
+ if (s.complete) {
+ s.complete.call(s.context, xhr, status);
+ }
+
+ callbackProcessed = true;
+ if (s.timeout) {
+ clearTimeout(timeoutHandle);
+ }
+
+ // clean up
+ setTimeout(function() {
+ if (!s.iframeTarget) {
+ $io.remove();
+ }
+ else { //adding else to clean up existing iframe response.
+ $io.attr('src', s.iframeSrc);
+ }
+ xhr.responseXML = null;
+ }, 100);
+ }
+
+ var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
+ if (window.ActiveXObject) {
+ doc = new ActiveXObject('Microsoft.XMLDOM');
+ doc.async = 'false';
+ doc.loadXML(s);
+ }
+ else {
+ doc = (new DOMParser()).parseFromString(s, 'text/xml');
+ }
+ return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
+ };
+ var parseJSON = $.parseJSON || function(s) {
+ /*jslint evil:true */
+ return window['eval']('(' + s + ')');
+ };
+
+ var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
+
+ var ct = xhr.getResponseHeader('content-type') || '',
+ xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if (xml && data.documentElement.nodeName === 'parsererror') {
+ if ($.error) {
+ $.error('parsererror');
+ }
+ }
+ if (s && s.dataFilter) {
+ data = s.dataFilter(data, type);
+ }
+ if (typeof data === 'string') {
+ if (type === 'json' || !type && ct.indexOf('json') >= 0) {
+ data = parseJSON(data);
+ } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
+ $.globalEval(data);
+ }
+ }
+ return data;
+ };
+
+ return deferred;
+ }
+};
+
+/**
+ * ajaxForm() provides a mechanism for fully automating form submission.
+ *
+ * The advantages of using this method instead of ajaxSubmit() are:
+ *
+ * 1: This method will include coordinates for elements (if the element
+ * is used to submit the form).
+ * 2. This method will include the submit element's name/value data (for the element that was
+ * used to submit the form).
+ * 3. This method binds the submit() method to the form for you.
+ *
+ * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
+ * passes the options argument along after properly binding events for submit elements and
+ * the form itself.
+ */
+$.fn.ajaxForm = function(options) {
+ options = options || {};
+ options.delegation = options.delegation && $.isFunction($.fn.on);
+
+ // in jQuery 1.3+ we can fix mistakes with the ready state
+ if (!options.delegation && this.length === 0) {
+ var o = { s: this.selector, c: this.context };
+ if (!$.isReady && o.s) {
+ log('DOM not ready, queuing ajaxForm');
+ $(function() {
+ $(o.s,o.c).ajaxForm(options);
+ });
+ return this;
+ }
+ // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
+ log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
+ return this;
+ }
+
+ if ( options.delegation ) {
+ $(document)
+ .off('submit.form-plugin', this.selector, doAjaxSubmit)
+ .off('click.form-plugin', this.selector, captureSubmittingElement)
+ .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
+ .on('click.form-plugin', this.selector, options, captureSubmittingElement);
+ return this;
+ }
+
+ return this.ajaxFormUnbind()
+ .bind('submit.form-plugin', options, doAjaxSubmit)
+ .bind('click.form-plugin', options, captureSubmittingElement);
+};
+
+// private event handlers
+function doAjaxSubmit(e) {
+ /*jshint validthis:true */
+ var options = e.data;
+ if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
+ e.preventDefault();
+ $(e.target).ajaxSubmit(options); // #365
+ }
+}
+
+function captureSubmittingElement(e) {
+ /*jshint validthis:true */
+ var target = e.target;
+ var $el = $(target);
+ if (!($el.is("[type=submit],[type=image]"))) {
+ // is this a child element of the submit el? (ex: a span within a button)
+ var t = $el.closest('[type=submit]');
+ if (t.length === 0) {
+ return;
+ }
+ target = t[0];
+ }
+ var form = this;
+ form.clk = target;
+ if (target.type == 'image') {
+ if (e.offsetX !== undefined) {
+ form.clk_x = e.offsetX;
+ form.clk_y = e.offsetY;
+ } else if (typeof $.fn.offset == 'function') {
+ var offset = $el.offset();
+ form.clk_x = e.pageX - offset.left;
+ form.clk_y = e.pageY - offset.top;
+ } else {
+ form.clk_x = e.pageX - target.offsetLeft;
+ form.clk_y = e.pageY - target.offsetTop;
+ }
+ }
+ // clear form vars
+ setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
+}
+
+
+// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
+$.fn.ajaxFormUnbind = function() {
+ return this.unbind('submit.form-plugin click.form-plugin');
+};
+
+/**
+ * formToArray() gathers form element data into an array of objects that can
+ * be passed to any of the following ajax functions: $.get, $.post, or load.
+ * Each object in the array has both a 'name' and 'value' property. An example of
+ * an array for a simple login form might be:
+ *
+ * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
+ *
+ * It is this array that is passed to pre-submit callback functions provided to the
+ * ajaxSubmit() and ajaxForm() methods.
+ */
+$.fn.formToArray = function(semantic, elements) {
+ var a = [];
+ if (this.length === 0) {
+ return a;
+ }
+
+ var form = this[0];
+ var formId = this.attr('id');
+ var els = semantic ? form.getElementsByTagName('*') : form.elements;
+ var els2;
+
+ if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
+ els = $(els).get(); // convert to standard array
+ }
+
+ // #386; account for inputs outside the form which use the 'form' attribute
+ if ( formId ) {
+ els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
+ if ( els2.length ) {
+ els = (els || []).concat(els2);
+ }
+ }
+
+ if (!els || !els.length) {
+ return a;
+ }
+
+ var i,j,n,v,el,max,jmax;
+ for(i=0, max=els.length; i < max; i++) {
+ el = els[i];
+ n = el.name;
+ if (!n || el.disabled) {
+ continue;
+ }
+
+ if (semantic && form.clk && el.type == "image") {
+ // handle image inputs on the fly when semantic == true
+ if(form.clk == el) {
+ a.push({name: n, value: $(el).val(), type: el.type });
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
+ }
+ continue;
+ }
+
+ v = $.fieldValue(el, true);
+ if (v && v.constructor == Array) {
+ if (elements) {
+ elements.push(el);
+ }
+ for(j=0, jmax=v.length; j < jmax; j++) {
+ a.push({name: n, value: v[j]});
+ }
+ }
+ else if (feature.fileapi && el.type == 'file') {
+ if (elements) {
+ elements.push(el);
+ }
+ var files = el.files;
+ if (files.length) {
+ for (j=0; j < files.length; j++) {
+ a.push({name: n, value: files[j], type: el.type});
+ }
+ }
+ else {
+ // #180
+ a.push({ name: n, value: '', type: el.type });
+ }
+ }
+ else if (v !== null && typeof v != 'undefined') {
+ if (elements) {
+ elements.push(el);
+ }
+ a.push({name: n, value: v, type: el.type, required: el.required});
+ }
+ }
+
+ if (!semantic && form.clk) {
+ // input type=='image' are not found in elements array! handle it here
+ var $input = $(form.clk), input = $input[0];
+ n = input.name;
+ if (n && !input.disabled && input.type == 'image') {
+ a.push({name: n, value: $input.val()});
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
+ }
+ }
+ return a;
+};
+
+/**
+ * Serializes form data into a 'submittable' string. This method will return a string
+ * in the format: name1=value1&name2=value2
+ */
+$.fn.formSerialize = function(semantic) {
+ //hand off to jQuery.param for proper encoding
+ return $.param(this.formToArray(semantic));
+};
+
+/**
+ * Serializes all field elements in the jQuery object into a query string.
+ * This method will return a string in the format: name1=value1&name2=value2
+ */
+$.fn.fieldSerialize = function(successful) {
+ var a = [];
+ this.each(function() {
+ var n = this.name;
+ if (!n) {
+ return;
+ }
+ var v = $.fieldValue(this, successful);
+ if (v && v.constructor == Array) {
+ for (var i=0,max=v.length; i < max; i++) {
+ a.push({name: n, value: v[i]});
+ }
+ }
+ else if (v !== null && typeof v != 'undefined') {
+ a.push({name: this.name, value: v});
+ }
+ });
+ //hand off to jQuery.param for proper encoding
+ return $.param(a);
+};
+
+/**
+ * Returns the value(s) of the element in the matched set. For example, consider the following form:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * var v = $('input[type=text]').fieldValue();
+ * // if no values are entered into the text inputs
+ * v == ['','']
+ * // if values entered into the text inputs are 'foo' and 'bar'
+ * v == ['foo','bar']
+ *
+ * var v = $('input[type=checkbox]').fieldValue();
+ * // if neither checkbox is checked
+ * v === undefined
+ * // if both checkboxes are checked
+ * v == ['B1', 'B2']
+ *
+ * var v = $('input[type=radio]').fieldValue();
+ * // if neither radio is checked
+ * v === undefined
+ * // if first radio is checked
+ * v == ['C1']
+ *
+ * The successful argument controls whether or not the field element must be 'successful'
+ * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
+ * The default value of the successful argument is true. If this value is false the value(s)
+ * for each element is returned.
+ *
+ * Note: This method *always* returns an array. If no valid value can be determined the
+ * array will be empty, otherwise it will contain one or more values.
+ */
+$.fn.fieldValue = function(successful) {
+ for (var val=[], i=0, max=this.length; i < max; i++) {
+ var el = this[i];
+ var v = $.fieldValue(el, successful);
+ if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
+ continue;
+ }
+ if (v.constructor == Array) {
+ $.merge(val, v);
+ }
+ else {
+ val.push(v);
+ }
+ }
+ return val;
+};
+
+/**
+ * Returns the value of the field element.
+ */
+$.fieldValue = function(el, successful) {
+ var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
+ if (successful === undefined) {
+ successful = true;
+ }
+
+ if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
+ (t == 'checkbox' || t == 'radio') && !el.checked ||
+ (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
+ tag == 'select' && el.selectedIndex == -1)) {
+ return null;
+ }
+
+ if (tag == 'select') {
+ var index = el.selectedIndex;
+ if (index < 0) {
+ return null;
+ }
+ var a = [], ops = el.options;
+ var one = (t == 'select-one');
+ var max = (one ? index+1 : ops.length);
+ for(var i=(one ? index : 0); i < max; i++) {
+ var op = ops[i];
+ if (op.selected) {
+ var v = op.value;
+ if (!v) { // extra pain for IE...
+ v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
+ }
+ if (one) {
+ return v;
+ }
+ a.push(v);
+ }
+ }
+ return a;
+ }
+ return $(el).val();
+};
+
+/**
+ * Clears the form data. Takes the following actions on the form's input fields:
+ * - input text fields will have their 'value' property set to the empty string
+ * - select elements will have their 'selectedIndex' property set to -1
+ * - checkbox and radio inputs will have their 'checked' property set to false
+ * - inputs of type submit, button, reset, and hidden will *not* be effected
+ * - button elements will *not* be effected
+ */
+$.fn.clearForm = function(includeHidden) {
+ return this.each(function() {
+ $('input,select,textarea', this).clearFields(includeHidden);
+ });
+};
+
+/**
+ * Clears the selected form elements.
+ */
+$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
+ var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
+ return this.each(function() {
+ var t = this.type, tag = this.tagName.toLowerCase();
+ if (re.test(t) || tag == 'textarea') {
+ this.value = '';
+ }
+ else if (t == 'checkbox' || t == 'radio') {
+ this.checked = false;
+ }
+ else if (tag == 'select') {
+ this.selectedIndex = -1;
+ }
+ else if (t == "file") {
+ if (/MSIE/.test(navigator.userAgent)) {
+ $(this).replaceWith($(this).clone(true));
+ } else {
+ $(this).val('');
+ }
+ }
+ else if (includeHidden) {
+ // includeHidden can be the value true, or it can be a selector string
+ // indicating a special test; for example:
+ // $('#myForm').clearForm('.special:hidden')
+ // the above would clean hidden inputs that have the class of 'special'
+ if ( (includeHidden === true && /hidden/.test(t)) ||
+ (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) {
+ this.value = '';
+ }
+ }
+ });
+};
+
+/**
+ * Resets the form data. Causes all form elements to be reset to their original value.
+ */
+$.fn.resetForm = function() {
+ return this.each(function() {
+ // guard against an input with the name of 'reset'
+ // note that IE reports the reset function as an 'object'
+ if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
+ this.reset();
+ }
+ });
+};
+
+/**
+ * Enables or disables any matching elements.
+ */
+$.fn.enable = function(b) {
+ if (b === undefined) {
+ b = true;
+ }
+ return this.each(function() {
+ this.disabled = !b;
+ });
+};
+
+/**
+ * Checks/unchecks any matching checkboxes or radio buttons and
+ * selects/deselects and matching option elements.
+ */
+$.fn.selected = function(select) {
+ if (select === undefined) {
+ select = true;
+ }
+ return this.each(function() {
+ var t = this.type;
+ if (t == 'checkbox' || t == 'radio') {
+ this.checked = select;
+ }
+ else if (this.tagName.toLowerCase() == 'option') {
+ var $sel = $(this).parent('select');
+ if (select && $sel[0] && $sel[0].type == 'select-one') {
+ // deselect all other options
+ $sel.find('option').selected(false);
+ }
+ this.selected = select;
+ }
+ });
+};
+
+// expose debug var
+$.fn.ajaxSubmit.debug = false;
+
+// helper fn for console logging
+function log() {
+ if (!$.fn.ajaxSubmit.debug) {
+ return;
+ }
+ var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
+ if (window.console && window.console.log) {
+ window.console.log(msg);
+ }
+ else if (window.opera && window.opera.postError) {
+ window.opera.postError(msg);
+ }
+}
+
+}));
\ No newline at end of file
diff --git a/template/js/functions.js b/template/js/functions.js
new file mode 100644
index 0000000..cf11ebe
--- /dev/null
+++ b/template/js/functions.js
@@ -0,0 +1,123 @@
+function loading(show)
+{
+ if(show)
+ $('#loadinginfo').css('display', 'block');
+ else
+ $('#loadinginfo').css('display', 'none');
+}
+
+function help_notice_check()
+{
+ $.getJSON(home+'help/section/notice/go',
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'empty')
+ {
+ hnt = false; help_notice_title(true);
+ $('#help_notice').css('display', 'none');
+ }
+
+ if(i == 'reply')
+ {
+ if(val)
+ {
+ if(val > getCookie('help') || getCookie('help') == undefined)
+ {
+ setCookie('help', val, {expires: 86400});
+ help_notice_sound();
+ }
+ }
+
+ $('#help_notice').css('display', 'inline-block');
+ }
+
+ if(i == 'notice')
+ {
+ help_notice_sound();
+ $('#help_notice').css('display', 'block');
+ $('#help_notice_block').css('display', 'block');
+ $('#help_notice_block').html(val);
+ }
+ });
+ });
+
+ setTimeout(function() {help_notice_check()}, 10000);
+}
+
+function help_notice_sound()
+{
+
+ hnt = true; help_notice_title();
+
+ var audio = new Audio();
+ audio.preload = 'auto';
+ audio.src = '/notice.wav';
+ audio.play();
+}
+
+function help_notice_title(stop)
+{
+ if(document.title == title && !stop)
+ document.title = 'Новое сообщение';
+ else
+ document.title = title;
+
+ if(hnt)
+ setTimeout(function() {help_notice_title()}, 1000);
+}
+
+function setCookie(name, value, options)
+{
+ options = options || {};
+
+ var expires = options.expires;
+
+ if(typeof expires == 'number' && expires)
+ {
+ var d = new Date();
+ d.setTime(d.getTime() + expires * 1000);
+ expires = options.expires = d;
+ }
+
+ if(expires && expires.toUTCString)
+ options.expires = expires.toUTCString();
+
+ value = encodeURIComponent(value);
+
+ var updatedCookie = name + '=' + value + '; path=/';
+
+ for (var propName in options)
+ {
+ updatedCookie += '; ' + propName;
+ var propValue = options[propName];
+
+ if(propValue !== true)
+ updatedCookie += '=' + propValue;
+ }
+
+ document.cookie = updatedCookie;
+}
+
+function getCookie(name)
+{
+ var matches = document.cookie.match(new RegExp(
+ "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
+ ));
+
+ return matches ? decodeURIComponent(matches[1]) : undefined;
+}
+
+function deleteCookie(name)
+{
+ setCookie(name, '', {expires: -1})
+}
+
+$(document).ready(function(){
+ $('.spoiler').click(function(){
+ $(this).parent().children('div.spoiler_main').toggle(0);
+ });
+
+ hljs.initHighlightingOnLoad();
+});
\ No newline at end of file
diff --git a/template/js/google-translate.js b/template/js/google-translate.js
new file mode 100644
index 0000000..3beff38
--- /dev/null
+++ b/template/js/google-translate.js
@@ -0,0 +1,55 @@
+const googleTranslateConfig = {
+ lang: "ru",
+ /* Если скрипт не работает на поддомене,
+ раскомментируйте и
+ укажите основной домен в свойстве domain */
+ /*domain: "enginegp.ru" */
+};
+
+function TranslateInit() {
+ let code = TranslateGetCode();
+ // Находим флаг с выбранным языком для перевода и добавляем к нему активный класс
+ $('[data-google-lang="' + code + '"]').addClass('language__img_active');
+
+ if (code == googleTranslateConfig.lang) {
+ // Если язык по умолчанию, совпадает с языком на который переводим
+ // То очищаем куки
+ TranslateCookieHandler(null, googleTranslateConfig.domain);
+ }
+
+ // Инициализируем виджет с языком по умолчанию
+ new google.translate.TranslateElement({
+ pageLanguage: googleTranslateConfig.lang,
+ });
+
+ // Вешаем событие клик на флаги
+ $('[data-google-lang]').click(function () {
+ TranslateCookieHandler("/auto/" + $(this).attr("data-google-lang"), googleTranslateConfig.domain);
+ // Перезагружаем страницу
+ window.location.reload();
+ });
+}
+
+function TranslateGetCode() {
+ // Если куки нет, то передаем дефолтный язык
+ let lang = ($.cookie('googtrans') != undefined && $.cookie('googtrans') != "null") ? $.cookie('googtrans') : googleTranslateConfig.lang;
+ return lang.match(/(?!^\/)[^\/]*$/gm)[0];
+}
+
+function TranslateCookieHandler(val, domain) {
+ // Записываем куки /язык_который_переводим/язык_на_который_переводим
+ $.cookie('googtrans', val);
+ $.cookie("googtrans", val, {
+ domain: "." + document.domain,
+ });
+
+ if (domain == "undefined") return;
+ // записываем куки для домена, если он назначен в конфиге
+ $.cookie("googtrans", val, {
+ domain: domain,
+ });
+
+ $.cookie("googtrans", val, {
+ domain: "." + domain,
+ });
+}
\ No newline at end of file
diff --git a/template/js/help.js b/template/js/help.js
new file mode 100644
index 0000000..9e112b4
--- /dev/null
+++ b/template/js/help.js
@@ -0,0 +1,504 @@
+$('#create').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ location.href=home+'help/section/dialog/id/'+val;
+ });
+
+ loading(0);
+
+ return false;
+ }
+});
+
+$('#reply').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 'с')
+ {
+ bootbox.dialog('Внимание '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ location.reload();
+ }
+
+ if(i == 'i')
+ {
+ bootbox.dialog('Внимание '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ dialog_update(false);
+ }
+
+ if(i == 's')
+ {
+ $('#text').val('');
+ $('#text').html('');
+ document.getElementById("text").removeAttribute("style");
+ $('#img-input').html('');
+ $('#uploaded-files').html('');
+ $('#uploaded-files').css('display', 'none');
+ $('#frm').html($('#frm').html());
+
+ dialog_update(false);
+ }
+ });
+
+ loading(0);
+
+ return false;
+ }
+});
+
+function help_open(id)
+{
+ loading(1);
+
+ $.getJSON(home+'help/section/close/action/open/id/'+id,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ location.href=home+'help/section/dialog/id/'+id;
+
+ loading(0)
+ });
+ });
+}
+
+function help_msg_del(id, msg)
+{
+ loading(1);
+
+ $.getJSON(home+'help/section/dialog/action/remove/id/'+id+'/msg/'+msg,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ dialog_update(false);
+
+ loading(0)
+ });
+ });
+
+ return false;
+}
+
+function help_close(id)
+{
+ loading(1);
+
+ $.getJSON(home+'help/section/open/action/close/id/'+id,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+
+ loading(0)
+ });
+ });
+}
+
+function help_readers()
+{
+ $.get(home+'help/section/dialog/action/read/id/'+help,
+ function(readers)
+ {
+ $('#help_readers').html(readers);
+
+ setTimeout(function() {help_readers()}, 9000);
+ });
+}
+
+function help_writers(now)
+{
+ write = '';
+
+ if($('#text').val() != '')
+ write = '/write/1';
+
+ $.get(home+'help/section/dialog/action/write/id/'+help+write,
+ function(writers)
+ {
+ $('#help_writers').html(writers);
+
+ if(!now)
+ setTimeout(function() {help_writers(false)}, 9000);
+ });
+}
+
+function dialog_update(go)
+{
+ if(go)
+ {
+ spoilers = $('.spoiler_main');
+ update = true;
+ for(var i = 0; i < spoilers.length; i++)
+ {
+ if(spoilers[i].style.display == 'block')
+ {
+ setTimeout(function() {dialog_update(true)}, 15000);
+
+ return false;
+ }
+ }
+ }
+
+ loading(1);
+
+ $.getJSON(home+'help/section/dialog/id/'+help+'/ajax',
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'dialog')
+ {
+ $('#dialog').html(val);
+
+ $('.spoiler').click(function(){
+ $(this).parent().children('div.spoiler_main').toggle(0);
+ });
+
+ $('pre code').each(function(i, block){
+ hljs.highlightBlock(block);
+ });
+ }
+
+ if(i == 'status')
+ $('#status').html(val);
+ });
+
+ loading(0);
+
+ if(go)
+ setTimeout(function() {dialog_update(true)}, 15000);
+ });
+}
+
+// Переустановка сервера (подтверждение)
+function help_delete(id)
+{
+ bootbox.dialog('Внимание Вы уверены, что хотите удалить этот вопрос?',
+ [{
+ "label" : "Подтвердить",
+ "class" : "btn-small btn-primary",
+ callback: function() {help_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+}
+
+function help_delete_go(id)
+{
+ loading(1);
+
+ $.getJSON(home+'help/section/open/action/delete/id/'+id,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+
+ loading(0)
+ });
+ });
+}
+
+function clearFileUpload(id)
+{
+ fileField = document.getElementById(id);
+ parentNod = fileField.parentNode;
+ tmpForm = document.createElement("form");
+ parentNod.replaceChild(tmpForm,fileField);
+ tmpForm.appendChild(fileField);
+ tmpForm.reset();
+ parentNod.replaceChild(fileField,tmpForm);
+}
+
+function bbcode(bbbegin, bbend)
+{
+ form = document.getElementById('text');
+ begin = form.value.substr(0, form.selectionStart);
+ end = form.value.substr(form.selectionEnd);
+ sel = form.value.substr(form.selectionStart, form.selectionEnd-form.selectionStart);
+ var text = form.firstChild;
+ form.value = begin+bbbegin+sel+bbend+end;
+ selPos = bbbegin.length+begin.length+sel.length+bbend.length;
+ form.setSelectionRange(begin.length, selPos);
+
+ return false;
+}
+
+jQuery.event.props.push('dataTransfer');
+
+var maxFiles = 8;
+var dataArray = [];
+
+$('#uploaded-files').hide();
+
+$('#drop-files').on('drop', function(e)
+{
+ var files = e.dataTransfer.files;
+
+ if(files.length <= maxFiles)
+ {
+ for(i = 0; i < files.length; i++)
+ {
+ if(files[i].size < (1024*1024*1))
+ loadInView(files[i]);
+ else
+ alert('Слишком большой файл. Максимально 1024Kb.');
+ }
+ }else{
+ alert('Нельзя загружать больше '+maxFiles+' изображений.');
+ files.length = 0;
+ }
+
+ return false;
+});
+
+$('#img').on('change', function()
+{
+ var files = $(this)[0].files;
+
+ if(files.length <= maxFiles)
+ {
+ for(i = 0; i < files.length; i++)
+ {
+ if(files[i].size < (1024*1024*1))
+ loadInView(files[i]);
+ else
+ alert('Слишком большой файл. Максимально 1024Kb.');
+ }
+ }else{
+ alert('Нельзя загружать больше '+maxFiles+' изображений.');
+ files.length = 0;
+ }
+
+ clearFileUpload('img');
+});
+
+var upload = 0;
+var files = 0;
+
+function loadInView(file)
+{
+ if(files < 0)
+ files = 0;
+
+ $('#uploaded-holder').css('display', 'inline-block');
+
+ if(!file.type.match('image.*'))
+ {
+ $('#drop-files p').html('Файл не является изображением.');
+
+ return false;
+ }
+
+ files = files+1;
+
+ if(files <= maxFiles)
+ $('#upload-button').css({'display' : 'block'});
+ else{
+ alert('Нельзя загружать больше '+maxFiles+' изображений.');
+
+ files = files-1;
+
+ return false;
+ }
+
+ var fileReader = new FileReader();
+
+ fileReader.onload = (function(file){
+ return function(e){
+ dataArray.push({name : file.name, value : this.result, check : null, sel : null});
+ addImage((dataArray.length-1));
+ };
+ })(file);
+
+ fileReader.readAsDataURL(file);
+
+ return false;
+}
+
+function delImage(id)
+{
+ $(this).empty();
+ dataArray.splice(id, 1);
+ $('#dropped-files > .img-block').remove();
+ addImage(-1);
+
+ return false;
+}
+
+function addImage(ind)
+{
+ if(ind < 0 )
+ {
+ start = 0;
+ end = dataArray.length;
+ files = files-1;
+ }else{
+ start = ind;
+ end = ind+1;
+ }
+
+ if(dataArray.length == 0)
+ {
+ $('#upload-button').hide();
+ $('#uploaded-holder').hide();
+ }
+
+ for(i = start; i < end; i++)
+ if($('#dropped-files > .img-block').length <= maxFiles)
+ $('#dropped-files').append('');
+
+ return false;
+}
+
+function restartFiles(go)
+{
+ $('#loading-bar .loading-color').css({'width' : '0%'});
+ $('#loading').css({'display' : 'none'});
+ $('#loading-content').html(' ');
+ $('#upload-button').hide();
+ $('#dropped-files > .img-block').remove();
+ $('#uploaded-holder').hide();
+ dataArray.length = 0;
+ if(go != 1)
+ files = upload;
+
+ return false;
+}
+
+$('#dropped-files #upload-button .delete').click(restartFiles);
+
+$('#upload-button .upload').click(function()
+{
+ $("#loading").show();
+
+ var totalPercent = 100 / dataArray.length;
+ var x = 0;
+
+ $('#loading-content').html('Загружен '+dataArray[0].name);
+
+ $.each(dataArray, function(index, file)
+ {
+ upload = upload + 1;
+
+ $.post(home+'help/section/upload', dataArray[index], function(data)
+ {
+ var fileName = dataArray[index].name;
+ ++x;
+
+ $('#loading-bar .loading-color').css({'width' : totalPercent*(x)+'%'});
+
+ if(totalPercent*(x) == 100)
+ {
+ $('#loading-content').html('Загрузка завершена.');
+ setTimeout(restartFiles(1), 1000);
+ }else if(totalPercent*(x) < 100)
+ $('#loading-content').html('Загружается '+fileName);
+
+ var dataSplit = data.split(':');
+
+ if(dataSplit[1] == 'ok')
+ {
+ $('#uploaded-files').append('');
+ $('#img-input').append('
');
+ }else
+ $('#uploaded-files').append(data);
+ });
+
+ if(upload == maxFiles)
+ $('#upload_block').hide();
+ });
+
+ $('#uploaded-files').show();
+
+ return false;
+});
+
+$('#drop-files').on('dragenter', function()
+{
+ $(this).css({'border-color' : '#74b084'});
+
+ return false;
+});
+
+$('#drop-files').on('drop', function()
+{
+ $(this).css({'border-color' : '#dcdcdc'});
+ files = files-1;
+
+ return false;
+});
diff --git a/template/js/img.js b/template/js/img.js
new file mode 100644
index 0000000..5dbfe13
--- /dev/null
+++ b/template/js/img.js
@@ -0,0 +1,28 @@
+;(function(b){function H(){v.hide();r.onerror=r.onload=null;F&&F.abort();l.empty()}function Q(){b.fancybox('The requested content cannot be loaded. Please try again later.
',{scrolling:"no",padding:20,transitionIn:"none",transitionOut:"none"})}function B(){H();var a=q[s];e=b.extend({},b.fn.fancybox.defaults,typeof b(a).data("fancybox")=="undefined"?e:b(a).data("fancybox"));var d,f,o=a.title||b(a).title||e.title||"";if(a.nodeName&&!e.orig)e.orig=b(a).children("img:first").length?
+b(a).children("img:first"):b(a);if(o==""&&e.orig)o=e.orig.attr("alt");d=a.nodeName&&/^(?:javascript|#)/i.test(a.href)?e.href||null:e.href||a.href||null;if(e.type){f=e.type;if(!d)d=e.content}else if(e.content)f="html";else if(d)if(d.match(I))f="image";else if(d.match(T))f="swf";else if(b(a).hasClass("iframe"))f="iframe";else if(d.match(/#/)){a=d.substr(d.indexOf("#"));f=b(a).length>0?"inline":"ajax"}else f="ajax";else f="inline";e.type=f;e.href=d;e.title=o;if(e.autoDimensions&&e.type!=="iframe"&&e.type!==
+"swf"){e.width="auto";e.height="auto"}if(e.modal){e.overlayShow=true;e.hideOnOverlayClick=false;e.hideOnContentClick=false;e.enableEscapeButton=false;e.showCloseButton=false}if(b.isFunction(e.onStart))if(e.onStart(q,s,e)===false){h=false;return}l.css("padding",t+e.padding+e.margin);b(".fancybox-inline-tmp").unbind("fancybox-cancel").bind("fancybox-change",function(){b(this).replaceWith(i.children())});switch(f){case "html":l.html(e.content);G();break;case "inline":b('
').hide().insertBefore(b(a)).bind("fancybox-cleanup",
+function(){b(this).replaceWith(i.children())}).bind("fancybox-cancel",function(){b(this).replaceWith(l.children())});b(a).appendTo(l);G();break;case "image":h=false;b.fancybox.showActivity();r=new Image;r.onerror=function(){Q()};r.onload=function(){r.onerror=null;r.onload=null;U()};r.src=d;break;case "swf":var u="",w="";u+=' ';b.each(e.swf,function(p,R){u+=
+' ';w+=" "+p+'="'+R+'"'});u+=' ";l.html(u);G();break;case "ajax":a=d.split("#",2);f=e.ajax.data||{};if(a.length>1){d=a[0];typeof f=="string"?(f+="&selector="+a[1]):(f.selector=a[1])}h=false;b.fancybox.showActivity();F=b.ajax(b.extend(e.ajax,{url:d,data:f,error:Q,success:function(p){if(F.status==200){l.html(p);G()}}}));break;case "iframe":b('').appendTo(l);J();break}}function U(){h=true;e.width=r.width;e.height=r.height;b(" ").attr({id:"fancybox-img",src:r.src,alt:e.title}).appendTo(l);J()}function G(){l.width(e.width);l.height(e.height);if(e.width=="auto")e.width=l.width();if(e.height=="auto")e.height=l.height();J()}function J(){v.hide();if(g.is(":visible")&&b.isFunction(c.onCleanup))if(c.onCleanup(j,n,c)===false){b.event.trigger("fancybox-cancel");
+h=false;return}j=q;n=s;c=e;i.get(0).scrollTop=0;i.get(0).scrollLeft=0;if(c.overlayShow){K&&b("select:not(#fancybox-tmp select)").filter(function(){return this.style.visibility!=="hidden"}).css({visibility:"hidden"}).one("fancybox-cleanup",function(){this.style.visibility="inherit"});y.css({"background-color":c.overlayColor,opacity:c.overlayOpacity}).unbind().show()}m=V();W();if(g.is(":visible")){b(z.add(C).add(D)).hide();var a=g.position();k={top:a.top,left:a.left,width:g.width(),height:g.height()};
+var d=k.width==m.width&&k.height==m.height;i.fadeOut(c.changeFade,function(){function f(){i.html(l.contents()).fadeIn(c.changeFade,L)}b.event.trigger("fancybox-change");i.css({top:c.padding,left:c.padding,width:Math.max(k.width-c.padding*2,1),height:Math.max(k.height-c.padding*2,1)}).empty().css("overflow","hidden");A.prop=0;b(A).animate({prop:1},{duration:d?0:c.changeSpeed,easing:c.easingChange,step:M,complete:f})})}else{g.css("opacity",1);if(c.transitionIn=="elastic"){k=S();i.css({top:c.padding,
+left:c.padding,width:Math.max(k.width-c.padding*2,1),height:Math.max(k.height-c.padding*2,1)}).html(l.contents());g.css(k).show();if(c.opacity)m.opacity=0;A.prop=0;b(A).animate({prop:1},{duration:c.speedIn,easing:c.easingIn,step:M,complete:L})}else{i.css({top:c.padding,left:c.padding,width:Math.max(m.width-c.padding*2,1),height:Math.max(m.height-c.padding*2-x,1)}).html(l.contents());g.css(m).fadeIn(c.transitionIn=="none"?0:c.speedIn,L)}}}function M(a){var d=Math.round(k.width+(m.width-k.width)*a),
+f=Math.round(k.height+(m.height-k.height)*a),o=Math.round(k.top+(m.top-k.top)*a),u=Math.round(k.left+(m.left-k.left)*a);g.css({width:d+"px",height:f+"px",top:o+"px",left:u+"px"});d=Math.max(d-c.padding*2,0);f=Math.max(f-(c.padding*2+x*a),0);i.css({width:d+"px",height:f+"px"});if(typeof m.opacity!=="undefined")g.css("opacity",a<0.5?0.5:a)}function L(){i.css("overflow",overflow=c.scrolling=="auto"?c.type=="image"||c.type=="iframe"||c.type=="swf"?"hidden":"auto":c.scrolling=="yes"?"auto":"visible");
+if(!b.support.opacity){i.get(0).style.removeAttribute("filter");g.get(0).style.removeAttribute("filter")}b("#fancybox-title").show();c.hideOnContentClick&&i.one("click",b.fancybox.close);c.hideOnOverlayClick&&y.one("click",b.fancybox.close);c.showCloseButton&&z.show();X();b(window).bind("resize.fb",b.fancybox.center);c.centerOnScroll?b(window).bind("scroll.fb",b.fancybox.center):b(window).unbind("scroll.fb");b.isFunction(c.onComplete)&&c.onComplete(j,n,c);h=false;Y()}function V(){var a=N(),d={},f=
+c.margin,o=c.autoScale,u=(t+f)*2,w=(t+f)*2,p=c.padding*2;if(c.width.toString().indexOf("%")>-1){d.width=a[0]*parseFloat(c.width)/100-t*2;o=false}else d.width=c.width+p;if(c.height.toString().indexOf("%")>-1){d.height=a[1]*parseFloat(c.height)/100-t*2;o=false}else d.height=c.height+p;if(o&&(d.width>a[0]-u||d.height>a[1]-w))if(e.type=="image"||e.type=="swf"){u+=p;w+=p;o=Math.min(Math.min(a[0]-u,c.width)/c.width,Math.min(a[1]-w,c.height)/c.height);d.width=Math.round(o*(d.width-p))+p;d.height=Math.round(o*
+(d.height-p))+p}else{d.width=Math.min(d.width,a[0]-u);d.height=Math.min(d.height,a[1]-w)}d.top=a[3];d.left=a[2]+(a[0]-(d.width+t*2))*0.5;if(c.autoScale==false){d.top=Math.max(a[3]+f,d.top);d.left=Math.max(a[2]+f,d.left)}return d}function S(){var a=e.orig?b(e.orig):false,d={};if(a&&a.length){a=Z(a);d={width:a.width+c.padding*2,height:a.height+c.padding*2,top:a.top-c.padding-t,left:a.left-c.padding-t}}else{a=N();d={width:1,height:1,top:a[3]+a[1]*0.5,left:a[2]+a[0]*0.5}}return d}
+function X(){b(document).unbind("keydown.fb").bind("keydown.fb",function(a){if(a.keyCode==27&&c.enableEscapeButton){a.preventDefault();b.fancybox.close()}else if(a.keyCode==37){a.preventDefault();b.fancybox.prev()}else if(a.keyCode==39){a.preventDefault();b.fancybox.next()}});if(b.fn.mousewheel){g.unbind("mousewheel.fb");j.length>1&&g.bind("mousewheel.fb",function(a,d){a.preventDefault();h||d==0||(d>0?b.fancybox.prev():b.fancybox.next())})}if(c.showNavArrows){if(c.cyclic&&j.length>1||n!=0)C.show();
+if(c.cyclic&&j.length>1||n!=j.length-1)D.show()}}function Y(){if(j.length-1>n){var a=j[n+1].href;if(typeof a!=="undefined"&&a.match(I)){var d=new Image;d.src=a}}if(n>0){a=j[n-1].href;if(typeof a!=="undefined"&&a.match(I)){d=new Image;d.src=a}}}function $(){if(v.is(":visible")){b("div",v).css("top",O*-40+"px");O=(O+1)%12}else clearInterval(P)}function N(){return[b(window).width(),b(window).height(),b(document).scrollLeft(),b(document).scrollTop()]}function Z(a){var d=a.offset();d.top+=parseFloat(a.css("paddingTop"))||
+0;d.left+=parseFloat(a.css("paddingLeft"))||0;d.top+=parseFloat(a.css("border-top-width"))||0;d.left+=parseFloat(a.css("border-left-width"))||0;d.width=a.width();d.height=a.height();return d}function W(){b("#fancybox-title").remove();x=0;if(c.titleShow!=false){var a=c.title;a=b.isFunction(c.titleFormat)?c.titleFormat(a,j,n,c):aa(a);if(!(!a||a=="")){var d=m.width-c.padding*2;b('
').css({width:d,paddingLeft:c.padding,paddingRight:c.padding}).html(a).appendTo("body");
+switch(c.titlePosition){case "inside":x=b("#fancybox-title").outerHeight(true)-c.padding;m.height+=x;break;case "over":b("#fancybox-title").css("bottom",c.padding);break;default:b("#fancybox-title").css("bottom",b("#fancybox-title").outerHeight(true)*-1);break}b("#fancybox-title").appendTo(E).hide();K&&b("#fancybox-title span").fixPNG()}}}function aa(a){if(a&&a.length)switch(c.titlePosition){case "inside":return a;case "over":return''+a+" ";default:return''+
+a+' '}return false}function ba(){if(!b("#fancybox-wrap").length){b("body").append(l=b('
'),v=b(''),y=b('
'),g=b('
'));E=b('
').append('
').appendTo(g);
+E.append(i=b('
'),z=b(' '),C=b(' '),D=b(' '));z.click(b.fancybox.close);v.click(b.fancybox.cancel);C.click(function(a){a.preventDefault();b.fancybox.prev()});D.click(function(a){a.preventDefault();b.fancybox.next()});b.support.opacity||E.find(".fancy-bg").fixPNG();
+if(K){b(z.add(".fancy-ico").add("div",v)).fixPNG();y.get(0).style.setExpression("height","document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'");v.get(0).style.setExpression("top","(-20 + (document.documentElement.clientHeight ? document.documentElement.clientHeight/2 : document.body.clientHeight/2 ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop )) + 'px'");E.prepend('')}}}
+var l,v,y,g,E,i,z,C,D,s=0,e={},q=[],n=0,c={},j=[],F=null,r=new Image,I=/\.(jpg|gif|png|bmp|jpeg)(.*)?$/i,T=/[^\.]\.(swf)\s*$/i,P,O=1,k,m,h=false,t=20,A=b.extend(b("
")[0],{prop:0}),x=0,K=!b.support.opacity&&!window.XMLHttpRequest;b.fn.fixPNG=function(){return this.each(function(){var a=b(this).css("backgroundImage");if(a.match(/^url\(["']?(.*\.png)["']?\)$/i)){a=RegExp.$1;b(this).css({backgroundImage:"none",filter:"progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod="+
+(b(this).css("backgroundRepeat")=="no-repeat"?"crop":"scale")+", src='"+a+"')"}).each(function(){var d=b(this).css("position");d!="absolute"&&d!="relative"&&b(this).css("position","relative")}).css("zoom",1)}})};b.fn.fancybox=function(a){b(this).data("fancybox",b.extend({},a));b(this).unbind("click.fb").bind("click.fb",function(d){d.preventDefault();if(!h){h=true;b(this).blur();q=[];s=0;d=b(this).attr("rel")||"";if(!d||d==""||d==="nofollow")q.push(this);else{q=b("a[rel="+d+"], area[rel="+d+"]");s=
+q.index(this)}B();return false}});return this};b.fancybox=function(a,d){if(!h){h=true;q=[];s=0;if(b.isArray(a)){for(var f=0,o=a.length;f-1&&j.length>a){s=a;B()}if(c.cyclic&&j.length>1&&a<0){s=j.length-1;B()}if(c.cyclic&&j.length>1&&a>=j.length){s=0;B()}}};b.fancybox.cancel=function(){if(!h){h=true;b.event.trigger("fancybox-cancel");H();e&&b.isFunction(e.onCancel)&&e.onCancel(q,s,e);h=false}};b.fancybox.close=function(){function a(){y.fadeOut("fast");
+g.hide();b.event.trigger("fancybox-cleanup");i.empty();b.isFunction(c.onClosed)&&c.onClosed(j,n,c);j=e=[];n=s=0;c=e={};h=false}if(!(h||g.is(":hidden"))){h=true;if(c&&b.isFunction(c.onCleanup))if(c.onCleanup(j,n,c)===false){h=false;return}H();b(z.add(C).add(D)).hide();b("#fancybox-title").remove();g.add(i).add(y).unbind();b(window).unbind("resize.fb scroll.fb");b(document).unbind("keydown.fb");i.css("overflow","hidden");if(c.transitionOut=="elastic"){k=S();var d=g.position();m={top:d.top,left:d.left,
+width:g.width(),height:g.height()};if(c.opacity)m.opacity=1;A.prop=1;b(A).animate({prop:0},{duration:c.speedOut,easing:c.easingOut,step:M,complete:a})}else g.fadeOut(c.transitionOut=="none"?0:c.speedOut,a)}};b.fancybox.resize=function(){if(!(h||g.is(":hidden"))){h=true;var a=i.wrapInner("
").children(),d=a.height();g.css({height:d+c.padding*2+x});i.css({height:d});a.replaceWith(a.children());b.fancybox.center()}};b.fancybox.center=function(){h=true;var a=N(),d=c.margin,
+f={};f.top=a[3]+(a[1]-(g.height()-x+t*2))*0.5;f.left=a[2]+(a[0]-(g.width()+t*2))*0.5;f.top=Math.max(a[3]+d,f.top);f.left=Math.max(a[2]+d,f.left);g.css(f);h=false};b.fn.fancybox.defaults={padding:10,margin:20,opacity:false,modal:false,cyclic:false,scrolling:"auto",width:560,height:340,autoScale:true,autoDimensions:true,centerOnScroll:false,ajax:{},swf:{wmode:"transparent"},hideOnOverlayClick:true,hideOnContentClick:false,overlayShow:true,overlayOpacity:0.3,overlayColor:"#666",titleShow:true,titlePosition:"outside",
+titleFormat:null,transitionIn:"fade",transitionOut:"fade",speedIn:300,speedOut:300,changeSpeed:300,changeFade:"fast",easingIn:"swing",easingOut:"swing",showCloseButton:true,showNavArrows:true,enableEscapeButton:true,onStart:null,onCancel:null,onComplete:null,onCleanup:null,onClosed:null};b(document).ready(function(){ba()})})(jQuery);
\ No newline at end of file
diff --git a/template/js/jquery.cookie.min.js b/template/js/jquery.cookie.min.js
new file mode 100644
index 0000000..c0f19d8
--- /dev/null
+++ b/template/js/jquery.cookie.min.js
@@ -0,0 +1,2 @@
+/*! jquery.cookie v1.4.1 | MIT */
+!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?a(require("jquery")):a(jQuery)}(function(a){function b(a){return h.raw?a:encodeURIComponent(a)}function c(a){return h.raw?a:decodeURIComponent(a)}function d(a){return b(h.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(g," ")),h.json?JSON.parse(a):a}catch(b){}}function f(b,c){var d=h.raw?b:e(b);return a.isFunction(c)?c(d):d}var g=/\+/g,h=a.cookie=function(e,g,i){if(void 0!==g&&!a.isFunction(g)){if(i=a.extend({},h.defaults,i),"number"==typeof i.expires){var j=i.expires,k=i.expires=new Date;k.setTime(+k+864e5*j)}return document.cookie=[b(e),"=",d(g),i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}for(var l=e?void 0:{},m=document.cookie?document.cookie.split("; "):[],n=0,o=m.length;o>n;n++){var p=m[n].split("="),q=c(p.shift()),r=p.join("=");if(e&&e===q){l=f(r,g);break}e||void 0===(r=f(r))||(l[q]=r)}return l};h.defaults={},a.removeCookie=function(b,c){return void 0===a.cookie(b)?!1:(a.cookie(b,"",a.extend({},c,{expires:-1})),!a.cookie(b))}});
\ No newline at end of file
diff --git a/template/js/jquery.js b/template/js/jquery.js
new file mode 100644
index 0000000..e6e07fc
--- /dev/null
+++ b/template/js/jquery.js
@@ -0,0 +1,9472 @@
+/*!
+ * jQuery JavaScript Library v1.8.3
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time)
+ */
+(function( window, undefined ) {
+var
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // The deferred used on DOM ready
+ readyList,
+
+ // Use the correct document accordingly with window argument (sandbox)
+ document = window.document,
+ location = window.location,
+ navigator = window.navigator,
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // Save a reference to some core methods
+ core_push = Array.prototype.push,
+ core_slice = Array.prototype.slice,
+ core_indexOf = Array.prototype.indexOf,
+ core_toString = Object.prototype.toString,
+ core_hasOwn = Object.prototype.hasOwnProperty,
+ core_trim = String.prototype.trim,
+
+ // Define a local copy of jQuery
+ jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Used for matching numbers
+ core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,
+
+ // Used for detecting and trimming whitespace
+ core_rnotwhite = /\S/,
+ core_rspace = /\s+/,
+
+ // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
+ rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+ // A simple way to check for HTML strings
+ // Prioritize #id over to avoid XSS via location.hash (#9521)
+ rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,
+
+ // Matches dashed string for camelizing
+ rmsPrefix = /^-ms-/,
+ rdashAlpha = /-([\da-z])/gi,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return ( letter + "" ).toUpperCase();
+ },
+
+ // The ready event handler and self cleanup method
+ DOMContentLoaded = function() {
+ if ( document.addEventListener ) {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ } else if ( document.readyState === "complete" ) {
+ // we're here because readyState === "complete" in oldIE
+ // which is good enough for us to call the dom ready!
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ },
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), $(undefined), $(false)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = rquickExpr.exec( selector );
+ }
+
+ // Match html or make sure no context is specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+ doc = ( context && context.nodeType ? context.ownerDocument || context : document );
+
+ // scripts is true for back-compat
+ selector = jQuery.parseHTML( match[1], doc, true );
+ if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+ this.attr.call( selector, context, true );
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $(#id)
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || rootjQuery ).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.8.3",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return core_slice.call( this );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+
+ // Build a new jQuery matched element set
+ var ret = jQuery.merge( this.constructor(), elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Add the callback
+ jQuery.ready.promise().done( fn );
+
+ return this;
+ },
+
+ eq: function( i ) {
+ i = +i;
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( core_slice.apply( this, arguments ),
+ "slice", core_slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: core_push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+
+ // Abort if there are pending holds or we're already ready
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+ return;
+ }
+
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger("ready").off("ready");
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ isWindow: function( obj ) {
+ return obj != null && obj == obj.window;
+ },
+
+ isNumeric: function( obj ) {
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ core_toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !core_hasOwn.call(obj, "constructor") &&
+ !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+ } catch ( e ) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || core_hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ var name;
+ for ( name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ // data: string of html
+ // context (optional): If specified, the fragment will be created in this context, defaults to document
+ // scripts (optional): If true, will include scripts passed in the html string
+ parseHTML: function( data, context, scripts ) {
+ var parsed;
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+ if ( typeof context === "boolean" ) {
+ scripts = context;
+ context = 0;
+ }
+ context = context || document;
+
+ // Single tag
+ if ( (parsed = rsingleTag.exec( data )) ) {
+ return [ context.createElement( parsed[1] ) ];
+ }
+
+ parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] );
+ return jQuery.merge( [],
+ (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes );
+ },
+
+ parseJSON: function( data ) {
+ if ( !data || typeof data !== "string") {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return window.JSON.parse( data );
+ }
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return ( new Function( "return " + data ) )();
+
+ }
+ jQuery.error( "Invalid JSON: " + data );
+ },
+
+ // Cross-browser xml parsing
+ parseXML: function( data ) {
+ var xml, tmp;
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+ try {
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+ } catch( e ) {
+ xml = undefined;
+ }
+ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && core_rnotwhite.test( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+ },
+
+ // args is for internal usage only
+ each: function( obj, callback, args ) {
+ var name,
+ i = 0,
+ length = obj.length,
+ isObj = length === undefined || jQuery.isFunction( obj );
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in obj ) {
+ if ( callback.apply( obj[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( obj[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in obj ) {
+ if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return obj;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
+ function( text ) {
+ return text == null ?
+ "" :
+ core_trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ ( text + "" ).replace( rtrim, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( arr, results ) {
+ var type,
+ ret = results || [];
+
+ if ( arr != null ) {
+ // The window, strings (and functions) also have 'length'
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ type = jQuery.type( arr );
+
+ if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) {
+ core_push.call( ret, arr );
+ } else {
+ jQuery.merge( ret, arr );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, arr, i ) {
+ var len;
+
+ if ( arr ) {
+ if ( core_indexOf ) {
+ return core_indexOf.call( arr, elem, i );
+ }
+
+ len = arr.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+ for ( ; i < len; i++ ) {
+ // Skip accessing in sparse arrays
+ if ( i in arr && arr[ i ] === elem ) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var l = second.length,
+ i = first.length,
+ j = 0;
+
+ if ( typeof l === "number" ) {
+ for ( ; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var retVal,
+ ret = [],
+ i = 0,
+ length = elems.length;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( ; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value, key,
+ ret = [],
+ i = 0,
+ length = elems.length,
+ // jquery objects are treated as arrays
+ isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( key in elems ) {
+ value = callback( elems[ key ], key, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ var tmp, args, proxy;
+
+ if ( typeof context === "string" ) {
+ tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ args = core_slice.call( arguments, 2 );
+ proxy = function() {
+ return fn.apply( context, args.concat( core_slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Multifunctional method to get and set values of a collection
+ // The value/s can optionally be executed if it's a function
+ access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
+ var exec,
+ bulk = key == null,
+ i = 0,
+ length = elems.length;
+
+ // Sets many values
+ if ( key && typeof key === "object" ) {
+ for ( i in key ) {
+ jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
+ }
+ chainable = 1;
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = pass === undefined && jQuery.isFunction( value );
+
+ if ( bulk ) {
+ // Bulk operations only iterate when executing function values
+ if ( exec ) {
+ exec = fn;
+ fn = function( elem, key, value ) {
+ return exec.call( jQuery( elem ), value );
+ };
+
+ // Otherwise they run against the entire set
+ } else {
+ fn.call( elems, value );
+ fn = null;
+ }
+ }
+
+ if ( fn ) {
+ for (; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+ }
+
+ chainable = 1;
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ length ? fn( elems[0], key ) : emptyGet;
+ },
+
+ now: function() {
+ return ( new Date() ).getTime();
+ }
+});
+
+jQuery.ready.promise = function( obj ) {
+ if ( !readyList ) {
+
+ readyList = jQuery.Deferred();
+
+ // Catch cases where $(document).ready() is called after the browser event has already occurred.
+ // we once tried to use readyState "interactive" here, but it caused issues like the one
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ setTimeout( jQuery.ready, 1 );
+
+ // Standards-based browsers support DOMContentLoaded
+ } else if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else {
+ // Ensure firing before onload, maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var top = false;
+
+ try {
+ top = window.frameElement == null && document.documentElement;
+ } catch(e) {}
+
+ if ( top && top.doScroll ) {
+ (function doScrollCheck() {
+ if ( !jQuery.isReady ) {
+
+ try {
+ // Use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ top.doScroll("left");
+ } catch(e) {
+ return setTimeout( doScrollCheck, 50 );
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+ }
+ })();
+ }
+ }
+ }
+ return readyList.promise( obj );
+};
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+ var object = optionsCache[ options ] = {};
+ jQuery.each( options.split( core_rspace ), function( _, flag ) {
+ object[ flag ] = true;
+ });
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * options: an optional list of space-separated options that will change how
+ * the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+ // Convert options from String-formatted to Object-formatted if needed
+ // (we check in cache first)
+ options = typeof options === "string" ?
+ ( optionsCache[ options ] || createOptions( options ) ) :
+ jQuery.extend( {}, options );
+
+ var // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list was already fired
+ fired,
+ // Flag to know if list is currently firing
+ firing,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = !options.once && [],
+ // Fire callbacks
+ fire = function( data ) {
+ memory = options.memory && data;
+ fired = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ firing = true;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+ memory = false; // To prevent further calls using add
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( stack ) {
+ if ( stack.length ) {
+ fire( stack.shift() );
+ }
+ } else if ( memory ) {
+ list = [];
+ } else {
+ self.disable();
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ // First, we save the current length
+ var start = list.length;
+ (function add( args ) {
+ jQuery.each( args, function( _, arg ) {
+ var type = jQuery.type( arg );
+ if ( type === "function" ) {
+ if ( !options.unique || !self.has( arg ) ) {
+ list.push( arg );
+ }
+ } else if ( arg && arg.length && type !== "string" ) {
+ // Inspect recursively
+ add( arg );
+ }
+ });
+ })( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away
+ } else if ( memory ) {
+ firingStart = start;
+ fire( memory );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ jQuery.each( arguments, function( _, arg ) {
+ var index;
+ while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+ list.splice( index, 1 );
+ // Handle firing indexes
+ if ( firing ) {
+ if ( index <= firingLength ) {
+ firingLength--;
+ }
+ if ( index <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ });
+ }
+ return this;
+ },
+ // Control if a given callback is in the list
+ has: function( fn ) {
+ return jQuery.inArray( fn, list ) > -1;
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ args = args || [];
+ args = [ context, args.slice ? args.slice() : args ];
+ if ( list && ( !fired || stack ) ) {
+ if ( firing ) {
+ stack.push( args );
+ } else {
+ fire( args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
+
+ return self;
+};
+jQuery.extend({
+
+ Deferred: function( func ) {
+ var tuples = [
+ // action, add listener, listener list, final state
+ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+ [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+ [ "notify", "progress", jQuery.Callbacks("memory") ]
+ ],
+ state = "pending",
+ promise = {
+ state: function() {
+ return state;
+ },
+ always: function() {
+ deferred.done( arguments ).fail( arguments );
+ return this;
+ },
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
+ var fns = arguments;
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( tuples, function( i, tuple ) {
+ var action = tuple[ 0 ],
+ fn = fns[ i ];
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
+ deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
+ function() {
+ var returned = fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise()
+ .done( newDefer.resolve )
+ .fail( newDefer.reject )
+ .progress( newDefer.notify );
+ } else {
+ newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
+ }
+ } :
+ newDefer[ action ]
+ );
+ });
+ fns = null;
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ return obj != null ? jQuery.extend( obj, promise ) : promise;
+ }
+ },
+ deferred = {};
+
+ // Keep pipe for back-compat
+ promise.pipe = promise.then;
+
+ // Add list-specific methods
+ jQuery.each( tuples, function( i, tuple ) {
+ var list = tuple[ 2 ],
+ stateString = tuple[ 3 ];
+
+ // promise[ done | fail | progress ] = list.add
+ promise[ tuple[1] ] = list.add;
+
+ // Handle state
+ if ( stateString ) {
+ list.add(function() {
+ // state = [ resolved | rejected ]
+ state = stateString;
+
+ // [ reject_list | resolve_list ].disable; progress_list.lock
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+ }
+
+ // deferred[ resolve | reject | notify ] = list.fire
+ deferred[ tuple[0] ] = list.fire;
+ deferred[ tuple[0] + "With" ] = list.fireWith;
+ });
+
+ // Make the deferred a promise
+ promise.promise( deferred );
+
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+
+ // All done!
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( subordinate /* , ..., subordinateN */ ) {
+ var i = 0,
+ resolveValues = core_slice.call( arguments ),
+ length = resolveValues.length,
+
+ // the count of uncompleted subordinates
+ remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+ // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+ // Update function for both resolve and progress values
+ updateFunc = function( i, contexts, values ) {
+ return function( value ) {
+ contexts[ i ] = this;
+ values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
+ if( values === progressValues ) {
+ deferred.notifyWith( contexts, values );
+ } else if ( !( --remaining ) ) {
+ deferred.resolveWith( contexts, values );
+ }
+ };
+ },
+
+ progressValues, progressContexts, resolveContexts;
+
+ // add listeners to Deferred subordinates; treat others as resolved
+ if ( length > 1 ) {
+ progressValues = new Array( length );
+ progressContexts = new Array( length );
+ resolveContexts = new Array( length );
+ for ( ; i < length; i++ ) {
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+ resolveValues[ i ].promise()
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
+ .fail( deferred.reject )
+ .progress( updateFunc( i, progressContexts, progressValues ) );
+ } else {
+ --remaining;
+ }
+ }
+ }
+
+ // if we're not waiting on anything, resolve the master
+ if ( !remaining ) {
+ deferred.resolveWith( resolveContexts, resolveValues );
+ }
+
+ return deferred.promise();
+ }
+});
+jQuery.support = (function() {
+
+ var support,
+ all,
+ a,
+ select,
+ opt,
+ input,
+ fragment,
+ eventName,
+ i,
+ isSupported,
+ clickFn,
+ div = document.createElement("div");
+
+ // Setup
+ div.setAttribute( "className", "t" );
+ div.innerHTML = " a ";
+
+ // Support tests won't run in some limited or non-browser environments
+ all = div.getElementsByTagName("*");
+ a = div.getElementsByTagName("a")[ 0 ];
+ if ( !all || !a || !all.length ) {
+ return {};
+ }
+
+ // First batch of tests
+ select = document.createElement("select");
+ opt = select.appendChild( document.createElement("option") );
+ input = div.getElementsByTagName("input")[ 0 ];
+
+ a.style.cssText = "top:1px;float:left;opacity:.5";
+ support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ style: /top/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: ( a.getAttribute("href") === "/a" ),
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.5/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: ( input.value === "on" ),
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+ getSetAttribute: div.className !== "t",
+
+ // Tests for enctype support on a form (#6743)
+ enctype: !!document.createElement("form").enctype,
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>",
+
+ // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
+ boxModel: ( document.compatMode === "CSS1Compat" ),
+
+ // Will be defined later
+ submitBubbles: true,
+ changeBubbles: true,
+ focusinBubbles: false,
+ deleteExpando: true,
+ noCloneEvent: true,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableMarginRight: true,
+ boxSizingReliable: true,
+ pixelPosition: false
+ };
+
+ // Make sure checked status is properly cloned
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+
+ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+ div.attachEvent( "onclick", clickFn = function() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ support.noCloneEvent = false;
+ });
+ div.cloneNode( true ).fireEvent("onclick");
+ div.detachEvent( "onclick", clickFn );
+ }
+
+ // Check if a radio maintains its value
+ // after being appended to the DOM
+ input = document.createElement("input");
+ input.value = "t";
+ input.setAttribute( "type", "radio" );
+ support.radioValue = input.value === "t";
+
+ input.setAttribute( "checked", "checked" );
+
+ // #11217 - WebKit loses check when the name is after the checked attribute
+ input.setAttribute( "name", "t" );
+
+ div.appendChild( input );
+ fragment = document.createDocumentFragment();
+ fragment.appendChild( div.lastChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ support.appendChecked = input.checked;
+
+ fragment.removeChild( input );
+ fragment.appendChild( div );
+
+ // Technique from Juriy Zaytsev
+ // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
+ // We only care about the case where non-standard event systems
+ // are used, namely in IE. Short-circuiting here helps us to
+ // avoid an eval call (in setAttribute) which can cause CSP
+ // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+ if ( div.attachEvent ) {
+ for ( i in {
+ submit: true,
+ change: true,
+ focusin: true
+ }) {
+ eventName = "on" + i;
+ isSupported = ( eventName in div );
+ if ( !isSupported ) {
+ div.setAttribute( eventName, "return;" );
+ isSupported = ( typeof div[ eventName ] === "function" );
+ }
+ support[ i + "Bubbles" ] = isSupported;
+ }
+ }
+
+ // Run tests that need a body at doc ready
+ jQuery(function() {
+ var container, div, tds, marginDiv,
+ divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;",
+ body = document.getElementsByTagName("body")[0];
+
+ if ( !body ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ container = document.createElement("div");
+ container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px";
+ body.insertBefore( container, body.firstChild );
+
+ // Construct the test element
+ div = document.createElement("div");
+ container.appendChild( div );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ div.innerHTML = "";
+ tds = div.getElementsByTagName("td");
+ tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE <= 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+ // Check box-sizing and margin behavior
+ div.innerHTML = "";
+ div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
+ support.boxSizing = ( div.offsetWidth === 4 );
+ support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
+
+ // NOTE: To any future maintainer, we've window.getComputedStyle
+ // because jsdom on node.js will break without it.
+ if ( window.getComputedStyle ) {
+ support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+ support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. For more
+ // info see bug #3333
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ marginDiv = document.createElement("div");
+ marginDiv.style.cssText = div.style.cssText = divReset;
+ marginDiv.style.marginRight = marginDiv.style.width = "0";
+ div.style.width = "1px";
+ div.appendChild( marginDiv );
+ support.reliableMarginRight =
+ !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
+ }
+
+ if ( typeof div.style.zoom !== "undefined" ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.innerHTML = "";
+ div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "block";
+ div.style.overflow = "visible";
+ div.innerHTML = "
";
+ div.firstChild.style.width = "5px";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+
+ container.style.zoom = 1;
+ }
+
+ // Null elements to avoid leaks in IE
+ body.removeChild( container );
+ container = div = tds = marginDiv = null;
+ });
+
+ // Null elements to avoid leaks in IE
+ fragment.removeChild( div );
+ all = a = select = opt = input = fragment = div = null;
+
+ return support;
+})();
+var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
+ rmultiDash = /([A-Z])/g;
+
+jQuery.extend({
+ cache: {},
+
+ deletedIds: [],
+
+ // Remove at next major release (1.9/2.0)
+ uuid: 0,
+
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, ret,
+ internalKey = jQuery.expando,
+ getByName = typeof name === "string",
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++;
+ } else {
+ id = internalKey;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ cache[ id ] = {};
+
+ // Avoids exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ] = jQuery.extend( cache[ id ], name );
+ } else {
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+ }
+ }
+
+ thisCache = cache[ id ];
+
+ // jQuery data() is stored in a separate object inside the object's internal data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
+ }
+
+ thisCache = thisCache.data;
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // Check for both converted-to-camel and non-converted data property names
+ // If a data property was specified
+ if ( getByName ) {
+
+ // First Try to find as-is property data
+ ret = thisCache[ name ];
+
+ // Test for null|undefined property data
+ if ( ret == null ) {
+
+ // Try to find the camelCased property
+ ret = thisCache[ jQuery.camelCase( name ) ];
+ }
+ } else {
+ ret = thisCache;
+ }
+
+ return ret;
+ },
+
+ removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, i, l,
+
+ isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+ if ( thisCache ) {
+
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split(" ");
+ }
+ }
+ }
+
+ for ( i = 0, l = name.length; i < l; i++ ) {
+ delete thisCache[ name[i] ];
+ }
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( !pvt ) {
+ delete cache[ id ].data;
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject( cache[ id ] ) ) {
+ return;
+ }
+ }
+
+ // Destroy the cache
+ if ( isNode ) {
+ jQuery.cleanData( [ elem ], true );
+
+ // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+ } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
+ delete cache[ id ];
+
+ // When all else fails, null
+ } else {
+ cache[ id ] = null;
+ }
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return jQuery.data( elem, name, data, true );
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ // nodes accept data unless otherwise specified; rejection can be conditional
+ return !noData || noData !== true && elem.getAttribute("classid") === noData;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var parts, part, attr, name, l,
+ elem = this[0],
+ i = 0,
+ data = null;
+
+ // Gets all values
+ if ( key === undefined ) {
+ if ( this.length ) {
+ data = jQuery.data( elem );
+
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+ attr = elem.attributes;
+ for ( l = attr.length; i < l; i++ ) {
+ name = attr[i].name;
+
+ if ( !name.indexOf( "data-" ) ) {
+ name = jQuery.camelCase( name.substring(5) );
+
+ dataAttr( elem, name, data[ name ] );
+ }
+ }
+ jQuery._data( elem, "parsedAttrs", true );
+ }
+ }
+
+ return data;
+ }
+
+ // Sets multiple values
+ if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ parts = key.split( ".", 2 );
+ parts[1] = parts[1] ? "." + parts[1] : "";
+ part = parts[1] + "!";
+
+ return jQuery.access( this, function( value ) {
+
+ if ( value === undefined ) {
+ data = this.triggerHandler( "getData" + part, [ parts[0] ] );
+
+ // Try to fetch any internally stored data first
+ if ( data === undefined && elem ) {
+ data = jQuery.data( elem, key );
+ data = dataAttr( elem, key, data );
+ }
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ }
+
+ parts[1] = value;
+ this.each(function() {
+ var self = jQuery( this );
+
+ self.triggerHandler( "setData" + part, parts );
+ jQuery.data( this, key, value );
+ self.triggerHandler( "changeData" + part, parts );
+ });
+ }, null, value, arguments.length > 1, null, false );
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+
+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ // Only convert to a number if it doesn't change the string
+ +data + "" === data ? +data :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+ var name;
+ for ( name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+ continue;
+ }
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+jQuery.extend({
+ queue: function( elem, type, data ) {
+ var queue;
+
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ queue = jQuery._data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !queue || jQuery.isArray(data) ) {
+ queue = jQuery._data( elem, type, jQuery.makeArray(data) );
+ } else {
+ queue.push( data );
+ }
+ }
+ return queue || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ startLength = queue.length,
+ fn = queue.shift(),
+ hooks = jQuery._queueHooks( elem, type ),
+ next = function() {
+ jQuery.dequeue( elem, type );
+ };
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ startLength--;
+ }
+
+ if ( fn ) {
+
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
+
+ // clear up the last queue stop function
+ delete hooks.stop;
+ fn.call( elem, next, hooks );
+ }
+
+ if ( !startLength && hooks ) {
+ hooks.empty.fire();
+ }
+ },
+
+ // not intended for public consumption - generates a queueHooks object, or returns the current one
+ _queueHooks: function( elem, type ) {
+ var key = type + "queueHooks";
+ return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+ empty: jQuery.Callbacks("once memory").add(function() {
+ jQuery.removeData( elem, type + "queue", true );
+ jQuery.removeData( elem, key, true );
+ })
+ });
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ var setter = 2;
+
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ setter--;
+ }
+
+ if ( arguments.length < setter ) {
+ return jQuery.queue( this[0], type );
+ }
+
+ return data === undefined ?
+ this :
+ this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ // ensure a hooks for this queue
+ jQuery._queueHooks( this, type );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, obj ) {
+ var tmp,
+ count = 1,
+ defer = jQuery.Deferred(),
+ elements = this,
+ i = this.length,
+ resolve = function() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ };
+
+ if ( typeof type !== "string" ) {
+ obj = type;
+ type = undefined;
+ }
+ type = type || "fx";
+
+ while( i-- ) {
+ tmp = jQuery._data( elements[ i ], type + "queueHooks" );
+ if ( tmp && tmp.empty ) {
+ count++;
+ tmp.empty.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise( obj );
+ }
+});
+var nodeHook, boolHook, fixSpecified,
+ rclass = /[\t\r\n]/g,
+ rreturn = /\r/g,
+ rtype = /^(?:button|input)$/i,
+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
+ rclickable = /^a(?:rea|)$/i,
+ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+ getSetAttribute = jQuery.support.getSetAttribute;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
+
+ prop: function( name, value ) {
+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+ },
+
+ removeProp: function( name ) {
+ name = jQuery.propFix[ name ] || name;
+ return this.each(function() {
+ // try/catch handles cases where IE balks (such as removing a property on window)
+ try {
+ this[ name ] = undefined;
+ delete this[ name ];
+ } catch( e ) {}
+ });
+ },
+
+ addClass: function( value ) {
+ var classNames, i, l, elem,
+ setClass, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).addClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ classNames = value.split( core_rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className && classNames.length === 1 ) {
+ elem.className = value;
+
+ } else {
+ setClass = " " + elem.className + " ";
+
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) {
+ setClass += classNames[ c ] + " ";
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ var removes, className, elem, c, cl, i, l;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).removeClass( value.call(this, j, this.className) );
+ });
+ }
+ if ( (value && typeof value === "string") || value === undefined ) {
+ removes = ( value || "" ).split( core_rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+ if ( elem.nodeType === 1 && elem.className ) {
+
+ className = (" " + elem.className + " ").replace( rclass, " " );
+
+ // loop over each item in the removal list
+ for ( c = 0, cl = removes.length; c < cl; c++ ) {
+ // Remove until there is nothing to remove,
+ while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) {
+ className = className.replace( " " + removes[ c ] + " " , " " );
+ }
+ }
+ elem.className = value ? jQuery.trim( className ) : "";
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.split( core_rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space separated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery._data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ var hooks, ret, isFunction,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
+
+ ret = elem.value;
+
+ return typeof ret === "string" ?
+ // handle most common string cases
+ ret.replace(rreturn, "") :
+ // handle cases where value is null/undef or number
+ ret == null ? "" : ret;
+ }
+
+ return;
+ }
+
+ isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var val,
+ self = jQuery(this);
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, self.val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value, option,
+ options = elem.options,
+ index = elem.selectedIndex,
+ one = elem.type === "select-one" || index < 0,
+ values = one ? null : [],
+ max = one ? index + 1 : options.length,
+ i = index < 0 ?
+ max :
+ one ? index : 0;
+
+ // Loop through all the selected options
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
+
+ // oldIE doesn't update selected after form reset (#2551)
+ if ( ( option.selected || i === index ) &&
+ // Don't return options that are disabled or in a disabled optgroup
+ ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
+ ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var values = jQuery.makeArray( value );
+
+ jQuery(elem).find("option").each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
+
+ // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9
+ attrFn: {},
+
+ attr: function( elem, name, value, pass ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) {
+ return jQuery( elem )[ name ]( value );
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( typeof elem.getAttribute === "undefined" ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
+ if ( notxml ) {
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+ return;
+
+ } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, value + "" );
+ return value;
+ }
+
+ } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+
+ ret = elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret === null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, value ) {
+ var propName, attrNames, name, isBool,
+ i = 0;
+
+ if ( value && elem.nodeType === 1 ) {
+
+ attrNames = value.split( core_rspace );
+
+ for ( ; i < attrNames.length; i++ ) {
+ name = attrNames[ i ];
+
+ if ( name ) {
+ propName = jQuery.propFix[ name ] || name;
+ isBool = rboolean.test( name );
+
+ // See #9699 for explanation of this approach (setting first, then removal)
+ // Do not do this for boolean attributes (see #10870)
+ if ( !isBool ) {
+ jQuery.attr( elem, name, "" );
+ }
+ elem.removeAttribute( getSetAttribute ? name : propName );
+
+ // Set corresponding property to false for boolean attributes
+ if ( isBool && propName in elem ) {
+ elem[ propName ] = false;
+ }
+ }
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to it's default in case type is set after value
+ // This is for element creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ },
+ // Use the value property for back compat
+ // Use the nodeHook for button elements in IE6/7 (#1954)
+ value: {
+ get: function( elem, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.get( elem, name );
+ }
+ return name in elem ?
+ elem.value :
+ null;
+ },
+ set: function( elem, value, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.set( elem, value, name );
+ }
+ // Does not return so that setAttribute is also used
+ elem.value = value;
+ }
+ }
+ },
+
+ propFix: {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ "for": "htmlFor",
+ "class": "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder",
+ contenteditable: "contentEditable"
+ },
+
+ prop: function( elem, name, value ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ if ( notxml ) {
+ // Fix name and attach hooks
+ name = jQuery.propFix[ name ] || name;
+ hooks = jQuery.propHooks[ name ];
+ }
+
+ if ( value !== undefined ) {
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return ( elem[ name ] = value );
+ }
+
+ } else {
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+ return elem[ name ];
+ }
+ }
+ },
+
+ propHooks: {
+ tabIndex: {
+ get: function( elem ) {
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ var attributeNode = elem.getAttributeNode("tabindex");
+
+ return attributeNode && attributeNode.specified ?
+ parseInt( attributeNode.value, 10 ) :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+ }
+ }
+});
+
+// Hook for boolean attributes
+boolHook = {
+ get: function( elem, name ) {
+ // Align boolean attributes with corresponding properties
+ // Fall back to attribute presence where some booleans are not supported
+ var attrNode,
+ property = jQuery.prop( elem, name );
+ return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ var propName;
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ // value is true since we know at this point it's type boolean and not false
+ // Set boolean attributes to the same name and set the DOM property
+ propName = jQuery.propFix[ name ] || name;
+ if ( propName in elem ) {
+ // Only set the IDL specifically if it already exists on the element
+ elem[ propName ] = true;
+ }
+
+ elem.setAttribute( name, name.toLowerCase() );
+ }
+ return name;
+ }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+ fixSpecified = {
+ name: true,
+ id: true,
+ coords: true
+ };
+
+ // Use this for any attribute in IE6/7
+ // This fixes almost every IE6/7 issue
+ nodeHook = jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret;
+ ret = elem.getAttributeNode( name );
+ return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ?
+ ret.value :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ // Set the existing or create a new attribute node
+ var ret = elem.getAttributeNode( name );
+ if ( !ret ) {
+ ret = document.createAttribute( name );
+ elem.setAttributeNode( ret );
+ }
+ return ( ret.value = value + "" );
+ }
+ };
+
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+ // This is for removals
+ jQuery.each([ "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ set: function( elem, value ) {
+ if ( value === "" ) {
+ elem.setAttribute( name, "auto" );
+ return value;
+ }
+ }
+ });
+ });
+
+ // Set contenteditable to false on removals(#10429)
+ // Setting to empty string throws an error as an invalid value
+ jQuery.attrHooks.contenteditable = {
+ get: nodeHook.get,
+ set: function( elem, value, name ) {
+ if ( value === "" ) {
+ value = "false";
+ }
+ nodeHook.set( elem, value, name );
+ }
+ };
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ get: function( elem ) {
+ var ret = elem.getAttribute( name, 2 );
+ return ret === null ? undefined : ret;
+ }
+ });
+ });
+}
+
+if ( !jQuery.support.style ) {
+ jQuery.attrHooks.style = {
+ get: function( elem ) {
+ // Return undefined in the case of empty string
+ // Normalize to lowercase since IE uppercases css property names
+ return elem.style.cssText.toLowerCase() || undefined;
+ },
+ set: function( elem, value ) {
+ return ( elem.style.cssText = value + "" );
+ }
+ };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ return null;
+ }
+ });
+}
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+ jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+ jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ get: function( elem ) {
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+ };
+ });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+ }
+ }
+ });
+});
+var rformElems = /^(?:textarea|input|select)$/i,
+ rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/,
+ rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ hoverHack = function( events ) {
+ return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
+ };
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+ add: function( elem, types, handler, data, selector ) {
+
+ var elemData, eventHandle, events,
+ t, tns, type, namespaces, handleObj,
+ handleObjIn, handlers, special;
+
+ // Don't attach events to noData or text/comment nodes (allow plain objects tho)
+ if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ selector = handleObjIn.selector;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ events = elemData.events;
+ if ( !events ) {
+ elemData.events = events = {};
+ }
+ eventHandle = elemData.handle;
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+ eventHandle.elem = elem;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = jQuery.trim( hoverHack(types) ).split( " " );
+ for ( t = 0; t < types.length; t++ ) {
+
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = tns[1];
+ namespaces = ( tns[2] || "" ).split( "." ).sort();
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: tns[1],
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ handlers = events[ type ];
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener/attachEvent if the special events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+
+ var t, tns, type, origType, namespaces, origCount,
+ j, events, special, eventType, handleObj,
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem );
+
+ if ( !elemData || !(events = elemData.events) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
+ for ( t = 0; t < types.length; t++ ) {
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tns[1];
+ namespaces = tns[2];
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector? special.delegateType : special.bindType ) || type;
+ eventType = events[ type ] || [];
+ origCount = eventType.length;
+ namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
+
+ // Remove matching events
+ for ( j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+ eventType.splice( j--, 1 );
+
+ if ( handleObj.selector ) {
+ eventType.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( eventType.length === 0 && origCount !== eventType.length ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ delete elemData.handle;
+
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery.removeData( elem, "events", true );
+ }
+ },
+
+ // Events that are safe to short-circuit if no handlers are attached.
+ // Native DOM events should not be added, they may have inline handlers.
+ customEvent: {
+ "getData": true,
+ "setData": true,
+ "changeData": true
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ // Don't do events on text and comment nodes
+ if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
+ return;
+ }
+
+ // Event object or event type
+ var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,
+ type = event.type || event,
+ namespaces = [];
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
+
+ if ( type.indexOf( "!" ) >= 0 ) {
+ // Exclusive events trigger only for the exact event (no namespaces)
+ type = type.slice(0, -1);
+ exclusive = true;
+ }
+
+ if ( type.indexOf( "." ) >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+
+ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
+ // No jQuery handlers for this event type, and it can't have inline handlers
+ return;
+ }
+
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ jQuery.expando ] ? event :
+ // Object literal
+ new jQuery.Event( type, event ) :
+ // Just the event type (string)
+ new jQuery.Event( type );
+
+ event.type = type;
+ event.isTrigger = true;
+ event.exclusive = exclusive;
+ event.namespace = namespaces.join( "." );
+ event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
+ ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
+
+ // Handle a global trigger
+ if ( !elem ) {
+
+ // TODO: Stop taunting the data cache; remove global events and always attach to document
+ cache = jQuery.cache;
+ for ( i in cache ) {
+ if ( cache[ i ].events && cache[ i ].events[ type ] ) {
+ jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
+ }
+ }
+ return;
+ }
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ if ( !event.target ) {
+ event.target = elem;
+ }
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data != null ? jQuery.makeArray( data ) : [];
+ data.unshift( event );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ eventPath = [[ elem, special.bindType || type ]];
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+ bubbleType = special.delegateType || type;
+ cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
+ for ( old = elem; cur; cur = cur.parentNode ) {
+ eventPath.push([ cur, bubbleType ]);
+ old = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( old === (elem.ownerDocument || document) ) {
+ eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
+ }
+ }
+
+ // Fire handlers on the event path
+ for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
+
+ cur = eventPath[i][0];
+ event.type = eventPath[i][1];
+
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+ // Note that this is a bare JS function and not a jQuery handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
+ event.preventDefault();
+ }
+ }
+ event.type = type;
+
+ // If nobody prevented the default action, do it now
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ // IE<9 dies on focus/blur to hidden element (#1486)
+ if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ old = elem[ ontype ];
+
+ if ( old ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ jQuery.event.triggered = undefined;
+
+ if ( old ) {
+ elem[ ontype ] = old;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event || window.event );
+
+ var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related,
+ handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
+ delegateCount = handlers.delegateCount,
+ args = core_slice.call( arguments ),
+ run_all = !event.exclusive && !event.namespace,
+ special = jQuery.event.special[ event.type ] || {},
+ handlerQueue = [];
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[0] = event;
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers that should run if there are delegated events
+ // Avoid non-left-click bubbling in Firefox (#3861)
+ if ( delegateCount && !(event.button && event.type === "click") ) {
+
+ for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
+
+ // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764)
+ if ( cur.disabled !== true || event.type !== "click" ) {
+ selMatch = {};
+ matches = [];
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+ sel = handleObj.selector;
+
+ if ( selMatch[ sel ] === undefined ) {
+ selMatch[ sel ] = handleObj.needsContext ?
+ jQuery( sel, this ).index( cur ) >= 0 :
+ jQuery.find( sel, this, null, [ cur ] ).length;
+ }
+ if ( selMatch[ sel ] ) {
+ matches.push( handleObj );
+ }
+ }
+ if ( matches.length ) {
+ handlerQueue.push({ elem: cur, matches: matches });
+ }
+ }
+ }
+ }
+
+ // Add the remaining (directly-bound) handlers
+ if ( handlers.length > delegateCount ) {
+ handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
+ }
+
+ // Run delegates first; they may want to stop propagation beneath us
+ for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
+ matched = handlerQueue[ i ];
+ event.currentTarget = matched.elem;
+
+ for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
+ handleObj = matched.matches[ j ];
+
+ // Triggered event must either 1) be non-exclusive and have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+ if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
+ return event.result;
+ },
+
+ // Includes some event props shared by KeyEvent and MouseEvent
+ // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
+ props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+ filter: function( event, original ) {
+ var eventDoc, doc, body,
+ button = original.button,
+ fromElement = original.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && fromElement ) {
+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+ }
+
+ return event;
+ }
+ },
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop,
+ originalEvent = event,
+ fixHook = jQuery.event.fixHooks[ event.type ] || {},
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+ event = jQuery.Event( originalEvent );
+
+ for ( i = copy.length; i; ) {
+ prop = copy[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
+ if ( !event.target ) {
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Target should not be a text node (#504, Safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8)
+ event.metaKey = !!event.metaKey;
+
+ return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
+ },
+
+ special: {
+ load: {
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
+
+ focus: {
+ delegateType: "focusin"
+ },
+ blur: {
+ delegateType: "focusout"
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( jQuery.isWindow( this ) ) {
+ this.onbeforeunload = eventHandle;
+ }
+ },
+
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ },
+
+ simulate: function( type, elem, event, bubble ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ { type: type,
+ isSimulated: true,
+ originalEvent: {}
+ }
+ );
+ if ( bubble ) {
+ jQuery.event.trigger( e, null, elem );
+ } else {
+ jQuery.event.dispatch.call( elem, e );
+ }
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
+ }
+};
+
+// Some plugins are using, but it's undocumented/deprecated and will be removed.
+// The 1.7 special event interface should provide all the hooks needed now.
+jQuery.event.handle = jQuery.event.dispatch;
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ var name = "on" + type;
+
+ if ( elem.detachEvent ) {
+
+ // #8545, #7054, preventing memory leaks for custom events in IE6-8
+ // detachEvent needed property on element, by name of that event, to properly expose it to GC
+ if ( typeof elem[ name ] === "undefined" ) {
+ elem[ name ] = null;
+ }
+
+ elem.detachEvent( name, handle );
+ }
+ };
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !(this instanceof jQuery.Event) ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // otherwise set the returnValue property of the original event to false (IE)
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var ret,
+ target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj,
+ selector = handleObj.selector;
+
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
+ }
+ };
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+ // Node name check avoids a VML-related crash in IE (#9807)
+ var elem = e.target,
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+ if ( form && !jQuery._data( form, "_submit_attached" ) ) {
+ jQuery.event.add( form, "submit._submit", function( event ) {
+ event._submit_bubble = true;
+ });
+ jQuery._data( form, "_submit_attached", true );
+ }
+ });
+ // return undefined since we don't need an event listener
+ },
+
+ postDispatch: function( event ) {
+ // If form was submitted by the user, bubble the event up the tree
+ if ( event._submit_bubble ) {
+ delete event._submit_bubble;
+ if ( this.parentNode && !event.isTrigger ) {
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
+ }
+ }
+ },
+
+ teardown: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+ jQuery.event.remove( this, "._submit" );
+ }
+ };
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+ jQuery.event.special.change = {
+
+ setup: function() {
+
+ if ( rformElems.test( this.nodeName ) ) {
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
+ // after a propertychange. Eat the blur-change in special.change.handle.
+ // This still fires onchange a second time for check/radio after blur.
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ jQuery.event.add( this, "propertychange._change", function( event ) {
+ if ( event.originalEvent.propertyName === "checked" ) {
+ this._just_changed = true;
+ }
+ });
+ jQuery.event.add( this, "click._change", function( event ) {
+ if ( this._just_changed && !event.isTrigger ) {
+ this._just_changed = false;
+ }
+ // Allow triggered, simulated change events (#11500)
+ jQuery.event.simulate( "change", this, event, true );
+ });
+ }
+ return false;
+ }
+ // Delegated event; lazy-add a change handler on descendant inputs
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
+ var elem = e.target;
+
+ if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) {
+ jQuery.event.add( elem, "change._change", function( event ) {
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+ jQuery.event.simulate( "change", this.parentNode, event, true );
+ }
+ });
+ jQuery._data( elem, "_change_attached", true );
+ }
+ });
+ },
+
+ handle: function( event ) {
+ var elem = event.target;
+
+ // Swallow native change events from checkbox/radio, we already triggered them above
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+ return event.handleObj.handler.apply( this, arguments );
+ }
+ },
+
+ teardown: function() {
+ jQuery.event.remove( this, "._change" );
+
+ return !rformElems.test( this.nodeName );
+ }
+ };
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler while someone wants focusin/focusout
+ var attaches = 0,
+ handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+ };
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( attaches++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --attaches === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+ });
+}
+
+jQuery.fn.extend({
+
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+ var origFn, type;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) { // && selector != null
+ // ( types-Object, data )
+ data = data || selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ this.on( type, selector, data, types[ type ], one );
+ }
+ return this;
+ }
+
+ if ( data == null && fn == null ) {
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return this;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
+ };
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return this.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ });
+ },
+ one: function( types, selector, data, fn ) {
+ return this.on( types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ var handleObj, type;
+ if ( types && types.preventDefault && types.handleObj ) {
+ // ( event ) dispatched jQuery.Event
+ handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
+ }
+ if ( typeof types === "object" ) {
+ // ( types-object [, selector] )
+ for ( type in types ) {
+ this.off( type, selector, types[ type ] );
+ }
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ }
+ return this.each(function() {
+ jQuery.event.remove( this, types, fn, selector );
+ });
+ },
+
+ bind: function( types, data, fn ) {
+ return this.on( types, null, data, fn );
+ },
+ unbind: function( types, fn ) {
+ return this.off( types, null, fn );
+ },
+
+ live: function( types, data, fn ) {
+ jQuery( this.context ).on( types, this.selector, data, fn );
+ return this;
+ },
+ die: function( types, fn ) {
+ jQuery( this.context ).off( types, this.selector || "**", fn );
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.on( types, selector, data, fn );
+ },
+ undelegate: function( selector, types, fn ) {
+ // ( namespace ) or ( selector, types [, fn] )
+ return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ return jQuery.event.trigger( type, data, this[0], true );
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
+
+ // link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
+ while ( i < args.length ) {
+ args[ i++ ].guid = guid;
+ }
+
+ return this.click( toggler );
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ if ( fn == null ) {
+ fn = data;
+ data = null;
+ }
+
+ return arguments.length > 0 ?
+ this.on( name, null, data, fn ) :
+ this.trigger( name );
+ };
+
+ if ( rkeyEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
+ }
+
+ if ( rmouseEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
+ }
+});
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://sizzlejs.com/
+ */
+(function( window, undefined ) {
+
+var cachedruns,
+ assertGetIdNotName,
+ Expr,
+ getText,
+ isXML,
+ contains,
+ compile,
+ sortOrder,
+ hasDuplicate,
+ outermostContext,
+
+ baseHasDuplicate = true,
+ strundefined = "undefined",
+
+ expando = ( "sizcache" + Math.random() ).replace( ".", "" ),
+
+ Token = String,
+ document = window.document,
+ docElem = document.documentElement,
+ dirruns = 0,
+ done = 0,
+ pop = [].pop,
+ push = [].push,
+ slice = [].slice,
+ // Use a stripped-down indexOf if a native one is unavailable
+ indexOf = [].indexOf || function( elem ) {
+ var i = 0,
+ len = this.length;
+ for ( ; i < len; i++ ) {
+ if ( this[i] === elem ) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ // Augment a function for special use by Sizzle
+ markFunction = function( fn, value ) {
+ fn[ expando ] = value == null || value;
+ return fn;
+ },
+
+ createCache = function() {
+ var cache = {},
+ keys = [];
+
+ return markFunction(function( key, value ) {
+ // Only keep the most recent entries
+ if ( keys.push( key ) > Expr.cacheLength ) {
+ delete cache[ keys.shift() ];
+ }
+
+ // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157)
+ return (cache[ key + " " ] = value);
+ }, cache );
+ },
+
+ classCache = createCache(),
+ tokenCache = createCache(),
+ compilerCache = createCache(),
+
+ // Regex
+
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+ whitespace = "[\\x20\\t\\r\\n\\f]",
+ // http://www.w3.org/TR/css3-syntax/#characters
+ characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",
+
+ // Loosely modeled on CSS identifier characters
+ // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+ identifier = characterEncoding.replace( "w", "w#" ),
+
+ // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
+ operators = "([*^$|!~]?=)",
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+ "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+
+ // Prefer arguments not in parens/brackets,
+ // then attribute selectors and non-pseudos (denoted by :),
+ // then anything else
+ // These preferences are here to reduce the number of selectors
+ // needing tokenize in the PSEUDO preFilter
+ pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)",
+
+ // For matchExpr.POS and matchExpr.needsContext
+ pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
+ "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)",
+
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+ rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
+ rpseudo = new RegExp( pseudos ),
+
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
+ rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,
+
+ rnot = /^:not/,
+ rsibling = /[\x20\t\r\n\f]*[+~]/,
+ rendsWithNot = /:not\($/,
+
+ rheader = /h\d/i,
+ rinputs = /input|select|textarea|button/i,
+
+ rbackslash = /\\(?!\\)/g,
+
+ matchExpr = {
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+ "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+ "ATTR": new RegExp( "^" + attributes ),
+ "PSEUDO": new RegExp( "^" + pseudos ),
+ "POS": new RegExp( pos, "i" ),
+ "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace +
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ // For use in libraries implementing .is()
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" )
+ },
+
+ // Support
+
+ // Used for testing something on an element
+ assert = function( fn ) {
+ var div = document.createElement("div");
+
+ try {
+ return fn( div );
+ } catch (e) {
+ return false;
+ } finally {
+ // release memory in IE
+ div = null;
+ }
+ },
+
+ // Check if getElementsByTagName("*") returns only elements
+ assertTagNameNoComments = assert(function( div ) {
+ div.appendChild( document.createComment("") );
+ return !div.getElementsByTagName("*").length;
+ }),
+
+ // Check if getAttribute returns normalized href attributes
+ assertHrefNotNormalized = assert(function( div ) {
+ div.innerHTML = " ";
+ return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
+ div.firstChild.getAttribute("href") === "#";
+ }),
+
+ // Check if attributes should be retrieved by attribute nodes
+ assertAttributes = assert(function( div ) {
+ div.innerHTML = " ";
+ var type = typeof div.lastChild.getAttribute("multiple");
+ // IE8 returns a string for some attributes even when not present
+ return type !== "boolean" && type !== "string";
+ }),
+
+ // Check if getElementsByClassName can be trusted
+ assertUsableClassName = assert(function( div ) {
+ // Opera can't find a second classname (in 9.6)
+ div.innerHTML = "
";
+ if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
+ return false;
+ }
+
+ // Safari 3.2 caches class attributes and doesn't catch changes
+ div.lastChild.className = "e";
+ return div.getElementsByClassName("e").length === 2;
+ }),
+
+ // Check if getElementById returns elements by name
+ // Check if getElementsByName privileges form controls or returns elements by ID
+ assertUsableName = assert(function( div ) {
+ // Inject content
+ div.id = expando + 0;
+ div.innerHTML = "
";
+ docElem.insertBefore( div, docElem.firstChild );
+
+ // Test
+ var pass = document.getElementsByName &&
+ // buggy browsers will return fewer than the correct 2
+ document.getElementsByName( expando ).length === 2 +
+ // buggy browsers will return more than the correct 0
+ document.getElementsByName( expando + 0 ).length;
+ assertGetIdNotName = !document.getElementById( expando );
+
+ // Cleanup
+ docElem.removeChild( div );
+
+ return pass;
+ });
+
+// If slice is not available, provide a backup
+try {
+ slice.call( docElem.childNodes, 0 )[0].nodeType;
+} catch ( e ) {
+ slice = function( i ) {
+ var elem,
+ results = [];
+ for ( ; (elem = this[i]); i++ ) {
+ results.push( elem );
+ }
+ return results;
+ };
+}
+
+function Sizzle( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+ var match, elem, xml, m,
+ nodeType = context.nodeType;
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ if ( nodeType !== 1 && nodeType !== 9 ) {
+ return [];
+ }
+
+ xml = isXML( context );
+
+ if ( !xml && !seed ) {
+ if ( (match = rquickExpr.exec( selector )) ) {
+ // Speed-up: Sizzle("#ID")
+ if ( (m = match[1]) ) {
+ if ( nodeType === 9 ) {
+ elem = context.getElementById( m );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE, Opera, and Webkit return items
+ // by name instead of ID
+ if ( elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ } else {
+ return results;
+ }
+ } else {
+ // Context is not a document
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+ contains( context, elem ) && elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ }
+
+ // Speed-up: Sizzle("TAG")
+ } else if ( match[2] ) {
+ push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
+ return results;
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {
+ push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
+ return results;
+ }
+ }
+ }
+
+ // All others
+ return select( selector.replace( rtrim, "$1" ), context, results, seed, xml );
+}
+
+Sizzle.matches = function( expr, elements ) {
+ return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+ return Sizzle( expr, null, null, [ elem ] ).length > 0;
+};
+
+// Returns a function to use in pseudos for input types
+function createInputPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === type;
+ };
+}
+
+// Returns a function to use in pseudos for buttons
+function createButtonPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && elem.type === type;
+ };
+}
+
+// Returns a function to use in pseudos for positionals
+function createPositionalPseudo( fn ) {
+ return markFunction(function( argument ) {
+ argument = +argument;
+ return markFunction(function( seed, matches ) {
+ var j,
+ matchIndexes = fn( [], seed.length, argument ),
+ i = matchIndexes.length;
+
+ // Match elements found at the specified indexes
+ while ( i-- ) {
+ if ( seed[ (j = matchIndexes[i]) ] ) {
+ seed[j] = !(matches[j] = seed[j]);
+ }
+ }
+ });
+ });
+}
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+ var node,
+ ret = "",
+ i = 0,
+ nodeType = elem.nodeType;
+
+ if ( nodeType ) {
+ if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent for elements
+ // innerText usage removed for consistency of new lines (see #11153)
+ if ( typeof elem.textContent === "string" ) {
+ return elem.textContent;
+ } else {
+ // Traverse its children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ // Do not include comment or processing instruction nodes
+ } else {
+
+ // If no nodeType, this is expected to be an array
+ for ( ; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ ret += getText( node );
+ }
+ }
+ return ret;
+};
+
+isXML = Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+// Element contains another
+contains = Sizzle.contains = docElem.contains ?
+ function( a, b ) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
+ } :
+ docElem.compareDocumentPosition ?
+ function( a, b ) {
+ return b && !!( a.compareDocumentPosition( b ) & 16 );
+ } :
+ function( a, b ) {
+ while ( (b = b.parentNode) ) {
+ if ( b === a ) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+Sizzle.attr = function( elem, name ) {
+ var val,
+ xml = isXML( elem );
+
+ if ( !xml ) {
+ name = name.toLowerCase();
+ }
+ if ( (val = Expr.attrHandle[ name ]) ) {
+ return val( elem );
+ }
+ if ( xml || assertAttributes ) {
+ return elem.getAttribute( name );
+ }
+ val = elem.getAttributeNode( name );
+ return val ?
+ typeof elem[ name ] === "boolean" ?
+ elem[ name ] ? name : null :
+ val.specified ? val.value : null :
+ null;
+};
+
+Expr = Sizzle.selectors = {
+
+ // Can be adjusted by the user
+ cacheLength: 50,
+
+ createPseudo: markFunction,
+
+ match: matchExpr,
+
+ // IE6/7 return a modified href
+ attrHandle: assertHrefNotNormalized ?
+ {} :
+ {
+ "href": function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ },
+ "type": function( elem ) {
+ return elem.getAttribute("type");
+ }
+ },
+
+ find: {
+ "ID": assertGetIdNotName ?
+ function( id, context, xml ) {
+ if ( typeof context.getElementById !== strundefined && !xml ) {
+ var m = context.getElementById( id );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ } :
+ function( id, context, xml ) {
+ if ( typeof context.getElementById !== strundefined && !xml ) {
+ var m = context.getElementById( id );
+
+ return m ?
+ m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
+ [m] :
+ undefined :
+ [];
+ }
+ },
+
+ "TAG": assertTagNameNoComments ?
+ function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== strundefined ) {
+ return context.getElementsByTagName( tag );
+ }
+ } :
+ function( tag, context ) {
+ var results = context.getElementsByTagName( tag );
+
+ // Filter out possible comments
+ if ( tag === "*" ) {
+ var elem,
+ tmp = [],
+ i = 0;
+
+ for ( ; (elem = results[i]); i++ ) {
+ if ( elem.nodeType === 1 ) {
+ tmp.push( elem );
+ }
+ }
+
+ return tmp;
+ }
+ return results;
+ },
+
+ "NAME": assertUsableName && function( tag, context ) {
+ if ( typeof context.getElementsByName !== strundefined ) {
+ return context.getElementsByName( name );
+ }
+ },
+
+ "CLASS": assertUsableClassName && function( className, context, xml ) {
+ if ( typeof context.getElementsByClassName !== strundefined && !xml ) {
+ return context.getElementsByClassName( className );
+ }
+ }
+ },
+
+ relative: {
+ ">": { dir: "parentNode", first: true },
+ " ": { dir: "parentNode" },
+ "+": { dir: "previousSibling", first: true },
+ "~": { dir: "previousSibling" }
+ },
+
+ preFilter: {
+ "ATTR": function( match ) {
+ match[1] = match[1].replace( rbackslash, "" );
+
+ // Move the given value to match[3] whether quoted or unquoted
+ match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" );
+
+ if ( match[2] === "~=" ) {
+ match[3] = " " + match[3] + " ";
+ }
+
+ return match.slice( 0, 4 );
+ },
+
+ "CHILD": function( match ) {
+ /* matches from matchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 3 xn-component of xn+y argument ([+-]?\d*n|)
+ 4 sign of xn-component
+ 5 x of xn-component
+ 6 sign of y-component
+ 7 y of y-component
+ */
+ match[1] = match[1].toLowerCase();
+
+ if ( match[1] === "nth" ) {
+ // nth-child requires argument
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // numeric x and y parameters for Expr.filter.CHILD
+ // remember that false/true cast respectively to 0/1
+ match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) );
+ match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" );
+
+ // other types prohibit arguments
+ } else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ return match;
+ },
+
+ "PSEUDO": function( match ) {
+ var unquoted, excess;
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
+ return null;
+ }
+
+ if ( match[3] ) {
+ match[2] = match[3];
+ } else if ( (unquoted = match[4]) ) {
+ // Only check arguments that contain a pseudo
+ if ( rpseudo.test(unquoted) &&
+ // Get excess from tokenize (recursively)
+ (excess = tokenize( unquoted, true )) &&
+ // advance to the next closing parenthesis
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+ // excess is a negative index
+ unquoted = unquoted.slice( 0, excess );
+ match[0] = match[0].slice( 0, excess );
+ }
+ match[2] = unquoted;
+ }
+
+ // Return only captures needed by the pseudo filter method (type and argument)
+ return match.slice( 0, 3 );
+ }
+ },
+
+ filter: {
+ "ID": assertGetIdNotName ?
+ function( id ) {
+ id = id.replace( rbackslash, "" );
+ return function( elem ) {
+ return elem.getAttribute("id") === id;
+ };
+ } :
+ function( id ) {
+ id = id.replace( rbackslash, "" );
+ return function( elem ) {
+ var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+ return node && node.value === id;
+ };
+ },
+
+ "TAG": function( nodeName ) {
+ if ( nodeName === "*" ) {
+ return function() { return true; };
+ }
+ nodeName = nodeName.replace( rbackslash, "" ).toLowerCase();
+
+ return function( elem ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ };
+ },
+
+ "CLASS": function( className ) {
+ var pattern = classCache[ expando ][ className + " " ];
+
+ return pattern ||
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+ classCache( className, function( elem ) {
+ return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
+ });
+ },
+
+ "ATTR": function( name, operator, check ) {
+ return function( elem, context ) {
+ var result = Sizzle.attr( elem, name );
+
+ if ( result == null ) {
+ return operator === "!=";
+ }
+ if ( !operator ) {
+ return true;
+ }
+
+ result += "";
+
+ return operator === "=" ? result === check :
+ operator === "!=" ? result !== check :
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
+ operator === "$=" ? check && result.substr( result.length - check.length ) === check :
+ operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+ operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" :
+ false;
+ };
+ },
+
+ "CHILD": function( type, argument, first, last ) {
+
+ if ( type === "nth" ) {
+ return function( elem ) {
+ var node, diff,
+ parent = elem.parentNode;
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ if ( parent ) {
+ diff = 0;
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ diff++;
+ if ( elem === node ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Incorporate the offset (or cast to NaN), then check against cycle size
+ diff -= last;
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
+ };
+ }
+
+ return function( elem ) {
+ var node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ if ( type === "first" ) {
+ return true;
+ }
+
+ node = elem;
+
+ /* falls through */
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ };
+ },
+
+ "PSEUDO": function( pseudo, argument ) {
+ // pseudo-class names are case-insensitive
+ // http://www.w3.org/TR/selectors/#pseudo-classes
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+ // Remember that setFilters inherits from pseudos
+ var args,
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+ Sizzle.error( "unsupported pseudo: " + pseudo );
+
+ // The user may use createPseudo to indicate that
+ // arguments are needed to create the filter function
+ // just as Sizzle does
+ if ( fn[ expando ] ) {
+ return fn( argument );
+ }
+
+ // But maintain support for old signatures
+ if ( fn.length > 1 ) {
+ args = [ pseudo, pseudo, "", argument ];
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+ markFunction(function( seed, matches ) {
+ var idx,
+ matched = fn( seed, argument ),
+ i = matched.length;
+ while ( i-- ) {
+ idx = indexOf.call( seed, matched[i] );
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
+ }
+ }) :
+ function( elem ) {
+ return fn( elem, 0, args );
+ };
+ }
+
+ return fn;
+ }
+ },
+
+ pseudos: {
+ "not": markFunction(function( selector ) {
+ // Trim the selector passed to compile
+ // to avoid treating leading and trailing
+ // spaces as combinators
+ var input = [],
+ results = [],
+ matcher = compile( selector.replace( rtrim, "$1" ) );
+
+ return matcher[ expando ] ?
+ markFunction(function( seed, matches, context, xml ) {
+ var elem,
+ unmatched = matcher( seed, null, xml, [] ),
+ i = seed.length;
+
+ // Match elements unmatched by `matcher`
+ while ( i-- ) {
+ if ( (elem = unmatched[i]) ) {
+ seed[i] = !(matches[i] = elem);
+ }
+ }
+ }) :
+ function( elem, context, xml ) {
+ input[0] = elem;
+ matcher( input, null, xml, results );
+ return !results.pop();
+ };
+ }),
+
+ "has": markFunction(function( selector ) {
+ return function( elem ) {
+ return Sizzle( selector, elem ).length > 0;
+ };
+ }),
+
+ "contains": markFunction(function( text ) {
+ return function( elem ) {
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+ };
+ }),
+
+ "enabled": function( elem ) {
+ return elem.disabled === false;
+ },
+
+ "disabled": function( elem ) {
+ return elem.disabled === true;
+ },
+
+ "checked": function( elem ) {
+ // In CSS3, :checked should return both checked and selected elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ var nodeName = elem.nodeName.toLowerCase();
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+ },
+
+ "selected": function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ "parent": function( elem ) {
+ return !Expr.pseudos["empty"]( elem );
+ },
+
+ "empty": function( elem ) {
+ // http://www.w3.org/TR/selectors/#empty-pseudo
+ // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
+ // not comment, processing instructions, or others
+ // Thanks to Diego Perini for the nodeName shortcut
+ // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
+ var nodeType;
+ elem = elem.firstChild;
+ while ( elem ) {
+ if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {
+ return false;
+ }
+ elem = elem.nextSibling;
+ }
+ return true;
+ },
+
+ "header": function( elem ) {
+ return rheader.test( elem.nodeName );
+ },
+
+ "text": function( elem ) {
+ var type, attr;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" &&
+ (type = elem.type) === "text" &&
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type );
+ },
+
+ // Input types
+ "radio": createInputPseudo("radio"),
+ "checkbox": createInputPseudo("checkbox"),
+ "file": createInputPseudo("file"),
+ "password": createInputPseudo("password"),
+ "image": createInputPseudo("image"),
+
+ "submit": createButtonPseudo("submit"),
+ "reset": createButtonPseudo("reset"),
+
+ "button": function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === "button" || name === "button";
+ },
+
+ "input": function( elem ) {
+ return rinputs.test( elem.nodeName );
+ },
+
+ "focus": function( elem ) {
+ var doc = elem.ownerDocument;
+ return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+ },
+
+ "active": function( elem ) {
+ return elem === elem.ownerDocument.activeElement;
+ },
+
+ // Positional types
+ "first": createPositionalPseudo(function() {
+ return [ 0 ];
+ }),
+
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
+ return [ length - 1 ];
+ }),
+
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ return [ argument < 0 ? argument + length : argument ];
+ }),
+
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
+ for ( var i = 0; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
+ for ( var i = 1; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ })
+ }
+};
+
+function siblingCheck( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+}
+
+sortOrder = docElem.compareDocumentPosition ?
+ function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?
+ a.compareDocumentPosition :
+ a.compareDocumentPosition(b) & 4
+ ) ? -1 : 1;
+ } :
+ function( a, b ) {
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes
+ } else if ( a.sourceIndex && b.sourceIndex ) {
+ return a.sourceIndex - b.sourceIndex;
+ }
+
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+// Always assume the presence of duplicates if sort doesn't
+// pass them to our comparison function (as in Google Chrome).
+[0, 0].sort( sortOrder );
+baseHasDuplicate = !hasDuplicate;
+
+// Document sorting and removing duplicates
+Sizzle.uniqueSort = function( results ) {
+ var elem,
+ duplicates = [],
+ i = 1,
+ j = 0;
+
+ hasDuplicate = baseHasDuplicate;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( ; (elem = results[i]); i++ ) {
+ if ( elem === results[ i - 1 ] ) {
+ j = duplicates.push( i );
+ }
+ }
+ while ( j-- ) {
+ results.splice( duplicates[ j ], 1 );
+ }
+ }
+
+ return results;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+function tokenize( selector, parseOnly ) {
+ var matched, match, tokens, type,
+ soFar, groups, preFilters,
+ cached = tokenCache[ expando ][ selector + " " ];
+
+ if ( cached ) {
+ return parseOnly ? 0 : cached.slice( 0 );
+ }
+
+ soFar = selector;
+ groups = [];
+ preFilters = Expr.preFilter;
+
+ while ( soFar ) {
+
+ // Comma and first run
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
+ if ( match ) {
+ // Don't consume trailing commas as valid
+ soFar = soFar.slice( match[0].length ) || soFar;
+ }
+ groups.push( tokens = [] );
+ }
+
+ matched = false;
+
+ // Combinators
+ if ( (match = rcombinators.exec( soFar )) ) {
+ tokens.push( matched = new Token( match.shift() ) );
+ soFar = soFar.slice( matched.length );
+
+ // Cast descendant combinators to space
+ matched.type = match[0].replace( rtrim, " " );
+ }
+
+ // Filters
+ for ( type in Expr.filter ) {
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+ (match = preFilters[ type ]( match ))) ) {
+
+ tokens.push( matched = new Token( match.shift() ) );
+ soFar = soFar.slice( matched.length );
+ matched.type = type;
+ matched.matches = match;
+ }
+ }
+
+ if ( !matched ) {
+ break;
+ }
+ }
+
+ // Return the length of the invalid excess
+ // if we're just parsing
+ // Otherwise, throw an error or return tokens
+ return parseOnly ?
+ soFar.length :
+ soFar ?
+ Sizzle.error( selector ) :
+ // Cache the tokens
+ tokenCache( selector, groups ).slice( 0 );
+}
+
+function addCombinator( matcher, combinator, base ) {
+ var dir = combinator.dir,
+ checkNonElements = base && combinator.dir === "parentNode",
+ doneName = done++;
+
+ return combinator.first ?
+ // Check against closest ancestor/preceding element
+ function( elem, context, xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( checkNonElements || elem.nodeType === 1 ) {
+ return matcher( elem, context, xml );
+ }
+ }
+ } :
+
+ // Check against all ancestor/preceding elements
+ function( elem, context, xml ) {
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+ if ( !xml ) {
+ var cache,
+ dirkey = dirruns + " " + doneName + " ",
+ cachedkey = dirkey + cachedruns;
+ while ( (elem = elem[ dir ]) ) {
+ if ( checkNonElements || elem.nodeType === 1 ) {
+ if ( (cache = elem[ expando ]) === cachedkey ) {
+ return elem.sizset;
+ } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) {
+ if ( elem.sizset ) {
+ return elem;
+ }
+ } else {
+ elem[ expando ] = cachedkey;
+ if ( matcher( elem, context, xml ) ) {
+ elem.sizset = true;
+ return elem;
+ }
+ elem.sizset = false;
+ }
+ }
+ }
+ } else {
+ while ( (elem = elem[ dir ]) ) {
+ if ( checkNonElements || elem.nodeType === 1 ) {
+ if ( matcher( elem, context, xml ) ) {
+ return elem;
+ }
+ }
+ }
+ }
+ };
+}
+
+function elementMatcher( matchers ) {
+ return matchers.length > 1 ?
+ function( elem, context, xml ) {
+ var i = matchers.length;
+ while ( i-- ) {
+ if ( !matchers[i]( elem, context, xml ) ) {
+ return false;
+ }
+ }
+ return true;
+ } :
+ matchers[0];
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+ var elem,
+ newUnmatched = [],
+ i = 0,
+ len = unmatched.length,
+ mapped = map != null;
+
+ for ( ; i < len; i++ ) {
+ if ( (elem = unmatched[i]) ) {
+ if ( !filter || filter( elem, context, xml ) ) {
+ newUnmatched.push( elem );
+ if ( mapped ) {
+ map.push( i );
+ }
+ }
+ }
+ }
+
+ return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+ if ( postFilter && !postFilter[ expando ] ) {
+ postFilter = setMatcher( postFilter );
+ }
+ if ( postFinder && !postFinder[ expando ] ) {
+ postFinder = setMatcher( postFinder, postSelector );
+ }
+ return markFunction(function( seed, results, context, xml ) {
+ var temp, i, elem,
+ preMap = [],
+ postMap = [],
+ preexisting = results.length,
+
+ // Get initial elements from seed or context
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
+ matcherIn = preFilter && ( seed || !selector ) ?
+ condense( elems, preMap, preFilter, context, xml ) :
+ elems,
+
+ matcherOut = matcher ?
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+ // ...intermediate processing is necessary
+ [] :
+
+ // ...otherwise use results directly
+ results :
+ matcherIn;
+
+ // Find primary matches
+ if ( matcher ) {
+ matcher( matcherIn, matcherOut, context, xml );
+ }
+
+ // Apply postFilter
+ if ( postFilter ) {
+ temp = condense( matcherOut, postMap );
+ postFilter( temp, [], context, xml );
+
+ // Un-match failing elements by moving them back to matcherIn
+ i = temp.length;
+ while ( i-- ) {
+ if ( (elem = temp[i]) ) {
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+ }
+ }
+ }
+
+ if ( seed ) {
+ if ( postFinder || preFilter ) {
+ if ( postFinder ) {
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
+ temp = [];
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) ) {
+ // Restore matcherIn since elem is not yet a final match
+ temp.push( (matcherIn[i] = elem) );
+ }
+ }
+ postFinder( null, (matcherOut = []), temp, xml );
+ }
+
+ // Move matched elements from seed to results to keep them synchronized
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) &&
+ (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+ seed[temp] = !(results[temp] = elem);
+ }
+ }
+ }
+
+ // Add elements to results, through postFinder if defined
+ } else {
+ matcherOut = condense(
+ matcherOut === results ?
+ matcherOut.splice( preexisting, matcherOut.length ) :
+ matcherOut
+ );
+ if ( postFinder ) {
+ postFinder( null, results, matcherOut, xml );
+ } else {
+ push.apply( results, matcherOut );
+ }
+ }
+ });
+}
+
+function matcherFromTokens( tokens ) {
+ var checkContext, matcher, j,
+ len = tokens.length,
+ leadingRelative = Expr.relative[ tokens[0].type ],
+ implicitRelative = leadingRelative || Expr.relative[" "],
+ i = leadingRelative ? 1 : 0,
+
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
+ matchContext = addCombinator( function( elem ) {
+ return elem === checkContext;
+ }, implicitRelative, true ),
+ matchAnyContext = addCombinator( function( elem ) {
+ return indexOf.call( checkContext, elem ) > -1;
+ }, implicitRelative, true ),
+ matchers = [ function( elem, context, xml ) {
+ return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ (checkContext = context).nodeType ?
+ matchContext( elem, context, xml ) :
+ matchAnyContext( elem, context, xml ) );
+ } ];
+
+ for ( ; i < len; i++ ) {
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+ matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
+ } else {
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+ // Return special upon seeing a positional matcher
+ if ( matcher[ expando ] ) {
+ // Find the next relative operator (if any) for proper handling
+ j = ++i;
+ for ( ; j < len; j++ ) {
+ if ( Expr.relative[ tokens[j].type ] ) {
+ break;
+ }
+ }
+ return setMatcher(
+ i > 1 && elementMatcher( matchers ),
+ i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ),
+ matcher,
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+ j < len && tokens.join("")
+ );
+ }
+ matchers.push( matcher );
+ }
+ }
+
+ return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ var bySet = setMatchers.length > 0,
+ byElement = elementMatchers.length > 0,
+ superMatcher = function( seed, context, xml, results, expandContext ) {
+ var elem, j, matcher,
+ setMatched = [],
+ matchedCount = 0,
+ i = "0",
+ unmatched = seed && [],
+ outermost = expandContext != null,
+ contextBackup = outermostContext,
+ // We must always have either seed elements or context
+ elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
+ // Nested matchers should use non-integer dirruns
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E);
+
+ if ( outermost ) {
+ outermostContext = context !== document && context;
+ cachedruns = superMatcher.el;
+ }
+
+ // Add elements passing elementMatchers directly to results
+ for ( ; (elem = elems[i]) != null; i++ ) {
+ if ( byElement && elem ) {
+ for ( j = 0; (matcher = elementMatchers[j]); j++ ) {
+ if ( matcher( elem, context, xml ) ) {
+ results.push( elem );
+ break;
+ }
+ }
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ cachedruns = ++superMatcher.el;
+ }
+ }
+
+ // Track unmatched elements for set filters
+ if ( bySet ) {
+ // They will have gone through all possible matchers
+ if ( (elem = !matcher && elem) ) {
+ matchedCount--;
+ }
+
+ // Lengthen the array for every element, matched or not
+ if ( seed ) {
+ unmatched.push( elem );
+ }
+ }
+ }
+
+ // Apply set filters to unmatched elements
+ matchedCount += i;
+ if ( bySet && i !== matchedCount ) {
+ for ( j = 0; (matcher = setMatchers[j]); j++ ) {
+ matcher( unmatched, setMatched, context, xml );
+ }
+
+ if ( seed ) {
+ // Reintegrate element matches to eliminate the need for sorting
+ if ( matchedCount > 0 ) {
+ while ( i-- ) {
+ if ( !(unmatched[i] || setMatched[i]) ) {
+ setMatched[i] = pop.call( results );
+ }
+ }
+ }
+
+ // Discard index placeholder values to get only actual matches
+ setMatched = condense( setMatched );
+ }
+
+ // Add matches to results
+ push.apply( results, setMatched );
+
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
+ if ( outermost && !seed && setMatched.length > 0 &&
+ ( matchedCount + setMatchers.length ) > 1 ) {
+
+ Sizzle.uniqueSort( results );
+ }
+ }
+
+ // Override manipulation of globals by nested matchers
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ outermostContext = contextBackup;
+ }
+
+ return unmatched;
+ };
+
+ superMatcher.el = 0;
+ return bySet ?
+ markFunction( superMatcher ) :
+ superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
+ var i,
+ setMatchers = [],
+ elementMatchers = [],
+ cached = compilerCache[ expando ][ selector + " " ];
+
+ if ( !cached ) {
+ // Generate a function of recursive functions that can be used to check each element
+ if ( !group ) {
+ group = tokenize( selector );
+ }
+ i = group.length;
+ while ( i-- ) {
+ cached = matcherFromTokens( group[i] );
+ if ( cached[ expando ] ) {
+ setMatchers.push( cached );
+ } else {
+ elementMatchers.push( cached );
+ }
+ }
+
+ // Cache the compiled function
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+ }
+ return cached;
+};
+
+function multipleContexts( selector, contexts, results ) {
+ var i = 0,
+ len = contexts.length;
+ for ( ; i < len; i++ ) {
+ Sizzle( selector, contexts[i], results );
+ }
+ return results;
+}
+
+function select( selector, context, results, seed, xml ) {
+ var i, tokens, token, type, find,
+ match = tokenize( selector ),
+ j = match.length;
+
+ if ( !seed ) {
+ // Try to minimize operations if there is only one group
+ if ( match.length === 1 ) {
+
+ // Take a shortcut and set the context if the root selector is an ID
+ tokens = match[0] = match[0].slice( 0 );
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+ context.nodeType === 9 && !xml &&
+ Expr.relative[ tokens[1].type ] ) {
+
+ context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0];
+ if ( !context ) {
+ return results;
+ }
+
+ selector = selector.slice( tokens.shift().length );
+ }
+
+ // Fetch a seed set for right-to-left matching
+ for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) {
+ token = tokens[i];
+
+ // Abort if we hit a combinator
+ if ( Expr.relative[ (type = token.type) ] ) {
+ break;
+ }
+ if ( (find = Expr.find[ type ]) ) {
+ // Search, expanding context for leading sibling combinators
+ if ( (seed = find(
+ token.matches[0].replace( rbackslash, "" ),
+ rsibling.test( tokens[0].type ) && context.parentNode || context,
+ xml
+ )) ) {
+
+ // If seed is empty or no tokens remain, we can return early
+ tokens.splice( i, 1 );
+ selector = seed.length && tokens.join("");
+ if ( !selector ) {
+ push.apply( results, slice.call( seed, 0 ) );
+ return results;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Compile and execute a filtering function
+ // Provide `match` to avoid retokenization if we modified the selector above
+ compile( selector, match )(
+ seed,
+ context,
+ xml,
+ results,
+ rsibling.test( selector )
+ );
+ return results;
+}
+
+if ( document.querySelectorAll ) {
+ (function() {
+ var disconnectedMatch,
+ oldSelect = select,
+ rescape = /'|\\/g,
+ rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
+
+ // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA
+ // A support test would require too much code (would include document ready)
+ rbuggyQSA = [ ":focus" ],
+
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+ // A support test would require too much code (would include document ready)
+ // just skip matchesSelector for :active
+ rbuggyMatches = [ ":active" ],
+ matches = docElem.matchesSelector ||
+ docElem.mozMatchesSelector ||
+ docElem.webkitMatchesSelector ||
+ docElem.oMatchesSelector ||
+ docElem.msMatchesSelector;
+
+ // Build QSA regex
+ // Regex strategy adopted from Diego Perini
+ assert(function( div ) {
+ // Select is set to empty string on purpose
+ // This is to test IE's treatment of not explictly
+ // setting a boolean content attribute,
+ // since its presence should be enough
+ // http://bugs.jquery.com/ticket/12359
+ div.innerHTML = " ";
+
+ // IE8 - Some boolean attributes are not treated correctly
+ if ( !div.querySelectorAll("[selected]").length ) {
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
+ }
+
+ // Webkit/Opera - :checked should return selected option elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ // IE8 throws error here (do not put tests after this one)
+ if ( !div.querySelectorAll(":checked").length ) {
+ rbuggyQSA.push(":checked");
+ }
+ });
+
+ assert(function( div ) {
+
+ // Opera 10-12/IE9 - ^= $= *= and empty values
+ // Should not select anything
+ div.innerHTML = "
";
+ if ( div.querySelectorAll("[test^='']").length ) {
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
+ }
+
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ // IE8 throws error here (do not put tests after this one)
+ div.innerHTML = " ";
+ if ( !div.querySelectorAll(":enabled").length ) {
+ rbuggyQSA.push(":enabled", ":disabled");
+ }
+ });
+
+ // rbuggyQSA always contains :focus, so no need for a length check
+ rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") );
+
+ select = function( selector, context, results, seed, xml ) {
+ // Only use querySelectorAll when not filtering,
+ // when this is not xml,
+ // and when no QSA bugs apply
+ if ( !seed && !xml && !rbuggyQSA.test( selector ) ) {
+ var groups, i,
+ old = true,
+ nid = expando,
+ newContext = context,
+ newSelector = context.nodeType === 9 && selector;
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ groups = tokenize( selector );
+
+ if ( (old = context.getAttribute("id")) ) {
+ nid = old.replace( rescape, "\\$&" );
+ } else {
+ context.setAttribute( "id", nid );
+ }
+ nid = "[id='" + nid + "'] ";
+
+ i = groups.length;
+ while ( i-- ) {
+ groups[i] = nid + groups[i].join("");
+ }
+ newContext = rsibling.test( selector ) && context.parentNode || context;
+ newSelector = groups.join(",");
+ }
+
+ if ( newSelector ) {
+ try {
+ push.apply( results, slice.call( newContext.querySelectorAll(
+ newSelector
+ ), 0 ) );
+ return results;
+ } catch(qsaError) {
+ } finally {
+ if ( !old ) {
+ context.removeAttribute("id");
+ }
+ }
+ }
+ }
+
+ return oldSelect( selector, context, results, seed, xml );
+ };
+
+ if ( matches ) {
+ assert(function( div ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9)
+ disconnectedMatch = matches.call( div, "div" );
+
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ try {
+ matches.call( div, "[test!='']:sizzle" );
+ rbuggyMatches.push( "!=", pseudos );
+ } catch ( e ) {}
+ });
+
+ // rbuggyMatches always contains :active and :focus, so no need for a length check
+ rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") );
+
+ Sizzle.matchesSelector = function( elem, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace( rattributeQuotes, "='$1']" );
+
+ // rbuggyMatches always contains :active, so no need for an existence check
+ if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) {
+ try {
+ var ret = matches.call( elem, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9
+ elem.document && elem.document.nodeType !== 11 ) {
+ return ret;
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle( expr, null, null, [ elem ] ).length > 0;
+ };
+ }
+ })();
+}
+
+// Deprecated
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Back-compat
+function setFilters() {}
+Expr.filters = setFilters.prototype = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})( window );
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prev(?:Until|All))/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ rneedsContext = jQuery.expr.match.needsContext,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var i, l, length, n, r, ret,
+ self = this;
+
+ if ( typeof selector !== "string" ) {
+ return jQuery( selector ).filter(function() {
+ for ( i = 0, l = self.length; i < l; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ });
+ }
+
+ ret = this.pushStack( "", "find", selector );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( n = length; n < ret.length; n++ ) {
+ for ( r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var i,
+ targets = jQuery( target, this ),
+ len = targets.length;
+
+ return this.filter(function() {
+ for ( i = 0; i < len; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && (
+ typeof selector === "string" ?
+ // If this is a positional/relative selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ rneedsContext.test( selector ) ?
+ jQuery( selector, this.context ).index( this[0] ) >= 0 :
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
+
+ closest: function( selectors, context ) {
+ var cur,
+ i = 0,
+ l = this.length,
+ ret = [],
+ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( ; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+ }
+ cur = cur.parentNode;
+ }
+ }
+
+ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+ return this.pushStack( ret, "closest", selectors );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
+ }
+
+ // index in selector
+ if ( typeof elem === "string" ) {
+ return jQuery.inArray( this[0], jQuery( elem ) );
+ }
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context ) :
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ addBack: function( selector ) {
+ return this.add( selector == null ?
+ this.prevObject : this.prevObject.filter(selector)
+ );
+ }
+});
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+function sibling( cur, dir ) {
+ do {
+ cur = cur[ dir ];
+ } while ( cur && cur.nodeType !== 1 );
+
+ return cur;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return sibling( elem, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return sibling( elem, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.merge( [], elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+ if ( this.length > 1 && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, core_slice.call( arguments ).join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+ // Can't pass null or undefined to indexOf in Firefox 4
+ // Set to 0 to skip string check
+ qualifier = qualifier || 0;
+
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( elem === qualifier ) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+ });
+}
+function createSafeFragment( document ) {
+ var list = nodeNames.split( "|" ),
+ safeFrag = document.createDocumentFragment();
+
+ if ( safeFrag.createElement ) {
+ while ( list.length ) {
+ safeFrag.createElement(
+ list.pop()
+ );
+ }
+ }
+ return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+ rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+ rtagName = /<([\w:]+)/,
+ rtbody = / ]", "i"),
+ rcheckableType = /^(?:checkbox|radio)$/,
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /\/(java|ecma)script/i,
+ rcleanScript = /^\s*\s*$/g,
+ wrapMap = {
+ option: [ 1, "", " " ],
+ legend: [ 1, "", " " ],
+ thead: [ 1, "" ],
+ tr: [ 2, "" ],
+ td: [ 3, "" ],
+ col: [ 2, "" ],
+ area: [ 1, "", " " ],
+ _default: [ 0, "", "" ]
+ },
+ safeFragment = createSafeFragment( document ),
+ fragmentDiv = safeFragment.appendChild( document.createElement("div") );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+// unless wrapped in a div with non-breaking characters in front of it.
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "X", "
" ];
+}
+
+jQuery.fn.extend({
+ text: function( value ) {
+ return jQuery.access( this, function( value ) {
+ return value === undefined ?
+ jQuery.text( this ) :
+ this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+ }, null, value, arguments.length );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append( this );
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ var isFunction = jQuery.isFunction( html );
+
+ return this.each(function(i) {
+ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 || this.nodeType === 11 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 || this.nodeType === 11 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( !isDisconnected( this[0] ) ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ }
+
+ if ( arguments.length ) {
+ var set = jQuery.clean( arguments );
+ return this.pushStack( jQuery.merge( set, this ), "before", this.selector );
+ }
+ },
+
+ after: function() {
+ if ( !isDisconnected( this[0] ) ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ }
+
+ if ( arguments.length ) {
+ var set = jQuery.clean( arguments );
+ return this.pushStack( jQuery.merge( this, set ), "after", this.selector );
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ var elem,
+ i = 0;
+
+ for ( ; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ var elem,
+ i = 0;
+
+ for ( ; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( dataAndEvents, deepDataAndEvents ) {
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+ return this.map( function () {
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+ });
+ },
+
+ html: function( value ) {
+ return jQuery.access( this, function( value ) {
+ var elem = this[0] || {},
+ i = 0,
+ l = this.length;
+
+ if ( value === undefined ) {
+ return elem.nodeType === 1 ?
+ elem.innerHTML.replace( rinlinejQuery, "" ) :
+ undefined;
+ }
+
+ // See if we can take a shortcut and just use innerHTML
+ if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+ ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
+ ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+ !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
+
+ value = value.replace( rxhtmlTag, "<$1>$2>" );
+
+ try {
+ for (; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ elem = this[i] || {};
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName( "*" ) );
+ elem.innerHTML = value;
+ }
+ }
+
+ elem = 0;
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {}
+ }
+
+ if ( elem ) {
+ this.empty().append( value );
+ }
+ }, null, value, arguments.length );
+ },
+
+ replaceWith: function( value ) {
+ if ( !isDisconnected( this[0] ) ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery( value ).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling,
+ parent = this.parentNode;
+
+ jQuery( this ).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ }
+
+ return this.length ?
+ this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
+ this;
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+
+ // Flatten any nested arrays
+ args = [].concat.apply( [], args );
+
+ var results, first, fragment, iNoClone,
+ i = 0,
+ value = args[0],
+ scripts = [],
+ l = this.length;
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call( this, i, table ? self.html() : undefined );
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ results = jQuery.buildFragment( args, this, scripts );
+ fragment = results.fragment;
+ first = fragment.firstChild;
+
+ if ( fragment.childNodes.length === 1 ) {
+ fragment = first;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ // Use the original fragment for the last item instead of the first because it can end up
+ // being emptied incorrectly in certain situations (#8070).
+ // Fragments from the fragment cache must always be cloned and never used in place.
+ for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) {
+ callback.call(
+ table && jQuery.nodeName( this[i], "table" ) ?
+ findOrAppend( this[i], "tbody" ) :
+ this[i],
+ i === iNoClone ?
+ fragment :
+ jQuery.clone( fragment, true, true )
+ );
+ }
+ }
+
+ // Fix #11809: Avoid leaking memory
+ fragment = first = null;
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, function( i, elem ) {
+ if ( elem.src ) {
+ if ( jQuery.ajax ) {
+ jQuery.ajax({
+ url: elem.src,
+ type: "GET",
+ dataType: "script",
+ async: false,
+ global: false,
+ "throws": true
+ });
+ } else {
+ jQuery.error("no ajax");
+ }
+ } else {
+ jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ });
+ }
+ }
+
+ return this;
+ }
+});
+
+function findOrAppend( elem, tag ) {
+ return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
+}
+
+function cloneCopyEvent( src, dest ) {
+
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+ return;
+ }
+
+ var type, i, l,
+ oldData = jQuery._data( src ),
+ curData = jQuery._data( dest, oldData ),
+ events = oldData.events;
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( type in events ) {
+ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type, events[ type ][ i ] );
+ }
+ }
+ }
+
+ // make the cloned public data object a copy from the original
+ if ( curData.data ) {
+ curData.data = jQuery.extend( {}, curData.data );
+ }
+}
+
+function cloneFixAttributes( src, dest ) {
+ var nodeName;
+
+ // We do not need to do anything for non-Elements
+ if ( dest.nodeType !== 1 ) {
+ return;
+ }
+
+ // clearAttributes removes the attributes, which we don't want,
+ // but also removes the attachEvent events, which we *do* want
+ if ( dest.clearAttributes ) {
+ dest.clearAttributes();
+ }
+
+ // mergeAttributes, in contrast, only merges back on the
+ // original attributes, not the events
+ if ( dest.mergeAttributes ) {
+ dest.mergeAttributes( src );
+ }
+
+ nodeName = dest.nodeName.toLowerCase();
+
+ if ( nodeName === "object" ) {
+ // IE6-10 improperly clones children of object elements using classid.
+ // IE10 throws NoModificationAllowedError if parent is null, #12132.
+ if ( dest.parentNode ) {
+ dest.outerHTML = src.outerHTML;
+ }
+
+ // This path appears unavoidable for IE9. When cloning an object
+ // element in IE9, the outerHTML strategy above is not sufficient.
+ // If the src has innerHTML and the destination does not,
+ // copy the src.innerHTML into the dest.innerHTML. #10324
+ if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) {
+ dest.innerHTML = src.innerHTML;
+ }
+
+ } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
+ // IE6-8 fails to persist the checked state of a cloned checkbox
+ // or radio button. Worse, IE6-7 fail to give the cloned element
+ // a checked appearance if the defaultChecked value isn't also set
+
+ dest.defaultChecked = dest.checked = src.checked;
+
+ // IE6-7 get confused and end up setting the value of a cloned
+ // checkbox/radio button to an empty string instead of "on"
+ if ( dest.value !== src.value ) {
+ dest.value = src.value;
+ }
+
+ // IE6-8 fails to return the selected option to the default selected
+ // state when cloning options
+ } else if ( nodeName === "option" ) {
+ dest.selected = src.defaultSelected;
+
+ // IE6-8 fails to set the defaultValue to the correct value when
+ // cloning other types of input fields
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
+ dest.defaultValue = src.defaultValue;
+
+ // IE blanks contents when cloning scripts
+ } else if ( nodeName === "script" && dest.text !== src.text ) {
+ dest.text = src.text;
+ }
+
+ // Event data gets referenced instead of copied if the expando
+ // gets copied too
+ dest.removeAttribute( jQuery.expando );
+}
+
+jQuery.buildFragment = function( args, context, scripts ) {
+ var fragment, cacheable, cachehit,
+ first = args[ 0 ];
+
+ // Set context from what may come in as undefined or a jQuery collection or a node
+ // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 &
+ // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception
+ context = context || document;
+ context = !context.nodeType && context[0] || context;
+ context = context.ownerDocument || context;
+
+ // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put or elements in a fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+ // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
+ if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document &&
+ first.charAt(0) === "<" && !rnocache.test( first ) &&
+ (jQuery.support.checkClone || !rchecked.test( first )) &&
+ (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
+
+ // Mark cacheable and look for a hit
+ cacheable = true;
+ fragment = jQuery.fragments[ first ];
+ cachehit = fragment !== undefined;
+ }
+
+ if ( !fragment ) {
+ fragment = context.createDocumentFragment();
+ jQuery.clean( args, context, fragment, scripts );
+
+ // Update the cache, but only store false
+ // unless this is a second parsing of the same content
+ if ( cacheable ) {
+ jQuery.fragments[ first ] = cachehit && fragment;
+ }
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var elems,
+ i = 0,
+ ret = [],
+ insert = jQuery( selector ),
+ l = insert.length,
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+ } else {
+ for ( ; i < l; i++ ) {
+ elems = ( i > 0 ? this.clone(true) : this ).get();
+ jQuery( insert[i] )[ original ]( elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+function getAll( elem ) {
+ if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ return elem.getElementsByTagName( "*" );
+
+ } else if ( typeof elem.querySelectorAll !== "undefined" ) {
+ return elem.querySelectorAll( "*" );
+
+ } else {
+ return [];
+ }
+}
+
+// Used in clean, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+ if ( rcheckableType.test( elem.type ) ) {
+ elem.defaultChecked = elem.checked;
+ }
+}
+
+jQuery.extend({
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+ var srcElements,
+ destElements,
+ i,
+ clone;
+
+ if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
+ clone = elem.cloneNode( true );
+
+ // IE<=8 does not properly clone detached, unknown element nodes
+ } else {
+ fragmentDiv.innerHTML = elem.outerHTML;
+ fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
+ }
+
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+ // IE copies events bound via attachEvent when using cloneNode.
+ // Calling detachEvent on the clone will also remove the events
+ // from the original. In order to get around this, we use some
+ // proprietary methods to clear the events. Thanks to MooTools
+ // guys for this hotness.
+
+ cloneFixAttributes( elem, clone );
+
+ // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ // Weird iteration because IE will replace the length property
+ // with an element if you are cloning the body and one of the
+ // elements on the page has a name or id of "length"
+ for ( i = 0; srcElements[i]; ++i ) {
+ // Ensure that the destination node is not null; Fixes #9587
+ if ( destElements[i] ) {
+ cloneFixAttributes( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ // Copy the events from the original to the clone
+ if ( dataAndEvents ) {
+ cloneCopyEvent( elem, clone );
+
+ if ( deepDataAndEvents ) {
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneCopyEvent( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ srcElements = destElements = null;
+
+ // Return the cloned set
+ return clone;
+ },
+
+ clean: function( elems, context, fragment, scripts ) {
+ var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags,
+ safe = context === document && safeFragment,
+ ret = [];
+
+ // Ensure that context is a document
+ if ( !context || typeof context.createDocumentFragment === "undefined" ) {
+ context = document;
+ }
+
+ // Use the already-created safe fragment if context permits
+ for ( i = 0; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ if ( !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+ } else {
+ // Ensure a safe container in which to render the html
+ safe = safe || createSafeFragment( context );
+ div = context.createElement("div");
+ safe.appendChild( div );
+
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, "<$1>$2>");
+
+ // Go to html and back, then peel off extra wrappers
+ tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
+ wrap = wrapMap[ tag ] || wrapMap._default;
+ depth = wrap[0];
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a , *may* have spurious
+ hasBody = rtbody.test(elem);
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare or
+ wrap[1] === "" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+ }
+
+ elem = div.childNodes;
+
+ // Take out of fragment container (we need a fresh div each time)
+ div.parentNode.removeChild( div );
+ }
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ jQuery.merge( ret, elem );
+ }
+ }
+
+ // Fix #11356: Clear elements from safeFragment
+ if ( div ) {
+ elem = div = safe = null;
+ }
+
+ // Reset defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ if ( !jQuery.support.appendChecked ) {
+ for ( i = 0; (elem = ret[i]) != null; i++ ) {
+ if ( jQuery.nodeName( elem, "input" ) ) {
+ fixDefaultChecked( elem );
+ } else if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+ }
+ }
+ }
+
+ // Append elements to a provided document fragment
+ if ( fragment ) {
+ // Special handling of each script element
+ handleScript = function( elem ) {
+ // Check if we consider it executable
+ if ( !elem.type || rscriptType.test( elem.type ) ) {
+ // Detach the script and store it in the scripts array (if provided) or the fragment
+ // Return truthy to indicate that it has been handled
+ return scripts ?
+ scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
+ fragment.appendChild( elem );
+ }
+ };
+
+ for ( i = 0; (elem = ret[i]) != null; i++ ) {
+ // Check if we're done after handling an executable script
+ if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
+ // Append to fragment and handle embedded scripts
+ fragment.appendChild( elem );
+ if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
+ jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
+
+ // Splice the scripts into ret after their former ancestor and advance our index beyond them
+ ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+ i += jsTags.length;
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems, /* internal */ acceptData ) {
+ var data, id, elem, type,
+ i = 0,
+ internalKey = jQuery.expando,
+ cache = jQuery.cache,
+ deleteExpando = jQuery.support.deleteExpando,
+ special = jQuery.event.special;
+
+ for ( ; (elem = elems[i]) != null; i++ ) {
+
+ if ( acceptData || jQuery.acceptData( elem ) ) {
+
+ id = elem[ internalKey ];
+ data = id && cache[ id ];
+
+ if ( data ) {
+ if ( data.events ) {
+ for ( type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+ }
+
+ // Remove cache only if it was not already removed by jQuery.event.remove
+ if ( cache[ id ] ) {
+
+ delete cache[ id ];
+
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( deleteExpando ) {
+ delete elem[ internalKey ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( internalKey );
+
+ } else {
+ elem[ internalKey ] = null;
+ }
+
+ jQuery.deletedIds.push( id );
+ }
+ }
+ }
+ }
+ }
+});
+// Limit scope pollution from any deprecated API
+(function() {
+
+var matched, browser;
+
+// Use of jQuery.browser is frowned upon.
+// More details: http://api.jquery.com/jQuery.browser
+// jQuery.uaMatch maintained for back-compat
+jQuery.uaMatch = function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
+ /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
+ /(msie) ([\w.]+)/.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
+ [];
+
+ return {
+ browser: match[ 1 ] || "",
+ version: match[ 2 ] || "0"
+ };
+};
+
+matched = jQuery.uaMatch( navigator.userAgent );
+browser = {};
+
+if ( matched.browser ) {
+ browser[ matched.browser ] = true;
+ browser.version = matched.version;
+}
+
+// Chrome is Webkit, but Webkit is also Safari.
+if ( browser.chrome ) {
+ browser.webkit = true;
+} else if ( browser.webkit ) {
+ browser.safari = true;
+}
+
+jQuery.browser = browser;
+
+jQuery.sub = function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+ context = jQuerySub( context );
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ return jQuerySub;
+};
+
+})();
+var curCSS, iframe, iframeDoc,
+ ralpha = /alpha\([^)]*\)/i,
+ ropacity = /opacity=([^)]*)/,
+ rposition = /^(top|right|bottom|left)$/,
+ // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
+ // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
+ rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+ rmargin = /^margin/,
+ rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
+ rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
+ rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ),
+ elemdisplay = { BODY: "block" },
+
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+ cssNormalTransform = {
+ letterSpacing: 0,
+ fontWeight: 400
+ },
+
+ cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+ cssPrefixes = [ "Webkit", "O", "Moz", "ms" ],
+
+ eventsToggle = jQuery.fn.toggle;
+
+// return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
+
+ // shortcut for names that are not vendor prefixed
+ if ( name in style ) {
+ return name;
+ }
+
+ // check for vendor prefixed names
+ var capName = name.charAt(0).toUpperCase() + name.slice(1),
+ origName = name,
+ i = cssPrefixes.length;
+
+ while ( i-- ) {
+ name = cssPrefixes[ i ] + capName;
+ if ( name in style ) {
+ return name;
+ }
+ }
+
+ return origName;
+}
+
+function isHidden( elem, el ) {
+ elem = el || elem;
+ return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+}
+
+function showHide( elements, show ) {
+ var elem, display,
+ values = [],
+ index = 0,
+ length = elements.length;
+
+ for ( ; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+ values[ index ] = jQuery._data( elem, "olddisplay" );
+ if ( show ) {
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !values[ index ] && elem.style.display === "none" ) {
+ elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( elem.style.display === "" && isHidden( elem ) ) {
+ values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
+ }
+ } else {
+ display = curCSS( elem, "display" );
+
+ if ( !values[ index ] && display !== "none" ) {
+ jQuery._data( elem, "olddisplay", display );
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( index = 0; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+ if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+ elem.style.display = show ? values[ index ] || "" : "none";
+ }
+ }
+
+ return elements;
+}
+
+jQuery.fn.extend({
+ css: function( name, value ) {
+ return jQuery.access( this, function( elem, name, value ) {
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ }, name, value, arguments.length > 1 );
+ },
+ show: function() {
+ return showHide( this, true );
+ },
+ hide: function() {
+ return showHide( this );
+ },
+ toggle: function( state, fn2 ) {
+ var bool = typeof state === "boolean";
+
+ if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) {
+ return eventsToggle.apply( this, arguments );
+ }
+
+ return this.each(function() {
+ if ( bool ? state : isHidden( this ) ) {
+ jQuery( this ).show();
+ } else {
+ jQuery( this ).hide();
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity" );
+ return ret === "" ? "1" : ret;
+
+ }
+ }
+ }
+ },
+
+ // Exclude the following css properties to add px
+ cssNumber: {
+ "fillOpacity": true,
+ "fontWeight": true,
+ "lineHeight": true,
+ "opacity": true,
+ "orphans": true,
+ "widows": true,
+ "zIndex": true,
+ "zoom": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ // normalize float css property
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, type, hooks,
+ origName = jQuery.camelCase( name ),
+ style = elem.style;
+
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
+
+ // gets hook for the prefixed version
+ // followed by the unprefixed version
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ type = typeof value;
+
+ // convert relative number strings (+= or -=) to relative numbers. #7345
+ if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+ value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+ // Fixes bug #9237
+ type = "number";
+ }
+
+ // Make sure that NaN and null values aren't set. See: #7116
+ if ( value == null || type === "number" && isNaN( value ) ) {
+ return;
+ }
+
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+ // Fixes bug #5509
+ try {
+ style[ name ] = value;
+ } catch(e) {}
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, numeric, extra ) {
+ var val, num, hooks,
+ origName = jQuery.camelCase( name );
+
+ // Make sure that we're working with the right name
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
+
+ // gets hook for the prefixed version
+ // followed by the unprefixed version
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks ) {
+ val = hooks.get( elem, true, extra );
+ }
+
+ // Otherwise, if a way to get the computed value exists, use that
+ if ( val === undefined ) {
+ val = curCSS( elem, name );
+ }
+
+ //convert "normal" to computed value
+ if ( val === "normal" && name in cssNormalTransform ) {
+ val = cssNormalTransform[ name ];
+ }
+
+ // Return, converting to number if forced or a qualifier was provided and val looks numeric
+ if ( numeric || extra !== undefined ) {
+ num = parseFloat( val );
+ return numeric || jQuery.isNumeric( num ) ? num || 0 : val;
+ }
+ return val;
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var ret, name,
+ old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ ret = callback.call( elem );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+
+ return ret;
+ }
+});
+
+// NOTE: To any future maintainer, we've window.getComputedStyle
+// because jsdom on node.js will break without it.
+if ( window.getComputedStyle ) {
+ curCSS = function( elem, name ) {
+ var ret, width, minWidth, maxWidth,
+ computed = window.getComputedStyle( elem, null ),
+ style = elem.style;
+
+ if ( computed ) {
+
+ // getPropertyValue is only needed for .css('filter') in IE9, see #12537
+ ret = computed.getPropertyValue( name ) || computed[ name ];
+
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+
+ // A tribute to the "awesome hack by Dean Edwards"
+ // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
+ // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+ // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+ if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+ width = style.width;
+ minWidth = style.minWidth;
+ maxWidth = style.maxWidth;
+
+ style.minWidth = style.maxWidth = style.width = ret;
+ ret = computed.width;
+
+ style.width = width;
+ style.minWidth = minWidth;
+ style.maxWidth = maxWidth;
+ }
+ }
+
+ return ret;
+ };
+} else if ( document.documentElement.currentStyle ) {
+ curCSS = function( elem, name ) {
+ var left, rsLeft,
+ ret = elem.currentStyle && elem.currentStyle[ name ],
+ style = elem.style;
+
+ // Avoid setting ret to empty string here
+ // so we don't default to auto
+ if ( ret == null && style && style[ name ] ) {
+ ret = style[ name ];
+ }
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ // but not position css attributes, as those are proportional to the parent element instead
+ // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
+ if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
+
+ // Remember the original values
+ left = style.left;
+ rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ }
+ style.left = name === "fontSize" ? "1em" : ret;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+function setPositiveNumber( elem, value, subtract ) {
+ var matches = rnumsplit.exec( value );
+ return matches ?
+ Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+ value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox ) {
+ var i = extra === ( isBorderBox ? "border" : "content" ) ?
+ // If we already have the right measurement, avoid augmentation
+ 4 :
+ // Otherwise initialize for horizontal or vertical properties
+ name === "width" ? 1 : 0,
+
+ val = 0;
+
+ for ( ; i < 4; i += 2 ) {
+ // both box models exclude margin, so add it if we want it
+ if ( extra === "margin" ) {
+ // we use jQuery.css instead of curCSS here
+ // because of the reliableMarginRight CSS hook!
+ val += jQuery.css( elem, extra + cssExpand[ i ], true );
+ }
+
+ // From this point on we use curCSS for maximum performance (relevant in animations)
+ if ( isBorderBox ) {
+ // border-box includes padding, so remove it if we want content
+ if ( extra === "content" ) {
+ val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
+ }
+
+ // at this point, extra isn't border nor margin, so remove border
+ if ( extra !== "margin" ) {
+ val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+ }
+ } else {
+ // at this point, extra isn't content, so add padding
+ val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
+
+ // at this point, extra isn't content nor padding, so add border
+ if ( extra !== "padding" ) {
+ val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+ }
+ }
+ }
+
+ return val;
+}
+
+function getWidthOrHeight( elem, name, extra ) {
+
+ // Start with offset property, which is equivalent to the border-box value
+ var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+ valueIsBorderBox = true,
+ isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box";
+
+ // some non-html elements return undefined for offsetWidth, so check for null/undefined
+ // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
+ // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
+ if ( val <= 0 || val == null ) {
+ // Fall back to computed then uncomputed css if necessary
+ val = curCSS( elem, name );
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ];
+ }
+
+ // Computed unit is not pixels. Stop here and return.
+ if ( rnumnonpx.test(val) ) {
+ return val;
+ }
+
+ // we need the check for style in case a browser which returns unreliable values
+ // for getComputedStyle silently falls back to the reliable elem.style
+ valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
+
+ // Normalize "", auto, and prepare for extra
+ val = parseFloat( val ) || 0;
+ }
+
+ // use the active box-sizing model to add/subtract irrelevant styles
+ return ( val +
+ augmentWidthOrHeight(
+ elem,
+ name,
+ extra || ( isBorderBox ? "border" : "content" ),
+ valueIsBorderBox
+ )
+ ) + "px";
+}
+
+
+// Try to determine the default display value of an element
+function css_defaultDisplay( nodeName ) {
+ if ( elemdisplay[ nodeName ] ) {
+ return elemdisplay[ nodeName ];
+ }
+
+ var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ),
+ display = elem.css("display");
+ elem.remove();
+
+ // If the simple way fails,
+ // get element's real default display by attaching it to a temp iframe
+ if ( display === "none" || display === "" ) {
+ // Use the already-created iframe if possible
+ iframe = document.body.appendChild(
+ iframe || jQuery.extend( document.createElement("iframe"), {
+ frameBorder: 0,
+ width: 0,
+ height: 0
+ })
+ );
+
+ // Create a cacheable copy of the iframe document on first call.
+ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
+ // document to it; WebKit & Firefox won't allow reusing the iframe document.
+ if ( !iframeDoc || !iframe.createElement ) {
+ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+ iframeDoc.write("");
+ iframeDoc.close();
+ }
+
+ elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) );
+
+ display = curCSS( elem, "display" );
+ document.body.removeChild( iframe );
+ }
+
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+
+ return display;
+}
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ if ( computed ) {
+ // certain elements can have dimension info if we invisibly show them
+ // however, it must have a current display style that would benefit from this
+ if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) {
+ return jQuery.swap( elem, cssShow, function() {
+ return getWidthOrHeight( elem, name, extra );
+ });
+ } else {
+ return getWidthOrHeight( elem, name, extra );
+ }
+ }
+ },
+
+ set: function( elem, value, extra ) {
+ return setPositiveNumber( elem, value, extra ?
+ augmentWidthOrHeight(
+ elem,
+ name,
+ extra,
+ jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"
+ ) : 0
+ );
+ }
+ };
+});
+
+if ( !jQuery.support.opacity ) {
+ jQuery.cssHooks.opacity = {
+ get: function( elem, computed ) {
+ // IE uses filters for opacity
+ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+ ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
+ computed ? "1" : "";
+ },
+
+ set: function( elem, value ) {
+ var style = elem.style,
+ currentStyle = elem.currentStyle,
+ opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+ filter = currentStyle && currentStyle.filter || style.filter || "";
+
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+ if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
+ style.removeAttribute ) {
+
+ // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+ // if "filter:" is present at all, clearType is disabled, we want to avoid this
+ // style.removeAttribute is IE Only, but so apparently is this code path...
+ style.removeAttribute( "filter" );
+
+ // if there there is no filter style applied in a css rule, we are done
+ if ( currentStyle && !currentStyle.filter ) {
+ return;
+ }
+ }
+
+ // otherwise, set new filter values
+ style.filter = ralpha.test( filter ) ?
+ filter.replace( ralpha, opacity ) :
+ filter + " " + opacity;
+ }
+ };
+}
+
+// These hooks cannot be added until DOM ready because the support test
+// for it is not run until after DOM ready
+jQuery(function() {
+ if ( !jQuery.support.reliableMarginRight ) {
+ jQuery.cssHooks.marginRight = {
+ get: function( elem, computed ) {
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // Work around by temporarily setting element display to inline-block
+ return jQuery.swap( elem, { "display": "inline-block" }, function() {
+ if ( computed ) {
+ return curCSS( elem, "marginRight" );
+ }
+ });
+ }
+ };
+ }
+
+ // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+ // getComputedStyle returns percent when specified for top/left/bottom/right
+ // rather than make the css module depend on the offset module, we just check for it here
+ if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
+ jQuery.each( [ "top", "left" ], function( i, prop ) {
+ jQuery.cssHooks[ prop ] = {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ var ret = curCSS( elem, prop );
+ // if curCSS returns percentage, fallback to offset
+ return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret;
+ }
+ }
+ };
+ });
+ }
+
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none");
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+ margin: "",
+ padding: "",
+ border: "Width"
+}, function( prefix, suffix ) {
+ jQuery.cssHooks[ prefix + suffix ] = {
+ expand: function( value ) {
+ var i,
+
+ // assumes a single number if not a string
+ parts = typeof value === "string" ? value.split(" ") : [ value ],
+ expanded = {};
+
+ for ( i = 0; i < 4; i++ ) {
+ expanded[ prefix + cssExpand[ i ] + suffix ] =
+ parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+ }
+
+ return expanded;
+ }
+ };
+
+ if ( !rmargin.test( prefix ) ) {
+ jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+ }
+});
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ rselectTextarea = /^(?:select|textarea)/i;
+
+jQuery.fn.extend({
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray( this.elements ) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ ( this.checked || rselectTextarea.test( this.nodeName ) ||
+ rinput.test( this.type ) );
+ })
+ .map(function( i, elem ){
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val, i ){
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
+});
+
+//Serialize an array of form elements or a set of
+//key/values into a query string
+jQuery.param = function( a, traditional ) {
+ var prefix,
+ s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+};
+
+function buildParams( prefix, obj, traditional, add ) {
+ var name;
+
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+ // Serialize object item.
+ for ( name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+var
+ // Document location
+ ajaxLocParts,
+ ajaxLocation,
+
+ rhash = /#.*$/,
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
+ rquery = /\?/,
+ rscript = /
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Загрузка
+
Пожалуйста подождите.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Back to top
+
+
+
\ No newline at end of file
diff --git a/template/megp/css/animate.css b/template/megp/css/animate.css
new file mode 100644
index 0000000..635a02b
--- /dev/null
+++ b/template/megp/css/animate.css
@@ -0,0 +1,3272 @@
+@charset "UTF-8";
+
+/*!
+Animate.css - http://daneden.me/animate
+Licensed under the MIT license - http://opensource.org/licenses/MIT
+
+Copyright (c) 2015 Daniel Eden
+*/
+
+.animated {
+ -webkit-animation-duration: 1s;
+ animation-duration: 1s;
+ -webkit-animation-fill-mode: both;
+ animation-fill-mode: both;
+}
+
+.animated.infinite {
+ -webkit-animation-iteration-count: infinite;
+ animation-iteration-count: infinite;
+}
+
+.animated.hinge {
+ -webkit-animation-duration: 2s;
+ animation-duration: 2s;
+}
+
+.animated.bounceIn,
+.animated.bounceOut {
+ -webkit-animation-duration: .75s;
+ animation-duration: .75s;
+}
+
+.animated.flipOutX,
+.animated.flipOutY {
+ -webkit-animation-duration: .75s;
+ animation-duration: .75s;
+}
+
+@-webkit-keyframes bounce {
+ 0%, 20%, 53%, 80%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -webkit-transform: translate3d(0,0,0);
+ transform: translate3d(0,0,0);
+ }
+
+ 40%, 43% {
+ -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
+ animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
+ -webkit-transform: translate3d(0, -30px, 0);
+ transform: translate3d(0, -30px, 0);
+ }
+
+ 70% {
+ -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
+ animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
+ -webkit-transform: translate3d(0, -15px, 0);
+ transform: translate3d(0, -15px, 0);
+ }
+
+ 90% {
+ -webkit-transform: translate3d(0,-4px,0);
+ transform: translate3d(0,-4px,0);
+ }
+}
+
+@keyframes bounce {
+ 0%, 20%, 53%, 80%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ -webkit-transform: translate3d(0,0,0);
+ transform: translate3d(0,0,0);
+ }
+
+ 40%, 43% {
+ -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
+ animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
+ -webkit-transform: translate3d(0, -30px, 0);
+ transform: translate3d(0, -30px, 0);
+ }
+
+ 70% {
+ -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
+ animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
+ -webkit-transform: translate3d(0, -15px, 0);
+ transform: translate3d(0, -15px, 0);
+ }
+
+ 90% {
+ -webkit-transform: translate3d(0,-4px,0);
+ transform: translate3d(0,-4px,0);
+ }
+}
+
+.bounce {
+ -webkit-animation-name: bounce;
+ animation-name: bounce;
+ -webkit-transform-origin: center bottom;
+ transform-origin: center bottom;
+}
+
+@-webkit-keyframes flash {
+ 0%, 50%, 100% {
+ opacity: 1;
+ }
+
+ 25%, 75% {
+ opacity: 0;
+ }
+}
+
+@keyframes flash {
+ 0%, 50%, 100% {
+ opacity: 1;
+ }
+
+ 25%, 75% {
+ opacity: 0;
+ }
+}
+
+.flash {
+ -webkit-animation-name: flash;
+ animation-name: flash;
+}
+
+/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
+
+@-webkit-keyframes pulse {
+ 0% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+
+ 50% {
+ -webkit-transform: scale3d(1.05, 1.05, 1.05);
+ transform: scale3d(1.05, 1.05, 1.05);
+ }
+
+ 100% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+@keyframes pulse {
+ 0% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+
+ 50% {
+ -webkit-transform: scale3d(1.05, 1.05, 1.05);
+ transform: scale3d(1.05, 1.05, 1.05);
+ }
+
+ 100% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+.pulse {
+ -webkit-animation-name: pulse;
+ animation-name: pulse;
+}
+
+@-webkit-keyframes rubberBand {
+ 0% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+
+ 30% {
+ -webkit-transform: scale3d(1.25, 0.75, 1);
+ transform: scale3d(1.25, 0.75, 1);
+ }
+
+ 40% {
+ -webkit-transform: scale3d(0.75, 1.25, 1);
+ transform: scale3d(0.75, 1.25, 1);
+ }
+
+ 50% {
+ -webkit-transform: scale3d(1.15, 0.85, 1);
+ transform: scale3d(1.15, 0.85, 1);
+ }
+
+ 65% {
+ -webkit-transform: scale3d(.95, 1.05, 1);
+ transform: scale3d(.95, 1.05, 1);
+ }
+
+ 75% {
+ -webkit-transform: scale3d(1.05, .95, 1);
+ transform: scale3d(1.05, .95, 1);
+ }
+
+ 100% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+@keyframes rubberBand {
+ 0% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+
+ 30% {
+ -webkit-transform: scale3d(1.25, 0.75, 1);
+ transform: scale3d(1.25, 0.75, 1);
+ }
+
+ 40% {
+ -webkit-transform: scale3d(0.75, 1.25, 1);
+ transform: scale3d(0.75, 1.25, 1);
+ }
+
+ 50% {
+ -webkit-transform: scale3d(1.15, 0.85, 1);
+ transform: scale3d(1.15, 0.85, 1);
+ }
+
+ 65% {
+ -webkit-transform: scale3d(.95, 1.05, 1);
+ transform: scale3d(.95, 1.05, 1);
+ }
+
+ 75% {
+ -webkit-transform: scale3d(1.05, .95, 1);
+ transform: scale3d(1.05, .95, 1);
+ }
+
+ 100% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+.rubberBand {
+ -webkit-animation-name: rubberBand;
+ animation-name: rubberBand;
+}
+
+@-webkit-keyframes shake {
+ 0%, 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ 10%, 30%, 50%, 70%, 90% {
+ -webkit-transform: translate3d(-10px, 0, 0);
+ transform: translate3d(-10px, 0, 0);
+ }
+
+ 20%, 40%, 60%, 80% {
+ -webkit-transform: translate3d(10px, 0, 0);
+ transform: translate3d(10px, 0, 0);
+ }
+}
+
+@keyframes shake {
+ 0%, 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ 10%, 30%, 50%, 70%, 90% {
+ -webkit-transform: translate3d(-10px, 0, 0);
+ transform: translate3d(-10px, 0, 0);
+ }
+
+ 20%, 40%, 60%, 80% {
+ -webkit-transform: translate3d(10px, 0, 0);
+ transform: translate3d(10px, 0, 0);
+ }
+}
+
+.shake {
+ -webkit-animation-name: shake;
+ animation-name: shake;
+}
+
+@-webkit-keyframes swing {
+ 20% {
+ -webkit-transform: rotate3d(0, 0, 1, 15deg);
+ transform: rotate3d(0, 0, 1, 15deg);
+ }
+
+ 40% {
+ -webkit-transform: rotate3d(0, 0, 1, -10deg);
+ transform: rotate3d(0, 0, 1, -10deg);
+ }
+
+ 60% {
+ -webkit-transform: rotate3d(0, 0, 1, 5deg);
+ transform: rotate3d(0, 0, 1, 5deg);
+ }
+
+ 80% {
+ -webkit-transform: rotate3d(0, 0, 1, -5deg);
+ transform: rotate3d(0, 0, 1, -5deg);
+ }
+
+ 100% {
+ -webkit-transform: rotate3d(0, 0, 1, 0deg);
+ transform: rotate3d(0, 0, 1, 0deg);
+ }
+}
+
+@keyframes swing {
+ 20% {
+ -webkit-transform: rotate3d(0, 0, 1, 15deg);
+ transform: rotate3d(0, 0, 1, 15deg);
+ }
+
+ 40% {
+ -webkit-transform: rotate3d(0, 0, 1, -10deg);
+ transform: rotate3d(0, 0, 1, -10deg);
+ }
+
+ 60% {
+ -webkit-transform: rotate3d(0, 0, 1, 5deg);
+ transform: rotate3d(0, 0, 1, 5deg);
+ }
+
+ 80% {
+ -webkit-transform: rotate3d(0, 0, 1, -5deg);
+ transform: rotate3d(0, 0, 1, -5deg);
+ }
+
+ 100% {
+ -webkit-transform: rotate3d(0, 0, 1, 0deg);
+ transform: rotate3d(0, 0, 1, 0deg);
+ }
+}
+
+.swing {
+ -webkit-transform-origin: top center;
+ transform-origin: top center;
+ -webkit-animation-name: swing;
+ animation-name: swing;
+}
+
+@-webkit-keyframes tada {
+ 0% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+
+ 10%, 20% {
+ -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg);
+ transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg);
+ }
+
+ 30%, 50%, 70%, 90% {
+ -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
+ transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
+ }
+
+ 40%, 60%, 80% {
+ -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
+ transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
+ }
+
+ 100% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+@keyframes tada {
+ 0% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+
+ 10%, 20% {
+ -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg);
+ transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg);
+ }
+
+ 30%, 50%, 70%, 90% {
+ -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
+ transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
+ }
+
+ 40%, 60%, 80% {
+ -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
+ transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
+ }
+
+ 100% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+.tada {
+ -webkit-animation-name: tada;
+ animation-name: tada;
+}
+
+/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
+
+@-webkit-keyframes wobble {
+ 0% {
+ -webkit-transform: none;
+ transform: none;
+ }
+
+ 15% {
+ -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
+ transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
+ }
+
+ 30% {
+ -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
+ transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
+ }
+
+ 45% {
+ -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
+ transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
+ }
+
+ 60% {
+ -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
+ transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
+ }
+
+ 75% {
+ -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
+ transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
+ }
+
+ 100% {
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes wobble {
+ 0% {
+ -webkit-transform: none;
+ transform: none;
+ }
+
+ 15% {
+ -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
+ transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
+ }
+
+ 30% {
+ -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
+ transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
+ }
+
+ 45% {
+ -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
+ transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
+ }
+
+ 60% {
+ -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
+ transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
+ }
+
+ 75% {
+ -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
+ transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
+ }
+
+ 100% {
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.wobble {
+ -webkit-animation-name: wobble;
+ animation-name: wobble;
+}
+
+@-webkit-keyframes jello {
+ 11.1% {
+ -webkit-transform: none;
+ transform: none
+ }
+
+ 22.2% {
+ -webkit-transform: skewX(-12.5deg) skewY(-12.5deg);
+ transform: skewX(-12.5deg) skewY(-12.5deg)
+ }
+ 33.3% {
+ -webkit-transform: skewX(6.25deg) skewY(6.25deg);
+ transform: skewX(6.25deg) skewY(6.25deg)
+ }
+ 44.4% {
+ -webkit-transform: skewX(-3.125deg) skewY(-3.125deg);
+ transform: skewX(-3.125deg) skewY(-3.125deg)
+ }
+ 55.5% {
+ -webkit-transform: skewX(1.5625deg) skewY(1.5625deg);
+ transform: skewX(1.5625deg) skewY(1.5625deg)
+ }
+ 66.6% {
+ -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg);
+ transform: skewX(-0.78125deg) skewY(-0.78125deg)
+ }
+ 77.7% {
+ -webkit-transform: skewX(0.390625deg) skewY(0.390625deg);
+ transform: skewX(0.390625deg) skewY(0.390625deg)
+ }
+ 88.8% {
+ -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg);
+ transform: skewX(-0.1953125deg) skewY(-0.1953125deg)
+ }
+ 100% {
+ -webkit-transform: none;
+ transform: none
+ }
+}
+
+@keyframes jello {
+ 11.1% {
+ -webkit-transform: none;
+ transform: none
+ }
+
+ 22.2% {
+
+ -webkit-transform: skewX(-12.5deg) skewY(-12.5deg);
+ transform: skewX(-12.5deg) skewY(-12.5deg)
+ }
+ 33.3% {
+ -webkit-transform: skewX(6.25deg) skewY(6.25deg);
+ transform: skewX(6.25deg) skewY(6.25deg)
+ }
+ 44.4% {
+ -webkit-transform: skewX(-3.125deg) skewY(-3.125deg);
+ transform: skewX(-3.125deg) skewY(-3.125deg)
+ }
+ 55.5% {
+ -webkit-transform: skewX(1.5625deg) skewY(1.5625deg);
+ transform: skewX(1.5625deg) skewY(1.5625deg)
+ }
+ 66.6% {
+ -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg);
+ transform: skewX(-0.78125deg) skewY(-0.78125deg)
+ }
+ 77.7% {
+ -webkit-transform: skewX(0.390625deg) skewY(0.390625deg);
+ transform: skewX(0.390625deg) skewY(0.390625deg)
+ }
+ 88.8% {
+ -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg);
+ transform: skewX(-0.1953125deg) skewY(-0.1953125deg)
+ }
+ 100% {
+ -webkit-transform: none;
+ transform: none
+ }
+}
+
+
+
+.jello{
+ -webkit-animation-name:jello;
+ animation-name:jello;
+ -webkit-transform-origin: center;
+
+ transform-origin: center
+}
+
+@-webkit-keyframes bounceIn {
+ 0%, 20%, 40%, 60%, 80%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.3, .3, .3);
+ transform: scale3d(.3, .3, .3);
+ }
+
+ 20% {
+ -webkit-transform: scale3d(1.1, 1.1, 1.1);
+ transform: scale3d(1.1, 1.1, 1.1);
+ }
+
+ 40% {
+ -webkit-transform: scale3d(.9, .9, .9);
+ transform: scale3d(.9, .9, .9);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: scale3d(1.03, 1.03, 1.03);
+ transform: scale3d(1.03, 1.03, 1.03);
+ }
+
+ 80% {
+ -webkit-transform: scale3d(.97, .97, .97);
+ transform: scale3d(.97, .97, .97);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+@keyframes bounceIn {
+ 0%, 20%, 40%, 60%, 80%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.3, .3, .3);
+ transform: scale3d(.3, .3, .3);
+ }
+
+ 20% {
+ -webkit-transform: scale3d(1.1, 1.1, 1.1);
+ transform: scale3d(1.1, 1.1, 1.1);
+ }
+
+ 40% {
+ -webkit-transform: scale3d(.9, .9, .9);
+ transform: scale3d(.9, .9, .9);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: scale3d(1.03, 1.03, 1.03);
+ transform: scale3d(1.03, 1.03, 1.03);
+ }
+
+ 80% {
+ -webkit-transform: scale3d(.97, .97, .97);
+ transform: scale3d(.97, .97, .97);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+.bounceIn {
+ -webkit-animation-name: bounceIn;
+ animation-name: bounceIn;
+}
+
+@-webkit-keyframes bounceInDown {
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -3000px, 0);
+ transform: translate3d(0, -3000px, 0);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: translate3d(0, 25px, 0);
+ transform: translate3d(0, 25px, 0);
+ }
+
+ 75% {
+ -webkit-transform: translate3d(0, -10px, 0);
+ transform: translate3d(0, -10px, 0);
+ }
+
+ 90% {
+ -webkit-transform: translate3d(0, 5px, 0);
+ transform: translate3d(0, 5px, 0);
+ }
+
+ 100% {
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes bounceInDown {
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -3000px, 0);
+ transform: translate3d(0, -3000px, 0);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: translate3d(0, 25px, 0);
+ transform: translate3d(0, 25px, 0);
+ }
+
+ 75% {
+ -webkit-transform: translate3d(0, -10px, 0);
+ transform: translate3d(0, -10px, 0);
+ }
+
+ 90% {
+ -webkit-transform: translate3d(0, 5px, 0);
+ transform: translate3d(0, 5px, 0);
+ }
+
+ 100% {
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.bounceInDown {
+ -webkit-animation-name: bounceInDown;
+ animation-name: bounceInDown;
+}
+
+@-webkit-keyframes bounceInLeft {
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-3000px, 0, 0);
+ transform: translate3d(-3000px, 0, 0);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: translate3d(25px, 0, 0);
+ transform: translate3d(25px, 0, 0);
+ }
+
+ 75% {
+ -webkit-transform: translate3d(-10px, 0, 0);
+ transform: translate3d(-10px, 0, 0);
+ }
+
+ 90% {
+ -webkit-transform: translate3d(5px, 0, 0);
+ transform: translate3d(5px, 0, 0);
+ }
+
+ 100% {
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes bounceInLeft {
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-3000px, 0, 0);
+ transform: translate3d(-3000px, 0, 0);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: translate3d(25px, 0, 0);
+ transform: translate3d(25px, 0, 0);
+ }
+
+ 75% {
+ -webkit-transform: translate3d(-10px, 0, 0);
+ transform: translate3d(-10px, 0, 0);
+ }
+
+ 90% {
+ -webkit-transform: translate3d(5px, 0, 0);
+ transform: translate3d(5px, 0, 0);
+ }
+
+ 100% {
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.bounceInLeft {
+ -webkit-animation-name: bounceInLeft;
+ animation-name: bounceInLeft;
+}
+
+@-webkit-keyframes bounceInRight {
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(3000px, 0, 0);
+ transform: translate3d(3000px, 0, 0);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: translate3d(-25px, 0, 0);
+ transform: translate3d(-25px, 0, 0);
+ }
+
+ 75% {
+ -webkit-transform: translate3d(10px, 0, 0);
+ transform: translate3d(10px, 0, 0);
+ }
+
+ 90% {
+ -webkit-transform: translate3d(-5px, 0, 0);
+ transform: translate3d(-5px, 0, 0);
+ }
+
+ 100% {
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes bounceInRight {
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(3000px, 0, 0);
+ transform: translate3d(3000px, 0, 0);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: translate3d(-25px, 0, 0);
+ transform: translate3d(-25px, 0, 0);
+ }
+
+ 75% {
+ -webkit-transform: translate3d(10px, 0, 0);
+ transform: translate3d(10px, 0, 0);
+ }
+
+ 90% {
+ -webkit-transform: translate3d(-5px, 0, 0);
+ transform: translate3d(-5px, 0, 0);
+ }
+
+ 100% {
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.bounceInRight {
+ -webkit-animation-name: bounceInRight;
+ animation-name: bounceInRight;
+}
+
+@-webkit-keyframes bounceInUp {
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 3000px, 0);
+ transform: translate3d(0, 3000px, 0);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: translate3d(0, -20px, 0);
+ transform: translate3d(0, -20px, 0);
+ }
+
+ 75% {
+ -webkit-transform: translate3d(0, 10px, 0);
+ transform: translate3d(0, 10px, 0);
+ }
+
+ 90% {
+ -webkit-transform: translate3d(0, -5px, 0);
+ transform: translate3d(0, -5px, 0);
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+@keyframes bounceInUp {
+ 0%, 60%, 75%, 90%, 100% {
+ -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 3000px, 0);
+ transform: translate3d(0, 3000px, 0);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: translate3d(0, -20px, 0);
+ transform: translate3d(0, -20px, 0);
+ }
+
+ 75% {
+ -webkit-transform: translate3d(0, 10px, 0);
+ transform: translate3d(0, 10px, 0);
+ }
+
+ 90% {
+ -webkit-transform: translate3d(0, -5px, 0);
+ transform: translate3d(0, -5px, 0);
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.bounceInUp {
+ -webkit-animation-name: bounceInUp;
+ animation-name: bounceInUp;
+}
+
+@-webkit-keyframes bounceOut {
+ 20% {
+ -webkit-transform: scale3d(.9, .9, .9);
+ transform: scale3d(.9, .9, .9);
+ }
+
+ 50%, 55% {
+ opacity: 1;
+ -webkit-transform: scale3d(1.1, 1.1, 1.1);
+ transform: scale3d(1.1, 1.1, 1.1);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale3d(.3, .3, .3);
+ transform: scale3d(.3, .3, .3);
+ }
+}
+
+@keyframes bounceOut {
+ 20% {
+ -webkit-transform: scale3d(.9, .9, .9);
+ transform: scale3d(.9, .9, .9);
+ }
+
+ 50%, 55% {
+ opacity: 1;
+ -webkit-transform: scale3d(1.1, 1.1, 1.1);
+ transform: scale3d(1.1, 1.1, 1.1);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale3d(.3, .3, .3);
+ transform: scale3d(.3, .3, .3);
+ }
+}
+
+.bounceOut {
+ -webkit-animation-name: bounceOut;
+ animation-name: bounceOut;
+}
+
+@-webkit-keyframes bounceOutDown {
+ 20% {
+ -webkit-transform: translate3d(0, 10px, 0);
+ transform: translate3d(0, 10px, 0);
+ }
+
+ 40%, 45% {
+ opacity: 1;
+ -webkit-transform: translate3d(0, -20px, 0);
+ transform: translate3d(0, -20px, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 2000px, 0);
+ transform: translate3d(0, 2000px, 0);
+ }
+}
+
+@keyframes bounceOutDown {
+ 20% {
+ -webkit-transform: translate3d(0, 10px, 0);
+ transform: translate3d(0, 10px, 0);
+ }
+
+ 40%, 45% {
+ opacity: 1;
+ -webkit-transform: translate3d(0, -20px, 0);
+ transform: translate3d(0, -20px, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 2000px, 0);
+ transform: translate3d(0, 2000px, 0);
+ }
+}
+
+.bounceOutDown {
+ -webkit-animation-name: bounceOutDown;
+ animation-name: bounceOutDown;
+}
+
+@-webkit-keyframes bounceOutLeft {
+ 20% {
+ opacity: 1;
+ -webkit-transform: translate3d(20px, 0, 0);
+ transform: translate3d(20px, 0, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(-2000px, 0, 0);
+ transform: translate3d(-2000px, 0, 0);
+ }
+}
+
+@keyframes bounceOutLeft {
+ 20% {
+ opacity: 1;
+ -webkit-transform: translate3d(20px, 0, 0);
+ transform: translate3d(20px, 0, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(-2000px, 0, 0);
+ transform: translate3d(-2000px, 0, 0);
+ }
+}
+
+.bounceOutLeft {
+ -webkit-animation-name: bounceOutLeft;
+ animation-name: bounceOutLeft;
+}
+
+@-webkit-keyframes bounceOutRight {
+ 20% {
+ opacity: 1;
+ -webkit-transform: translate3d(-20px, 0, 0);
+ transform: translate3d(-20px, 0, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(2000px, 0, 0);
+ transform: translate3d(2000px, 0, 0);
+ }
+}
+
+@keyframes bounceOutRight {
+ 20% {
+ opacity: 1;
+ -webkit-transform: translate3d(-20px, 0, 0);
+ transform: translate3d(-20px, 0, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(2000px, 0, 0);
+ transform: translate3d(2000px, 0, 0);
+ }
+}
+
+.bounceOutRight {
+ -webkit-animation-name: bounceOutRight;
+ animation-name: bounceOutRight;
+}
+
+@-webkit-keyframes bounceOutUp {
+ 20% {
+ -webkit-transform: translate3d(0, -10px, 0);
+ transform: translate3d(0, -10px, 0);
+ }
+
+ 40%, 45% {
+ opacity: 1;
+ -webkit-transform: translate3d(0, 20px, 0);
+ transform: translate3d(0, 20px, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -2000px, 0);
+ transform: translate3d(0, -2000px, 0);
+ }
+}
+
+@keyframes bounceOutUp {
+ 20% {
+ -webkit-transform: translate3d(0, -10px, 0);
+ transform: translate3d(0, -10px, 0);
+ }
+
+ 40%, 45% {
+ opacity: 1;
+ -webkit-transform: translate3d(0, 20px, 0);
+ transform: translate3d(0, 20px, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -2000px, 0);
+ transform: translate3d(0, -2000px, 0);
+ }
+}
+
+.bounceOutUp {
+ -webkit-animation-name: bounceOutUp;
+ animation-name: bounceOutUp;
+}
+
+@-webkit-keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+.fadeIn {
+ -webkit-animation-name: fadeIn;
+ animation-name: fadeIn;
+}
+
+@-webkit-keyframes fadeInDown {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInDown {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInDown {
+ -webkit-animation-name: fadeInDown;
+ animation-name: fadeInDown;
+}
+
+@-webkit-keyframes fadeInDownBig {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -2000px, 0);
+ transform: translate3d(0, -2000px, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInDownBig {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -2000px, 0);
+ transform: translate3d(0, -2000px, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInDownBig {
+ -webkit-animation-name: fadeInDownBig;
+ animation-name: fadeInDownBig;
+}
+
+@-webkit-keyframes fadeInLeft {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInLeft {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInLeft {
+ -webkit-animation-name: fadeInLeft;
+ animation-name: fadeInLeft;
+}
+
+@-webkit-keyframes fadeInLeftBig {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-2000px, 0, 0);
+ transform: translate3d(-2000px, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInLeftBig {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-2000px, 0, 0);
+ transform: translate3d(-2000px, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInLeftBig {
+ -webkit-animation-name: fadeInLeftBig;
+ animation-name: fadeInLeftBig;
+}
+
+@-webkit-keyframes fadeInRight {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInRight {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInRight {
+ -webkit-animation-name: fadeInRight;
+ animation-name: fadeInRight;
+}
+
+@-webkit-keyframes fadeInRightBig {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(2000px, 0, 0);
+ transform: translate3d(2000px, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInRightBig {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(2000px, 0, 0);
+ transform: translate3d(2000px, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInRightBig {
+ -webkit-animation-name: fadeInRightBig;
+ animation-name: fadeInRightBig;
+}
+
+@-webkit-keyframes fadeInUp {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInUp {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInUp {
+ -webkit-animation-name: fadeInUp;
+ animation-name: fadeInUp;
+}
+
+@-webkit-keyframes fadeInUpBig {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 2000px, 0);
+ transform: translate3d(0, 2000px, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInUpBig {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 2000px, 0);
+ transform: translate3d(0, 2000px, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInUpBig {
+ -webkit-animation-name: fadeInUpBig;
+ animation-name: fadeInUpBig;
+}
+
+@-webkit-keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+.fadeOut {
+ -webkit-animation-name: fadeOut;
+ animation-name: fadeOut;
+}
+
+@-webkit-keyframes fadeOutDown {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+}
+
+@keyframes fadeOutDown {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+}
+
+.fadeOutDown {
+ -webkit-animation-name: fadeOutDown;
+ animation-name: fadeOutDown;
+}
+
+@-webkit-keyframes fadeOutDownBig {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 2000px, 0);
+ transform: translate3d(0, 2000px, 0);
+ }
+}
+
+@keyframes fadeOutDownBig {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 2000px, 0);
+ transform: translate3d(0, 2000px, 0);
+ }
+}
+
+.fadeOutDownBig {
+ -webkit-animation-name: fadeOutDownBig;
+ animation-name: fadeOutDownBig;
+}
+
+@-webkit-keyframes fadeOutLeft {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+}
+
+@keyframes fadeOutLeft {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+}
+
+.fadeOutLeft {
+ -webkit-animation-name: fadeOutLeft;
+ animation-name: fadeOutLeft;
+}
+
+@-webkit-keyframes fadeOutLeftBig {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(-2000px, 0, 0);
+ transform: translate3d(-2000px, 0, 0);
+ }
+}
+
+@keyframes fadeOutLeftBig {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(-2000px, 0, 0);
+ transform: translate3d(-2000px, 0, 0);
+ }
+}
+
+.fadeOutLeftBig {
+ -webkit-animation-name: fadeOutLeftBig;
+ animation-name: fadeOutLeftBig;
+}
+
+@-webkit-keyframes fadeOutRight {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+}
+
+@keyframes fadeOutRight {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+}
+
+.fadeOutRight {
+ -webkit-animation-name: fadeOutRight;
+ animation-name: fadeOutRight;
+}
+
+@-webkit-keyframes fadeOutRightBig {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(2000px, 0, 0);
+ transform: translate3d(2000px, 0, 0);
+ }
+}
+
+@keyframes fadeOutRightBig {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(2000px, 0, 0);
+ transform: translate3d(2000px, 0, 0);
+ }
+}
+
+.fadeOutRightBig {
+ -webkit-animation-name: fadeOutRightBig;
+ animation-name: fadeOutRightBig;
+}
+
+@-webkit-keyframes fadeOutUp {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+}
+
+@keyframes fadeOutUp {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+}
+
+.fadeOutUp {
+ -webkit-animation-name: fadeOutUp;
+ animation-name: fadeOutUp;
+}
+
+@-webkit-keyframes fadeOutUpBig {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -2000px, 0);
+ transform: translate3d(0, -2000px, 0);
+ }
+}
+
+@keyframes fadeOutUpBig {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -2000px, 0);
+ transform: translate3d(0, -2000px, 0);
+ }
+}
+
+.fadeOutUpBig {
+ -webkit-animation-name: fadeOutUpBig;
+ animation-name: fadeOutUpBig;
+}
+
+@-webkit-keyframes flip {
+ 0% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, -360deg);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+
+ 40% {
+ -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
+ transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+
+ 50% {
+ -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg);
+ transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+
+ 80% {
+ -webkit-transform: perspective(400px) scale3d(.95, .95, .95);
+ transform: perspective(400px) scale3d(.95, .95, .95);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+
+ 100% {
+ -webkit-transform: perspective(400px);
+ transform: perspective(400px);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+}
+
+@keyframes flip {
+ 0% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, -360deg);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+
+ 40% {
+ -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
+ transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+
+ 50% {
+ -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg);
+ transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+
+ 80% {
+ -webkit-transform: perspective(400px) scale3d(.95, .95, .95);
+ transform: perspective(400px) scale3d(.95, .95, .95);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+
+ 100% {
+ -webkit-transform: perspective(400px);
+ transform: perspective(400px);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+}
+
+.animated.flip {
+ -webkit-backface-visibility: visible;
+ backface-visibility: visible;
+ -webkit-animation-name: flip;
+ animation-name: flip;
+}
+
+@-webkit-keyframes flipInX {
+ 0% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ opacity: 0;
+ }
+
+ 40% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+
+ 60% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
+ opacity: 1;
+ }
+
+ 80% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
+ }
+
+ 100% {
+ -webkit-transform: perspective(400px);
+ transform: perspective(400px);
+ }
+}
+
+@keyframes flipInX {
+ 0% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ opacity: 0;
+ }
+
+ 40% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+
+ 60% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
+ opacity: 1;
+ }
+
+ 80% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
+ }
+
+ 100% {
+ -webkit-transform: perspective(400px);
+ transform: perspective(400px);
+ }
+}
+
+.flipInX {
+ -webkit-backface-visibility: visible !important;
+ backface-visibility: visible !important;
+ -webkit-animation-name: flipInX;
+ animation-name: flipInX;
+}
+
+@-webkit-keyframes flipInY {
+ 0% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ opacity: 0;
+ }
+
+ 40% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+
+ 60% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
+ opacity: 1;
+ }
+
+ 80% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
+ }
+
+ 100% {
+ -webkit-transform: perspective(400px);
+ transform: perspective(400px);
+ }
+}
+
+@keyframes flipInY {
+ 0% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ opacity: 0;
+ }
+
+ 40% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+
+ 60% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
+ opacity: 1;
+ }
+
+ 80% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
+ }
+
+ 100% {
+ -webkit-transform: perspective(400px);
+ transform: perspective(400px);
+ }
+}
+
+.flipInY {
+ -webkit-backface-visibility: visible !important;
+ backface-visibility: visible !important;
+ -webkit-animation-name: flipInY;
+ animation-name: flipInY;
+}
+
+@-webkit-keyframes flipOutX {
+ 0% {
+ -webkit-transform: perspective(400px);
+ transform: perspective(400px);
+ }
+
+ 30% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ opacity: 0;
+ }
+}
+
+@keyframes flipOutX {
+ 0% {
+ -webkit-transform: perspective(400px);
+ transform: perspective(400px);
+ }
+
+ 30% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ opacity: 0;
+ }
+}
+
+.flipOutX {
+ -webkit-animation-name: flipOutX;
+ animation-name: flipOutX;
+ -webkit-backface-visibility: visible !important;
+ backface-visibility: visible !important;
+}
+
+@-webkit-keyframes flipOutY {
+ 0% {
+ -webkit-transform: perspective(400px);
+ transform: perspective(400px);
+ }
+
+ 30% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
+ opacity: 0;
+ }
+}
+
+@keyframes flipOutY {
+ 0% {
+ -webkit-transform: perspective(400px);
+ transform: perspective(400px);
+ }
+
+ 30% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
+ transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
+ opacity: 0;
+ }
+}
+
+.flipOutY {
+ -webkit-backface-visibility: visible !important;
+ backface-visibility: visible !important;
+ -webkit-animation-name: flipOutY;
+ animation-name: flipOutY;
+}
+
+@-webkit-keyframes lightSpeedIn {
+ 0% {
+ -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg);
+ transform: translate3d(100%, 0, 0) skewX(-30deg);
+ opacity: 0;
+ }
+
+ 60% {
+ -webkit-transform: skewX(20deg);
+ transform: skewX(20deg);
+ opacity: 1;
+ }
+
+ 80% {
+ -webkit-transform: skewX(-5deg);
+ transform: skewX(-5deg);
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+@keyframes lightSpeedIn {
+ 0% {
+ -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg);
+ transform: translate3d(100%, 0, 0) skewX(-30deg);
+ opacity: 0;
+ }
+
+ 60% {
+ -webkit-transform: skewX(20deg);
+ transform: skewX(20deg);
+ opacity: 1;
+ }
+
+ 80% {
+ -webkit-transform: skewX(-5deg);
+ transform: skewX(-5deg);
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+.lightSpeedIn {
+ -webkit-animation-name: lightSpeedIn;
+ animation-name: lightSpeedIn;
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+}
+
+@-webkit-keyframes lightSpeedOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(100%, 0, 0) skewX(30deg);
+ transform: translate3d(100%, 0, 0) skewX(30deg);
+ opacity: 0;
+ }
+}
+
+@keyframes lightSpeedOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(100%, 0, 0) skewX(30deg);
+ transform: translate3d(100%, 0, 0) skewX(30deg);
+ opacity: 0;
+ }
+}
+
+.lightSpeedOut {
+ -webkit-animation-name: lightSpeedOut;
+ animation-name: lightSpeedOut;
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+}
+
+@-webkit-keyframes rotateIn {
+ 0% {
+ -webkit-transform-origin: center;
+ transform-origin: center;
+ -webkit-transform: rotate3d(0, 0, 1, -200deg);
+ transform: rotate3d(0, 0, 1, -200deg);
+ opacity: 0;
+ }
+
+ 100% {
+ -webkit-transform-origin: center;
+ transform-origin: center;
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+@keyframes rotateIn {
+ 0% {
+ -webkit-transform-origin: center;
+ transform-origin: center;
+ -webkit-transform: rotate3d(0, 0, 1, -200deg);
+ transform: rotate3d(0, 0, 1, -200deg);
+ opacity: 0;
+ }
+
+ 100% {
+ -webkit-transform-origin: center;
+ transform-origin: center;
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+.rotateIn {
+ -webkit-animation-name: rotateIn;
+ animation-name: rotateIn;
+}
+
+@-webkit-keyframes rotateInDownLeft {
+ 0% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: rotate3d(0, 0, 1, -45deg);
+ transform: rotate3d(0, 0, 1, -45deg);
+ opacity: 0;
+ }
+
+ 100% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+@keyframes rotateInDownLeft {
+ 0% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: rotate3d(0, 0, 1, -45deg);
+ transform: rotate3d(0, 0, 1, -45deg);
+ opacity: 0;
+ }
+
+ 100% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+.rotateInDownLeft {
+ -webkit-animation-name: rotateInDownLeft;
+ animation-name: rotateInDownLeft;
+}
+
+@-webkit-keyframes rotateInDownRight {
+ 0% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: rotate3d(0, 0, 1, 45deg);
+ transform: rotate3d(0, 0, 1, 45deg);
+ opacity: 0;
+ }
+
+ 100% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+@keyframes rotateInDownRight {
+ 0% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: rotate3d(0, 0, 1, 45deg);
+ transform: rotate3d(0, 0, 1, 45deg);
+ opacity: 0;
+ }
+
+ 100% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+.rotateInDownRight {
+ -webkit-animation-name: rotateInDownRight;
+ animation-name: rotateInDownRight;
+}
+
+@-webkit-keyframes rotateInUpLeft {
+ 0% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: rotate3d(0, 0, 1, 45deg);
+ transform: rotate3d(0, 0, 1, 45deg);
+ opacity: 0;
+ }
+
+ 100% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+@keyframes rotateInUpLeft {
+ 0% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: rotate3d(0, 0, 1, 45deg);
+ transform: rotate3d(0, 0, 1, 45deg);
+ opacity: 0;
+ }
+
+ 100% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+.rotateInUpLeft {
+ -webkit-animation-name: rotateInUpLeft;
+ animation-name: rotateInUpLeft;
+}
+
+@-webkit-keyframes rotateInUpRight {
+ 0% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: rotate3d(0, 0, 1, -90deg);
+ transform: rotate3d(0, 0, 1, -90deg);
+ opacity: 0;
+ }
+
+ 100% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+@keyframes rotateInUpRight {
+ 0% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: rotate3d(0, 0, 1, -90deg);
+ transform: rotate3d(0, 0, 1, -90deg);
+ opacity: 0;
+ }
+
+ 100% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: none;
+ transform: none;
+ opacity: 1;
+ }
+}
+
+.rotateInUpRight {
+ -webkit-animation-name: rotateInUpRight;
+ animation-name: rotateInUpRight;
+}
+
+@-webkit-keyframes rotateOut {
+ 0% {
+ -webkit-transform-origin: center;
+ transform-origin: center;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform-origin: center;
+ transform-origin: center;
+ -webkit-transform: rotate3d(0, 0, 1, 200deg);
+ transform: rotate3d(0, 0, 1, 200deg);
+ opacity: 0;
+ }
+}
+
+@keyframes rotateOut {
+ 0% {
+ -webkit-transform-origin: center;
+ transform-origin: center;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform-origin: center;
+ transform-origin: center;
+ -webkit-transform: rotate3d(0, 0, 1, 200deg);
+ transform: rotate3d(0, 0, 1, 200deg);
+ opacity: 0;
+ }
+}
+
+.rotateOut {
+ -webkit-animation-name: rotateOut;
+ animation-name: rotateOut;
+}
+
+@-webkit-keyframes rotateOutDownLeft {
+ 0% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: rotate3d(0, 0, 1, 45deg);
+ transform: rotate3d(0, 0, 1, 45deg);
+ opacity: 0;
+ }
+}
+
+@keyframes rotateOutDownLeft {
+ 0% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: rotate3d(0, 0, 1, 45deg);
+ transform: rotate3d(0, 0, 1, 45deg);
+ opacity: 0;
+ }
+}
+
+.rotateOutDownLeft {
+ -webkit-animation-name: rotateOutDownLeft;
+ animation-name: rotateOutDownLeft;
+}
+
+@-webkit-keyframes rotateOutDownRight {
+ 0% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: rotate3d(0, 0, 1, -45deg);
+ transform: rotate3d(0, 0, 1, -45deg);
+ opacity: 0;
+ }
+}
+
+@keyframes rotateOutDownRight {
+ 0% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: rotate3d(0, 0, 1, -45deg);
+ transform: rotate3d(0, 0, 1, -45deg);
+ opacity: 0;
+ }
+}
+
+.rotateOutDownRight {
+ -webkit-animation-name: rotateOutDownRight;
+ animation-name: rotateOutDownRight;
+}
+
+@-webkit-keyframes rotateOutUpLeft {
+ 0% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: rotate3d(0, 0, 1, -45deg);
+ transform: rotate3d(0, 0, 1, -45deg);
+ opacity: 0;
+ }
+}
+
+@keyframes rotateOutUpLeft {
+ 0% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform-origin: left bottom;
+ transform-origin: left bottom;
+ -webkit-transform: rotate3d(0, 0, 1, -45deg);
+ transform: rotate3d(0, 0, 1, -45deg);
+ opacity: 0;
+ }
+}
+
+.rotateOutUpLeft {
+ -webkit-animation-name: rotateOutUpLeft;
+ animation-name: rotateOutUpLeft;
+}
+
+@-webkit-keyframes rotateOutUpRight {
+ 0% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: rotate3d(0, 0, 1, 90deg);
+ transform: rotate3d(0, 0, 1, 90deg);
+ opacity: 0;
+ }
+}
+
+@keyframes rotateOutUpRight {
+ 0% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: rotate3d(0, 0, 1, 90deg);
+ transform: rotate3d(0, 0, 1, 90deg);
+ opacity: 0;
+ }
+}
+
+.rotateOutUpRight {
+ -webkit-animation-name: rotateOutUpRight;
+ animation-name: rotateOutUpRight;
+}
+
+@-webkit-keyframes hinge {
+ 0% {
+ -webkit-transform-origin: top left;
+ transform-origin: top left;
+ -webkit-animation-timing-function: ease-in-out;
+ animation-timing-function: ease-in-out;
+ }
+
+ 20%, 60% {
+ -webkit-transform: rotate3d(0, 0, 1, 80deg);
+ transform: rotate3d(0, 0, 1, 80deg);
+ -webkit-transform-origin: top left;
+ transform-origin: top left;
+ -webkit-animation-timing-function: ease-in-out;
+ animation-timing-function: ease-in-out;
+ }
+
+ 40%, 80% {
+ -webkit-transform: rotate3d(0, 0, 1, 60deg);
+ transform: rotate3d(0, 0, 1, 60deg);
+ -webkit-transform-origin: top left;
+ transform-origin: top left;
+ -webkit-animation-timing-function: ease-in-out;
+ animation-timing-function: ease-in-out;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 700px, 0);
+ transform: translate3d(0, 700px, 0);
+ opacity: 0;
+ }
+}
+
+@keyframes hinge {
+ 0% {
+ -webkit-transform-origin: top left;
+ transform-origin: top left;
+ -webkit-animation-timing-function: ease-in-out;
+ animation-timing-function: ease-in-out;
+ }
+
+ 20%, 60% {
+ -webkit-transform: rotate3d(0, 0, 1, 80deg);
+ transform: rotate3d(0, 0, 1, 80deg);
+ -webkit-transform-origin: top left;
+ transform-origin: top left;
+ -webkit-animation-timing-function: ease-in-out;
+ animation-timing-function: ease-in-out;
+ }
+
+ 40%, 80% {
+ -webkit-transform: rotate3d(0, 0, 1, 60deg);
+ transform: rotate3d(0, 0, 1, 60deg);
+ -webkit-transform-origin: top left;
+ transform-origin: top left;
+ -webkit-animation-timing-function: ease-in-out;
+ animation-timing-function: ease-in-out;
+ opacity: 1;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 700px, 0);
+ transform: translate3d(0, 700px, 0);
+ opacity: 0;
+ }
+}
+
+.hinge {
+ -webkit-animation-name: hinge;
+ animation-name: hinge;
+}
+
+/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
+
+@-webkit-keyframes rollIn {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
+ transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes rollIn {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
+ transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+.rollIn {
+ -webkit-animation-name: rollIn;
+ animation-name: rollIn;
+}
+
+/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
+
+@-webkit-keyframes rollOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
+ transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
+ }
+}
+
+@keyframes rollOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
+ transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
+ }
+}
+
+.rollOut {
+ -webkit-animation-name: rollOut;
+ animation-name: rollOut;
+}
+
+@-webkit-keyframes zoomIn {
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.3, .3, .3);
+ transform: scale3d(.3, .3, .3);
+ }
+
+ 50% {
+ opacity: 1;
+ }
+}
+
+@keyframes zoomIn {
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.3, .3, .3);
+ transform: scale3d(.3, .3, .3);
+ }
+
+ 50% {
+ opacity: 1;
+ }
+}
+
+.zoomIn {
+ -webkit-animation-name: zoomIn;
+ animation-name: zoomIn;
+}
+
+@-webkit-keyframes zoomInDown {
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0);
+ transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0);
+ transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+@keyframes zoomInDown {
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0);
+ transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0);
+ transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+.zoomInDown {
+ -webkit-animation-name: zoomInDown;
+ animation-name: zoomInDown;
+}
+
+@-webkit-keyframes zoomInLeft {
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0);
+ transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0);
+ transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+@keyframes zoomInLeft {
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0);
+ transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0);
+ transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+.zoomInLeft {
+ -webkit-animation-name: zoomInLeft;
+ animation-name: zoomInLeft;
+}
+
+@-webkit-keyframes zoomInRight {
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0);
+ transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0);
+ transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+@keyframes zoomInRight {
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0);
+ transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0);
+ transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+.zoomInRight {
+ -webkit-animation-name: zoomInRight;
+ animation-name: zoomInRight;
+}
+
+@-webkit-keyframes zoomInUp {
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0);
+ transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0);
+ transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+@keyframes zoomInUp {
+ 0% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0);
+ transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 60% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0);
+ transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+.zoomInUp {
+ -webkit-animation-name: zoomInUp;
+ animation-name: zoomInUp;
+}
+
+@-webkit-keyframes zoomOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 50% {
+ opacity: 0;
+ -webkit-transform: scale3d(.3, .3, .3);
+ transform: scale3d(.3, .3, .3);
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes zoomOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 50% {
+ opacity: 0;
+ -webkit-transform: scale3d(.3, .3, .3);
+ transform: scale3d(.3, .3, .3);
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+.zoomOut {
+ -webkit-animation-name: zoomOut;
+ animation-name: zoomOut;
+}
+
+@-webkit-keyframes zoomOutDown {
+ 40% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0);
+ transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0);
+ transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0);
+ -webkit-transform-origin: center bottom;
+ transform-origin: center bottom;
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+@keyframes zoomOutDown {
+ 40% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0);
+ transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0);
+ transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0);
+ -webkit-transform-origin: center bottom;
+ transform-origin: center bottom;
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+.zoomOutDown {
+ -webkit-animation-name: zoomOutDown;
+ animation-name: zoomOutDown;
+}
+
+@-webkit-keyframes zoomOutLeft {
+ 40% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0);
+ transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale(.1) translate3d(-2000px, 0, 0);
+ transform: scale(.1) translate3d(-2000px, 0, 0);
+ -webkit-transform-origin: left center;
+ transform-origin: left center;
+ }
+}
+
+@keyframes zoomOutLeft {
+ 40% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0);
+ transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale(.1) translate3d(-2000px, 0, 0);
+ transform: scale(.1) translate3d(-2000px, 0, 0);
+ -webkit-transform-origin: left center;
+ transform-origin: left center;
+ }
+}
+
+.zoomOutLeft {
+ -webkit-animation-name: zoomOutLeft;
+ animation-name: zoomOutLeft;
+}
+
+@-webkit-keyframes zoomOutRight {
+ 40% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0);
+ transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale(.1) translate3d(2000px, 0, 0);
+ transform: scale(.1) translate3d(2000px, 0, 0);
+ -webkit-transform-origin: right center;
+ transform-origin: right center;
+ }
+}
+
+@keyframes zoomOutRight {
+ 40% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0);
+ transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale(.1) translate3d(2000px, 0, 0);
+ transform: scale(.1) translate3d(2000px, 0, 0);
+ -webkit-transform-origin: right center;
+ transform-origin: right center;
+ }
+}
+
+.zoomOutRight {
+ -webkit-animation-name: zoomOutRight;
+ animation-name: zoomOutRight;
+}
+
+@-webkit-keyframes zoomOutUp {
+ 40% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0);
+ transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0);
+ transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0);
+ -webkit-transform-origin: center bottom;
+ transform-origin: center bottom;
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+@keyframes zoomOutUp {
+ 40% {
+ opacity: 1;
+ -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0);
+ transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0);
+ -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0);
+ transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0);
+ -webkit-transform-origin: center bottom;
+ transform-origin: center bottom;
+ -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1);
+ }
+}
+
+.zoomOutUp {
+ -webkit-animation-name: zoomOutUp;
+ animation-name: zoomOutUp;
+}
+
+@-webkit-keyframes slideInDown {
+ 0% {
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ visibility: visible;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+@keyframes slideInDown {
+ 0% {
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ visibility: visible;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.slideInDown {
+ -webkit-animation-name: slideInDown;
+ animation-name: slideInDown;
+}
+
+@-webkit-keyframes slideInLeft {
+ 0% {
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ visibility: visible;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+@keyframes slideInLeft {
+ 0% {
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ visibility: visible;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.slideInLeft {
+ -webkit-animation-name: slideInLeft;
+ animation-name: slideInLeft;
+}
+
+@-webkit-keyframes slideInRight {
+ 0% {
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ visibility: visible;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+@keyframes slideInRight {
+ 0% {
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ visibility: visible;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.slideInRight {
+ -webkit-animation-name: slideInRight;
+ animation-name: slideInRight;
+}
+
+@-webkit-keyframes slideInUp {
+ 0% {
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ visibility: visible;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+@keyframes slideInUp {
+ 0% {
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ visibility: visible;
+ }
+
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.slideInUp {
+ -webkit-animation-name: slideInUp;
+ animation-name: slideInUp;
+}
+
+@-webkit-keyframes slideOutDown {
+ 0% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ 100% {
+ visibility: hidden;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+}
+
+@keyframes slideOutDown {
+ 0% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ 100% {
+ visibility: hidden;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+}
+
+.slideOutDown {
+ -webkit-animation-name: slideOutDown;
+ animation-name: slideOutDown;
+}
+
+@-webkit-keyframes slideOutLeft {
+ 0% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ 100% {
+ visibility: hidden;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+}
+
+@keyframes slideOutLeft {
+ 0% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ 100% {
+ visibility: hidden;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+}
+
+.slideOutLeft {
+ -webkit-animation-name: slideOutLeft;
+ animation-name: slideOutLeft;
+}
+
+@-webkit-keyframes slideOutRight {
+ 0% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ 100% {
+ visibility: hidden;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+}
+
+@keyframes slideOutRight {
+ 0% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ 100% {
+ visibility: hidden;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+}
+
+.slideOutRight {
+ -webkit-animation-name: slideOutRight;
+ animation-name: slideOutRight;
+}
+
+@-webkit-keyframes slideOutUp {
+ 0% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ 100% {
+ visibility: hidden;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+}
+
+@keyframes slideOutUp {
+ 0% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+
+ 100% {
+ visibility: hidden;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+}
+
+.slideOutUp {
+ -webkit-animation-name: slideOutUp;
+ animation-name: slideOutUp;
+}
diff --git a/template/megp/css/bootbox.css b/template/megp/css/bootbox.css
new file mode 100644
index 0000000..3690231
--- /dev/null
+++ b/template/megp/css/bootbox.css
@@ -0,0 +1,121 @@
+.fade
+{
+ opacity:0;
+ -webkit-transition:opacity .15s linear;
+ -moz-transition:opacity .15s linear;
+ -o-transition:opacity .15s linear;
+ transition:opacity .15s linear
+}
+.fade.in{
+ opacity:1
+}
+.modal-backdrop
+{
+ position:fixed;
+ top:0;
+ right:0;
+ bottom:0;
+ left:0;
+ z-index:1040;
+ background-color:#000
+}
+.modal-backdrop.fade{
+ opacity:0
+}
+.modal-backdrop,.modal-backdrop.fade.in
+{
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+ background-color: #90D3F4;
+}
+.modal
+{
+ position:fixed;
+ top:40%;
+ left:45%;
+ z-index:1050;
+ width: 98%;
+ background-color:#fff;
+ border:1px solid #EEE;
+ outline:0;
+ -webkit-background-clip:padding-box;
+ -moz-background-clip:padding-box;
+ background-clip:padding-box
+}
+.modal.fade
+{
+ top:-25%;
+ -webkit-transition:opacity .3s linear,top .3s ease-out;
+ -moz-transition:opacity .3s linear,top .3s ease-out;
+ -o-transition:opacity .3s linear,top .3s ease-out;
+ transition:opacity .3s linear,top .3s ease-out
+}
+.modal.fade.in
+{
+ top: 35%;
+ left: 1%;
+}
+.modal-header
+{
+ padding:9px 15px;
+ border-bottom:1px solid #eee
+}
+.modal-header .close
+{
+ margin-top:2px
+}
+.modal-header h3
+{
+ margin:0;
+ line-height:30px
+}
+.modal-body
+{
+ position:relative;
+ max-height:400px;
+ padding:15px;
+ overflow-y:auto
+}
+.modal-form
+{
+ margin-bottom:0
+}
+.modal-footer
+{
+ padding:14px 15px 15px;
+ margin-bottom:0;
+ text-align:right;
+ background-color:#f5f5f5;
+ border-top:1px solid #ddd;
+ -webkit-border-radius:0 0 6px 6px;
+ -moz-border-radius:0 0 6px 6px;
+ border-radius:0 0 6px 6px;
+ *zoom:1;
+ -webkit-box-shadow:inset 0 1px 0 #fff;
+ -moz-box-shadow:inset 0 1px 0 #fff;
+ box-shadow:inset 0 1px 0 #fff
+}
+.modal-footer:before,.modal-footer:after
+{
+ display:table;
+ line-height:0;
+ content:""
+}
+.modal-footer:after
+{
+ clear:both
+}
+.modal-footer .btn+.btn
+{
+ margin-bottom:0;
+ margin-left:5px
+}
+.modal-footer .btn-group .btn+.btn
+{
+ margin-left:-1px
+}
+.modal-footer .btn-block+.btn-block
+{
+ margin-left:0
+}
+
diff --git a/template/megp/css/font-awesome.css b/template/megp/css/font-awesome.css
new file mode 100644
index 0000000..540440c
--- /dev/null
+++ b/template/megp/css/font-awesome.css
@@ -0,0 +1,4 @@
+/*!
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
diff --git a/template/megp/css/framework.css b/template/megp/css/framework.css
new file mode 100644
index 0000000..dda12bc
--- /dev/null
+++ b/template/megp/css/framework.css
@@ -0,0 +1,7531 @@
+@import url(http://fonts.googleapis.com/css?family=Roboto:400,100,100italic,300,300italic,400italic,500,500italic,700,700italic,900,900italic);
+@import url(http://fonts.googleapis.com/css?family=Crete+Round:400,400italic);
+@import url(http://fonts.googleapis.com/css?family=Petit+Formal+Script);
+@import url(http://fonts.googleapis.com/css?family=Lato:100,300,400,700,900,100italic,300italic,400italic,700italic,900italic);
+
+/*-------------*/
+/*----Reset----*/
+/*-------------*/
+* {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ vertical-align: baseline;
+ outline: none;
+ font-size-adjust: none;
+ -webkit-text-size-adjust: none;
+ -moz-text-size-adjust: none;
+ -ms-text-size-adjust: none;
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+}
+*:focus {
+ outline: none;
+}
+
+*,*:after,*:before {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+}
+
+.all-elements *{
+ -webkit-text-size-adjust:none;
+ -webkit-transform: translateZ(0);
+ min-height:auto;
+ max-height:auto;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ overflow-x:hidden;
+}
+
+body::-webkit-scrollbar {
+ display: none;
+}
+
+a:hover{
+ text-decoration:none!important;
+}
+
+::selection {
+ background-color:#CCC;
+ color: #000;
+}
+
+div, a, p, img, blockquote, form, fieldset, textarea, input, label, iframe, code, pre {
+ display: block;
+ position:relative;
+}
+
+textarea, input { outline: none; }
+
+input {
+ -webkit-appearance: none!important;
+ border-radius: 0!important;
+ -webkit-border-radius:0px!important;
+}
+
+textarea {
+ -webkit-appearance: none!important;
+ border-radius: 0!important;
+ -webkit-border-radius:0px!important;
+}
+
+/*------------------*/
+/*------------------*/
+/*----Typography----*/
+/*------------------*/
+/*------------------*/
+
+p {
+ line-height: 30px;
+ margin-bottom: 30px;
+ font-size:14px;
+}
+
+p > a {
+ display: inline;
+ text-decoration: none;
+}
+
+p > a:hover {
+ text-decoration: underline;
+}
+
+p > span {
+ display: inline;
+}
+
+p strong{
+ font-size:15px;
+ color:#1f1f1f;
+}
+
+span > a {
+ display: inline;
+}
+
+a > span {
+ display: inline;
+}
+
+a {
+ color:#2980b9;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: none;
+}
+
+strong a, em a{
+ display:inline;
+}
+
+h1 > a, h2 > a, h3 > a, h4 > a, h5 > a, h6 > a {
+ display: inline;
+}
+
+table {
+ border-collapse: separate;
+ border-spacing: 0;
+ background-color: #f6f6f6;
+ border-left: 1px solid #e9e9e9;
+ border-top: 1px solid #e9e9e9;
+ width: 100%;
+ clear: both;
+ margin-bottom: 27px;
+}
+
+thead {}
+
+th {
+ vertical-align: middle;
+ border-bottom: 1px solid #e9e9e9;
+ border-right: 1px solid #e9e9e9;
+ font-weight: bold;
+ color: #555;
+ background-color: #f6f6f6;
+}
+
+tr {
+ line-height: 18px;
+}
+
+td {
+ border-right: 1px solid #e9e9e9;
+ border-bottom: 1px solid #e9e9e9;
+ text-align: center;
+ color: #666;
+ padding-top: 9px;
+ padding-bottom: 9px;
+ line-height: 18px;
+ vertical-align: middle;
+ background-color: #fdfdfd;
+}
+
+tr:hover > td {
+ background: #fff;
+}
+
+ul {
+ margin-bottom: 30px;
+}
+
+ul ul{
+ margin-bottom:5px;
+}
+
+ol ol{
+ margin-bottom:5px;
+}
+
+ol {
+ margin-bottom: 30px;
+}
+li {
+ line-height: 24px;
+ margin-bottom:5px;
+}
+ol > li:last-child, ul > li:last-child {
+ margin-bottom:0px;
+}
+
+textarea{
+ font-family:'Roboto', sans-serif;
+}
+
+/*-------------------------------------------*/
+/*---Background and text colors and hovers---*/
+/*-------------------------------------------*/
+
+.bg-teal-light{ background-color: #1abc9c!important;}
+.bg-teal-dark{ background-color: #16a085!important;}
+.bg-hover-teal-light:hover{ background-color: #1abc9c!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.bg-hover-teal-dark:hover{ background-color: #16a085!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.color-teal-light{ color: #1abc9c;}
+.color-teal-dark{ color: #16a085;}
+
+.bg-green-light{background-color: #2ecc71!important;}
+.bg-green-dark{background-color: #2abb67!important;}
+.bg-hover-green-light:hover{background-color: #2ecc71!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.bg-hover-green-dark:hover{background-color: #2abb67!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.color-green-light{color: #2ecc71;}
+.color-green-dark{color: #2abb67;}
+
+.bg-blue-light{background-color: #3498db!important;}
+.bg-blue-dark{background-color: #2980b9!important;}
+.bg-hover-blue-light:hover{background-color: #3498db!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.bg-hover-blue-dark:hover{background-color: #2980b9!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.color-blue-light{color: #3498db;}
+.color-blue-dark{color: #2980b9;}
+
+.bg-magenta-light{background-color: #9b59b6!important;}
+.bg-magenta-dark{background-color: #8e44ad!important;}
+.bg-hover-magenta-light:hover{background-color: #9b59b6!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.bg-hover-magenta-dark:hover{background-color: #8e44ad!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.color-magenta-light{color: #9b59b6;}
+.color-magenta-dark{color: #8e44ad;}
+
+.bg-night-light{background-color: #34495e!important;}
+.bg-night-dark{background-color: #2c3e50!important;}
+.bg-hover-night-light:hover{background-color: #34495e!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.bg-hover-night-dark:hover{background-color: #2c3e50!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.color-night-light{color: #34495e;}
+.color-night-dark{color: #2c3e50;}
+
+.bg-yellow-light{background-color: #f1c40f!important;}
+.bg-yellow-dark{background-color: #f39c12!important;}
+.bg-hover-yellow-light:hover{background-color: #f1c40f!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.bg-hover-yellow-dark:hover{background-color: #f39c12!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.color-yellow-light{color: #f1c40f;}
+.color-yellow-dark{color: #f39c12;}
+
+.bg-orange-light{background-color: #e67e22!important;}
+.bg-orange-dark{background-color: #d35400!important;}
+.bg-hover-orange-light:hover{background-color: #e67e22!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.bg-hover-orange-dark:hover{background-color: #d35400!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.color-orange-light{color: #e67e22;}
+.color-orange-dark{color: #d35400;}
+
+.bg-red-light{background-color: #e74c3c!important;}
+.bg-red-dark{background-color: #c0392b!important;}
+.bg-hover-red-light:hover{background-color: #e74c3c!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.bg-hover-red-dark:hover{background-color: #c0392b!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.color-red-light{color: #e74c3c;}
+.color-red-dark{color: #c0392b;}
+
+.bg-gray-light{background-color: #bdc3c7!important;}
+.bg-gray-dark{background-color: #95a5a6!important;}
+.bg-hover-gray-light:hover{background-color: #bdc3c7!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.bg-hover-gray-dark:hover{background-color: #95a5a6!important; color:#FFFFFF!important; transition:all 200ms ease;}
+.color-gray-light{color: #bdc3c7;}
+.color-gray-dark{olor: #95a5a6;}
+
+.bg-white{background-color:#FFFFFF;}
+.color-white{color:#FFFFFF;}
+
+.bg-black{background-color:#000000;}
+.color-black{color:#000000;}
+
+.bg-green-light i,
+.bg-green-dark i,
+.bg-teal-light i,
+.bg-teal-dark i,
+.bg-magenta-light i,
+.bg-magenta-dark i,
+.bg-yellow-light i,
+.bg-yellow-dark i,
+.bg-orange-light i,
+.bg-orange-dark i,
+.bg-red-dark i,
+.bg-red-light i,
+.bg-gray-light i,
+.bg-gray-dark i{
+ color:#FFFFFF;
+}
+
+.bg-1{background-image:url(../images/pictures/1.jpg);}
+.bg-2{background-image:url(../images/pictures/2.jpg);}
+.bg-3{background-image:url(../images/pictures/3.jpg);}
+.bg-4{background-image:url(../images/pictures/4.jpg);}
+.bg-5{background-image:url(../images/pictures/5.jpg);}
+.bg-6{background-image:url(../images/pictures/6.jpg);}
+.bg-7{background-image:url(../images/pictures/7.jpg);}
+.cover-screen{background-size:cover; background-position: center center;}
+
+.facebook-color{ background-color:#3b5998; color:#FFFFFF;}
+.twitter-color{ background-color:#4099ff; color:#FFFFFF;}
+.google-color{ background-color:#d34836; color:#FFFFFF;}
+.pinterest-color{ background-color:#C92228; color:#FFFFFF;}
+.sms-color{ background-color:#27ae60; color:#FFFFFF;}
+.mail-color{ background-color:#3498db; color:#FFFFFF;}
+.dribbble-color{ background-color:#EA4C89; color:#FFFFFF;}
+.tumblr-color{ background-color:#2C3D52; color:#FFFFFF;}
+.reddit-color{ background-color:#336699; color:#FFFFFF;}
+.youtube-color{ background-color:#D12827; color:#FFFFFF;}
+.phone-color{ background-color:#27ae60; color:#FFFFFF;}
+
+/*----------------------*/
+/*---Heading Settings---*/
+/*----------------------*/
+
+h1, h2, h3, h4, h5, h6{
+ font-family:'Roboto', sans-serif;
+ color:#1f1f1f;
+ margin-bottom:10px;
+}
+
+.sub-heading{
+ color:#4c4c4c;
+ font-weight:500;
+}
+
+.small-heading{
+ font-size:12px;
+ margin-top:-10px;
+ margin-bottom:10px;
+ display:block;
+}
+
+h1{
+ font-size:24px;
+ line-height:36px;
+ font-weight:700;
+}
+
+h2{
+ font-size:22px;
+ line-height:44px;
+ font-weight:700;
+}
+
+h3{
+ font-size:20px;
+ line-height:22px;
+ font-weight:700;
+}
+
+h4{
+ font-size:18px;
+ line-height:22px;
+ font-weight:700;
+}
+
+h5{
+ font-size:16px;
+ line-height:20px;
+ font-weight:700;
+}
+
+h6{
+ font-size:14px;
+ line-height:30px;
+ font-weight:800;
+}
+
+body{
+ font-size:14px;
+ font-family:'Roboto', sans-serif;
+ line-height:30px;
+ font-weight:400;
+ color:#666666;
+}
+
+.highlighted{padding:4px 10px; margin-bottom:5px;}
+
+/*-------------------------------*/
+/*---Lists and Font Icon Lists---*/
+/*-------------------------------*/
+
+.font-icon-list li{
+ list-style:none;
+ padding-left:0px;
+ margin-left:-20px;
+ color:#666666;
+ line-height:30px;
+}
+
+.font-icon-list li a{
+ color:#666666;
+}
+
+.icon-list li a{
+ color:#666666;
+}
+
+.font-icon-list i{
+ margin-right:10px;
+ width:30px;
+}
+
+
+.icon-list{
+ list-style: none;
+ padding-left:0px;
+}
+
+.icon-list i{
+ width:20px;
+ text-align:center;
+ margin-left:-5px;
+}
+
+.icon-list ul{
+ padding-left:40px;
+}
+
+ul{
+ padding-left:20px;
+ list-style-type:disc;
+}
+
+ol{
+ padding-left:20px;
+ list-style-type: decimal;
+}
+
+ol ol{
+ list-style-type:lower-alpha;
+}
+
+ol ol ol{
+ list-style-type:disc;
+}
+
+/*--------------------*/
+/*---Table Settings---*/
+/*--------------------*/
+
+.table{
+ width:100%;
+ margin-bottom:35px;
+}
+
+.table-title{
+ font-family:'Dosis', sans-serif;
+ font-size:12px;
+}
+
+.table-sub-title{
+ font-family:'Dosis', sans-serif;
+ font-weight:bold;
+ font-size:12px;
+}
+
+.price{
+ font-size:12px;
+}
+
+.small-price{
+ position:absolute;
+ margin-left:0px;
+ font-size:10px;
+ margin-top:-3px!important;
+}
+
+.table {
+ font-size:12px;
+ text-shadow: 1px 1px 0px #fff;
+ background:#eaebec;
+ border:#ccc 1px solid;
+ margin-bottom:25px;
+}
+
+.table th {
+ padding:21px 25px 22px 25px;
+ border-bottom:1px solid #e0e0e0;
+}
+
+.table th:first-child{
+ text-align: center;
+ padding-left:20px;
+}
+
+.table tr{
+ text-align: center;
+ padding-left:20px;
+}
+
+.table tr td:first-child{
+ text-align: left;
+ padding-left:20px;
+ border-left: 0;
+}
+
+.table tr td {
+ padding:14px;
+ border-top: 1px solid #ffffff;
+ border-bottom:1px solid #e0e0e0;
+ border-left: 0px solid #e0e0e0;
+}
+
+.table tr.even td{
+ background: #efefef;
+}
+
+.table tr:last-child td{
+ border-bottom:0;
+}
+
+.table tr:hover td{
+ background: #f8f8f8;
+}
+
+.table-heading{
+ padding-top:10px;
+ padding-bottom:15px;
+}
+
+.table-text{
+ color:#6f6f6f;
+}
+
+/*---------------------------*/
+/*---Custom Heading Styles---*/
+/*---------------------------*/
+
+.morphext > .animated {
+ display: inline-block;
+}
+
+/*Heading Style 1*/
+
+.heading-style-1,
+.heading-style-2,
+.heading-style-3,
+.heading-style-4,
+.heading-style-5{
+ cursor:default;
+}
+
+.heading-style-1{
+ margin-top:10px;
+}
+
+.heading-style-1 .heading-decoration{
+ clear:both;
+ display:block;
+ height:4px;
+ margin-top:13px;
+}
+
+
+.heading-style-1 a{
+ color:#FFFFFF;
+ z-index:2;
+}
+
+.heading-style-1 i{
+ font-size:14px;
+ margin-top:-5px;
+ width:40px;
+ height:40px;
+ text-align:center;
+ line-height:40px;
+ z-index:2;
+ position:relative;
+ color:#FFFFFF;
+}
+
+.heading-style-1 *{
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.heading-style-1 a{
+ position:absolute;
+ right:0px;
+}
+
+.heading-style-1 .heading-block{
+ position:absolute;
+ height:40px;
+ width:40px;
+ right:0px;
+ top:-5px;
+ z-index:1;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+}
+
+/*Heading Style 2*/
+
+.heading-style-2{
+ width:100%;
+ padding-left:30px;
+ padding-right:30px;
+ padding-top:30px;
+ padding-bottom:30px;
+ background-size:cover;
+}
+
+.heading-style-2 .overlay{
+ opacity:0.8;
+ position:absolute;
+ width:100%;
+ height:100%;
+ overflow:hidden;
+ top:0px;
+ left:0px;
+ right:0px;
+ bottom:0px;
+ z-index:1;
+}
+
+.heading-style-2 .heading-title{
+ z-index:2;
+ position:relative;
+ color:#FFFFFF;
+ margin-bottom:5px;
+}
+
+.heading-style-2 .heading-subtitle{
+ display:block;
+ position:relative;
+ color:#FFFFFF;
+ z-index:2;
+ opacity:0.6;
+ font-style:normal;
+ font-size:13px;
+}
+
+.heading-style-2 .heading-icon{
+ float:right;
+ font-size:35px;
+ color:#FFFFFF;
+ z-index:10;
+ position:relative!important;
+ display:inline-block!important;
+ margin-top:-47px;
+ opacity:0.9;
+}
+
+.heading-style-2{
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/*Heading Style 3*/
+
+@media(max-width:768px){
+ .heading-style-3{
+ width:100%;
+ padding-left:30px;
+ padding-right:30px;
+ padding-top:25px;
+ padding-bottom:20px;
+ background-size:cover;
+ }
+}
+
+@media(min-width:768px){
+ .heading-style-3{
+ width:100%;
+ padding-left:30px;
+ padding-right:30px;
+ padding-top:35px;
+ padding-bottom:30px;
+ background-size:cover;
+ }
+}
+
+.heading-style-3 .overlay{
+ opacity:0.7;
+ position:absolute;
+ width:100%;
+ height:100%;
+ overflow:hidden;
+ top:0px;
+ left:0px;
+ right:0px;
+ bottom:0px;
+ z-index:1;
+}
+
+.heading-style-3 .heading-title{
+ z-index:2;
+ position:relative;
+ color:#FFFFFF;
+ margin-bottom:0px;
+ text-align:center;
+ font-weight:500;
+}
+
+.heading-style-3 .heading-subtitle{
+ display:block;
+ position:relative;
+ color:#FFFFFF;
+ z-index:2;
+ opacity:0.6;
+ font-style:normal;
+ font-size:13px;
+ text-align:center;
+}
+
+.heading-style-3{
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/*Heading Style 4*/
+
+.heading-style-4{
+ padding-left:20px;
+ padding-right:20px;
+ text-align:center;
+}
+
+.heading-style-4 .heading-title{
+ padding-bottom:5px;
+}
+
+.heading-style-4 .heading-subtitle{
+ font-size:13px;
+ line-height:35px;
+}
+
+.heading-style-4 .line{
+ height:4px;
+ width:70px;
+ margin:15px auto;
+ opacity:0.5;
+}
+
+.heading-style-4{
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/*Heading Style 5*/
+
+.heading-style-5{
+ padding-left:20px;
+ padding-right:20px;
+ text-align:center;
+}
+
+.heading-style-5 .heading-subtitle{
+ font-size:13px;
+ margin-bottom:20px;
+}
+
+.heading-style-5 .heading-icon{
+ display:block;
+ text-align:center;
+ background-color:#FFFFFF;
+ position:relative;
+ z-index:1;
+ width:60px;
+ height:40px;
+ line-height:40px;
+ margin-left:auto;
+ margin-right:auto;
+ font-size:24px;
+ display:block;
+ overflow:hidden;
+}
+
+.heading-style-5 .line{
+ height:1px;
+ background-color:rgba(0,0,0,0.15);
+ width:100%;
+ margin-top:-20px;
+ margin-bottom:30px;
+}
+
+@media(min-width:700px){
+
+ .heading-style-5 .line{
+ height:1px;
+ background-color:rgba(0,0,0,0.15);
+ width:300px;
+ margin-left:auto;
+ margin-right:auto;
+ margin-top:-20px;
+ margin-bottom:30px;
+ }
+
+}
+
+.heading-style-5{
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/*Heading Style 6*/
+
+.heading-style-6 .heading-title{
+ margin-bottom:5px;
+}
+
+.heading-style-6 .heading-category{
+ font-style:normal;
+ font-size:12px;
+ font-weight:500;
+ padding-bottom:5px;
+}
+
+.heading-style-6 .heading-category a{
+ display:inline;
+ padding-left:5px;
+}
+
+.heading-style-6 .heading-subtitle{
+ display:inline;
+ font-style:normal;
+ font-size:12px;
+ margin-right:-2px;
+}
+
+.heading-style-6 .heading-subtitle a{
+ display:inline;
+ padding-left:5px;
+ border-right:solid 2px #cacaca;
+ padding-right:5px;
+ margin-right:5px;
+}
+
+.heading-style-6 .heading-text{
+ font-size:12px;
+ margin-top:20px;
+ line-height:31px;
+}
+
+/*-----------------*/
+/*---Text Fields---*/
+/*-----------------*/
+
+.icon-field .text-field{
+ padding-left:45px!important;
+}
+
+.icon-field i:first-child{
+ position:absolute;
+ top:15px;
+ left:0px;
+ width:40px;
+ text-align:center;
+}
+
+.text-icon-field{
+ margin-bottom:35px;
+}
+
+.text-icon-field .text-field{
+ padding-left:45px!important;
+}
+
+.text-icon-field i:first-child{
+ position:absolute;
+ top:40px;
+ left:0px;
+ line-height:45px;
+ height:45px;
+ width:45px;
+ text-align:center;
+}
+
+.text-icon-field h6{
+ font-weight:500;
+ float:left;
+}
+
+.text-icon-field em{
+ letter-spacing:1px;
+ opacity:0.5;
+ float:right;
+ font-size:10px;
+ font-style:normal;
+}
+
+.text-field{
+ width:100%;
+ height:45px;
+ padding-left:10px;
+ padding-right:10px;
+ margin-bottom:10px;
+ background-color:rgba(255,255,255,0.4);
+ font-size:12px;
+ color:#666;
+ border:solid 1px #cacaca;
+ transition:all 250ms ease;
+}
+
+.green-field:focus{
+ transition:all 250ms ease;
+ border:solid 1px #2cc36b;
+}
+
+.red-field:focus{
+ transition:all 250ms ease;
+ border:solid 1px #c0392b;
+}
+
+/*-----------------------------*/
+/*----Checkboxes and Radios----*/
+/*-----------------------------*/
+
+label{
+ -webkit-user-select: none; /* webkit (safari, chrome) browsers */
+ -moz-user-select: none; /* mozilla browsers */
+ -khtml-user-select: none; /* webkit (konqueror) browsers */
+ -ms-user-select: none; /* IE10+ */
+ cursor:pointer;
+ margin-left:20px;
+}
+
+.checkbox{
+ -moz-appearance:checkbox!important; /* Firefox */
+ -webkit-appearance:checkbox!important; /* Safari and Chrome */
+ appearance:checkbox!important;
+ margin-left:10px;
+ float:left;
+ line-height:24px;
+ margin-top:9px;
+ margin-right:10px;
+}
+
+.radiobox{
+ -moz-appearance:radio!important; /* Firefox */
+ -webkit-appearance:radio!important; /* Safari and Chrome */
+ appearance:radio!important;
+ float:left;
+ line-height:24px;
+ margin-top:9px;
+ margin-right:10px;
+ margin-left:10px;
+}
+
+/*----------------------------*/
+/*---Image & Video Settings---*/
+/*----------------------------*/
+
+.img-responsive{
+ display:block;
+ width:100%;
+}
+
+.responsive-image{
+ display:block;
+ width:100%;
+}
+
+.preload-image{
+ display:none;
+}
+
+.responsive-video{
+ position:relative;
+ padding-bottom:56.25%;
+ padding-top:30px;
+ height:0;
+ overflow:hidden;
+}
+
+.responsive-video iframe, .responsive-video object, .responsive-video embed {
+ position:absolute;
+ top:0;
+ left:0;
+ width:100%;
+ height:100%;
+}
+
+/*-----------------------------------------------*/
+/*-----------------------------------------------*/
+/*----Content Classes and Responsive Settings----*/
+/*-----------------------------------------------*/
+/*-----------------------------------------------*/
+
+.clear{clear:both!important;}
+.last-column{margin-right:0%!important;}
+.no-bottom{margin-bottom:0px!important;}
+.half-bottom{margin-bottom:15px!important;}
+.half-top{margin-top:15px!important;}
+.full-bottom{margin-bottom:30px!important;}
+.full-top{margin-top:30px!important;}
+.left-padding{padding-left:30px;}
+.right-padding{padding-right:30px;}
+
+.container:after{
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
+}
+
+.last-column:after{
+ visibility: hidden!important;
+ display: block!important;
+ content: ""!important;
+ clear: both!important;
+ height: 0!important;
+}
+
+.container{
+ margin-bottom:30px;
+ display:block;
+}
+
+.container-fullscreen{
+ margin-bottom:30px;
+}
+
+.hide-if-mobile{
+ display:none!important;
+}
+
+.hide-if-resposive{
+ display:block!important;
+}
+
+.uppercase{text-transform:uppercase;}
+.lowercase{text-transform:lowercase;}
+.thin{font-weight:300!important;}
+.thiner{font-weight:400!important;}
+.bold{font-weight:600!important;}
+.ultrabold{font-weight:1000!important;}
+
+.small-text{font-size:12px;}
+.smaller-text{font-size:13px;}
+.larger-text{font-size:15px;}
+.large-text{font-size:16px;}
+
+.center-text{text-align:center; display:block;}
+.left-text{text-align:left; display:block;}
+.right-text{text-align:right; display:block;}
+
+.center-text-mobile{text-align:center; display:block;}
+.left-text-mobile{text-align:left; display:block;}
+.right-text-mobile{text-align:right; display:block;}
+
+.center-text-tablet{text-align:left; display:block;}
+.left-text-tablet{text-align:left; display:block;}
+.right-text-tablet{text-align:left; display:block;}
+
+.space-left{padding-left:10px;}
+.space-right{padding-right:10px;}
+
+@media(min-width:750px){
+ .hide-if-mobile{
+ display:block!important;
+ }
+
+ .hide-if-responsive{
+ display:none!important;
+ }
+
+ .center-text-tablet{text-align:center;}
+ .left-text-tablet{text-align:left;}
+ .right-text-tablet{text-align:right;}
+
+ .center-text-mobile{text-align:left;}
+ .left-text-tablet{text-align:left;}
+ .right-text-tablet{text-align:left;}
+}
+
+.decoration{
+ height:1px;
+ width:100%;
+ display:block;
+ background-color:rgba(0,0,0,0.1);
+ margin-bottom:30px;
+ clear:both;
+}
+
+@media(max-width:768px){
+ .one-half{
+ width:45%;
+ float:left;
+ margin-right:10%;
+ }
+
+ .one-third{
+ width:28%;
+ float:left;
+ margin-right:8%;
+ }
+
+ .one-half-responsive{width:100%;}
+ .one-third-responsive{width:100%;}
+}
+
+@media(min-width:767px){
+ .one-half, .one-half-responsive{
+ width:45%;
+ float:left;
+ margin-right:10%;
+ }
+
+ .one-third, .one-third-responsive{
+ width:28%;
+ float:left;
+ margin-right:8%;
+ }
+
+ .one-forth-responsive{
+ float:left;
+ width:20%;
+ margin-right:5%;
+ }
+
+ .sidebar-left-big{
+ width:70%;
+ float:left;
+ margin-right:5%
+ }
+
+ .sidebar-right-small{
+ width:25%;
+ float:right;
+ }
+
+ .sidebar-right-big{
+ width:70%;
+ float:right;
+ }
+
+ .sidebar-left-small{
+ width:25%;
+ float:left;
+ margin-right:5%;
+ }
+}
+
+/*Animations*/
+
+.scale-hover{
+ -moz-transform: scale(1 1);
+ -webkit-transform: scale(1 1);
+ -o-transform: scale(1 1);
+ -ms-transform: scale(1 1);
+ transform: scale(1 1);
+ -webkit-transition: all 200ms ease;
+ -moz-transition: all 200ms ease;
+ -ms-transition: all 200ms ease;
+ -o-transition: all 200ms ease;
+ transition: all 200ms ease;
+}
+
+.scale-hover:hover{
+ -moz-transform: scale(1.1, 1.1);
+ -webkit-transform: scale(1.1, 1.1);
+ -o-transform: scale(1.1, 1.1);
+ -ms-transform: scale(1.1, 1.1);
+ transform: scale(1.1, 1.1);
+ -webkit-transition: all 200ms ease;
+ -moz-transition: all 200ms ease;
+ -ms-transition: all 200ms ease;
+ -o-transition: all 200ms ease;
+ transition: all 200ms ease;
+}
+
+/*-------------------------------*/
+/*-------------------------------*/
+/*----Blockquotes and Reviews----*/
+/*-------------------------------*/
+/*-------------------------------*/
+
+/*Quote 1*/
+.quote-1 .quote-text{
+ font-size:15px;
+ display:block;
+ font-weight:400;
+ color:rgba(0,0,0,0.7);
+ text-align:center;
+}
+
+.quote-1 .quote-image{
+ width:130px!important;
+ height:130px;
+ border-radius:100px;
+ margin-left:auto;
+ margin-right:auto;
+ margin-bottom:20px;
+}
+
+.quote-1 .quote-author{
+ font-size:18px;
+ display:block;
+ text-align:center;
+ padding-top:0px;
+ color:#1f1f1f;
+}
+
+.quote-1 .quote-rating{
+ width:115px;
+ margin-left:auto;
+ margin-right:auto;
+ margin-top:0px;
+ margin-bottom:0px;
+}
+
+.quote-1 i{
+ color:#f39c12;
+ font-size:16px;
+ margin-right:5px;
+}
+
+.quote-1 .quote-title{
+ font-size:11px;
+ display:block;
+ margin-bottom:10px;
+ font-style:normal;
+ text-align:center;
+}
+
+/*Quote 2*/
+.quote-2 .quote-image{
+ width:70px!important;
+ height:70px;
+ border-radius:70px;
+ float:left;
+ margin-right:20px;
+}
+
+.quote-2 .quote-author{
+ font-size:16px;
+ color:#1f1f1f;
+ display:block;
+ padding-top:5px;
+}
+
+.quote-2 .quote-rating{
+ float:left;
+ display:block;
+ width:150px;
+}
+
+.quote-2 em i{
+ font-size:16px;
+ color:#f39c12;
+ margin-right:5px;
+}
+
+.quote-2 .quote-text{
+ font-weight:400;
+ color:rgba(0,0,0,0.7);
+ padding-top:10px;
+ font-size:15px;
+ font-style:italic;
+}
+
+.quote-2 .quote-title{
+ font-size:11px;
+ display:block;
+ margin-bottom:10px;
+ font-style:normal;
+ text-align:right;
+ margin-top:-15px;
+}
+
+/*Quote 3*/
+.quote-3 .fa-quote-left{
+ font-size:20px;
+ display:block;
+ text-align:left;
+ position:absolute;
+ margin-top:10px;
+}
+
+.quote-3 p{
+ margin-bottom:0px;
+ padding-left:50px;
+}
+
+.quote-3 .quote-author{
+ display:block;
+ text-align:right;
+ color:#1f1f1f;
+ margin-top:20px;
+}
+
+/*Quote 4*/
+.quote-4 .fa-quote-right{
+ font-size:20px;
+ display:block;
+ text-align:left;
+ position:absolute;
+ right:0px;
+ margin-top:10px;
+}
+
+.quote-4 p{
+ margin-bottom:0px;
+ padding-right:50px;
+}
+
+.quote-4 .quote-author{
+ display:block;
+ text-align:left;
+ color:#1f1f1f;
+ margin-top:20px;
+}
+
+/*Quote 5*/
+.quote-5{
+ border-left:solid 5px #e34e47;
+ padding-left:20px;
+}
+
+.quote-5 p{
+ font-style:italic;
+ font-size:15px;
+ padding-top:10px;
+ margin-bottom:0px;
+}
+
+.quote-5 strong{
+ display:block;
+ text-align:right;
+ font-size:13px;
+ font-weight:500;
+ margin-top:-10px;
+ padding-bottom:0px;
+}
+
+/*----------------------*/
+/*----------------------*/
+/*-----User Reviews-----*/
+/*----------------------*/
+/*----------------------*/
+
+/* Review 1*/
+
+.review-1 strong{
+ color:#1f1f1f;
+ font-size:28px;
+ padding-right:10px;
+}
+
+.review-1 em{
+ color:#1f1f1f;
+ font-weight:600;
+ font-size:13px;
+ font-style:normal;
+}
+
+.review-1 .review-stars i{
+ color:#f3a01b;
+ font-size:18px;
+ padding-bottom:20px;
+ padding-top:5px;
+ padding-right:5px;
+}
+
+.review-1 .review-image{
+ position:absolute;
+ right:0px;
+ top:0px;
+ width:55px;
+ height:55px;
+ border-radius:55px;
+}
+
+.review-1 p{
+ font-size:16px;
+ line-height:36px;
+ font-style:italic;
+ font-weight:300;
+ letter-spacing:-0.6px;
+ color:#343434;
+ margin-bottom:10px;
+}
+
+.review-1 a{
+ text-align:right;
+ font-size:13px;
+ font-weight:500;
+}
+
+/* Review 2*/
+
+.review-2 strong{
+ color:#1f1f1f;
+ font-size:43px;
+ line-height:50px;
+}
+
+.review-2 em{
+ position:absolute;
+ top:0px;
+ left:100px;
+ font-size:10px;
+ font-style:normal;
+}
+
+.review-2 .review-stars{
+ position:absolute;
+ top:20px;
+ left:100px;
+}
+
+.review-2 .review-image{
+ width:50px;
+ height:50px;
+ position:absolute;
+ right:0px;
+ top:0px;
+ border-radius:50px;
+}
+
+.review-2 .review-stars i{
+ color:#f3a01b;
+ font-size:14px;
+}
+
+.review-2 p{
+ margin-top:20px;
+ font-size:16px;
+ line-height:36px;
+ font-style:italic;
+ font-weight:300;
+ letter-spacing:-0.6px;
+ color:#343434;
+ margin-bottom:15px;
+}
+
+.review-2 a{
+ text-align:right;
+ font-size:13px;
+ font-weight:500;
+}
+
+/*Review 3*/
+
+.review-3 .review-line i{
+ color:#f39c12;
+ float:right;
+ padding-top:10px;
+ width:22px;
+ text-align:center;
+}
+
+.review-3 .review-line strong{
+ color:#1f1f1f;
+}
+
+.review-3 .review-line{
+ margin-bottom:10px;
+}
+
+.review-3 .review-line-last{
+ margin-bottom:30px;
+}
+
+.review-3 .review-line-last i{
+ color:#d35400;
+ float:right;
+ padding-top:10px;
+ width:22px;
+ text-align:center;
+}
+
+.review-3 .review-line-last strong{
+ color:#1f1f1f;
+}
+
+.review-3 h4{
+ font-size:16px;
+ margin-bottom:0px;
+}
+
+.review-3 h5{
+ font-size:12px;
+ color:#666666;
+ font-weight:400;
+}
+
+.review-3 p{
+ font-style:italic;
+}
+
+/*Review 4*/
+
+.review-4 img{
+ position:absolute;
+ width:60px;
+ height:60px;
+ border-radius:70px;
+ top:-10px;
+}
+
+.review-4 h4{
+ color:#1f1f1f;
+ text-align:right;
+ margin-top:40px;
+ margin-left:80px;
+ margin-bottom:0px;
+}
+
+.review-4 .review-stars{
+ float:right;
+ margin-left:80px;
+ padding-bottom:20px;
+}
+
+
+.review-4 .review-stars i{
+ color:#f39c12;
+ padding-right:5px;
+ font-size:16px;
+}
+
+.review-4 p strong{
+ display:block;
+ color:#1f1f1f;
+ font-size:16px;
+}
+
+/*Review 5*/
+
+.review-5 img{
+ position:absolute;
+ width:60px;
+ height:60px;
+ border-radius:70px;
+ top:-10px;
+ right:0px;
+}
+
+.review-5 h4{
+ color:#1f1f1f;
+ margin-top:40px;
+ margin-bottom:0px;
+}
+
+.review-5 .review-stars{
+ padding-bottom:20px;
+}
+
+.review-5 .review-stars i{
+ color:#f39c12;
+ padding-right:5px;
+ font-size:16px;
+}
+
+.review-5 p strong{
+ display:block;
+ color:#1f1f1f;
+ font-size:16px;
+}
+
+/*Review 6*/
+
+.review-6 h4{
+ font-weight:300;
+ font-style:italic;
+ line-height:40px;
+ text-align:center;
+ padding-left:30px;
+ padding-right:30px;
+ color:#333333;
+}
+
+.review-6 strong{
+ text-align:center;
+ display:block;
+ color:#1f1f1f;
+ font-weight:700;
+ font-size:15px;
+ padding-top:10px;
+}
+
+.review-6 .review-stars{
+ width:149px;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.review-6 .review-stars i{
+ color:#f39c12;
+ padding-right:5px;
+ padding-left:5px;
+ font-size:18px;
+}
+
+.review-6 .fa-quote-right{
+ display:block;
+ font-size:24px;
+ padding-bottom:10px;
+}
+
+/*------------------*/
+/*------------------*/
+/*-----Dropcaps-----*/
+/*------------------*/
+/*------------------*/
+
+.dropcaps-1:first-letter{
+ float:left;
+ font-size:57px;
+ padding-top:14px!important;
+ padding-right:15px;
+ font-weight:800;
+ color:#1f1f1f;
+}
+
+.dropcaps-2:first-letter{
+ font-family: 'Petit Formal Script', cursive;
+ float:left;
+ font-size:42px;
+ padding-top:18px!important;
+ padding-right:15px;
+ font-weight:800;
+ color:#1f1f1f;
+}
+
+.dropcaps-3:first-letter{
+ background-color:#1f1f1f;
+ padding:10px;
+ padding-left:15px;
+ padding-right:15px;
+ margin-right:12px;
+ margin-top:5px;
+ float:left;
+ font-size:24px;
+ font-weight:800;
+ color:#FFFFFF;
+}
+
+.dropcaps-4:first-letter{
+ font-family: 'Petit Formal Script', cursive;
+ font-weight:800!important;
+ background-color:#1f1f1f;
+ padding:8px;
+ padding-left:12px;
+ padding-right:12px;
+ margin-right:12px;
+ margin-top:5px;
+ float:left;
+ font-size:20px;
+ font-weight:400;
+ color:#FFFFFF;
+}
+
+/*-----------------------*/
+/*-----------------------*/
+/*----Calendar Design----*/
+/*-----------------------*/
+/*-----------------------*/
+
+.calendar{
+ margin-bottom:30px;
+ display:block;
+ overflow:hidden;
+}
+
+calendar*{
+ display:block;
+ overflow:hidden;
+}
+
+.calendar-titles a{
+ width:14.28%;
+ float:left;
+ text-align:center;
+ height:40px;
+ line-height:40px;
+ font-size:10px;
+ color:#1f1f1f;
+ font-weight:800;
+}
+
+.calendar-titles .light-titles{
+ opacity:0.5;
+}
+
+.calendar-days{
+ margin-top:-10px;
+ font-size:14px;
+ margin-bottom:30px;
+}
+
+.calendar-days a{
+ width:14.28%;
+ float:left;
+ height:60px;
+ line-height:60px;
+ text-align:center;
+ color:#1f1f1f;
+ font-weight:700;
+}
+
+.calendar-days a:hover{
+ color:rgba(41, 128, 185,1.0);
+}
+
+.calendar-day .light-day{
+ opacity:0.3;
+}
+
+.calendar-days i{
+ position:absolute;
+ font-size:35px;
+ margin-top:12px;
+ margin-left:-7px;
+ z-index:-1;
+}
+
+.clear-day i{
+ opacity:0;
+}
+
+.light-day{
+ opacity:0.3;
+}
+
+.light-day i{
+ display:none;
+}
+
+.taken-day{
+ color:#FFFFFF!important;
+}
+
+.taken-day i{
+ color:#27ae60;
+}
+
+.close-day{
+ color:#FFFFFF!important;
+}
+
+.close-day i{
+ color:#c0392b;
+}
+
+.calendar-hours{
+ border-top:solid 1px rgba(0,0,0,0.1);
+}
+
+.calendar-hour{
+ height:90px;
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+}
+
+.calendar-hour-clear:hover{
+ background-color: rgba(39, 174, 96, 0.1);
+}
+
+.calendar-hour-taken:hover{
+ background-color: rgba(192, 57, 43,0.1);
+}
+
+.calendar-hour strong{
+ color:#666666;
+ opacity:0.7;
+}
+
+.calendar-hours a:last-child{
+ border-bottom:none;
+}
+
+.calendar-hour .cal-time{
+ position:absolute;
+ margin-top:30px;
+ font-weight:400;
+ font-size:11px;
+ padding-left:20px;
+}
+
+.calendar-hour .cal-from{
+ position:absolute;
+ margin-top:18px;
+ font-size:11px;
+ font-weight:400;
+ padding-left:20px;
+}
+
+.calendar-hour .cal-to{
+ position:absolute;
+ margin-top:40px;
+ font-size:11px;
+ font-weight:400;
+ padding-left:20px;
+}
+
+.calendar-hour h4{
+ font-size:14px;
+ padding-left:100px;
+ padding-top:17px;
+ margin-bottom:2px;
+}
+
+.calendar-hour em{
+ font-style:normal;
+ font-size:11px;
+ padding-left:100px;
+ color:#666666;
+}
+
+.calendar-hour em i{
+ margin-right:10px;
+}
+
+/*----------------------*/
+/*----------------------*/
+/*----Page Preloader----*/
+/*----------------------*/
+/*----------------------*/
+
+#preloader {
+ position:fixed;
+ z-index:2500;
+ top:0;
+ left:0;
+ right:0;
+ bottom:0;
+ background-color:#FFF;
+ z-index:99999999;
+}
+
+#status {
+ position:fixed;
+ z-index:99999999;
+ width:250px;
+ height:280px;
+ position:absolute;
+ left:50%;
+ top:50%;
+ padding-bottom:40px;
+ background-image:url(../images/framework/page-loader.gif);
+ background-size:64px 64px;
+ background-repeat:no-repeat;
+ background-position:center bottom;
+ margin:-125px 0 0 -125px;
+}
+
+.preloader-logo{
+ background-image:url(../images/preload-logo.png);
+ background-size:75px 75px;
+ height:75px;
+ width:75px;
+ display:block;
+ margin-left:auto;
+ margin-right:auto;
+ margin-bottom:40px;
+}
+
+#preloader h3{
+ margin-bottom:5px;
+}
+
+
+#status em{
+ font-size:10px!important;
+ display:block;
+ font-style:normal;
+}
+
+/*------------*/
+/*------------*/
+/*----Tabs----*/
+/*------------*/
+/*------------*/
+
+ul.tabs{
+ margin: 0px;
+ padding: 0px;
+ list-style: none;
+}
+ul.tabs li{
+ pointer-events:all;
+ color: #222;
+ display: inline-block;
+ padding: 8px 15px;
+ cursor: pointer;
+ -webkit-transition: all 250ms ease;
+ -moz-transition: all 250ms ease;
+ -ms-transition: all 250ms ease;
+ -o-transition: all 250ms ease;
+ transition: all 250ms ease;
+ margin-bottom:0px;
+}
+
+ul.tabs li.active-tab{
+ pointer:cursor;
+ pointer-events:none;
+ background: rgba(0,0,0,0.04);
+ color: #222;
+ -webkit-transition: all 250ms ease;
+ -moz-transition: all 250ms ease;
+ -ms-transition: all 250ms ease;
+ -o-transition: all 250ms ease;
+ transition: all 250ms ease;
+}
+
+
+.tab-content{
+ display: none;
+ background: rgba(0,0,0,0.04);
+ padding: 15px;
+}
+
+.active-tab-content{
+ display:block
+}
+
+/*-----------------*/
+/*-----------------*/
+/*----Accordion----*/
+/*-----------------*/
+/*-----------------*/
+
+.accordion-toggle {
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+ cursor: pointer;
+ font-size:14px;
+ height:60px;
+ line-height:60px;
+ font-weight:500;
+}
+
+.accordion-toggle i{
+ float:right;
+ line-height:60px;
+ height:60px;
+ margin-top:0px;
+ width:40px;
+ text-align:center;
+ margin-right:-10px;
+ -moz-transform: rotate(0deg);
+ -webkit-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ transform: rotate(0deg);
+ -webkit-transition: all 250ms ease-in-out;
+ -moz-transition: all 250ms ease-in-out;
+ -ms-transition: all 250ms ease-in-out;
+ -o-transition: all 250ms ease-in-out;
+ transition: all 250ms ease-in-out;
+}
+
+.accordion-content {
+ display: none;
+ padding-bottom:10px;
+}
+.accordion-content.active-accordion {
+ display: block;
+}
+
+.accordion-toggle{
+ margin-top:10px;
+}
+
+.accordion-content p{
+ margin-bottom:-10px;
+ padding-top:15px;
+ padding-bottom:15px;
+ padding-left:0px;
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+}
+
+.accordion-toggle{
+ margin-bottom:0px;
+ margin-top:0px;
+}
+
+.rotate-180{
+ -moz-transform: rotate(180deg)!important;
+ -webkit-transform: rotate(180deg)!important;
+ -o-transform: rotate(180deg)!important;
+ -ms-transform: rotate(180deg)!important;
+ transform: rotate(180deg)!important;
+ -webkit-transition: all 250ms ease-in-out;
+ -moz-transition: all 250ms ease-in-out;
+ -ms-transition: all 250ms ease-in-out;
+ -o-transition: all 250ms ease-in-out;
+ transition: all 250ms ease-in-out;
+}
+
+.rotate-90{
+ -moz-transform: rotate(90deg)!important;
+ -webkit-transform: rotate(90deg)!important;
+ -o-transform: rotate(90deg)!important;
+ -ms-transform: rotate(90deg)!important;
+ transform: rotate(90deg)!important;
+ -webkit-transition: all 250ms ease-in-out;
+ -moz-transition: all 250ms ease-in-out;
+ -ms-transition: all 250ms ease-in-out;
+ -o-transition: all 250ms ease-in-out;
+ transition: all 250ms ease-in-out;
+}
+
+/*---------------*/
+/*---------------*/
+/*----Toggles----*/
+/*---------------*/
+/*---------------*/
+
+.toggle{
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+}
+
+.toggle a:first-child{
+ height:60px;
+ line-height:60px;
+ color:#1f1f1f;
+ font-weight:500;
+ font-size:15px;
+ display:block;
+ width:100%;
+}
+
+.active-toggle .toggle-content{
+ display:block;
+}
+
+.toggle-content{
+ display:none;
+ padding-top:0px;
+ padding-bottom:20px;
+}
+
+.toggle-content p{
+ padding-bottom:0px;
+ margin-bottom:0px;
+}
+
+.toggle i{
+ font-size:10px!important;
+ pointer-events:none;
+ position:absolute;
+ top:1px;
+ width:20px;
+ height:60px;
+ line-height:60px;
+ text-align:center;
+ right:0px;
+ -moz-transform: rotate(0deg);
+ -webkit-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ transform: rotate(0deg);
+ -webkit-transition: all 250ms ease-in-out;
+ -moz-transition: all 250ms ease-in-out;
+ -ms-transition: all 250ms ease-in-out;
+ -o-transition: all 250ms ease-in-out;
+ transition: all 250ms ease-in-out;
+}
+
+.rotate-toggle{
+ color:#c0392b;
+ -moz-transform: rotate(45deg)!important;
+ -webkit-transform: rotate(45deg)!important;
+ -o-transform: rotate(45deg)!important;
+ -ms-transform: rotate(45deg)!important;
+ transform: rotate(45deg)!important;
+ -webkit-transition: all 250ms ease-in-out;
+ -moz-transition: all 250ms ease-in-out;
+ -ms-transition: all 250ms ease-in-out;
+ -o-transition: all 250ms ease-in-out;
+ transition: all 250ms ease-in-out;
+}
+
+/*-----------------------------*/
+/*-----------------------------*/
+/*----Mobile Style Switches----*/
+/*-----------------------------*/
+/*-----------------------------*/
+
+.switch-box h4{
+ font-size:15px;
+ line-height:29px;
+ float:left;
+ font-weight:600;
+ padding-top:2px;
+ margin-bottom:5px;
+}
+
+.switch-box .switch{
+ float:right!important;
+}
+
+.switch-box .switch-icon{
+ float:right!important;
+}
+
+.switch-box-content{
+ clear:both;
+ display:none;
+}
+
+.switch-box-subtitle{
+ display:block;
+ clear:both;
+ opacity:0.8;
+ font-size:13px;
+}
+
+/*Switch 1*/
+
+.switch-1{
+ width:50px;
+ height:30px;
+ background-color:#c0392b;
+ border-radius:30px;
+ transition:all 200ms ease;
+ float:left;
+}
+
+.switch-1 em:first-child{
+ position:absolute;
+ color:#FFFFFF;
+ height:30px;
+ line-height:30px;
+ margin-left:11px;
+ font-style:normal;
+ font-size:10px;
+}
+
+.switch-1 em:last-child{
+ position:absolute;
+ color:#FFFFFF;
+ height:30px;
+ line-height:30px;
+ margin-left:33px;
+ font-style:normal;
+ font-size:10px;
+}
+
+.switch-1 span{
+ width:26px;
+ height:26px;
+ position:absolute;
+ background-color:#FFFFFF;
+ border-radius:28px;
+ margin-top:2px;
+ transform:translateX(2px);
+ -webkit-transform:translateX(2px);
+ transition:all 200ms ease;
+}
+
+.switch-1-on{
+ background-color:#27ae60;
+ transition:all 200ms ease;
+}
+
+.switch-1-on span{
+ transform:translateX(22px);
+ -webkit-transform:translateX(22px);
+ transition:all 200ms ease;
+}
+
+.switch-icon em:first-child{
+ font-size:10px;
+ margin-top:2px;
+ margin-left:9px;
+ line-height:27px;
+}
+
+.switch-icon em:last-child{
+ font-size:10px;
+ margin-top:2px;
+ margin-left:32px;
+ line-height:27px;
+}
+
+/*Switch 2*/
+
+.switch-2{
+ width:85px;
+ background-color:#464646;
+ height:30px;
+}
+
+.switch-2 span{
+ margin-top:2px;
+ transform:translateX(2px);
+ -webkit-transform:translateX(2px);
+ width:40px;
+ background-color:#676767;
+ height:26px;
+ position:absolute;
+ text-align:center;
+ transition:all 200ms ease;
+}
+
+.switch-2 span em{
+ display:block;
+ color:#FFFFFF;
+ font-style:normal;
+ line-height:26px;
+ font-size:10px!important;
+ margin-top:-26px;
+ transition:all 200ms ease;
+}
+
+.switch-2-on span{
+ transform:translateX(43px);
+ -webkit-transform:translateX(43px);
+ /*background-color:#0e88b1;*/
+ background-color:#27ae60;
+ transition:all 200ms ease;
+}
+
+.switch-2-on span em{
+ margin-top:0px;
+ transition:all 200ms ease;
+}
+
+/*Switch 3*/
+
+.switch-3{
+ border:solid 3px #cacaca;
+ width:60px;
+ height:28px;
+ background-color:#27ae60;
+ transition:all 200ms ease;
+}
+
+.switch-3 span{
+ z-index:10;
+ position:absolute;
+ background-color:#cacaca;
+ width:25px;
+ border-right:solid 3px #FFFFFF;
+ border-left:solid 3px #FFFFFF;
+ height:28px;
+ margin-top:-3px;
+ transform:translateX(33px);
+ -webkit-transform:translateX(33px);
+ transition:all 200ms ease;
+}
+
+.switch-3-on{
+ background-color:#c0392b;
+ transition:all 200ms ease;
+}
+
+.switch-3-on span{
+ transform:translateX(-3px);
+ -webkit-transform:translateX(-3px);
+ transition:all 200ms ease;
+}
+
+.switch-3 strong{
+ position:absolute;
+ width:54px;
+ height:22px;
+ border:solid 3px #FFFFFF;
+}
+
+.switch-3 em{
+ position:absolute;
+ color:#FFFFFF;
+ margin-left:0px;
+ font-style:normal;
+ font-size:10px;
+ margin-top:-4px;
+ width:35px;
+ text-align:center;
+}
+
+.switch-3 em:last-child{
+ position:absolute;
+ color:#FFFFFF;
+ margin-left:19px!important;
+ font-style:normal;
+ font-size:10px;
+ margin-top:-4px;
+}
+
+/*---------------------*/
+/*---------------------*/
+/*----Notifications----*/
+/*---------------------*/
+/*---------------------*/
+
+.static-notification{
+ padding:15px;
+ margin: 15px 0;
+ background-color: #1f1f1f;
+}
+
+.static-notification h6{
+ color:#FFFFFF;
+ font-weight:500;
+ padding-bottom:10px;
+ border-bottom:solid 1px rgba(255,255,255,0.3);
+}
+
+.static-notification-icon{
+ padding-right:20px;
+}
+
+.static-notification p{
+ font-size:12px;
+ margin-bottom:0px;
+ color:rgba(255,255,255,0.8);
+}
+
+.static-notification-close{
+ width:50px;
+ height:50px;
+ color:#FFFFFF;
+ position:absolute;
+ top:5px;
+ right:0px;
+}
+
+.static-notification-close{
+ width:50px;
+ height:50px;
+ line-height:50px;
+ text-align:center;
+}
+
+.tap-dismiss i{
+ font-size:20px;
+ color:#FFFFFF;
+ position:absolute;
+ height:60px;
+ top:-15px;
+ line-height:60px;
+ width:60px;
+ text-align:center;
+ left:-15px;
+}
+
+.tap-dismiss p{
+ padding-left:50px;
+}
+
+/*-------------------------*/
+/*---Fixed Notifications---*/
+/*-------------------------*/
+
+/*top*/
+.top-notification{
+ display:none;
+ z-index:9999;
+ position:fixed;
+ top:60px;
+ left:0px;
+ right:0px;
+ width:100%;
+ padding:15px 20px;
+}
+
+@media(min-width:568px){
+ .top-notification{
+ width:320px;
+ right:40px;
+ left:auto;
+ top:20px;
+ }
+}
+
+.top-notification h4{
+ color:#FFFFFF;
+ font-size:15px;
+ margin-bottom:5px;
+}
+
+.top-notification p{
+ color:#FFFFFF;
+ font-size:12px;
+ margin-bottom:0px;
+}
+
+.top-notification a{
+ color:#FFFFFF;
+ text-decoration:underline;
+}
+
+.top-notification .close-top-notification{
+ width:50px;
+ height:50px;
+ position:absolute;
+ right:0px;
+ top:0px;
+}
+
+.top-notification .close-top-notification i{
+ width:50px;
+ height:50px;
+ line-height:50px;
+ text-align:center;
+}
+
+/*bottom*/
+.bottom-notification{
+ display:none;
+ z-index:9999;
+ position:fixed;
+ bottom:0px;
+ left:0px;
+ right:0px;
+ width:100%;
+ padding:15px 20px;
+}
+
+@media(min-width:568px){
+ .bottom-notification{
+ width:320px;
+ right:40px;
+ left:auto;
+ bottom:40px;
+ }
+}
+
+.bottom-notification h4{
+ color:#FFFFFF;
+ font-size:15px;
+ margin-bottom:5px;
+}
+
+.bottom-notification p{
+ color:#FFFFFF;
+ font-size:12px;
+ margin-bottom:0px;
+}
+
+.bottom-notification a{
+ color:#FFFFFF;
+ text-decoration:underline;
+}
+
+.bottom-notification .close-bottom-notification{
+ width:50px;
+ height:50px;
+ position:absolute;
+ right:0px;
+ top:0px;
+}
+
+.bottom-notification .close-bottom-notification i{
+ width:50px;
+ height:50px;
+ line-height:50px;
+ text-align:center;
+}
+
+/*----------------------*/
+/*----------------------*/
+/*----Speach Bubbles----*/
+/*----------------------*/
+/*----------------------*/
+
+.green-bubble{
+ background-color:#33c922!important;
+ color:#FFFFFF!important;
+ border:none!important;
+}
+
+.blue-bubble{
+ background-color:#0b84fe!important;
+ color:#FFFFFF!important;
+ border:none!important;
+}
+
+.speach-left{
+ float:left;
+ display:inline;
+ text-align:left;
+ max-width:75%;
+ font-size:13px;
+ padding-left:10px;
+ padding-right:10px;
+ padding-top:5px;
+ padding-bottom:5px;
+ border-radius:10px;
+ background-color:#FFFFFF;
+ border:solid 1px #cacaca;
+}
+
+.speach-left-title{
+ display:block;
+ width:100%;
+ font-size:10px;
+ font-style:normal;
+ padding-left:5px;
+ color:#000000;
+ text-shadow:0px 1px 0px #FFFFFF;
+}
+
+.speach-right{
+ float:right;
+ text-align:right;
+ text-align:left;
+ max-width:75%;
+ font-size:13px;
+ padding-left:10px;
+ padding-right:10px;
+ padding-top:5px;
+ padding-bottom:5px;
+ border-radius:10px;
+ background-color:#FFFFFF;
+ border:solid 1px #cacaca;
+}
+
+.speach-right-title{
+ text-align:right;
+ display:block;
+ font-size:10px;
+ font-style:normal;
+ padding-right:5px;
+ color:#000000;
+ text-shadow:0px 1px 0px #FFFFFF;
+}
+
+.speach-status{
+ font-size:11px;
+ font-weight:500;
+ float:right;
+ margin-top:-25px;
+ position:relative;
+ display:inline;
+}
+
+.speach-status i{
+ padding-right:5px;
+}
+
+.speach-status img{
+ width:15px;
+ height:15px;
+ border-radius:10px;
+ float:right;
+ margin-top:7px;
+ margin-left:10px;
+ display:inline;
+}
+
+/*--------------*/
+/*--------------*/
+/*----Charts----*/
+/*--------------*/
+/*--------------*/
+
+.chart-center{
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.chart div{
+ overflow:hidden!important;
+}
+
+.chart strong{
+ font-size:13px;
+ text-align:left;
+ font-weight:600;
+ font-family:'Source Sans Pro', sans-serif;
+ padding-left:5px;
+}
+
+.chart em{
+ font-size:13px;
+ text-align:right;
+ font-style:normal;
+ display:block;
+ margin-bottom:10px;
+ margin-top:-20px;
+ font-weight:300;
+ font-family:'Source Sans Pro', sans-serif;
+ padding-right:5px;
+}
+
+.chart-background{
+ background-color:#CCC;
+ width:100%;
+ display:block;
+ height:22px;
+ margin-bottom:20px;
+ margin-top:-5px;
+}
+
+.red-chart{
+ background-color:#c0392b;
+ margin:3px;
+ height:16px;
+ opacity:0.9;
+}
+
+.green-chart{
+ background-color:#27ae60;
+ margin:3px;
+ height:16px;
+ opacity:0.9;
+}
+
+.yellow-chart{
+ background-color:#f39c12;
+ margin:3px;
+ height:16px;
+ opacity:0.9;
+}
+
+.magenta-chart{
+ background-color:#8e44ad;
+ margin:3px;
+ height:16px;
+ opacity:0.9;
+}
+
+.blue-chart{
+ background-color:#2980b9;
+ margin:3px;
+ height:16px;
+ opacity:0.9;
+}
+
+.chart-round div{
+ border-radius:20px;
+}
+
+.p100{width:100%;}
+.p95{width:95%;}
+.p90{width:90%;}
+.p85{width:85%;}
+.p80{width:80%;}
+.p75{width:75%;}
+.p70{width:70%;}
+.p65{width:65%;}
+.p60{width:60%;}
+.p55{width:55%;}
+.p50{width:50%;}
+.p45{width:45%;}
+.p40{width:40%;}
+.p35{width:35%;}
+.p30{width:30%;}
+.p25{width:25%;}
+.p20{width:20%;}
+.p15{width:15%;}
+.p10{width:10%;}
+.p5{width:5%;}
+.p0{width:0%;}
+
+/*---------------*/
+/*---------------*/
+/*----Buttons----*/
+/*---------------*/
+/*---------------*/
+
+.button{
+ font-size:12px;
+ font-weight:500;
+ letter-spacing:0.6px;
+ display:inline-block;
+ padding:8px 18px;
+ margin-bottom:15px;
+ margin-right:12px;
+}
+
+.button-icon i{
+ position:absolute;
+ width:10px;
+ height:10px;
+ line-height:10px;
+ left:15px;
+ top:50%;
+ margin-top:-5px;
+}
+
+.button-icon{
+ padding-left:40px;
+}
+
+.button-small{
+ display:inline-block;
+ padding:5px 15px;
+ margin-bottom:15px;
+ margin-right:12px;
+ font-size:12px;
+}
+
+.button-xl{
+ font-size:16px;
+ padding:12px 30px;
+}
+
+.button-l{
+ font-size:15px;
+ padding:10px 30px;
+}
+
+.button-s{
+ font-size:13px;
+ padding:8px 20px;
+}
+
+.button-xs{
+ font-size:11px;
+ padding:6px 15px;
+}
+
+.button-fullscreen{
+ width:100%!important;
+ display:block;
+ text-align:center;
+}
+
+.social-button{
+ height:45px;
+ display:inline-block;
+ padding-right:30px;
+ padding-left:45px;
+ margin-bottom:10px;
+ margin-right:5px;
+ line-height:45px;
+ font-weight:500;
+ font-size:12px;
+ width:115px;
+}
+
+.social-button-fullscreen{
+ height:45px;
+ display:block;
+ padding-right:30px;
+ padding-left:45px;
+ margin-bottom:10px;
+ margin-right:5px;
+ line-height:45px;
+ font-weight:500;
+ font-size:12px;
+ width:100%;
+}
+
+.social-button i, .social-button-fullscreen i{
+ position:absolute;
+ height:45px;
+ width:45px;
+ line-height:45px;
+ text-align:center;
+ font-size:18px;
+ left:0px;
+ margin-top:1px;
+}
+
+.social-ball{
+ border-radius:45px;
+ display:inline-block;
+ margin-right:10px;
+ margin-bottom:10px;
+ width:45px;
+ height:45px;
+ line-height:45px;
+ text-align:center;
+}
+
+.social-square{
+ display:inline-block;
+ margin-right:10px;
+ margin-bottom:10px;
+ width:45px;
+ height:45px;
+ line-height:45px;
+ text-align:center;
+}
+
+.social-ball i{
+ width:45px;
+ height:45px;
+ line-height:45px;
+ text-align:center;
+}
+
+.social-square i{
+ width:45px;
+ height:45px;
+ line-height:45px;
+ text-align:center;
+}
+
+.button-round{border-radius:5px;}
+
+.button,
+.button:hover,
+.button-round,
+.button-round:hover{
+ transition:all 200ms ease;
+}
+
+.teal-3d{ padding-top:5px; padding-bottom:5px; border-bottom:solid 4px #117562;}
+.button-teal{ background-color:#16a085; color:#FFFFFF;}
+.button-teal:hover{ background-color:#1abc9c;}
+
+.green-3d{ padding-top:5px; padding-bottom:5px; border-bottom:solid 4px #1e8248;}
+.button-green{ background-color:#27ae60; color:#FFFFFF;}
+.button-green:hover{ background-color:#2ecc71;}
+
+.blue-3d{ padding-top:5px; padding-bottom:5px; border-bottom:solid 4px #194e70;}
+.button-blue{ background-color:#2980b9; color:#FFFFFF;}
+.button-blue:hover{ background-color:#3498db;}
+
+.magenta-3d{ padding-top:5px; padding-bottom:5px; border-bottom:solid 4px #602e75;}
+.button-magenta{ background-color:#8e44ad; color:#FFFFFF;}
+.button-magenta:hover{ background-color:#9b59b6;}
+
+.dark-3d{ padding-top:5px; padding-bottom:5px; border-bottom:solid 4px #19232d;}
+.button-dark{ background-color:#2c3e50; color:#FFFFFF;}
+.button-dark:hover{ background-color:#34495e;}
+
+.yellow-3d{ padding-top:5px; padding-bottom:5px; border-bottom:solid 4px #ba970b;}
+.button-yellow{ background-color:#f1c40f; color:#1f1f1f;}
+.button-yellow:hover{ background-color:#f39c12;}
+
+.orange-3d{ padding-top:5px; padding-bottom:5px; border-bottom:solid 4px #9d3e00;}
+.button-orange{ background-color:#d35400; color:#FFFFFF;}
+.button-orange:hover{ background-color:#e67e22;}
+
+.red-3d{ padding-top:5px; padding-bottom:5px; border-bottom:solid 4px #8a281e;}
+.button-red{ background-color:#c0392b; color:#FFFFFF;}
+.button-red:hover{ background-color:#e74c3c;}
+
+.light-3d{ padding-top:5px; padding-bottom:5px; border-bottom:solid 4px #868a8c;}
+.button-light{ background-color:#bdc3c7; color:#1f1f1f;}
+.button-light:hover{ background-color:#ecf0f1;}
+
+.grey-3d{ padding-top:5px; padding-bottom:5px; border-bottom:solid 4px #474d4e;}
+.button-grey{ background-color:#7f8c8d; color:#FFFFFF;}
+.button-grey:hover{ background-color:#95a5a6;}
+
+.button i{
+ padding-right:10px;
+}
+
+.button-center{
+ width:120px;
+ margin-left:auto!important;
+ margin-right:auto!important;
+ text-align:center;
+ display:block;
+ margin-bottom:30px;
+}
+
+.button-right{
+ float:right;
+}
+
+.button-right:after {
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
+}
+
+.read-more-link{
+ text-align:right;
+}
+
+.read-more-link i{
+ padding-right:20px;
+}
+
+.button-cover{
+ padding-top:5px;
+ padding-bottom:5px;
+ font-size:12px;
+ border-radius:5px;
+ color:#FFFFFF;
+ border:solid 1px rgba(255,255,255,0.5);
+ transition:all 200ms ease;
+ margin-right:0px;
+ margin-left:0px;
+}
+
+.button-cover:hover{
+ background-color:rgba(255,255,255,0.05);
+ transition:all 200ms ease;
+}
+
+/*-------------------*/
+/*---Login Buttons---*/
+/*-------------------*/
+
+.facebook-login,
+.twitter-login,
+.google-login{
+ height:50px;
+ line-height:50px;
+ padding-left:20px;
+ padding-right:20px;
+ margin-top:8px;
+ margin-bottom:8px;
+ font-size:12px;
+ text-align:center;
+ transition:all 250ms ease;
+}
+
+.facebook-login:hover,
+.twitter-login:hover,
+.google-login:hover{
+ opacity:0.9;
+ transition:all 250ms ease;
+}
+
+.facebook-login i,
+.twitter-login i,
+.google-login i{
+ width:10px;
+ margin-right:20px;
+ position:absolute;
+ left:20px;
+ top:18px;
+ font-size:16px;
+}
+
+/*--------------------*/
+/*--------------------*/
+/*----This or That----*/
+/*--------------------*/
+/*--------------------*/
+
+/*Style 1*/
+
+.or-buttons-style-1{
+ width:300px;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.or-buttons-style-1 a:first-child{
+ width:108px;
+ text-align:center;
+ float:left;
+ margin-right:30px;
+ margin-bottom:0px;
+ margin-left:0px;
+ font-size:13px;
+}
+
+.or-buttons-style-1 em{
+ position:relative;
+ z-index:10;
+ float:left;
+ background-color:#FFFFFF;
+ padding:7px 5px;
+ font-size:12px;
+}
+
+.or-buttons-style-1 strong{
+ position:absolute;
+ margin-top:22px;
+ margin-left:121px;
+ display:block;
+ height:1px;
+ width:50px;
+ background-color:rgba(0,0,0,0.2);
+}
+
+.or-buttons-style-1 a:last-child{
+ width:108px;
+ text-align:center;
+ float:left;
+ margin-left:30px;
+ margin-bottom:0px;
+ margin-right:0px;
+ font-size:13px;
+}
+
+/*Style 2*/
+
+.or-buttons-style-2{
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.or-buttons-style-2 a:first-child{
+ width:200px;
+ margin-left:auto;
+ margin-right:auto;
+ display:block;
+ text-align:center;
+ margin-bottom:0px;
+ font-size:13px;
+}
+
+.or-buttons-style-2 em{
+ position:relative;
+ z-index:10;
+ background-color:#FFFFFF;
+ padding:5px;
+ font-size:12px;
+ display:block;
+ width:30px;
+ margin-left:auto;
+ margin-right:auto;
+ text-align:center;
+}
+
+.or-buttons-style-2 strong{
+ position:absolute;
+ margin-top:-20px;
+ left:50%;
+ margin-left:-75px;
+ display:block;
+ height:1px;
+ width:150px;
+ background: -moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(202,202,202,1) 50%, rgba(255,255,255,0) 100%); /* FF3.6+ */
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,0)), color-stop(50%,rgba(202,202,202,1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(202,202,202,1) 50%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ */
+ background: -o-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(202,202,202,1) 50%,rgba(255,255,255,0) 100%); /* Opera 11.10+ */
+ background: -ms-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(202,202,202,1) 50%,rgba(255,255,255,0) 100%); /* IE10+ */
+ background: linear-gradient(to right, rgba(255,255,255,0) 0%,rgba(202,202,202,1) 50%,rgba(255,255,255,0) 100%); /* W3C */
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#00ffffff',GradientType=1 ); /* IE6-9 */
+}
+
+.or-buttons-style-2 a:last-child{
+ width:200px;
+ margin-left:auto;
+ margin-right:auto;
+ display:block;
+ text-align:center;
+ margin-bottom:0px;
+ font-size:13px;
+}
+
+/*Style 3*/
+
+.or-buttons-style-3 em{
+ position:relative;
+ z-index:10;
+ background-color:#FFFFFF;
+ padding:5px;
+ font-size:14px;
+ display:block;
+ width:40px;
+ margin-left:auto;
+ margin-right:auto;
+ text-align:center;
+}
+
+.or-buttons-style-3 strong{
+ position:absolute;
+ margin-top:-20px;
+ left:50%;
+ margin-left:-100px;
+ display:block;
+ height:1px;
+ width:200px;
+ background: -moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(202,202,202,1) 50%, rgba(255,255,255,0) 100%); /* FF3.6+ */
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,0)), color-stop(50%,rgba(202,202,202,1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(202,202,202,1) 50%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ */
+ background: -o-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(202,202,202,1) 50%,rgba(255,255,255,0) 100%); /* Opera 11.10+ */
+ background: -ms-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(202,202,202,1) 50%,rgba(255,255,255,0) 100%); /* IE10+ */
+ background: linear-gradient(to right, rgba(255,255,255,0) 0%,rgba(202,202,202,1) 50%,rgba(255,255,255,0) 100%); /* W3C */
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#00ffffff',GradientType=1 ); /* IE6-9 */
+
+}
+
+/*-------------------*/
+/*-------------------*/
+/*----Modal Forms----*/
+/*-------------------*/
+/*-------------------*/
+
+.rounded-modal{border-radius:10px;}
+.squared-modal{border-radius:0px;}
+
+/*-----------------------*/
+/*---Simple Modal Form---*/
+/*-----------------------*/
+
+#simplemodal-overlay {background-color:#000;}
+
+.simple-modal-content{
+ display:none;
+ background-color:rgba(255,255,255,0.9);
+ margin-top:20px;
+ padding-bottom:2px;
+}
+
+@media (max-width:350px){
+ #simplemodal-container{
+ width:300px!important;
+ }
+}
+
+@media (min-width:355px){
+ #simplemodal-container{
+ width:340px!important;
+ }
+}
+
+.simple-modal-content h4{
+ padding:20px;
+ padding-bottom:10px;
+}
+
+.simple-modal-content p{
+ padding:20px;
+ padding-top:0px;
+ padding-bottom:0px;
+}
+
+/*-----------------------*/
+/*---Social Modal Form---*/
+/*-----------------------*/
+
+.social-login-modal-content{
+ display:none;
+ padding-bottom:2px;
+}
+
+.login-modal-wrapper{
+ background-color:rgba(255,255,255,0.9);
+ border-radius:10px;
+ padding:20px 40px;
+}
+
+.login-modal-wrapper h4{
+ text-align:center;
+ padding-bottom:10px;
+}
+
+.facebook-connect{background-color:#3b5998;}
+.twitter-connect{background-color:#4099ff;}
+.google-connect{background-color:#d34836;}
+
+/*----------------------*/
+/*---Login Modal Form---*/
+/*----------------------*/
+
+.login-modal-wrapper .facebook-connect, .google-connect, .twitter-connect{
+ color:#FFFFFF;
+ border-radius:5px;
+ font-size:11px;
+ padding-top:3px;
+ padding-bottom:3px;
+ padding-left:40px;
+ padding-right:20px;
+ margin-bottom:10px;
+ text-align:center;
+}
+
+.login-modal-wrapper .facebook-connect i, .google-connect i, .twitter-connect i{
+ width:10px;
+ font-size:14px;
+ position:absolute;
+ width:35px;
+ height:35px;
+ top:1px;
+ left:5px;
+ text-align:center;
+ line-height:35px;
+}
+
+.login-modal-wrapper em{
+ font-family:'Crete Round', sans-serif;
+ display:block;
+ text-align:center;
+ font-size:12px;
+ margin-bottom:0px;
+}
+
+.login-modal-user, .login-modal-pass{
+ width:100%;
+ height:40px;
+ border-bottom:solid 1px #cacaca;
+ margin-bottom:10px;
+ font-size:13px;
+ -webkit-transition: all 250ms ease;
+ -moz-transition: all 250ms ease;
+ -ms-transition: all 250ms ease;
+ -o-transition: all 250ms ease;
+ transition: all 250ms ease;
+ background-color:transparent;
+}
+
+.login-modal-user:focus, .login-modal-pass:focus{
+ border-bottom:solid 1px #27ae60;
+ -webkit-transition: all 250ms ease;
+ -moz-transition: all 250ms ease;
+ -ms-transition: all 250ms ease;
+ -o-transition: all 250ms ease;
+ transition: all 250ms ease;
+}
+
+.login-modal-wrapper .login-button{
+ width:45%;
+ margin-right:10%;
+ color:#FFFFFF;
+ text-align:center;
+ height:35px;
+ line-height:35px;
+ border-radius:5px;
+ margin-bottom:10px;
+ font-size:12px;
+ margin-top:20px;
+ float:left;
+}
+
+.login-modal-wrapper .login-close{
+ float:left;
+ width:45%;
+ color:#FFFFFF;
+ text-align:center;
+ height:35px;
+ line-height:35px;
+ border-radius:5px;
+ margin-bottom:10px;
+ font-size:12px;
+ margin-top:20px;
+}
+
+.login-modal-wrapper .login-forgot{
+ font-size:12px;
+ text-align:center;
+ font-weight:500;
+ color:#666;
+ margin-top:10px;
+}
+
+/*------------------*/
+/*---Simple Login---*/
+/*------------------*/
+
+.simple-login-modal-content{
+ display:none;
+}
+
+/*--------------------------*/
+/*---Social Profile Modal---*/
+/*--------------------------*/
+
+.social-profile-modal-content{
+ display:none;
+ background-color:rgba(255,255,255,0.9);
+ padding:20px;
+ padding-top:30px;
+ padding-bottom:2px;
+}
+
+.social-profile-modal-content img{
+ width:100px;
+ height:100px;
+ border-radius:140px;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.social-profile-modal-content h3{
+ text-align:center;
+ margin-top:20px;
+ margin-bottom:20px;
+}
+
+.social-profile-modal-content strong{
+ font-weight:400;
+ text-align:center;
+ display:block;
+ font-size:13px;
+ margin-top:-10px;
+ margin-bottom:20px;
+}
+
+.social-profile-modal-content .social-profiles{
+ width:230px;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.facebook-profile{
+ width:65px;
+ height:65px;
+ float:left;
+ margin:0px 5px;
+ margin-bottom:40px;
+ text-align:center;
+ font-size:13px;
+ line-height:40px;
+ color:#1f1f1f;
+}
+
+.facebook-profile i{
+ display:block;
+ width:45px;
+ height:45px;
+ border-radius:45px;
+ line-height:45px;
+ text-align:center;
+ background-color:#3b5998;
+ color:#FFFFFF;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.facebook-profile em{
+ display:block;
+ text-align:center;
+ font-size:12px;
+ font-style:normal;
+ color:#1f1f1f;
+}
+
+.twitter-profile{
+ width:65px;
+ height:65px;
+ float:left;
+ margin:0px 5px;
+ margin-bottom:40px;
+ text-align:center;
+ font-size:13px;
+ line-height:40px;
+ color:#1f1f1f;
+}
+
+.twitter-profile i{
+ display:block;
+ width:45px;
+ height:45px;
+ border-radius:45px;
+ line-height:45px;
+ text-align:center;
+ background-color:#4099ff;
+ color:#FFFFFF;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.twitter-profile em{
+ display:block;
+ text-align:center;
+ font-size:12px;
+ font-style:normal;
+ color:#1f1f1f;
+}
+
+.google-profile{
+ width:65px;
+ height:65px;
+ float:left;
+ margin:0px 5px;
+ margin-bottom:40px;
+ text-align:center;
+ font-size:13px;
+ line-height:40px;
+ color:#1f1f1f;
+}
+
+.google-profile i{
+ display:block;
+ width:45px;
+ height:45px;
+ border-radius:45px;
+ line-height:45px;
+ text-align:center;
+ background-color:#d34836;
+ color:#FFFFFF;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.google-profile em{
+ display:block;
+ text-align:center;
+ font-size:12px;
+ font-style:normal;
+ color:#1f1f1f;
+}
+
+/*--------------------*/
+/*--------------------*/
+/*----Share Bottom----*/
+/*--------------------*/
+/*--------------------*/
+
+
+@media (min-width:768px){
+ .share-socials-bottom{
+ width:600px!important;
+ }
+}
+
+.share-bottom{
+ position:fixed;
+ width:100%;
+ z-index:99999;
+ background-color:#f8f8f8;
+ padding-top:15px;
+ padding-bottom:15px;
+ border-top:solid 1px rgba(0,0,0,0.08);
+ -webkit-box-shadow: 0 0 0 0 rgba(0,0,0,0.1);
+ box-shadow: 0 0 0 0 rgba(0,0,0,0.1);
+ bottom:-380px;
+ transition:all 350ms ease;
+}
+
+.active-share-bottom{
+ -webkit-box-shadow: 0 -5px 15px 1px rgba(0,0,0,0.1)!important;
+ box-shadow: 0 -5px 15px 1px rgba(0,0,0,0.1)!important;
+ bottom:0px!important;
+ transition:all 350ms ease;
+}
+
+.share-bottom h3{
+ text-align:center;
+ font-size:16px;
+ text-transform:uppercase;
+ font-weight:600;
+ padding-bottom:15px;
+ border-bottom:solid 1px rgba(0,0,0,0.05);
+ margin-left:-5px;
+}
+
+.share-socials-bottom{
+ width:300px;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.share-socials-bottom a{
+ font-family:'Roboto', serif;
+ font-size:13px!important;
+ font-weight:500;
+ color:#1f1f1f;
+ float:left;
+ width:60px;
+ margin-left:20px;
+ margin-right:20px;
+ text-align:center;
+ margin-bottom:10px;
+ margin-top:10px;
+}
+
+.share-socials-bottom a i:hover{
+ color:#FFFFFF;
+ -moz-transform: scale(1.01,1.01);
+ -webkit-transform: scale(1.01,1.01);
+ -o-transform: scale(1.01,1.01);
+ -ms-transform: scale(1.01,1.01);
+ transform: scale(1.01,1.01);
+ transition:all 150ms ease;
+}
+
+.share-socials-bottom a i{
+ width:60px;
+ height:60px;
+ border-radius:60px;
+ color:#FFFFFF;
+ line-height:60px;
+ font-size:18px;
+ margin-bottom:5px;
+ -moz-transform: scale(0.9,0.9);
+ -webkit-transform: scale(0.9,0.9);
+ -o-transform: scale(0.9,0.9);
+ -ms-transform: scale(0.9,0.9);
+ transform: scale(0.9,0.9);
+ transition:all 150ms ease;
+ opacity:1;
+}
+
+.share-socials-bottom a i:hover{
+ transition:all 250ms ease;
+ opacity:1;
+}
+
+.close-share-bottom{
+ font-family:'Roboto', serif;
+ display:block;
+ text-align:center;
+ padding-top:15px;
+ margin-top:15px;
+ border-top:solid 1px rgba(0,0,0,0.05);
+ text-transform:uppercase;
+ font-size:13px;
+ font-weight:600;
+ color:#1f1f1f;
+ transition:all 250ms ease;
+ width:100%;
+ text-align:center;
+ height:50px;
+ margin-bottom:-15px;
+ padding-top:10px;
+}
+
+.close-share-bottom:hover{
+ color:rgba(0,0,0,0.6);
+ transition:all 250ms ease;
+}
+
+/*------------------------*/
+/*------------------------*/
+/*----Device Detection----*/
+/*------------------------*/
+/*------------------------*/
+
+.device-detected img{
+ width:101px;
+ height:101px;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.device-detected h5{
+ text-align:Center;
+ padding-top:20px;
+}
+
+.device-detected p{
+ text-align:center;
+ padding-left:20px;
+ padding-right:20px;
+}
+
+/*----------------------------*/
+/*----------------------------*/
+/*----Animated Back To Top----*/
+/*----------------------------*/
+/*----------------------------*/
+
+.back-to-top-badge{
+ opacity:0;
+ width:120px;
+ height:34px;
+ border-radius:18px;
+ font-size:12px;
+ position:fixed;
+ z-index:9999;
+ left:50%;
+ margin-left:-60px;
+ bottom:40px;
+ color:#FFFFFF;
+ text-align:center;
+ line-height:32px;
+ background: #499bea; /* Old browsers */
+ background: -moz-linear-gradient(top, #499bea 0%, #207ce5 100%); /* FF3.6+ */
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#499bea), color-stop(100%,#207ce5)); /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(top, #499bea 0%,#207ce5 100%); /* Chrome10+,Safari5.1+ */
+ background: -o-linear-gradient(top, #499bea 0%,#207ce5 100%); /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #499bea 0%,#207ce5 100%); /* IE10+ */
+ background: linear-gradient(to bottom, #499bea 0%,#207ce5 100%); /* W3C */
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#499bea', endColorstr='#207ce5',GradientType=0 ); /* IE6-9 */
+ -webkit-box-shadow: 0 1px 5px 1px rgba(0,0,0,0.2);
+ box-shadow: 0 1px 5px 1px rgba(0,0,0,0.2);
+ -webkit-transition: all 250ms ease-in-out;
+ -moz-transition: all 250ms ease-in-out;
+ -ms-transition: all 250ms ease-in-out;
+ -o-transition: all 250ms ease-in-out;
+ transition: all 250ms ease-in-out;
+ pointer-events:none;
+}
+
+.back-to-top-badge i{
+ padding-right:10px;
+ line-height:30px;
+ text-align:center;
+ width:20px;
+}
+
+.back-to-top-badge:hover{
+ color:#FFFFFF;
+}
+
+.back-to-top-badge-visible{
+ opacity:1;
+ pointer-events:all;
+ -webkit-transition: all 250ms ease-in-out;
+ -moz-transition: all 250ms ease-in-out;
+ -ms-transition: all 250ms ease-in-out;
+ -o-transition: all 250ms ease-in-out;
+ transition: all 250ms ease-in-out;
+}
+
+/*-------------*/
+/*-------------*/
+/*---Userlist--*/
+/*-------------*/
+/*-------------*/
+
+.user-list-item{
+ margin-bottom:20px;
+}
+
+.user-list-item img{
+ float:left;
+ width:50px;
+ height:50px;
+ border-radius:50px;
+}
+
+.user-list-item strong{
+ color:#1f1f1f;
+ margin-left:60px;
+ font-size:15px;
+ margin-bottom:0px;
+ margin-top:-5px;
+ display:block;
+}
+
+.user-list-item em{
+ display:block;
+ font-size:12px;
+ padding-left:60px;
+ font-style:normal;
+ color:#666666;
+ font-weight:500;
+}
+
+.user-list-item i{
+ position:absolute;
+ right:0px;
+ top:23px;
+ font-size:12px;
+ color:#1f1f1f;
+}
+
+.user-list-follow{
+ padding-bottom:50px;
+ display:block;
+}
+
+.user-list-follow img{
+ float:left;
+ width:50px;
+ height:50px;
+ border-radius:50px;
+}
+
+.user-list-follow strong{
+ float:left;
+ font-size:14px;
+ color:#1f1f1f;
+ padding-left:10px;
+}
+
+.user-list-follow strong em{
+ font-size:11px;
+ font-style:normal;
+ color:#666666;
+ margin-top:-10px;
+ display:block;
+ font-weight:400;
+}
+
+.user-list-follow .follow{
+ float:right;
+ font-size:10px;
+ height:30px;
+ margin-top:10px;
+ line-height:30px;
+ padding-left:15px;
+ padding-right:15px;
+ border-radius:30px;
+ background-color:#3498db;
+ color:#FFFFFF;
+}
+
+.user-list-socials{
+ padding-bottom:50px;
+}
+
+.user-list-socials img{
+ float:left;
+ width:50px;
+ height:50px;
+ border-radius:50px;
+}
+
+.user-list-socials strong{
+ float:left;
+ font-size:14px;
+ color:#1f1f1f;
+ padding-left:10px;
+}
+
+.user-list-socials strong em{
+ font-size:11px;
+ font-style:normal;
+ color:#666666;
+ margin-top:-10px;
+ display:block;
+ font-weight:400;
+}
+
+.user-list-socials .icon1{
+ float:right;
+ font-size:10px;
+ height:30px;
+ width:30px;
+ text-align:center;
+ margin-top:10px;
+ line-height:30px;
+ border-radius:30px;
+ color:#FFFFFF;
+ margin-left:5px;
+}
+
+.user-list-socials .icon2{
+ float:right;
+ font-size:10px;
+ height:30px;
+ width:30px;
+ text-align:center;
+ margin-top:10px;
+ line-height:30px;
+ border-radius:30px;
+ color:#FFFFFF;
+}
+
+/*------------------*/
+/*------------------*/
+/*---Activity Feed--*/
+/*------------------*/
+/*------------------*/
+
+.activity-item:hover{
+ cursor:pointer;
+}
+
+.activity-item img{
+ width:40px;
+ height:40px;
+ border-radius:40px;
+ margin-top:5px;
+ position:absolute;
+}
+
+.activity-item h5{
+ font-weight:600;
+ font-size:14px;
+ margin-left:60px;
+ margin-bottom:0px;
+ padding-top:3px;
+}
+
+.activity-item em{
+ display:block;
+ margin-left:60px;
+ font-size:11px;
+ font-style:normal;
+ font-weight:500;
+ color:rgba(0,0,0,0.6);
+}
+
+.activity-item em i{
+ padding-right:10px;
+ font-size:12px;
+}
+
+.activity-item-toggle{
+ font-size:8px;
+ position:absolute;
+ height:50px;
+ width:30px;
+ top:0px;
+ right:0px;
+ line-height:50px;
+ text-align:center;
+ -moz-transform: rotate(0deg);
+ -webkit-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ transform: rotate(0deg);
+ color:#1f1f1f;
+ -webkit-transition: all 200ms ease;
+ -moz-transition: all 200ms ease;
+ -ms-transition: all 200ms ease;
+ -o-transition: all 200ms ease;
+ transition: all 200ms ease;
+}
+
+.activity-item-toggle-rotate{
+ -moz-transform: rotate(45deg);
+ -webkit-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ transform: rotate(45deg);
+ color:#c0392b;
+ -webkit-transition: all 200ms ease;
+ -moz-transition: all 200ms ease;
+ -ms-transition: all 200ms ease;
+ -o-transition: all 200ms ease;
+ transition: all 200ms ease;
+}
+
+.activity-item-toggle i{
+ height:50px;
+ top:0px;
+ width:30px;
+ text-align:center;
+ line-height:50px;
+}
+
+.activity-item-detail{
+ padding-top:20px;
+ font-size:13px;
+ display:none;
+}
+
+/*----------------*/
+/*----------------*/
+/*----Full Map----*/
+/*----------------*/
+/*----------------*/
+
+.map-fullscreen{
+ margin-bottom:0px;
+ cursor:pointer;
+}
+
+.map-fullscreen .map-overlay{
+ opacity:0.65;
+ position:absolute;
+ width:100%;
+ height:100%;
+ z-index:9;
+}
+
+.map-fullscreen .deactivate-map{
+ position:absolute;
+ z-index:10;
+ bottom:-15px;
+ font-size:13px;
+ height:60px;
+ line-height:60px;
+ padding:0px;
+ display:none;
+}
+
+.map-fullscreen .map-text{
+ position:absolute;
+ z-index:99;
+ width:280px;
+ height:100px;
+ top:50%;
+ margin-top:-50px;
+ left:50%;
+ margin-left:-140px;
+}
+
+.map-fullscreen .map-text h3{
+ font-size:24px;
+ color:#FFFFFF;
+ text-align:center;
+}
+
+.map-fullscreen .map-text p{
+ color:#FFFFFF;
+ text-align:center;
+ font-size:13px;
+ opacity:0.6;
+ padding-top:0px;
+}
+
+/*------------------*/
+/*------------------*/
+/*----Portfolios----*/
+/*------------------*/
+/*------------------*/
+
+/*------------------------*/
+/*---Adaptive Portfolio---*/
+/*------------------------*/
+
+.portfolio-switch{
+ transition:all 250ms ease;
+}
+
+.active-adaptive{
+ color:#e34e47!important;
+ transition:all 250ms ease;
+}
+
+.portfolio-adaptive{
+ margin-bottom:10px;
+}
+
+.portfolio-adaptive:after {
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
+}
+
+.portfolio-adaptive .portfolio-item img{
+ margin-bottom:20px;
+}
+
+/*1 Item*/
+
+.portfolio-item{
+ transition:all 250ms ease;
+}
+
+.portfolio-adaptive-one .portfolio-item{
+ width:100%;
+ float:left;
+ transition:all 200ms ease;
+}
+
+/*2 Items*/
+
+.portfolio-adaptive-two .portfolio-item{
+ width:50%;
+ float:left;
+ transition:all 200ms ease;
+}
+
+.portfolio-adaptive-two .portfolio-item:nth-child(2n+2){
+ padding-left:10px!important;
+}
+.portfolio-adaptive-two .portfolio-item:nth-child(2n+1){
+ padding-right:10px!important;
+}
+
+/*3 Items*/
+
+.portfolio-adaptive-three .portfolio-item{
+ width:33%;
+ float:left;
+ transition:all 200ms ease;
+}
+
+.portfolio-adaptive-three .portfolio-item:nth-child(3n+1){
+ padding-left:5px!important;
+ padding-right:5px!important;
+}
+
+.portfolio-adaptive-three .portfolio-item:nth-child(3n+2){
+ padding-left:10px!important;
+}
+
+.portfolio-adaptive-three .portfolio-item:nth-child(3n+3){
+ padding-right:10px!important;
+}
+
+/*Portfolio Controls*/
+
+.portfolio-adaptive-controls .portfolio-switch{
+ float:right;
+ margin-bottom:30px;
+ margin-top:-40px;
+ border-left:solid 1px rgba(0,0,0,0.1);
+ color:#1f1f1f;
+}
+
+.portfolio-adaptive-controls .adaptive-one{
+ border-left:0px;
+}
+
+.portfolio-adaptive-controls .portfolio-switch i{
+ width:50px;
+ height:30px;
+ line-height:30px;
+ text-align:center;
+}
+
+.portfolio-adaptive .portfolio-item .adaptive-more{
+ font-size:12px;
+ padding-top:10px;
+}
+
+.portfolio-adaptive .portfolio-item .adaptive-more i{
+ font-size:12px;
+ margin-left:10px;
+ margin-right:10px;
+}
+
+/*------------------------*/
+/*---One Item Portfolio---*/
+/*------------------------*/
+
+.portfolio-one .portfolio-item img{
+ padding-bottom:20px;
+}
+
+
+.portfolio-one .portfolio-item em{
+ font-size:12px;
+ margin-top:-10px;
+ margin-bottom:5px;
+ display:block;
+}
+
+.portfolio-one .portfolio-item .portfolio-share{
+ float:right;
+ margin:0px 10px;
+}
+
+.portfolio-one .portfolio-item .portfolio-share i{
+ padding-right:10px;
+}
+
+.portfolio-one .portfolio-item .portfolio-link{
+ float:right;
+ margin:0px 10px;
+}
+
+.portfolio-one .portfolio-item .portfolio-link i{
+ padding-right:10px;
+}
+
+.portfolio-one .portfolio-item:after {
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
+}
+
+/*------------------------*/
+/*---Two Item Portfolio---*/
+/*------------------------*/
+
+.portfolio-two .portfolio-item{
+ float:left;
+ width:50%;
+}
+
+.portfolio-two .portfolio-item:nth-child(2n+1){
+ padding-right:10px;
+}
+.portfolio-two .portfolio-item:nth-child(2n+0){
+ padding-left:10px;
+}
+
+.portfolio-two .portfolio-item img{
+ padding-bottom:20px;
+}
+
+.portfolio-two .portfolio-item em{
+ font-size:12px;
+ margin-top:-10px;
+ margin-bottom:5px;
+ display:block;
+}
+
+.portfolio-two .portfolio-item .portfolio-share{
+ float:right;
+ margin:0px 10px;
+}
+
+.portfolio-two .portfolio-item .portfolio-share i{
+ padding-right:10px;
+}
+
+.portfolio-two .portfolio-item .portfolio-link{
+ float:right;
+ margin:0px 10px;
+}
+
+.portfolio-two .portfolio-item .portfolio-link i{
+ padding-right:10px;
+}
+
+.portfolio-two .portfolio-item:after {
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
+}
+
+/*--------------------*/
+/*---Portfolio Wide---*/
+/*--------------------*/
+
+@media (max-width:768px){
+ .wide-text{
+ padding-left:30px;
+ padding-right:30px;
+ }
+}
+
+@media (min-width:768px){
+ .wide-text{
+ padding-left:50px;
+ padding-right:50px;
+ }
+}
+
+.portfolio-wide .portfolio-item{
+ margin-bottom:5px;
+}
+
+.portfolio-wide .wide-text{
+ margin-top:20px;
+ display:none;
+}
+
+.portfolio-wide .wide-text img{
+ float:left;
+ width:80px;
+ height:80px;
+ margin-right:15px;
+ margin-bottom:0px;
+ margin-top:5px;
+}
+
+.portfolio-wide .wide-text .portfolio-link i{padding-right:10px;}
+.portfolio-wide .wide-text .portfolio-link{
+ float:right;
+ margin-left:20px;
+ margin-bottom:20px;
+}
+
+.portfolio-wide .wide-text .portfolio-share i{padding-right:10px;}
+.portfolio-wide .wide-text .portfolio-share{
+ float:right;
+ margin-left:20px;
+ margin-bottom:20px;
+}
+
+.portfolio-wide .wide-text .portfolio-close i{padding-right:10px;}
+.portfolio-wide .wide-text .portfolio-close{
+ float:right;
+ margin-left:20px;
+ margin-bottom:20px;
+}
+
+.portfolio-wide .wide-title{
+ display:block;
+ position:relative;
+ overflow:hidden;
+}
+
+.portfolio-wide .wide-title img{
+ position:absolute;
+ top:0px;
+ width:100%;
+ display:block;
+ z-index:1;
+}
+
+.portfolio-wide .wide-title .overlay{
+ width:100%;
+ height:100%;
+ z-index:2;
+ position:absolute;
+ opacity:0.8!important;
+ top:0px;
+}
+
+.portfolio-wide .wide-title h3{
+ font-weight:400;
+ text-shadow:0px 1px 2px rgba(0,0,0,0.5);
+ text-align:center;
+ position:relative;
+ z-index:3;
+ color:#FFFFFF;
+ padding-top:35px;
+ margin-bottom:0px
+}
+
+.portfolio-wide .wide-title em{
+ text-shadow:0px 1px 2px rgba(0,0,0,0.5);
+ text-align:center;
+ position:relative;
+ display:block;
+ z-index:3;
+ color:rgba(255,255,255,0.6);
+ padding-bottom:30px;
+ font-style:normal;
+ font-size:12px;
+}
+
+/*----------------------*/
+/*---Filter Portfolio---*/
+/*----------------------*/
+
+.portfolio-filter-wrapper {
+ width:100%;
+ margin:20px 0px;
+ margin-bottom:0px;
+}
+
+.portfolio-filter-wrapper:after {
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
+ margin-bottom:10px;
+}
+
+.portfolio-filter-categories{
+ display:table;
+ width:100%;
+ padding-top:5px;
+ padding-bottom:5px;
+}
+
+.selected-filter{
+ background-color:#27ae60;
+ color:#FFFFFF!important;
+ transition:all 200ms ease;
+}
+
+.filter-category{
+ display:table-cell;
+ padding:2px 10px;
+ text-align:center;
+ font-size: 12px;
+ color: #333;
+ transition:all 200ms ease;
+}
+
+.portfolio-filter-item img{
+ margin-bottom:20px;
+}
+
+.gallery-filter-item img{
+ border-radius:5px;
+}
+
+@media(min-width:768px){
+ .portfolio-filter-item{
+ width:46%;
+ margin-right:2%;
+ margin-left:2%;
+ float:left;
+ }
+}
+
+@media(min-width:1020px){
+ .portfolio-filter-item{
+ width:28%;
+ margin-right:2%;
+ margin-left:2%;
+ float:left;
+ }
+}
+
+.gallery-filter-item{
+ width:46%;
+ margin-left:2%;
+ margin-right:2%;
+ float:left;
+ margin-bottom:4%;
+}
+
+@media(min-width:768px){
+ .gallery-filter-item{
+ width:46%;
+ margin-right:2%;
+ margin-left:2%;
+ margin-bottom:4%;
+ float:left;
+ }
+}
+
+@media(min-width:1020px){
+ .gallery-filter-item{
+ width:28%;
+ margin-right:2%;
+ margin-left:2%;
+ margin-bottom:4%;
+ float:left;
+ }
+}
+
+
+/*-----------------*/
+/*-----------------*/
+/*----Galleries----*/
+/*-----------------*/
+/*-----------------*/
+
+.gallery:after {
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
+ margin-bottom:10px;
+}
+
+/*-------------------------*/
+/*---Round Square Thumbs---*/
+/*-------------------------*/
+
+.square-thumbs a img{
+ border-radius:none;
+}
+
+.round-thumbs a img{
+ border-radius:300px;
+}
+
+.gallery a{
+ width:33.333333333%;
+ float:left;
+ padding-bottom:20px;
+}
+
+.gallery a:nth-child(3n+1){
+ padding-right:10px;
+}
+
+.gallery a:nth-child(3n+2){
+ padding-right:5px;
+ padding-left:5px;
+}
+.gallery a:nth-child(3n+3){
+ padding-left:10px;
+}
+
+.gallery a img{
+ display:block;
+ width:100%;
+}
+
+/*-----------------------*/
+/*---Justified Gallery---*/
+/*-----------------------*/
+
+.justified-gallery {
+ width: 100%;
+ position: relative;
+ overflow: hidden;
+ margin-bottom:25px;
+}
+.justified-gallery > a,
+.justified-gallery > div {
+ position: absolute;
+ display: inline-block;
+ overflow: hidden;
+ opacity: 0;
+}
+.justified-gallery > a > img,
+.justified-gallery > div > img,
+.justified-gallery > a > a > img,
+.justified-gallery > div > a > img {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+/*--------------------------*/
+/*--------------------------*/
+/*----Pages and PageApps----*/
+/*--------------------------*/
+/*--------------------------*/
+
+/*----------------*/
+/*--Page Contact--*/
+/*----------------*/
+
+.contactField{
+ background-color:rgba(255,255,255,0.4);
+ font-size:12px;
+ color:#666;
+ border:solid 1px #cacaca;
+ transition:all 200ms ease;
+}
+
+.contactField:focus{
+ border:solid 1px #27ae60;
+ transition:all 200ms ease;
+}
+
+.contactTextarea{
+ background-color:rgba(255,255,255,0.4);
+ font-size:12px;
+ border:solid 1px #cacaca;
+ color: #666;
+ transition:all 200ms ease;
+}
+
+.contactTextarea:focus{
+ border:solid 1px #27ae60;
+ transition:all 200ms ease;
+}
+
+.contactSubmitButton{
+ line-height:19px!important;
+ display:block!important;
+ height:36px!important;
+ margin-right:4px;
+ width:100%;
+}
+
+.contact-form{
+ margin-bottom:20px;
+}
+
+.contactNameField{
+ padding-bottom:5px;
+ font-size:12px;
+}
+
+.contactNameField span{
+ float:right;
+ opacity:0.2;
+}
+
+.contactEmailField{
+ padding-bottom:5px;
+ font-size:12px;
+}
+
+.contactEmailField span{
+ float:right;
+ opacity:0.2;
+}
+
+.contactMessageTextarea{
+ padding-bottom:5px;
+ font-size:12px;
+}
+
+.contactMessageTextarea span{
+ float:right;
+ opacity:0.2;
+}
+
+.formFieldWrap{
+ margin-top:-5px;
+}
+
+#contactNameField{
+ text-transform:capitalize;
+}
+
+.contactField{
+ min-width:100%;
+ display:block;
+ box-sizing:border-box;
+}
+
+.contactTextarea{
+ min-width:100%;
+ display:block;
+ box-sizing:border-box;
+}
+
+.buttonWrap{
+ line-height:20px;
+ padding-bottom:24px;
+ min-width:43%;
+ display:inline-block;
+}
+
+.formSubmitButtonErrorsWrap a{
+ width:25%;
+ text-align:center;
+ padding:8px 10px!important;
+}
+
+
+.formValidationError{
+ height:40px;
+ line-height:40px;
+ padding-top:5px;
+ margin-bottom:20px;
+}
+
+
+.contactField{
+ height:40px;
+ padding-left:10px;
+ padding-right:10px;
+ margin-bottom:10px;
+}
+
+.contactTextarea{
+ margin-bottom: 20px;
+ padding-left: 12px;
+ padding-right: 12px;
+ padding-top: 9px;
+ padding-bottom: 9px;
+ line-height: 18px;
+ height: 80px;
+}
+
+/*-------------------*/
+/*---Contact Icons---*/
+/*-------------------*/
+
+@media (min-width:768px){
+ .contact-information{
+ padding-left:20px;
+ }
+}
+
+.field-title{
+ padding-left:0px;
+ margin-left:0px;
+}
+
+.contact-call{
+ color:#666;
+ height:25px;
+ display:block;
+ margin-bottom:5px;
+}
+
+.contact-call i{
+ width:12px;
+ diplay:block;
+ margin-right:20px;
+ color:#1f1f1f;
+}
+
+.contact-text{
+ color:#666;
+ height:25px;
+ display:block;
+ margin-bottom:5px;
+}
+
+.contact-text i{
+ width:12px;
+ diplay:block;
+ margin-right:20px;
+ color:#1f1f1f;
+}
+
+.contact-mail{
+ color:#666;
+ height:25px;
+ display:block;
+ margin-bottom:5px;
+}
+
+.contact-mail i{
+ width:12px;
+ diplay:block;
+ margin-right:20px;
+ color:#1f1f1f;
+}
+
+.contact-facebook{
+ color:#666;
+ height:25px;
+ display:block;
+ margin-bottom:5px;
+}
+
+.contact-facebook i{
+ width:12px;
+ diplay:block;
+ margin-right:20px;
+ color:#1f1f1f;
+}
+
+.contact-twitter{
+ color:#666;
+ height:25px;
+ display:block;
+ margin-bottom:5px;
+}
+
+.contact-twitter i{
+ width:12px;
+ diplay:block;
+ margin-right:20px;
+ color:#1f1f1f;
+}
+
+.contact-linkedin{
+ color:#666;
+ height:25px;
+ display:block;
+}
+
+.contact-linkedin i{
+ width:12px;
+ diplay:block;
+ margin-right:20px;
+ color:#1f1f1f;
+}
+
+/*----------*/
+/*---Maps---*/
+/*----------*/
+
+.maps{
+ display:block!important;
+ width:100%;
+ height:200px;
+ overflow:hidden;
+}
+
+@media (min-width:768px){
+ .maps{
+ height:300px;
+ }
+}
+
+/*----------------*/
+/*----------------*/
+/*---Page Login---*/
+/*----------------*/
+/*----------------*/
+
+.page-login{
+ width:280px;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.page-login .page-login-logo{
+ width:125px;
+ height:100px;
+ background-image:url(../images/pages-logo-dark.png);
+ background-size:125px 100px;
+ margin-left:auto;
+ margin-right:auto;
+ margin-bottom:40px;
+}
+
+.page-login .login-password input,
+.page-login .login-mail input,
+.page-login .login-date input,
+.page-login .login-input input{
+ -webkit-appearance: none;
+ padding-top:0px;
+ padding-bottom:20px;
+ width:100%;
+ display:block;
+ background-color:rgba(255,255,255,1);
+ color:rgba(0,0,0,0.5);
+ border-bottom:solid 2px rgba(0,0,0,0.1);
+ margin-bottom:20px;
+ font-size:12px;
+ padding-left:40px;
+ transition:all 250ms ease;
+}
+
+.page-login .login-password i,
+.page-login .login-mail i,
+.page-login .login-date i,
+.page-login .login-input i{
+ -webkit-appearance: none;
+ position:absolute;
+ z-index:10;
+ width:20px;
+ text-align:center;
+ margin-top:1px;
+ margin-left:11px;
+}
+
+.page-login .login-password input:focus,
+.page-login .login-mail input:focus,
+.page-login .login-date input:focus,
+.page-login .login-input input:focus{
+ color:#1f1f1f;
+ border-bottom:solid 2px #27ae60;
+ transition:all 250ms ease;
+}
+
+.page-login .login-forgot:hover,
+.page-login .login-create:hover,
+.page-login .login-already:hover{
+ color:#2e8ece;
+ transition:all 250ms ease;
+}
+
+.page-login .login-forgot i,
+.page-login .login-create i,
+.page-login .login-already i{
+ padding-right:10px;
+ padding-left:10px;
+}
+
+.page-login .login-already{
+ text-align:center;
+ font-size:12px;
+ color:#cacaca;
+ margin-bottom:20px;
+ transition:all 250ms ease;
+}
+
+.page-login .login-create{
+ width:50%;
+ float:right;
+ text-align:right;
+ font-size:12px;
+ color:#cacaca;
+ margin-bottom:30px;
+ transition:all 250ms ease;
+}
+
+.page-login .login-forgot{
+ width:50%;
+ float:left;
+ text-align:left;
+ font-size:12px;
+ color:#cacaca;
+ margin-bottom:30px;
+ transition:all 250ms ease;
+}
+
+/*------------------*/
+/*------------------*/
+/*---Page Profile---*/
+/*------------------*/
+/*------------------*/
+
+.page-profile-header{
+ padding-top:40px;
+ padding-bottom:30px;
+ background-image:url(../images/pictures/3.jpg);
+ background-size:cover;
+}
+
+.page-profile-header .overlay{
+ position:absolute;
+ top:0px;
+ left:0px;
+ bottom:0px;
+ right:0px;
+ opacity:0.7;
+}
+
+.page-profile-header .follow-buttons a{
+ float:left;
+ text-align:center;
+ margin-top:30px;
+ color:#FFFFFF;
+ border:solid 2px rgba(255,255,255,0.5);
+ width:100px;
+ margin-left:auto;
+ margin-right:auto;
+ padding-top:3px;
+ padding-bottom:3px;
+ border-radius:25px;
+ margin-left:10px;
+ margin-right:10px;
+ font-size:12px;
+ opacity:0.8;
+ transition:all 200ms ease;
+}
+
+.page-profile-header .follow-buttons a:hover{
+ background-color:rgba(255,255,255,0.1);
+ opacity:1;
+ transition:all 200ms ease;
+}
+
+.page-profile-header .follow-buttons{
+ width:240px;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.page-profile-header img{
+ width:120px;
+ height:120px;
+ border-radius:120px;
+ margin-bottom:30px;
+ margin-left:auto;
+ margin-right:auto;
+ z-index:4;
+ border:solid 4px rgba(255,255,255,0.9);
+}
+
+.page-profile-header h3{
+ z-index:4;
+ position:relative;
+ text-align:center;
+ color:#FFFFFF;
+ font-weight:400;
+ margin-bottom:5px;
+}
+
+.page-profile .follow-buttons:after, .page-profile-followers a:after {
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
+}
+
+.page-profile-header em{
+ display:block;
+ position:relative;
+ text-align:center;
+ color:#FFFFFF;
+ opacity:0.7;
+ z-index:4;
+ font-size:12px;
+ font-style:normal;
+}
+
+.page-profile-header em i{
+ padding-right:10px;
+}
+
+.page-profile-followers a{
+ width:33%;
+ float:left;
+ text-align:center;
+ font-size:12px;
+ color:#666666;
+ margin-bottom:30px;
+}
+
+.page-profile-followers a i{
+ display:block;
+ font-size:20px;
+ color:#1f1f1f;
+ text-align:center;
+ padding-bottom:10px;
+}
+
+.page-profile-followers a:nth-child(3n+1){
+ border-right:solid 1px rgba(0,0,0,0.1);
+}
+
+.page-profile-followers a:nth-child(3n+3){
+ border-left:solid 1px rgba(0,0,0,0.1);
+}
+
+/*--------------------*/
+/*--------------------*/
+/*---Page Countdown---*/
+/*--------------------*/
+/*--------------------*/
+
+.boxed-layout{
+ background-color:rgba(255,255,255,1);
+ border-radius:5px;
+ padding:30px;
+}
+
+.date-years, date-years em{
+ display:none;
+}
+
+.countdown-page .overlay{
+ opacity:0.8;
+ position:absolute;
+ top:0px;
+ z-index:1;
+}
+
+.countdown-content{
+ width:300px;
+ margin-left:-150px!important;
+ position:relative;
+ z-index:10;
+}
+
+/*Unboxed Layout*/
+
+.countdown-content a{
+ color:#666666;
+ text-decoration:underline;
+}
+
+.countdown-page .unboxed-layout h3{
+ text-align:center;
+ color:#FFFFFF;
+ font-size:36px;
+ font-weight:300;
+ line-height:30px;
+}
+
+.countdown-page .unboxed-layout h4{
+ text-align:center;
+ color:rgba(255,255,255,0.5);
+ font-size:12px;
+ font-weight:400;
+}
+
+.countdown-page .unboxed-layout p{
+ text-align:center;
+ margin-left:auto;
+ margin-right:auto;
+ padding-top:20px;
+ color:rgba(255,255,255,0.8);
+ padding-bottom:30px;
+ border-bottom:solid 1px rgba(255,255,255,0.1);
+}
+
+.countdown-page .unboxed-layout .countdown-class div{
+ font-size:30px;
+ text-align:center;
+ color:#FFFFFF;
+ font-weight:400;
+}
+
+.countdown-page .unboxed-layout .countdown-class div em{
+ font-size:12px;
+ display:block;
+ font-style:normal;
+ opacity:0.4;
+}
+
+/*Boxed Layout*/
+
+.countdown-page .boxed-layout h3{
+ text-align:center;
+ color:#1f1f1f;
+ font-size:30px;
+ font-weight:400;
+ line-height:30px;
+}
+
+.countdown-page .boxed-layout h4{
+ text-align:center;
+ color:rgba(0,0,0,0.5);
+ font-size:13px;
+ font-weight:500;
+}
+
+.countdown-page .boxed-layout p{
+ text-align:center;
+ margin-left:auto;
+ margin-right:auto;
+ padding-top:20px;
+ color:rgba(0,0,0,0.8);
+ padding-bottom:20px;
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+}
+
+.countdown-page .boxed-layout .countdown-class div{
+ font-size:24px;
+ text-align:center;
+ color:#1f1f1f;
+ font-weight:400;
+}
+
+.countdown-page .boxed-layout .countdown-class div em{
+ font-size:12px;
+ display:block;
+ font-style:normal;
+ opacity:0.5;
+}
+
+.countdown-class div{
+ width:25%;
+ float:left;
+}
+
+.cover-screen .left-button{
+ position:absolute;
+ bottom:20px;
+ z-index:2;
+ color:rgba(255,255,255,0.3);
+ left:20px;
+ transition:all 200ms ease;
+}
+
+.cover-screen .left-button:hover{
+ color:#FFFFFF;
+ transition:all 200ms ease;
+}
+
+.cover-screen .left-button i{
+ padding-right:20px;
+ color:rgba(255,255,255,0.5);
+}
+
+.cover-screen .right-button{
+ position:absolute;
+ bottom:20px;
+ right:20px;
+ color:rgba(255,255,255,0.3);
+ z-index:2;
+ transition:all 200ms ease;
+}
+
+.cover-screen .right-button:hover{
+ color:#FFFFFF;
+ transition:all 200ms ease;
+}
+
+.cover-screen .right-button i{
+ padding-left:20px;
+ color:rgba(255,255,255,0.5);
+}
+
+/*----------------*/
+/*----------------*/
+/*---Page Error---*/
+/*----------------*/
+/*----------------*/
+
+.error-page .overlay{
+ opacity:0.8;
+}
+
+.error-page .error-content{
+ width:300px;
+ z-index:10;
+}
+
+/*Unboxed Layout*/
+.error-page .unboxed-layout h3{
+ font-size:36px;
+ text-align:center;
+ line-height:40px;
+ color:#FFFFFF;
+}
+
+.error-page .unboxed-layout h4{
+ text-align:center;
+ font-size:12px;
+ font-weight:400;
+ font-size:12px;
+ color:rgba(255,255,255,0.5);
+}
+
+.error-page .unboxed-layout p{
+ text-align:center;
+ color:rgba(255,255,255,0.8);
+ padding-top:20px;
+}
+
+.error-page .unboxed-layout .back-home i{
+ line-height:55px;
+ width:55px;
+ text-align:center;
+}
+
+.error-page .unboxed-layout .back-home{
+ margin-top:40px;
+ color:rgba(255,255,255,0.8);
+ font-size:22px;
+ width:70px;
+ height:70px;
+ line-height:65px;
+ text-align:center;
+ border-radius:70px;
+ border:solid 2px rgba(255,255,255,0.5);
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.error-page .unboxed-layout .back-home:hover{
+ border:solid 2px rgba(255,255,255,1);
+ transition:all 200ms ease;
+}
+
+/*Boxed Layout*/
+
+.error-page .boxed-layout h3{
+ text-align:center;
+ color:#1f1f1f;
+ font-size:30px;
+ font-weight:400;
+ line-height:30px;
+}
+
+.error-page .boxed-layout h4{
+ text-align:center;
+ color:rgba(0,0,0,0.5);
+ font-size:13px;
+ font-weight:500;
+}
+
+.error-page .boxed-layout p{
+ text-align:center;
+ margin-left:auto;
+ margin-right:auto;
+ padding-top:10px;
+ color:rgba(0,0,0,0.8);
+ padding-bottom:0px;
+}
+
+.error-page .boxed-layout .back-home i{
+ line-height:55px;
+ width:55px;
+ text-align:center;
+}
+
+.error-page .boxed-layout .back-home{
+ color:rgba(0,0,0,0.8);
+ font-size:22px;
+ width:70px;
+ height:70px;
+ line-height:65px;
+ text-align:center;
+ border-radius:70px;
+ border:solid 2px rgba(0,0,0,0.5);
+ margin-left:auto;
+ margin-right:auto;
+ transition:all 200ms ease;
+}
+
+.error-page .boxed-layout .back-home:hover{
+ border:solid 2px rgba(0,0,0,1);
+ transition:all 200ms ease;
+}
+
+/*-------------------*/
+/*-------------------*/
+/*---Pageapp Login---*/
+/*-------------------*/
+/*-------------------*/
+
+.pageapp-login-content{
+ width:300px;
+ z-index:10;
+}
+
+.pageapp-login .overlay{
+ opacity:0.8;
+ position:absolute;
+ top:0px;
+ z-index:1;
+}
+
+.pageapp-login input{
+ transition:all 200ms ease;
+}
+
+.pageapp-login input:focus{
+ border-bottom:solid 1px #27ae60!important;
+ transition:all 200ms ease;
+}
+
+.pageapp-login-button{
+ margin-top:25px;
+}
+
+/*Unboxed Layout*/
+
+.pageapp-login .unboxed-layout .pageapp-login-field i{
+ position:absolute;
+ color:#FFFFFF;
+ margin-top:12px;
+ margin-left:10px;
+}
+
+.pageapp-login .unboxed-layout div input{
+ display:block;
+ width:100%;
+ height:40px;
+ line-height:40px;
+ padding-left:40px;
+ background-color:rgba(255,255,255,0);
+ color:#FFFFFF;
+ border-bottom:solid 1px rgba(255,255,255,0.1);
+ margin-bottom:20px;
+ font-size:12px;
+}
+
+.pageapp-login .unboxed-layout .decoration{
+ background-color:rgba(255,255,255,0.1);
+ margin-top:20px;
+ margin-bottom:20px;
+}
+
+.pageapp-login .unboxed-layout .pageapp-login-logo{
+ width:125px;
+ height:100px;
+ background-image:url(../images/pages-logo-light.png);
+ background-size:125px 100px;
+ margin-left:auto;
+ margin-right:auto;
+ margin-bottom:20px;
+}
+
+@media screen and ( max-height: 570px ){
+ .pageapp-login-logo{
+ display:none;
+ }
+ .pageapp-login-content .unboxed-layout{
+ margin-top:-40px;
+ }
+
+ .pageapp-login-content .boxed-layout{
+ padding-top:10px;
+ padding-bottom:20px;
+ margin-top:-30px;
+ }
+
+ .pageapp-login-content .boxed-layout a{
+ height:40px;
+ line-height:40px;
+ padding-top:0px;
+ padding-bottom:0px;
+ }
+
+ .pageapp-login-content .boxed-layout a i{
+ line-height:40px;
+ padding-top:0px;
+ pading-bottom:0px;
+ margin-top:-17px;
+ }
+}
+
+/*Boxed Layout*/
+
+.pageapp-login .boxed-layout .pageapp-login-field i{
+ position:absolute;
+ color:#1f1f1f;
+ margin-top:12px;
+ margin-left:10px;
+}
+
+.pageapp-login .boxed-layout div input{
+ display:block;
+ width:100%;
+ height:40px;
+ line-height:40px;
+ padding-left:40px;
+ background-color:rgba(255,255,255,0);
+ color:#1f1f1f;
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+ margin-bottom:20px;
+ font-size:12px;
+}
+
+.pageapp-login .boxed-layout .decoration{
+ background-color:rgba(0,0,0,0.1);
+ margin-top:20px;
+ margin-bottom:20px;
+}
+
+.pageapp-login .boxed-layout .pageapp-login-logo{
+ width:125px;
+ height:100px;
+ background-image:url(../images/pages-logo-dark.png);
+ background-size:125px 100px;
+ margin-left:auto;
+ margin-right:auto;
+ margin-bottom:20px;
+}
+
+/*----------------------*/
+/*----------------------*/
+/*---Pageapp Register---*/
+/*----------------------*/
+/*----------------------*/
+
+.pageapp-signup-content{
+ width:300px;
+ position:relative;
+ z-index:10;
+}
+
+.pageapp-signup .overlay{
+ opacity:0.8;
+ position:absolute;
+ top:0px;
+ z-index:1;
+}
+
+.pageapp-signup input{
+ transition:all 200ms ease;
+}
+
+.pageapp-signup input:focus{
+ border-bottom:solid 1px #27ae60!important;
+ transition:all 200ms ease;
+}
+
+/*Unboxed Layout*/
+
+.pageapp-signup .unboxed-layout .pageapp-signup-field input{
+ display:block;
+ width:100%;
+ padding-top:18px;
+ padding-bottom:18px;
+ background-color:rgba(255,255,255,0);
+ color:#FFFFFF;
+ border-bottom:solid 1px rgba(255,255,255,0.1);
+ font-size:12px;
+ padding-left:40px;
+ margin-bottom:10px;
+}
+
+.pageapp-signup .unboxed-layout .pageapp-signup-field i{
+ position:absolute;
+ top:18px;
+ left:5px;
+}
+
+.pageapp-signup .unboxed-layout .pageapp-signup-logo{
+ width:125px;
+ height:100px;
+ background-image:url(../images/pages-logo-light.png);
+ background-size:125px 100px;
+ margin-left:auto;
+ margin-right:auto;
+ margin-bottom:20px;
+}
+
+
+@media screen and ( max-height: 570px ){
+ .pageapp-signup-logo{
+ display:none;
+ }
+ .pageapp-signup-content .unboxed-layout{
+ margin-top:-30px;
+ }
+
+ .pageapp-signup-content .boxed-layout{
+ padding-top:10px;
+ padding-bottom:20px;
+ margin-top:-30px;
+ }
+}
+
+.pageapp-signup .unboxed-layout .pageapp-signup-button{
+ margin-top:30px;
+}
+
+/*Boxed Layout*/
+
+.pageapp-signup .boxed-layout .pageapp-signup-field input{
+ display:block;
+ width:100%;
+ padding-top:15px;
+ padding-bottom:15px;
+ background-color:rgba(255,255,255,0);
+ color:#1f1f1f;
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+ font-size:12px;
+ padding-left:40px;
+ margin-bottom:10px;
+}
+
+.pageapp-signup .boxed-layout .pageapp-signup-field i{
+ position:absolute;
+ top:18px;
+ left:5px;
+}
+
+.pageapp-signup .boxed-layout .pageapp-signup-logo{
+ width:125px;
+ height:100px;
+ background-image:url(../images/pages-logo-dark.png);
+ background-size:125px 100px;
+ margin-left:auto;
+ margin-right:auto;
+ margin-bottom:20px;
+}
+
+.pageapp-signup .boxed-layout .pageapp-signup-button{
+ margin-top:30px;
+ margin-bottom:0px;
+}
+
+/*-------------------*/
+/*-------------------*/
+/*---Page Timeline---*/
+/*-------------------*/
+/*-------------------*/
+
+/*----------------*/
+/*---Timeline 1---*/
+/*----------------*/
+
+.pageapp-timeline-1{
+ overflow:hidden;
+ margin-top:-20px;
+}
+
+.pageapp-timeline-1 .timeline-decoration{
+ width:1px;
+ height:10000px;
+ position:absolute;
+ background-color:rgba(0,0,0,0.2);
+ left:50%;
+ margin-left:-1px;
+ top:0px;
+}
+
+.pageapp-timeline-1 p{
+ margin-bottom:5px;
+}
+
+.pageapp-timeline-1 .timeline-text,
+.pageapp-timeline-1 .timeline-video,
+.pageapp-timeline-1 .timeline-event,
+.pageapp-timeline-1 .timeline-quote,
+.pageapp-timeline-1 .timeline-image{
+ background-color:#FFFFFF;
+ display:block;
+ padding-top:20px;
+ padding-bottom:20px;
+}
+
+.pageapp-timeline-1 .timeline-icon{
+ padding-top:20px;
+ padding-bottom:2px;
+ margin-bottom:20px;
+ margin-top:20px;
+ display:block;
+ background-color:#FFFFFF;
+}
+
+.pageapp-timeline-1 .timeline-icon i{
+ font-size:18px;
+ width:60px;
+ height:60px;
+ margin-left:auto;
+ margin-right:auto;
+ display:block;
+ line-height:60px;
+ text-align:center;
+ border-radius:60px;
+ color:#1f1f1f;
+ border:solid 1px rgba(0,0,0,0.2);
+ margin-bottom:20px;
+ background-color:#FFFFFF;
+ transition:all 200ms ease;
+}
+
+.pageapp-timeline-1 .responsive-image,
+.pageapp-timeline-1 .gallery img{
+ background-color:#FFFFFF;
+ border:solid 1px rgba(0,0,0,0.2);
+ padding:8px;
+ margin-bottom:10px;
+}
+
+.pageapp-timeline-1 .timeline-event .event-link i{padding-right:10px}
+.pageapp-timeline-1 .timeline-event .event-maps i{padding-right:10px}
+
+.pageapp-timeline-1 .timeline-event .event-link{
+ font-size:12px;
+ text-align:center;
+}
+
+.pageapp-timeline-1 .timeline-event .event-maps{
+ font-size:12px;
+ text-align:center;
+ margin-bottom:10px;
+}
+
+.pageapp-timeline-1 .timeline-event .gallery a{
+ padding-bottom:0px;
+}
+
+/*----------------*/
+/*---Timeline 2---*/
+/*----------------*/
+
+.pageapp-timeline-2{
+ margin-top:30px;
+}
+
+.pageapp-timeline-2 .timeline-item{
+ position:relative;
+ z-index:10!important;
+}
+
+.pageapp-timeline-2 .timeline-decoration{
+ position:absolute;
+ width:1px;
+ height:96%;
+ z-index:2;
+ background-color:rgba(0,0,0,0.1);
+ margin-left:22px;
+}
+
+.pageapp-timeline-2 .timeline-icon{
+ position:relative;
+ z-index:9;
+ width:70px;
+ height:100%;
+ float:left;
+}
+
+.pageapp-timeline-2 .timeline-text{
+ display:inline-block;
+ padding-left:70px;
+ margin-top:-48px;
+ min-height:100px;
+ width:100%;
+}
+
+.pageapp-timeline-2 .timeline-text .subtitle{
+ font-size:11px;
+ font-style:normal;
+ display:block;
+ margin-top:-10px;
+ color:#e34e47;
+ margin-bottom:20px;
+}
+
+.pageapp-timeline-2 .timeline-text p{
+ margin-top:-10px;
+}
+
+.pageapp-timeline-2 .timeline-icon i{
+ cursor:pointer;
+ margin-left:auto;
+ margin-right:auto;
+ width:50px;
+ height:50px;
+ background-color:#FFFFFF;
+ color:#1f1f1f;
+ border:solid 1px rgba(0,0,0,0.2);
+ border-radius:50px;
+ font-size:17px;
+ line-height:50px;
+ text-align:center;
+}
+
+.pageapp-timeline-2 .timeline-quote .title{
+ font-size:14px;
+ line-height:30px;
+ font-weight:300;
+ margin-bottom:20px;
+ margin-top:-10px;
+}
+
+.pageapp-timeline-2 .timeline-quote .subtitle{
+ display:block;
+ text-align:right;
+}
+
+@media (min-width:768px){
+ .pageapp-timeline-2{
+ padding-left:30px;
+ padding-right:30px;
+ }
+
+ .pageapp-timeline-2 .timeline-decoration{
+ position:absolute;
+ width:1px;
+ height:96%;
+ z-index:2;
+ background-color:rgba(0,0,0,0.1);
+ margin-left:32px;
+ }
+
+ .pageapp-timeline-2 .timeline-icon{
+ width:150px;
+ height:100%;
+ float:left;
+ display:inline;
+ }
+
+ .pageapp-timeline-2 .timeline-text{
+ display:inline-block;
+ padding-left:100px;
+ margin-top:-52px;
+ }
+
+ .pageapp-timeline-2 .timeline-icon i{
+ cursor:pointer;
+ margin-left:auto;
+ margin-right:auto;
+ width:65px;
+ height:65px;
+ background-color:#FFFFFF;
+ color:#1f1f1f;
+ border:solid 1px rgba(0,0,0,0.2);
+ border-radius:65px;
+ font-size:17px;
+ line-height:65px;
+ text-align:center;
+ }
+
+ .pageapp-timeline-2 .timeline-quote .title{
+ font-size:14px;
+ line-height:30px;
+ font-weight:300;
+ margin-bottom:20px;
+ margin-top:-3px;
+ }
+
+}
+
+/*------------------------*/
+/*------------------------*/
+/*---PageApp Checklists---*/
+/*------------------------*/
+/*------------------------*/
+
+.checklist-square{
+ font-size:14px;
+ font-weight:500;
+ background-image:url(../images/framework/checkbox.png);
+ background-size:16px 18px;
+ background-position:0px 26px;
+ background-repeat:no-repeat;
+ padding-left:30px;
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+ padding-bottom:20px;
+ color:#1f1f1f;
+ transition:all 200ms ease;
+ padding-top:20px;
+}
+
+.checklist-square-selected:hover{
+ text-decoration:line-through!important;
+}
+
+.checklist-square-selected{
+ background-image:url(../images/framework/checkboxc.png);
+ text-decoration:line-through;
+ color:rgba(0,0,0,0.3);
+ transition:all 200ms ease;
+}
+
+.checklist-round{
+ font-size:14px;
+ font-weight:500;
+ background-image:url(../images/framework/radio.png);
+ background-size:16px 18px;
+ background-position:0px 26px;
+ background-repeat:no-repeat;
+ padding-left:30px;
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+ padding-bottom:20px;
+ color:#1f1f1f;
+ transition:all 200ms ease;
+ padding-top:20px;
+}
+
+.checklist-round-selected:hover{
+ text-decoration:line-through!important;
+}
+
+.checklist-round-selected{
+ background-image:url(../images/framework/radioc.png);
+ text-decoration:line-through;
+ color:rgba(0,0,0,0.3);
+ transition:all 200ms ease;
+}
+
+/*------------------*/
+/*------------------*/
+/*----TaskLists----*/
+/*------------------*/
+/*------------------*/
+
+
+.tasklist-item{
+ height:65px;
+ line-height:65px;
+ margin-bottom:0px;
+ padding-bottom:0px;
+ border-left:solid 4px #ecf0f1;
+ transition:all 200ms ease;
+}
+
+.tasklist-item i:first-child{
+ position:absolute;
+ height:65px;
+ line-height:65px;
+ width:40px;
+ text-align:center;
+ color:rgba(0,0,0,0.2);
+ transition:all 200ms ease;
+}
+
+.tasklist-item h5{
+ padding-left:50px;
+ line-height:65px;
+ font-size:14px;
+ margin-top:0px;
+ border-top:solid 1px rgba(0,0,0,0.1);
+ border-right:solid 1px rgba(0,0,0,0.1);
+ font-weight:500;
+ transition:all 200ms ease;
+}
+
+.tasklist-red{ border-left:solid 4px #c0392b; }
+.tasklist-green{ border-left:solid 4px #27ae60; }
+.tasklist-blue{ border-left:solid 4px #2980b9; }
+.tasklist-orange{ border-left:solid 4px #d35400; }
+.tasklist-yellow{ border-left:solid 4px #f1c40f; }
+.tasklist-gray{ border-left:solid 4px #666666; }
+.tasklist-dark{ border-left:solid 4px #1f1f1f; }
+.tasklist-magenta{ border-left:solid 4px #8e44ad; }
+
+.tasklist-completed{
+ background-color:#fafafa;
+ transition:all 200ms ease;
+}
+
+.tasklist-completed i:first-child{
+ color:#27ae60!important;
+ transition:all 200ms ease;
+}
+
+.tasklist-completed h5{
+ color:rgba(0,0,0,0.5);
+ transition:all 200ms ease;
+}
+
+
+/*-----------------------*/
+/*-----------------------*/
+/*---Pageapp Reminders---*/
+/*-----------------------*/
+/*-----------------------*/
+
+.reminder-check-square{
+ background-image:url(../images/framework/checkbox.png);
+ background-repeat:no-repeat;
+ background-size:16px 18px;
+ background-position:10px 40px;
+}
+
+.reminder-check-round{
+ background-image:url(../images/framework/radio.png);
+ background-repeat:no-repeat;
+ background-size:16px 18px;
+ background-position:10px 40px;
+}
+
+.reminder i{
+ padding-right:10px;
+}
+
+.reminder-check-square-selected{
+ background-image:url(../images/framework/checkboxc.png);
+}
+
+.reminder-check-round-selected{
+ background-image:url(../images/framework/radioc.png);
+}
+
+.reminder-check-square-selected em{opacity:0.3; transition:all 200ms ease;}
+.reminder-check-square-selected strong{opacity:0.3; transition:all 200ms ease;}
+
+.reminder-check-round-selected em{opacity:0.3; transition:all 200ms ease;}
+.reminder-check-round-selected strong{opacity:0.3; transition:all 200ms ease;}
+
+.reminder strong{
+ font-size:16px;
+ font-weight:800;
+ padding-left:50px;
+ margin-bottom:-4px;
+ display:block;
+ color:#1f1f1f;
+ transition:all 250ms ease;
+}
+
+.reminder em{
+ display:block;
+ padding-left:50px;
+ transition:all 250ms ease;
+ font-style:normal;
+ font-size:12px;
+}
+
+.reminder{
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+ padding-bottom:20px;
+ padding-top:20px;
+}
+
+/*-----------------*/
+/*-----------------*/
+/*----Page Blog----*/
+/*-----------------*/
+/*-----------------*/
+
+@media(min-width:768px){
+ .page-blog{
+ width:60%;
+ float:left;
+ }
+
+ .page-blog-sidebar{
+ width:35%;
+ float:left;
+ margin-left:5%;
+ }
+}
+
+.page-blog-list p{
+ margin-bottom:20px
+}
+
+.page-blog-list img{
+ margin-bottom:20px;
+}
+
+.page-blog-list .page-blog-list-by{
+ width:200px;
+}
+
+.page-blog-list .page-blog-list-by img{
+ width:40px;
+ height:40px;
+ border-radius:40px;
+ position:absolute;
+ margin-top:4px;
+}
+
+.page-blog-list .page-blog-list-by strong{
+ padding-left:55px;
+ font-size:12px;
+ font-weight:500;
+ display:inline;
+}
+
+.page-blog-list .page-blog-list-by strong a{
+ display:inline;
+}
+
+.page-blog-list .page-blog-list-by em{
+ padding-left:55px;
+ font-size:11px;
+ margin-top:-11px;
+ display:block;
+ opacity:0.5;
+ margin-bottom:20px;
+ clear:both;
+}
+
+.page-blog-list .page-blog-tags{
+ display:block;
+ margin-top:-10px;
+ margin-bottom:10px;
+ font-size:12px;
+ font-weight:500;
+}
+
+.page-blog-list .page-blog-tags a{
+ display:inline;
+}
+
+.page-blog-list-more i{
+ float:right;
+ width:40px;
+ height:40px;
+ margin-top:-66px;
+ font-size:12px;
+ text-align:center;
+ line-height:40px;
+ color:#FFFFFF;
+ background-color:#2980b9;
+ border-radius:40px;
+ transition:all 200ms ease;
+}
+
+.page-blog-list-more i:hover{
+ background-color:#3498db;
+ transition:all 200ms ease;
+}
+
+.page-blog-list-share i{
+ margin-right:55px;
+ float:right;
+ width:40px;
+ height:40px;
+ margin-top:-66px;
+ font-size:12px;
+ text-align:center;
+ line-height:40px;
+ color:#FFFFFF;
+ background-color:#27ae60;
+ border-radius:40px;
+ transition:all 200ms ease;
+}
+
+.page-blog-list-share i:hover{
+ background-color:#2ecc71;
+ transition:all 200ms ease;
+}
+
+/*------------------------*/
+/*----Page Blog Sidebar---*/
+/*------------------------*/
+/*Search*/
+
+.blog-sidebar-search i{
+ position:absolute;
+ top:12px;
+}
+
+.blog-sidebar-search input{
+ height:40px;
+ width:100%;
+ background-color:transparent;
+ font-size:12px;
+ border-bottom:solid 2px rgba(0,0,0,0.2);
+ padding-left:30px;
+ transition:all 200ms ease;
+}
+
+.blog-sidebar-search input:focus{
+ border-bottom:solid 2px #27ae60;
+ transition:all 200ms ease;
+}
+
+.blog-sidebar-text p{
+ font-size:12px;
+ margin-bottom:20px;
+}
+
+/*Recent*/
+
+.blog-sidebar-recent-projects a{
+ width:30%;
+ float:left;
+ overflow:hidden;
+ margin-right:3%;
+ margin-bottom:3%;
+}
+
+.blog-sidebar-recent-projects a img{
+ display:block;
+ width:100%;
+}
+
+.blog-sidebar-recent-posts a:last-child{
+ border-bottom:none;
+ padding-bottom:0px;
+}
+
+.blog-sidebar-recent-posts a{
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+ padding-bottom:10px;
+ margin-bottom:10px;
+}
+
+.blog-sidebar-recent-posts strong{
+ display:block;
+ color:#1f1f1f
+}
+
+.blog-sidebar-recent-posts em{
+ display:block;
+ font-size:10px;
+ margin-top:-10px;
+ color:rgba(0,0,0,0.5);
+ font-style:normal;
+ font-weight:600;
+}
+
+.blog-sidebar-recent-posts i{
+ position:absolute;
+ top:0px;
+ right:0px;
+ width:40px;
+ height:40px;
+ line-height:44px;
+ text-align:center;
+ font-size:20px;
+ color:#1f1f1f;
+}
+
+/*Categories*/
+
+.blog-sidebar-categories a:last-child{
+ border-bottom:none;
+ padding-bottom:0px;
+}
+
+.blog-sidebar-categories a{
+ font-size:12px;
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+ padding-top:5px;
+ padding-bottom:5px;
+ color:#1f1f1f;
+ font-weight:500;
+}
+
+.blog-sidebar-categories a em{
+ font-size:10px;
+ background-color:#e74c3c;
+ color:#FFFFFF;
+ padding-left:6px;
+ padding-right:6px;
+ padding-top:2px;
+ padding-bottom:2px;
+ margin-left:10px;
+ font-style:normal;
+ border-radius:3px;
+}
+
+.blog-sidebar-categories a i{
+ position:absolute;
+ right:0px;
+ top:18px;
+}
+
+/*Blog Comments*/
+
+.blog-post-comment-nested{
+ padding-left:50px;
+}
+
+.blog-post-comment em{
+ display:block;
+ font-size:12px;
+}
+
+.blog-post-comment strong{
+ font-size:15px;
+ color:#1f1f1f;
+}
+
+.blog-post-comment .blog-post-comment-reply{
+ text-align:right;
+ width:40px;
+ float:right;
+ margin-top:-25px;
+ font-size:12px;
+}
+
+.blog-post-comment-add strong{
+ display:block;
+}
+
+.blog-post-comment-add em{
+ display:block;
+ text-align:right;
+ font-size:10px;
+ margin-top:-30px;
+}
+
+.blog-post-comment-add input:focus{
+ border-bottom:solid 2px #27ae60;
+ transition:all 200ms ease;
+}
+
+.blog-post-comment-add input{
+ display:block;
+ width:100%;
+ height:40px;
+ font-size:12px;
+ border-bottom:solid 2px rgba(0,0,0,0.1);
+ margin-bottom:20px;
+ transition:all 200ms ease;
+}
+
+.blog-post-comment-add textarea{
+ overflow:visible;
+ font-size:12px;
+ line-height:30px;
+ display:block;
+ width:100%;
+ max-height:10000px;
+ border-bottom:solid 2px rgba(0,0,0,0.1);
+ transition:all 200ms ease;
+}
+
+.blog-post-comment-add textarea:focus{
+ border-bottom:solid 2px #27ae60;
+ transition:all 200ms ease;
+}
+
+/*-----------------------------*/
+/*----Image Slider Settings----*/
+/*-----------------------------*/
+
+/*----------------------*/
+/*---Coverpage Slider---*/
+/*----------------------*/
+
+.coverpage-slider{
+ margin-bottom:0px;
+}
+
+.coverpage-slider .owl-dots{
+ margin-top:-50px;
+ opacity:0.5;
+}
+
+.coverpage-slider .owl-dots .active{
+ opacity:1;
+}
+
+.cover-screen .overlay{opacity:0.8;}
+.cover-center{ position:absolute; top:50%; left:50%; position:absolute; z-index:10; width:300px;}
+.cover-left{ position:absolute; top:50%; left:30px; position:absolute; z-index:10;}
+.cover-right{ position:absolute; top:50%; right:30px; position:absolute; z-index:10;}
+
+.cover-slide h1{
+ font-size:40px;
+ line-height:50px;
+ padding-bottom:10px;
+}
+
+.cover-slide h2{
+ font-size:32px;
+ padding-bottom:10px;
+}
+
+.cover-slide h3{
+ font-size:28px;
+ padding-bottom:10px;
+}
+
+.cover-slide h4{
+ font-size:24px;
+ padding-bottom:10px;
+}
+
+.cover-slide h5{
+ font-size:20px;
+ padding-bottom:10px;
+}
+
+.cover-slide h6{
+ font-size:16px;
+ padding-bottom:10px;
+}
+
+.cover-slide h1,
+.cover-slide h2,
+.cover-slide h3,
+.cover-slide h4,
+.cover-slide h5,
+.cover-slide h6{
+ color:#FFFFFF;
+}
+
+.cover-slide p{
+ font-size:15px;
+ color:#b2b2b2;
+}
+
+.cover-icon-center{
+ font-size:24px!important;
+ color:#FFFFFF;
+ width:70px;
+ height:70px;
+ line-height:68px!important;
+ text-align:center;
+ margin-left:auto!important;
+ margin-right:auto!important;
+ display:block!important;
+ margin-bottom:50px!important
+}
+.cover-icon-right{}
+.cover-icon-left{}
+.cover-icon-round{border-radius:100%; border:solid 1px rgba(255,255,255,0.5);}
+
+/*----------------------*/
+/*----Homepage Slider---*/
+/*----------------------*/
+
+.demo-slider{
+ margin-bottom:50px;
+}
+
+
+.homepage-slider .owl-dots{
+ margin-top:20px;
+}
+
+.homepage-slider .overlay{
+ position:absolute;
+ top:0px;
+ left:0px;
+ width:100%;
+ height:100%;
+ background: linear-gradient( to bottom,
+ rgba(0, 0, 0, 0.01),
+ rgba(0, 0, 0, 0.7)
+ );
+}
+
+
+.homepage-slider h5{
+ text-shadow:0px 1px 0px #000000;
+ font-size:19px;
+ z-index:2;
+ position:absolute;
+ color:#FFFFFF;
+ bottom:40px;
+ padding-left:30px;
+ padding-right:30px;
+ display:block;
+ width:100%;
+}
+
+.homepage-slider p{
+ text-shadow:0px 1px 0px #000000;
+ font-size:12px;
+ z-index:2;
+ position:absolute;
+ color:#ededed;
+ bottom:-10px;
+ padding-left:30px;
+ padding-right:30px;
+ display:block;
+ width:100%;
+}
+
+/*------------------*/
+/*---Staff Slider---*/
+/*------------------*/
+
+.staff-slider img{
+ margin-top:8px;
+ width:150px!important;
+ height:150px!important;
+ border-radius:150px;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.staff-slider h3{
+ text-align:center;
+ margin-top:20px;
+}
+
+.staff-slider em{
+ display:block;
+ text-align:center;
+ margin-top:-10px;
+ margin-bottom:10px;
+}
+
+.staff-slider p{
+ text-align:center;
+ padding-left:20px;
+ padding-right:20px;
+ margin-bottom:20px;
+}
+
+.staff-icons a{
+ margin-left:6px;
+ margin-right:6px;
+ margin-bottom:30px;
+}
+
+.staff-icons-three{
+ width:180px;
+ margin:0 auto;
+}
+
+.staff-icons-two{
+ width:120px;
+ margin:0 auto;
+}
+
+.staff-icons-one{
+ width:60px;
+ margin:0 auto;
+}
+
+
+@media (min-width:0px) and (max-width:340px){/*Small Devices*/
+ .next-home-slider,
+ .prev-home-slider,
+ .next-staff-slider,
+ .prev-staff-slider,
+ .next-quote-slider,
+ .prev-quote-slider{
+ top:41%;
+ }
+}
+
+@media (min-width:340px) and (max-width:360px){/*Medium Devices*/
+ .next-home-slider,
+ .prev-home-slider,
+ .next-staff-slider,
+ .prev-staff-slider,
+ .next-quote-slider,
+ .prev-quote-slider{
+ top:43%;
+ }
+}
+
+@media (min-width:360px) and (max-width:568px){/*Phablet Devices*/
+ .next-home-slider,
+ .prev-home-slider,
+ .next-staff-slider,
+ .prev-staff-slider,
+ .next-quote-slider,
+ .prev-quote-slider{
+ top:47%;
+ }
+}
+
+@media (min-width:568px) and (max-width:768px){/*Phablet & Small Tablet Devices*/
+ .next-home-slider,
+ .prev-home-slider,
+ .next-staff-slider,
+ .prev-staff-slider,
+ .next-quote-slider,
+ .prev-quote-slider{
+ top:41%;
+ }
+}
+
+@media(min-width:768px){ /*Tablet in Landscape Devices*/
+ .next-home-slider,
+ .prev-home-slider,
+ .next-staff-slider,
+ .prev-staff-slider,
+ .next-quote-slider,
+ .prev-quote-slider{
+ top:45%;
+ }
+ .quote-slider{
+ padding-left:100px!important;
+ padding-right:100px!important;
+ }
+}
+
+.next-home-slider{
+ border-radius:50px;
+ margin-top:-20px;
+ width:50px;
+ height:50px;
+ right:10px;
+ color:#FFFFFF;
+ position:absolute;
+ z-index:9;
+ transition:all 200ms ease;
+}
+
+.next-home-slider i{
+ font-size:16px;
+ width:50px;
+ height:50px;
+ line-height:50px;
+ text-align:center;
+ text-shadow:0px 2px 0px #000000;
+}
+
+.next-home-slider:hover{
+ background-color:rgba(0,0,0,0.3);
+ border-radius:50px;
+ transition:all 200ms ease;
+}
+
+.prev-home-slider:hover{
+ background-color:rgba(0,0,0,0.3);
+ border-radius:50px;
+ transition:all 200ms ease;
+}
+
+.prev-home-slider{
+ border-radius:50px;
+ margin-top:-20px;
+ width:50px;
+ height:50px;
+ left:10px;
+ color:#FFFFFF;
+ position:absolute;
+ z-index:9;
+ transition:all 200ms ease;
+}
+
+.prev-home-slider i{
+ font-size:16px;
+ width:50px;
+ height:50px;
+ line-height:50px;
+ text-align:center;
+ text-shadow:0px 2px 0px #000000;
+}
+
+.next-staff-slider, .next-quote-slider{
+ margin-top:-25px;
+ right:0px;
+ position:absolute;
+ z-index:9;
+ width:50px;
+ height:50px;
+ line-height:50px;
+ border-radius:50px;
+ text-align:center;
+ color:#1f1f1f;
+ transition:all 200ms ease;
+}
+
+.next-staff-slider:hover, .next-quote-slider:hover{
+ background-color:rgba(0,0,0,0.1);
+ transition:all 200ms ease;
+}
+
+.next-staff-slider i, .next-quote-slider i{
+ font-size:16px;
+ width:50px;
+ height:50px;
+ line-height:50px;
+ text-align:center;
+}
+
+.prev-staff-slider, .prev-quote-slider{
+ margin-top:-25px;
+ left:0px;
+ position:absolute;
+ z-index:9;
+ width:50px;
+ height:50px;
+ border-radius:50px;
+ line-height:50px;
+ text-align:center;
+ color:#1f1f1f;
+ transition:all 200ms ease;
+}
+
+.prev-staff-slider:hover, .prev-quote-slider:hover{
+ background-color:rgba(0,0,0,0.1);
+ transition:all 200ms ease;
+}
+
+.prev-staff-slider i, .prev-quote-slider i{
+ font-size:16px;
+ width:50px;
+ height:50px;
+ line-height:50px;
+ text-align:center;
+}
+
+/*------------------*/
+/*---Quote Slider---*/
+/*------------------*/
+
+.quote-slider{
+ padding-left:30px;
+ padding-right:30px;
+}
+
+
+
+
+
+
+
+
+
+
+/*This section is vital to functions*/
+/* Main Content Class that houses all elements */
+
+.all-elements{
+ position:absolute;
+ overflow-x:hidden!important;
+ width:100%;
+ height:100%;
+ min-height:100%;
+ max-height:100%;
+ -webkit-overflow-scrolling: touch;
+}
+
+/*---------CSS Plugins----------*/
+/*------------------------------*/
+/*------------------------------*/
+/*---Sidebar Structure Plugin---*/
+/*------------------------------*/
+/*------------------------------*/
+
+.snap-content, #content {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: auto;
+ height: auto;
+ z-index: 2;
+ overflow: auto;
+ -webkit-overflow-scrolling: touch;
+ -webkit-transform: translate3d(0, 0, 0);
+ -moz-transform: translate3d(0, 0, 0);
+ -ms-transform: translate3d(0, 0, 0);
+ -o-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+}
+
+.snap-drawers {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: auto;
+ height: auto;
+ overflow-x:hidden;
+}
+
+
+.snap-drawer {
+ position: absolute;
+ top: 0;
+ right: auto;
+ bottom: 0;
+ left: auto;
+ width: 275px;
+ height: auto;
+ overflow: auto;
+ -webkit-overflow-scrolling: touch;
+ -webkit-transition: width 0.25s ease;
+ -moz-transition: width 0.25s ease;
+ -ms-transition: width 0.25s ease;
+ -o-transition: width 0.25s ease;
+ transition: width 0.25s ease;
+}
+
+.snap-drawer-left {
+ left: 0;
+ z-index: 1;
+}
+
+.snap-drawer-right {
+ right: 0;
+ z-index: 1;
+}
+
+.snapjs-left .snap-drawer-right,
+.snapjs-right .snap-drawer-left {
+ display: none;
+}
+
+.snapjs-expand-left .snap-drawer-left,
+.snapjs-expand-right .snap-drawer-right {
+ width: 100%;
+}
+
+.snapjs-right .snap-drawer-left {
+ display: block;
+ right: 0;
+ left: auto;
+}
+
+.snapjs-right .snap-drawer-right {
+ display: block !important;
+ right: 0 !important;
+ left: auto !important;
+}
+
+.snap-content {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: auto;
+ height: auto;
+ z-index: 2;
+ overflow: auto;
+ -webkit-overflow-scrolling: touch;
+ -webkit-transform: translate3d(0, 0, 0);
+ -moz-transform: translate3d(0, 0, 0);
+ -ms-transform: translate3d(0, 0, 0);
+ -o-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+}
+
+.snap-drawers {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: auto;
+ height: auto;
+}
+
+.snap-drawer {
+ position: absolute;
+ top: 0;
+ right: auto;
+ bottom: 0;
+ left: auto;
+ width: 286px;
+ height: auto;
+ overflow: auto;
+ -webkit-overflow-scrolling: touch;
+ -webkit-transition: width 0.25s ease;
+ -moz-transition: width 0.25s ease;
+ -ms-transition: width 0.25s ease;
+ -o-transition: width 0.25s ease;
+ transition: width 0.25s ease;
+}
+
+.snap-drawer-left {
+ left: 0;
+ z-index: 1;
+}
+
+.snap-drawer-right {
+ right: 0;
+ z-index: 1;
+ padding-left:20px;
+}
+
+.snapjs-left .snap-drawer-right,
+.snapjs-right .snap-drawer-left {
+ display: none;
+}
+
+.snapjs-expand-left .snap-drawer-left,
+.snapjs-expand-right .snap-drawer-right {
+ width: 100%;
+}
+
+.snapjs-right .snap-drawer-right {
+ display: none;
+}
+
+#content {
+ background-color:#FFFFFF;
+ overflow-x:hidden;
+}
+
+.drawer-inner {
+ -webkit-transition: opacity 0.25s ease;
+ -moz-transition: opacity 0.25s ease;
+ -ms-transition: opacity 0.25s ease;
+ -o-transition: opacity 0.25s ease;
+ transition: opacity 0.25s ease;
+}
+
+.snapjs-expand-left .drawer-inner,
+.snapjs-expand-right .drawer-inner {
+ opacity: 0;
+}
+
+.snap-drawers {
+ background-color:#1f1f1f;
+ background-size:100px 100px;
+}
+
+.snap-drawer {
+ background-color:#1f1f1f;
+ background-size:100px 100px;
+}
+
+.no-user-select{
+ -webkit-user-select: none; /* Chrome all / Safari all */
+ -moz-user-select: none; /* Firefox all */
+ -ms-user-select: none; /* IE 10+ */
+ -o-user-select: none;
+ user-select: none;
+}
+
+/*------------------------------*/
+/*------------------------------*/
+/*---Sliders and OWL Carousel---*/
+/*------------------------------*/
+/*------------------------------*/
+
+/*Fix Jumping Image*/
+
+.owl-item{
+ transform: translate3d(0, 0, 0)!important;
+}
+
+.owl-carousel .animated {
+ -webkit-animation-duration: 1000ms;
+ animation-duration: 1000ms;
+ -webkit-animation-fill-mode: both;
+ animation-fill-mode: both;
+}
+
+.owl-carousel .owl-animated-in {
+ z-index: 0;
+}
+
+.owl-carousel .owl-animated-out {
+ z-index: 1;
+}
+
+.owl-carousel .fadeOut {
+ -webkit-animation-name: fadeOut;
+ animation-name: fadeOut;
+}
+
+@-webkit-keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+/* Owl Carousel - Auto Height Plugin */
+.owl-height {
+ -webkit-transition: height 500ms ease-in-out;
+ -moz-transition: height 500ms ease-in-out;
+ -ms-transition: height 500ms ease-in-out;
+ -o-transition: height 500ms ease-in-out;
+ transition: height 500ms ease-in-out;
+}
+
+/* Core Owl Carousel CSS File */
+.owl-carousel {
+ display: none;
+ width: 100%;
+ -webkit-tap-highlight-color: transparent;
+ /* position relative and z-index fix webkit rendering fonts issue */
+ position: relative;
+ z-index: 1;
+}
+
+.owl-carousel .owl-stage {
+ position: relative;
+ -ms-touch-action: pan-Y;
+}
+
+.owl-carousel .owl-stage:after {
+ content: ".";
+ display: block;
+ clear: both;
+ visibility: hidden;
+ line-height: 0;
+ height: 0;
+}
+
+.owl-carousel .owl-stage-outer {
+ position: relative;
+ overflow: hidden;
+ /* fix for flashing background */
+ -webkit-transform: translate3d(0px, 0px, 0px);
+}
+
+.owl-carousel .owl-controls .owl-nav .owl-prev,
+.owl-carousel .owl-controls .owl-nav .owl-next,
+.owl-carousel .owl-controls .owl-dot {
+ cursor: pointer;
+ cursor: hand;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.owl-dots{
+ display:inline-block!important;
+}
+
+.owl-dot{
+ background-color:#bdc3c7;
+ margin-top:5px;
+ border-radius:13px;
+ height:14px;
+ width:14px;
+ float:left;
+ margin-left:5px;
+ margin-right:5px;
+}
+
+.owl-dots .active{
+ background-color:#7f8c8d!important;
+}
+
+.owl-carousel.owl-loaded {
+ display: block;
+}
+
+.owl-carousel.owl-loading {
+ opacity: 0;
+ display: block;
+}
+
+.owl-carousel.owl-hidden {
+ opacity: 0;
+}
+
+.owl-carousel .owl-refresh .owl-item {
+ display: none;
+}
+
+.owl-carousel .owl-item {
+ position: relative;
+ min-height: 1px;
+ float: left;
+ -webkit-backface-visibility: hidden;
+ -webkit-tap-highlight-color: transparent;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.owl-carousel .owl-item img {
+ display: block;
+ width: 100%;
+ -webkit-transform-style: preserve-3d;
+}
+
+.owl-carousel.owl-text-select-on .owl-item {
+ -webkit-user-select: auto;
+ -moz-user-select: auto;
+ -ms-user-select: auto;
+ user-select: auto;
+}
+
+.owl-carousel .owl-grab {
+ cursor: move;
+ cursor: -webkit-grab;
+ cursor: -o-grab;
+ cursor: -ms-grab;
+ cursor: grab;
+}
+
+.owl-carousel.owl-rtl {
+ direction: rtl;
+}
+
+.owl-carousel.owl-rtl .owl-item {
+ float: right;
+}
+
+/* No Js */
+.no-js .owl-carousel {
+ display: block;
+}
+
+/*Owl Carousel - Lazy Load Plugin*/
+.owl-carousel .owl-item .owl-lazy {
+ opacity: 0;
+ -webkit-transition: opacity 400ms ease;
+ -moz-transition: opacity 400ms ease;
+ -ms-transition: opacity 400ms ease;
+ -o-transition: opacity 400ms ease;
+ transition: opacity 400ms ease;
+}
+.owl-carousel .owl-item img {
+ transform-style: preserve-3d;
+}
+
+/*Owl Carousel - Video Plugin */
+.owl-carousel .owl-video-wrapper {
+ position: relative;
+ height: 100%;
+ background: #000;
+}
+
+.owl-carousel .owl-video-play-icon {
+ position: absolute;
+ height: 80px;
+ width: 80px;
+ left: 50%;
+ top: 50%;
+ margin-left: -40px;
+ margin-top: -40px;
+ background: url("http://www.enableds.com/products/panomobile/styles/owl.video.play.png") no-repeat;
+ cursor: pointer;
+ z-index: 1;
+ -webkit-backface-visibility: hidden;
+ -webkit-transition: scale 100ms ease;
+ -moz-transition: scale 100ms ease;
+ -ms-transition: scale 100ms ease;
+ -o-transition: scale 100ms ease;
+ transition: scale 100ms ease;
+}
+
+.owl-carousel .owl-video-play-icon:hover {
+ -webkit-transition: scale(1.3, 1.3);
+ -moz-transition: scale(1.3, 1.3);
+ -ms-transition: scale(1.3, 1.3);
+ -o-transition: scale(1.3, 1.3);
+ transition: scale(1.3, 1.3);
+}
+
+.owl-carousel .owl-video-playing .owl-video-tn,
+.owl-carousel .owl-video-playing .owl-video-play-icon {
+ display: none;
+}
+
+.owl-carousel .owl-video-tn {
+ opacity: 0;
+ height: 100%;
+ background-position: center center;
+ background-repeat: no-repeat;
+ -webkit-background-size: contain;
+ -moz-background-size: contain;
+ -o-background-size: contain;
+ background-size: contain;
+ -webkit-transition: opacity 400ms ease;
+ -moz-transition: opacity 400ms ease;
+ -ms-transition: opacity 400ms ease;
+ -o-transition: opacity 400ms ease;
+ transition: opacity 400ms ease;
+}
+
+.owl-carousel .owl-video-frame {
+ position: relative;
+ z-index: 1;
+}
+
+/*------------------------------*/
+/*------------------------------*/
+/*----Swipebox Touch Gallery----*/
+/*------------------------------*/
+/*------------------------------*/
+
+.gallery-fix{
+ display:none;
+ background-color:rgba(255,255,255,0);
+ position:fixed;
+ width:100%;
+ height:100%;
+ top:0px;
+ left:0px;
+ bottom:0px;
+ right:0px;
+ z-index:99999!important
+}
+
+@media (min-width:768px) and (orientation:landscape){
+ .gallery li { list-style: none!important; float: left; width: 16.5%; transition:all 200ms ease; }
+ .gallery{margin-left:-1%!important;}
+}
+
+@media (min-width:768px) and (orientation:portrait){
+ .gallery li { list-style: none!important; float: left; width: 25%; transition:all 200ms ease; }
+ .gallery{margin-left:-1%!important;}
+}
+
+.square-thumb li img{
+ border-radius:0px!important;
+}
+
+.round-thumb li img{
+ border-radius:500px;
+}
+
+.gallery li a{
+ font-size:12px;
+ text-align:center;
+ color:#1f1f1f;
+}
+
+.swipebox {
+ overflow: hidden!important;
+}
+
+.swipebox div{
+ overflow:visible!important;
+}
+
+#swipebox-overlay img {
+ border: none!important;
+}
+
+#swipebox-overlay {
+ width: 100%;
+ height: 100%;
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 999999!important;
+ overflow: hidden;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+}
+
+#swipebox-slider {
+ height: 100%;
+ left: 0;
+ top: 0;
+ width: 100%;
+ white-space: nowrap;
+ position: absolute;
+ display: none;
+}
+
+#swipebox-slider .slide {
+ background: url("http://www.enableds.com/products/panomobile/images/gallery/loader.gif") no-repeat center center;
+ height: 100%;
+ width: 100%;
+ line-height: 1px;
+ text-align: center;
+ display: inline-block;
+}
+
+#swipebox-slider .slide:before {
+ content: "";
+ display: inline-block;
+ height: 50%;
+ width: 1px;
+ margin-right: -1px;
+}
+
+#swipebox-slider .slide img {
+ display: inline-block;
+ max-height: 100%;
+ max-width: 100%;
+ margin: 0;
+ padding: 0;
+ width: auto;
+ height: auto;
+ vertical-align: middle;
+}
+
+#swipebox-action, #swipebox-caption {
+ position: absolute;
+ left: 0;
+ z-index: 999;
+ height: 50px;
+ width: 100%;
+}
+
+#swipebox-action {
+ bottom: -50px;
+}
+
+#swipebox-action.visible-bars {
+ bottom: 0;
+}
+
+#swipebox-action.force-visible-bars {
+ bottom: 0!important;
+}
+
+#swipebox-caption {
+ top: -50px;
+ text-align: center;
+}
+#swipebox-caption.visible-bars {
+ top: 0;
+}
+
+#swipebox-caption.force-visible-bars {
+ top: 0!important;
+}
+
+#swipebox-action #swipebox-prev, #swipebox-action #swipebox-next,
+#swipebox-action #swipebox-close {
+ background-image: url("../images/framework/icons.png");
+ background-repeat: no-repeat;
+ border: none!important;
+ text-decoration: none!important;
+ cursor: pointer;
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ top: 0;
+}
+
+#swipebox-action #swipebox-close {
+ background-position: 15px 12px;
+ left: 40px;
+}
+
+#swipebox-action #swipebox-prev {
+ background-position: -32px 13px;
+ right: 100px;
+}
+
+#swipebox-action #swipebox-next {
+ background-position: -78px 13px;
+ right: 40px;
+}
+
+#swipebox-action #swipebox-prev.disabled,
+#swipebox-action #swipebox-next.disabled {
+ filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30);
+ opacity: 0.3;
+}
+
+#swipebox-slider.rightSpring {
+ -moz-animation: rightSpring 0.3s;
+ -webkit-animation: rightSpring 0.3s;
+}
+
+#swipebox-slider.leftSpring {
+ -moz-animation: leftSpring 0.3s;
+ -webkit-animation: leftSpring 0.3s;
+}
+
+@-moz-keyframes rightSpring {
+ 0% { margin-left: 0px; }
+ 50% { margin-left: -30px; }
+ 100% { margin-left: 0px; }
+}
+
+@-moz-keyframes leftSpring {
+ 0% { margin-left: 0px; }
+ 50% { margin-left: 30px; }
+ 100% { margin-left: 0px; }
+}
+
+@-webkit-keyframes rightSpring {
+ 0% { margin-left: 0px; }
+ 50% { margin-left: -30px; }
+ 100% { margin-left: 0px; }
+}
+
+@-webkit-keyframes leftSpring {
+ 0% { margin-left: 0px; }
+ 50% { margin-left: 30px; }
+ 100% { margin-left: 0px; }
+}
+
+/* Skin*/
+#swipebox-overlay {
+ background: #0d0d0d;
+ opacity:1;
+}
+
+#swipebox-action, #swipebox-caption {
+ text-shadow: 1px 1px 1px black;
+ background-color: #0d0d0d;
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #0d0d0d), color-stop(100%, #000000));
+ background-image: -webkit-linear-gradient(#0d0d0d, #000000);
+ background-image: -moz-linear-gradient(#0d0d0d, #000000);
+ background-image: -o-linear-gradient(#0d0d0d, #000000);
+ background-image: linear-gradient(#0d0d0d, #000000);
+ -webkit-box-shadow: 0 1px 1px 1px #212121, inset 0 1px 1px 1px black;
+ -moz-box-shadow: 0 1px 1px 1px #212121, inset 0 1px 1px 1px black;
+ box-shadow: 0 1px 1px 1px #212121, inset 0 1px 1px 1px black;
+ filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=95);
+ opacity: 1;
+}
+
+#swipebox-action {
+ -webkit-box-shadow: 0 -1px -1px 1px #212121, inset 0 -1px -1px 1px black;
+ -moz-box-shadow: 0 -1px -1px 1px #212121, inset 0 -1px -1px 1px black;
+ box-shadow: 0 -1px -1px 1px #212121, inset 0 -1px -1px 1px black;
+}
+
+#swipebox-caption {
+ color: white!important;
+ font-size: 12px;
+ line-height: 50px;
+ font-family: 'Roboto', sans-serif;
+}
+
+/*-----------------------------*/
+/*-----------------------------*/
+/*---iOS/Android Add To Home---*/
+/*-----------------------------*/
+/*-----------------------------*/
+
+.ath-viewport * {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.ath-viewport {
+ position: relative;
+ z-index: 2147483641;
+ pointer-events: none;
+
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ -webkit-text-size-adjust: none;
+ -moz-text-size-adjust: none;
+ -ms-text-size-adjust: none;
+ -o-text-size-adjust: none;
+ text-size-adjust: none;
+}
+
+.ath-modal {
+ pointer-events: auto !important;
+ background: rgba(0,0,0,0.6);
+}
+
+.ath-mandatory {
+ background: #000;
+}
+
+.ath-container {
+ pointer-events: auto !important;
+ position: absolute;
+ z-index: 2147483641;
+ padding: 0.7em 0.6em;
+ width: 18em;
+
+ background: #eee;
+ background-size: 100% auto;
+
+ box-shadow: 0 0.2em 0 #d1d1d1;
+
+ font-family: sans-serif;
+ font-size: 15px;
+ line-height: 1.5em;
+ text-align: center;
+}
+
+.ath-container small {
+ font-size: 0.8em;
+ line-height: 1.3em;
+ display: block;
+ margin-top: 0.5em;
+}
+
+.ath-ios.ath-phone {
+ bottom: 1.8em;
+ left: 50%;
+ margin-left: -9em;
+}
+
+.ath-ios6.ath-tablet {
+ left: 5em;
+ top: 1.8em;
+}
+
+.ath-ios7.ath-tablet {
+ left: 0.7em;
+ top: 1.8em;
+}
+
+.ath-ios8.ath-tablet {
+ right: 0.4em;
+ top: 1.8em;
+}
+
+.ath-android {
+ bottom: 1.8em;
+ left: 50%;
+ margin-left: -9em;
+}
+
+/* close icon */
+.ath-container:before {
+ content: '';
+ position: relative;
+ display: block;
+ float: right;
+ margin: -0.7em -0.6em 0 0.5em;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIQAAACECAMAAABmmnOVAAAAdVBMVEUAAAA5OTkzMzM7Ozs3NzdBQUFAQEA/Pz8+Pj5BQUFAQEA/Pz8+Pj5BQUFAQEA/Pz9BQUE+Pj4/Pz8/Pz8+Pj4/Pz8/Pz8/Pz8+Pj4/Pz8+Pj4/Pz8/Pz8/Pz8/Pz8/Pz8+Pj4/Pz8/Pz8/Pz8/Pz9AQEA/Pz+fdCaPAAAAJnRSTlMACQoNDjM4OTo7PEFCQ0RFS6ytsbS1tru8vcTFxu7x8vX19vf4+C5yomAAAAJESURBVHgBvdzLTsJAGEfxr4C2KBcVkQsIDsK8/yPaqIsPzVlyzrKrX/5p0kkXEz81L23otc9NpIbbWia2YVLqdnhlqFlhGWpSDHe1aopsSIpRb8gK0dC3G30b9rVmhWZIimTICsvQtx/FsuYOrWHoDjX3Gu31gzJxdki934WrAIOsAIOsAIOiAMPhPsJTgKGN0BVsYIVsYIVpYIVpYIVpYIVpYIVpYIVpYIVpYIVlAIVgEBRs8BRs8BRs8BRs8BRs8BRs8BRs8BRTNmgKNngKNngKNngKNngKNhiKGxgiOlZoBlaYBlaYBlaYBlaYBlaYBlaYBlaYBlZIBlBMfQMrVAMr2KAqBENSHFHhGEABhi5CV6gGUKgGUKgGUKgGUFwuqgEUvoEVsoEVpoEUpgEUggF+gKTKY+h1fxSlC7/Z+RrxOQ3fcEoAPPHZBlaYBlaYBlaYBlZYBlYIhvLBCstw7PgM7hkiWOEZWGEaWGEaWGEaIsakEAysmHkGVpxmvoEVqoEVpoEVpoEVpoEVpoEVpoEVkoEVgkFQsEFSsEFQsGEcoSvY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnY4CnmbNAUT2c2WAo2eAo2eAo2eAo2eAo2eArNEPFACjZ4CjZ4CjZ4CjaIird/rBvFH6llNCvewdli1URWCIakSIZesUaDoFg36dKFWk9zCZDei3TtwmCj7pC22AwikiIZPEU29IpFNliKxa/hC9DFITjQPYhcAAAAAElFTkSuQmCC);
+ background-size: 20%;
+ background-repeat: no-repeat;
+ background-position: 50%;
+ width: 2.7em;
+ height: 2.7em;
+ text-align: center;
+ overflow: hidden;
+ color: #a33;
+ z-index: 2147483642;
+}
+
+.ath-container.ath-icon:before {
+ position: absolute;
+ top: 0;
+ right: 0;
+ margin: 0;
+ float: none;
+}
+
+.ath-mandatory .ath-container:before {
+ display: none;
+}
+
+.ath-container.ath-android:before {
+ float: left;
+ margin: -0.7em 0.5em 0 -0.6em;
+}
+
+.ath-container.ath-android.ath-icon:before {
+ position: absolute;
+ right: auto;
+ right:0px;
+ margin: 0;
+ float: none;
+}
+
+/* applied only if the application icon is shown */
+.ath-container.ath-icon {}
+
+.ath-action-icon {
+ display: inline-block;
+ vertical-align: middle;
+ background-position: 50%;
+ background-repeat: no-repeat;
+ text-indent: -9999em;
+ overflow: hidden;
+}
+
+.ath-ios7 .ath-action-icon,
+.ath-ios8 .ath-action-icon {
+ width: 1.6em;
+ height: 1.6em;
+ background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAACtCAYAAAB7l7tOAAAF6UlEQVR4AezZWWxUZRiH8VcQEdxZEFFiUZBFUCIa1ABBDARDcCciYGKMqTEGww3SOcNSAwQTjOBiiIpEhRjAhRgXRC8MFxojEhAFZUGttVhaoSxlaW3n8W3yXZxm6vTrOMM5Q98n+V9MMu1pvl++uZhKuypghu49KaaTWGdZSYoVN6VD95nMpLNYZ9XNbdQR2od2k88O3Gm6Bh0t7H0p5Vwp2Ax3ajpu2tYbciFWwkTFO63DY6+JcI4USFaSyYpWp8N7SVZJKR3EinkBk9JxvZFXxhnZSjBaoWp1ZL0ES8WKYXMZp0AndORgy8WKFe5Yf1zvvSBWDEpys2LU6MjD5kmEWQlGKsJRHXlcqUSQVcItEnDEA6gAb7LhjvD9WO6yIEfICQI5A1nzGCYB1T4og5bBiFcyv2f6ujYhl4iVxwKG6qp8MK55HsqPwK0rMr9v/yEo3uCPrJstVh5KMER30Aeh31Ioq0FrHfjXw9CYghnrvYFTuqfEymFzGSwBlT4ARYr7u+K6GLmCVGvAGg2NMG0d/sgJnpScZLjXSkC5z8H3eQ72/k24Q8NfzvwFyK4qtuJSZKaubRPyE/K/Mtx+EvCHL+7uasId1t10w0scz/RzSzYzAfgKV30D3LPaG7lRkR8RK4tKKJKAMp+D7r0EfmmOe0x3m2itAc/ZxBjgAt1mXHWKPPkdb+QGSTJdrDaU5EoJ2OtzwD0WwY7KNNzbRfMFFg24WPdtGHnS221Cflgsj56hjwTs8TnY7oq7/QDhjutGicsb2AVcovsO18l6uPPNNiE/JFaGAq7Q7fY50G4LYVtz3FrdaNGyBXbIl+q24DqhyHes9EaulwR3SwtZs+ktAT/7HORliru1gnCndONFyx44Dfn7MPLYN7yR6yTJZAllJeguAT/4HOBFz8I3ZWm4E0TLFbBD7qn7EVdtHYx53R9ZN0ksrZRuErDN5+AuLIWvm+Oe1k0ULdfADrmX7idcR0/DyBXeyCdlLuMMOGCBz4F1ng+f7yFcve5e0fIFHELeiav6BAx70Rt5p0yhY3u/wR0kyarW/uX35b403PtFyzewQ75ctwtXzSkY8WqruHslSV8RscrL6TJ1bcvfWJ0/HzbtIdw/ugdFyzdwOOAq3T6fmzxwGQ3vbmO8iFioIWqYSsHMj9M/ljfuTsOdItoZBXYBfXX7cVXVwvXLm/8+fU3lcdCqdEMNGBbgUmRmfQISQKd5sGEn4VK6YtEiAXYBA3QVuA4q8hCHrDcafR1ul65jewfuovsCl7vJrNlOuEbdo6JFCuwCrtb9hqusBu56Cw4cI1y1briIWEBn3Ue0XKPuMdGiBg4H9NdV0HJ/6QZLOEPmPN0GmpfSPS5arIBdwHUtIFfoBsl/ZsgfhHCfFi2WwC5goO4AmvanbqBkzJA76tboZokWa2AXMEi3RTdAvDLkDqJFAhzB32xFD2wZsGXA0WfAlgFbBmwZsGXAlgFbBpzk04JaKb0iA9ZnF9x5SQAFtRKKIgPWZxfaeRmwAZ/BGbAB37eaG6MCbnq2Aed5czYyKirgpmcbsAHHZAZswN0Wwo7KeG1fFf2jAm56dtzOQ42yB+65mDhWFBUwUETMUiMDNmADbp/APRaTAh6I2bpGCNw1bufRZJQ1cPdF/NueHZsgDEBBGLbMGoIu4AZu5gLOZeEaYmEXeznF3jRPyEv4frgJvvJe3qTefY0AAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwb8rwADBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgz4/sz1Nia/9hizA7zgklwy3RYwYMBzBRjw4bPjxAbAAizAAtwgwAIswAIswAIMGDBgARZgARZgAS4FWIAFWIAFWIABAwYswAIswAIswIUAC7AAC7AACzBgwIAFWIAFWIAFuBBgARZgARZgAQYMGPApQ99ZCdgWtzqwATbABtgAG2DbnxNb7zbRimsMLMACrDf2wMWI/WasfQAAAABJRU5ErkJggg==);
+ margin-top: -0.3em;
+ background-size: auto 100%;
+}
+
+.ath-ios6 .ath-action-icon {
+ width: 1.8em;
+ height: 1.8em;
+ background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJAAAAB0CAQAAADAmnOnAAAAAnNCSVQICFXsRgQAAAAJcEhZcwAAWwEAAFsBAXkZiFwAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAF4klEQVR4Ae3a/a+XdR3H8ec5HM45HDmKICoVohkZsxESRRCzcZM/2JKkdGR5MrSkleA0Pd00O4u5IVuNM2yYc6XSzCExU4oUNRPCJFdMUAhsYZpUGhscOHA4N8/WZzsL6HBxvofvdV3fa3yer//gsV3vH659KHzncBsJxUYhDzOEhCKQbORs+ip2wzgM+wvj+P9i35qAGLaHGcQSgKSTrxBLABJppZpYApCspoFYApBsZjSxBCD5OxOJJQBJG1cQSwCSLpqJJQCJ3MvgCGTinuSMCJS8LZwfgZL3FtMiUPIOcU0ESl4PLRHoRPsJtREoeRsYGYGS9yrvo6RmpbLaigWSfzOdErLs6+bLUMFA0sF1+QF1cz1UNlBYK9V5AHXyWSgEkKyiIWOgGh829Ki1lLcaxjCVK7mJRSxjBY+zgRf/u9pXcMB7jhEZAg32EUP3O6hMKOP5Iq2sZQeHMZXt5KKMgOpcY+iHVnFyjeQKlrCBdsxge5ieAVC9vzLUelI8H+A7bKIHM10H81IGGuKvDf1ggDxVTKOV1zG3/Yia1ICG+ltD32MgNTKfP2HuW0VDKkCNrjfUTOm9i6XswwrZJkaVHeh0f2fodkrtfO6jAytqrzG+rEDDfVG1x1sprZEs5RBW4PZxeT+Bbrf5hPu9arfzKaU6WjiAFbseWvoF1GW/6vYGSmkyW7Dit4xB5QHq9Br6Xx2t9GAhtp6zkoHsfNp1J9wX6H+jeR4LtJc4LxGopZZyNpN/YcG2mw9nBTSPLizgOmjKAujGgvJID3ekD7QYi7nGzkvmQtpA38Vi7iJf0TedlC7QTVjMfcY2QyvSBPpUMW/PIBfbo9pls1XpAX2EdizeznStob3OJpQO0DB2YfE21q2GtnghpAm0Gou3T9tm6BGHQppA12HRVt17eboNlydNoLHsx2JtmL801OYcQmkC/QKLtQt9ydBW3wNpA30ci7Ur3WdolUMhbaBqNhf/8qQJ9Hkszs5wjaH9XkUobaAqtmFRdoGbDb3sWMgG6DIs5852knO82RaXer+P+qyb3eWeo7ZNBrRZvm1otY2QFdBjeHIb6hTne49Put12+9ObMoDdYmfy5UkF6AK6cCCr9aM2u9IddptcOYCG+FNDB5xLKCugO7G01TndFp/xgAntdYvrfdwVLnORt3q9Vx25F27DUjbGPxr6qxMgW6Cd2N+d6wLXedA+6nKbK73Lr/pJxzusvE/wZrvX0FOOgGyBxmF/dprXutYOj6nNdS6xyYnWp/dGcaGdhr5vDWQN9E1MXrUzfcA2j2qPj/l1J1uT9iPOeh8w1O7nCGUN9HzyGZ7ndo9qp0ucanU2r1xH+wdDu5wIeQDVVx0+/kd1i697RNv8thdn+Qz4Uv9p6DeOhHyApmBfq3OBu+3Nfd7nVELZAX3Nw4ZarYG8gG7GY1dlk6/Zm3/2Rk8jlB1QvT82dNAmQjkBVf8Mj957fdrefM7ZVhPKEuidvmDob06CXIGGbsX/bZDf8KAhfdbJhLIGmuZuQ084HHIGatiLvRvrRkP6qldbBXkAzbfD0N0OhryBGqrEMOd50FC7d1hPKGugBh8ydMh5hPIGGouI1d5lj6F1vptQ9kDvcKOhN5wMlQH0QcRGnzC03yZCeQDN9G1D6xwBFQI07FI8x02GdjgB8gJqttPQcmuhYoAumzvG7YZWejrkA1TrPYYO+SVCFQO0aM4bqj0uJJQH0LluSP7PkyeQU9QOmyAvoBm+Zegpz4LKA/qYB/wE5AXUe3m81zqoRKAPOYWcuvP9dxvqcD6h7IAKkaNU3eUlHLcI9EzS5YlAi62h/zUy89QCqqKUmvgHywsJlEHnsQYxAvXVIJo5gIhnPhiBju1iNmLvLn85Ah1ZPYs5jBGo72awEzEC9dVwHqQHI9DxWoAYgSLQQKteGIESu/qhCJTYtT+PQBEoAkWgCBSBkotAEehUWwSKQBEoAkWg/BeBIlAEikARKAJFoFmealu4gVLy1Gt5dkARKAL9BzujPSurTmu/AAAAAElFTkSuQmCC);
+ margin-bottom: 0.4em;
+ background-size: 100% auto;
+}
+
+.ath-android .ath-action-icon {
+ width: 1.4em;
+ height: 1.5em;
+ background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAANlBMVEVmZmb///9mZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZW6fJrAAAAEXRSTlMAAAYHG21ub8fLz9DR8/T4+RrZ9owAAAB3SURBVHja7dNLDoAgDATQWv4gKve/rEajJOJiWLgg6WzpSyB0aHqHiNj6nL1lovb4C+hYzkSNAT7mryQFAVOeGAj4CjwEtgrWXpD/uZKtwEJApXt+Vn0flzRhgNiFZQkOXY0aADQZCOCPlsZJ46Rx0jhp3IiN2wGDHhxtldrlwQAAAABJRU5ErkJggg==);
+ background-size: 100% auto;
+}
+
+.ath-container p {
+ line-height:20px;
+ margin: 0;
+ padding: 0;
+ position: relative;
+ z-index: 2147483642;
+ text-shadow: 0 0.1em 0 #fff;
+ font-size: 12px;
+ padding-left:30px;
+ padding-right:30px;
+}
+
+.ath-container p small{
+ padding-top:10px;
+}
+
+.ath-ios.ath-phone:after {
+ content: '';
+ background: #eee;
+ position: absolute;
+ width: 1em;
+ height: 1em;
+ bottom: -0.4em;
+ left: 50%;
+ margin-left: -0.5em;
+ -webkit-transform: scaleX(0.9) rotate(45deg);
+ transform: scaleX(0.9) rotate(45deg);
+ box-shadow: 0.2em 0.2em 0 #d1d1d1;
+}
+
+.ath-ios.ath-tablet:after {
+ content: '';
+ background: #eee;
+ position: absolute;
+ width: 2em;
+ height: 2em;
+ top: -0.9em;
+ left: 50%;
+ margin-left: -1em;
+ -webkit-transform: scaleX(0.9) rotate(45deg);
+ transform: scaleX(0.9) rotate(45deg);
+ z-index: 2147483641;
+}
+
+.ath-application-icon {
+ margin-left:auto;
+ margin-right:auto;
+ position: relative;
+ padding: 0;
+ border: 0;
+ margin-top:10px;
+ margin-bottom:10px;
+ width:70px;
+ height:70px;
+ z-index: 2147483642;
+}
+
+.ath-container.ath-ios .ath-application-icon {
+ border-radius: 1em;
+ margin: 0 auto 0.4em auto;
+}
+
+@media only screen and (orientation: landscape) {
+ .ath-container.ath-phone {
+ width: 24em;
+ }
+
+ .ath-android.ath-phone {
+ margin-left: -12em;
+ }
+
+ .ath-ios.ath-phone {
+ margin-left: -12em;
+ }
+
+ .ath-ios6:after {
+ left: 39%;
+ }
+
+ .ath-ios8.ath-phone {
+ left: auto;
+ bottom: auto;
+ right: 0.4em;
+ top: 1.8em;
+ }
+
+ .ath-ios8.ath-phone:after {
+ bottom: auto;
+ top: -0.9em;
+ left: 68%;
+ z-index: 2147483641;
+ box-shadow: none;
+ }
+}
+
+
+
+
+/*Adjustments for Header*/
+
+.pageapp-signup-content .unboxed-layout,
+.pageapp-signup-content .boxed-layout,
+.pageapp-login-content .unboxed-layout,
+.pageapp-login-content .boxed-layout
+{
+ margin-top:-60px!important;
+}
+
+
+.countdown-content .unboxed-layout,
+.countdown-content .boxed-layout,
+.error-content .unboxed-layout,
+.error-content .unboxed-layout
+{
+ margin-top:-70px!important;
+}
+
+
+/* Servers Manger */
+
+.btn
+{
+ border: 0;
+ background-color: #2c3e50;
+ color: #fff;
+ outline: 0;
+ cursor: pointer;
+ display: inline-block;
+ text-align: center;
+ box-sizing: content-box;
+ padding: 5px 8px;
+ font-size: 10px;
+ text-transform: uppercase;
+ height: 15px;
+ line-height: 16px;
+ margin: 5px;
+}
+.btn-success
+{
+ background-color: #2abb67
+}
+.btn-error
+{
+ background-color: #c0392b
+}
+.btn-info
+{
+ background-color: #2980b9
+}
+.btn-blue
+{
+ background-color: #2980b9
+}
+.btn-red
+{
+ background-color: #c0392b
+}
+.btn-default
+{
+ background-color: #2c3e50
+}
+.btn-black
+{
+ background-color: #000
+}
+.btn-gray
+{
+ background-color: #ddd;
+ color: #222 !important
+}
+.server_console
+{
+ margin-top: 10px
+}
+.server_console pre
+{
+ width: 100%;
+ height: 350px;
+ overflow-y: scroll;
+ overflow-x: hidden;
+ white-space: pre-line;
+ line-height: 15px;
+ border-radius: 0;
+ padding: 5px;
+ margin-bottom: 10px;
+ background-color: #191919;
+ color: #FFF;
+ font-size: 12px
+}
+.server_console table, .server_console table td
+{
+ border: 0
+}
+#console_update
+{
+ font-size: 25px;
+ text-align: left
+}
+#console_update .fa-toggle-on
+{
+ color: #2abb67
+}
+#console_update .fa-toggle-off
+{
+ color: #c0392b
+}
+
+.text-left, .table-text-left td
+{
+ text-align: left
+}
+
+.mt5
+{
+ margin-top: 5px !important
+}
+.settings-select
+{
+ width: 80%;
+ margin-bottom: 0;
+ height: 30px;
+ padding-bottom: 0
+}
\ No newline at end of file
diff --git a/template/megp/css/style.css b/template/megp/css/style.css
new file mode 100644
index 0000000..03a0039
--- /dev/null
+++ b/template/megp/css/style.css
@@ -0,0 +1,977 @@
+img
+{
+ max-width: 100%
+}
+
+.ipas{
+ width:320px;
+}
+
+.disabled{
+ display:none!important;
+}
+
+.not-active{
+ opacity:0!important;
+ pointer-events: none!important;
+}
+
+.homepage-cover .overlay{
+ position:absolute;
+ top:0px;
+ left:0px;
+ width:100%;
+ height:100%;
+ background: linear-gradient( to bottom,
+ rgba(0, 0, 0, 0.04),
+ rgba(0, 0, 0, 0.95)
+ );
+}
+
+.homepage-cover{
+ background-size:cover;
+ background-position:center center;
+}
+
+.homepage-cover-bg-1{ background-image:url(../images/home-cover-1.jpg);}
+.homepage-cover-bg-2{ background-image:url(../images/home-cover-2.jpg);}
+.homepage-cover-bg-3{ background-image:url(../images/home-cover-3.jpg);}
+
+.homepage-cover h4{
+ position:absolute;
+ color:#FFFFFF;
+ font-size:21px;
+ line-height:28px;
+ font-weight:800;
+ text-transform: uppercase;
+ bottom:250px;
+ z-index:10;
+ padding-left:30px;
+ padding-right:30px;
+}
+
+.homepage-cover p{
+ position:absolute;
+ color:rgba(255,255,255,0.8);
+ z-index:10;
+ padding-left:30px;
+ padding-right:30px;
+ bottom:110px;
+ font-size:12px;
+}
+
+.homepage-cover-button{
+ position:absolute;
+ bottom:50px;
+ z-index:10;
+ width:85%;
+ left:50%;
+ margin-left:-42%;
+ text-align:center;
+ color:#FFFFFF;
+ font-weight:700;
+ padding-top:8px;
+ padding-bottom:8px;
+ text-transform:uppercase;
+ font-size:12px;
+}
+
+@media(min-width:340px) and (max-width:450px){
+ .homepage-cover h4{
+ font-size:23px;
+ }
+
+ .homepage-cover p{
+ font-size:14px;
+ }
+
+}
+
+@media (min-width:600px){
+ .homepage-cover h4{
+ max-width:360px;
+ }
+
+ .homepage-cover p{
+ max-width:360px;
+ }
+
+ .homepage-cover .homepage-cover-button{
+ max-width:360px;
+ left:0px;
+ margin-left:30px;
+ }
+}
+
+.header-clear{
+ height:60px;
+ display:block;
+}
+
+.header-clear-large{
+ height:90px;
+ display:block;
+}
+
+.header{
+ position:fixed;
+ width:100%;
+ z-index:9;
+}
+
+.header-elements{
+ position:absolute;
+ width:100%;
+ z-index:10000;
+ height:60px;
+ background-color:#1f1f1f;
+}
+
+.header .header-logo{
+ background-image:url(../images/logo-light.png);
+ width:112px;
+ height:13px;
+ background-size:112px 13px;
+ margin-top:23px;
+ margin-left:20px;
+ position:absolute;
+}
+
+.header .header-icon-1{
+ position:absolute;
+ width:50px;
+ height:60px;
+ line-height:56px;
+ text-align:center;
+ color:#FFFFFF;
+ font-size:12px;
+ right:0px;
+ top:0px;
+ border-left:dashed 1px rgba(255,255,255,0.05);
+}
+
+.header .header-icon-2{
+ position:absolute;
+ width:50px;
+ height:60px;
+ line-height:56px;
+ text-align:center;
+ color:#FFFFFF;
+ font-size:12px;
+ right:50px;
+ top:0px;
+ border-left:dashed 1px rgba(255,255,255,0.05);
+}
+
+.rotate-star{
+ color:#c0392b;
+ -moz-transform: rotate(180deg);
+ -webkit-transform: rotate(180deg);
+ -o-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+ transition:all 250ms ease;
+}
+
+.header-menu .fa-star{
+ transition:all 250ms ease;
+}
+
+.header .header-icon-3{
+ position:absolute;
+ width:50px;
+ height:50px;
+ line-height:47px;
+ margin-top:5px;
+ text-align:center;
+ color:#FFFFFF;
+ font-size:12px;
+ right:100px;
+ top:0px;
+ -moz-transform: rotate(0deg);
+ -webkit-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ transform: rotate(0deg);
+ transition:all 250ms ease;
+}
+
+
+
+.header-icon-3 {
+ -webkit-animation-name: blinker;
+ -webkit-animation-duration: 2s;
+ -webkit-animation-timing-function: linear;
+ -webkit-animation-iteration-count: 4;
+
+ -moz-animation-name: blinker;
+ -moz-animation-duration: 2s;
+ -moz-animation-timing-function: linear;
+ -moz-animation-iteration-count: 4;
+
+ animation-name: blinker;
+ animation-duration: 2s;
+ animation-timing-function: linear;
+ animation-iteration-count: 4;
+}
+
+@-moz-keyframes blinker {
+ 0% { opacity: 1.0; }
+ 50% { opacity: 0.2; }
+ 100% { opacity: 1.0; }
+}
+
+@-webkit-keyframes blinker {
+ 0% { opacity: 1.0; }
+ 50% { opacity: 0.2; }
+ 100% { opacity: 1.0; }
+}
+
+@keyframes blinker {
+ 0% { opacity: 1.0; }
+ 50% { opacity: 0.2; }
+ 100% { opacity: 1.0; }
+}
+
+.navigation{
+ position:fixed;
+ width:100%;
+ -webkit-transform:translateY(-480px);
+ -moz-transform:translateY(-480px);
+ -o-transform:translateY(-480px);
+ transform:translateY(-480px);
+ transition:all 400ms ease;
+ max-height:480px;
+ overflow:scroll;
+ overflow-x:hidden;
+}
+
+.navigation::-webkit-scrollbar {
+ display: none;
+}
+
+@media(min-width:768px){
+ .navigation{
+ width:300px;
+ right:0px;
+ background-color:#FFFFFF;
+ -webkit-box-shadow: 0px 3px 5px 2px rgba(0,0,0,0.8);
+ box-shadow: 0px 3px 5px 2px rgba(0,0,0,0.8);
+ }
+}
+
+.show-menu{
+ -webkit-transform:translateY(60px);
+ -moz-transform:translateY(60px);
+ -o-transform:translateY(60px);
+ transform:translateY(60px);
+ transition:all 200ms ease;
+}
+
+.navigation{
+ background-color:#FFFFFF;
+ -webkit-box-shadow: 0px 2px 5px 1px rgba(0,0,0,0.1);
+ box-shadow: 0px 2px 5px 1px rgba(0,0,0,0.1);
+}
+
+.navigation *{
+ transition:all 0ms ease!important;
+}
+
+
+.navigation-item{
+ line-height:50px;
+ padding-left:20px;
+ padding-right:15px;
+ border-bottom:solid 1px rgba(0,0,0,0.1);
+}
+
+.navigation-item i:first-child{
+ padding-right:20px;
+ width:30px;
+ text-align:left;
+ color:#666666;
+}
+
+.navigation-item em{
+ color:#494949;
+ font-style:normal;
+ font-size:12px;
+ font-weight:500;
+ transition:all 250ms ease;
+}
+
+.navigation-item i:last-child{
+ color:#494949;
+ float:right;
+ margin-top:22px;
+ font-size:6px;
+ margin-right:7px;
+}
+
+.navigation-item:hover{
+ background-color:rgba(0,0,0,0.03);
+ transition:all 250ms ease;
+}
+
+.navigation-item em:hover{
+ color:#000000;
+ transition:all 250ms ease;
+}
+
+.navigation-item-active i:first-child{color:#000000!important;}
+.navigation-item-active i:last-child{color:#2cc26b!important;}
+.navigation-item-active em{color:#000000; font-weight:700;}
+
+
+/*Sidebar*/
+
+/*Sidebar Header Left*/
+
+.sidebar-header-left{
+ height:60px;
+ border-bottom:solid 1px rgba(255,255,255,0.02);
+ background-color:#1f1f1f;
+}
+
+.sidebar-logo{
+ background-image:url(../images/logo-light.png);
+ width:112px;
+ height:13px;
+ background-size:112px 13px;
+ margin-top:23px;
+ margin-left:30px;
+ padding:0px!important;
+}
+
+.sidebar-header-left a:first-child{
+ float:left;
+}
+
+.sidebar-header-left a:last-child{
+ width:50px;
+ height:60px;
+ line-height:60px;
+ color:#ffffff;
+ position:absolute;
+ left:215px;
+ text-align:center;
+ border-left:dashed 1px rgba(255,255,255,0.05);
+ border-right:dashed 1px rgba(255,255,255,0.05);
+}
+
+.sidebar-header-left a i{width:54px height:60px; line-height:60px; text-align:center;}
+
+/*Sidebar Header Right*/
+
+.sidebar-header-right{
+ height:60px;
+ border-bottom:solid 1px rgba(255,255,255,0.02);
+ background-color:#1f1f1f;
+ padding-right:30px;
+}
+
+.sidebar-header-right a:first-child{
+ width:112px;
+ float:right;
+}
+
+.sidebar-header-right a:last-child{
+ width:50px;
+ height:60px;
+ line-height:60px;
+ color:#FFFFFF;
+ position:absolute;
+ left:0px;
+ text-align:center;
+ border-left:dashed 1px rgba(255,255,255,0.05);
+ border-right:dashed 1px rgba(255,255,255,0.05);
+}
+
+.sidebar-header-left a i{width:54px height:60px; line-height:60px; text-align:center;}
+
+/*Sidebar Menu*/
+
+.menu-item{
+ height:50px;
+ color:#6b757d;
+ transition:all 250ms ease;
+}
+
+.menu-item-active .fa-circle{
+ color:#2ecc71!important;
+ opacity:1!important;
+ font-size:6px!important;
+ margin-left:231px!important
+}
+
+.menu-item-active{
+ color:#FFFFFF;
+ transition:all 250ms ease;
+}
+
+.menu-item em:hover{
+ color:#FFFFFF;
+ transition:all 250ms ease;
+}
+
+.menu-item i:first-child{
+ position:absolute;
+ font-size:18px;
+ width:20px;
+ text-align:center;
+ top:50%;
+ margin-top:-8px;
+ margin-left:30px;
+}
+
+.menu-item i:last-child{
+ position:absolute;
+ font-size:5px;
+ margin-left:232px;
+ top:50%;
+ margin-top:-1px;
+ opacity:0.5;
+}
+
+.menu-item strong{
+ font-size:10px;
+ position:absolute;
+ left:214px;
+ color:rgba(255,255,255,0.5);
+ top:50%;
+ margin-top:-13px;
+ font-weight:500;
+ width:40px;
+ text-align:center;
+ transition:all 250ms ease;
+}
+
+.menu-item em{
+ font-family:'Roboto', sans-serif;
+ display:block;
+ line-height:52px;
+ font-style:normal;
+ padding-left:75px;
+ font-size:13px;
+ font-weight:500;
+ transition:all 250ms ease;
+}
+
+/*Submenu Items*/
+
+.submenu-item{
+ height:50px;
+ color:#6b757d;
+ transition:all 250ms ease;
+}
+
+.submenu-item em:hover{
+ color:#FFFFFF;
+ transition:all 250ms ease;
+}
+
+.submenu-item i:first-child{
+ position:absolute;
+ font-size:11px;
+ top:50%;
+ margin-top:-4px;
+ margin-left:75px;
+}
+
+.submenu-item i:last-child{
+ position:absolute;
+ font-size:5px;
+ margin-left:232px;
+ top:50%;
+ margin-top:-1px;
+ opacity:0.2;
+}
+
+.submenu-item em{
+ font-family:'Roboto', sans-serif;
+ display:block;
+ line-height:52px;
+ font-style:normal;
+ padding-left:100px;
+ font-size:12px;
+ font-weight:500;
+ transition:all 250ms ease;
+}
+
+.submenu-active{
+ display:block;
+ color:#FFFFFF;
+}
+
+.show-submenu{
+ max-height:52px!important;
+}
+
+.submenu{
+ overflow:hidden;
+ max-height: 0;
+ transition: max-height 0.25s ease-out;
+}
+
+.submenu-active{
+ max-height: 500px;
+ transition: max-height 0.25s ease-in;
+}
+
+.submenu-item-active{
+ display:block;
+ color:#FFFFFF;
+ transition:all 250ms ease;
+}
+
+.submenu-item-active .fa-circle{
+ color:#27ae60!important;
+ opacity:1!important;
+}
+
+.submenu-active strong{
+ color:#FFFFFF;
+ transition:all 250ms ease;
+}
+
+.snap-drawer, .snap-drawers{
+ /*background-color:#272c2e!important;*/
+ background-color:#1f1f1f!important;
+}
+
+.sidebar-menu{
+ margin-bottom:30px;
+}
+
+.sidebar-divider{
+ font-family:'Roboto', sans-serif;
+ margin-top:10px;
+ margin-bottom:20px;
+ padding-bottom:5px;
+ font-size:10px;
+ padding-left:30px;
+ font-weight:800;
+ text-transform: uppercase;
+ color:495254;
+ border-bottom:solid 1px rgba(255,255,255,0.02);
+}
+
+.sidebar-footer{
+ font-family:'Roboto', sans-serif;
+ margin-bottom:10px;
+ padding-top:10px;
+ font-size:10px;
+ padding-left:30px;
+ font-weight:800;
+ text-transform: uppercase;
+ color:495254;
+ border-top:solid 1px rgba(255,255,255,0.03);
+}
+
+/*Sidebar Contact Form*/
+
+.sidebar-form{
+ width:220px;
+ margin-left:20px;
+}
+
+.sidebar-form *{
+ transition:all 400ms ease!important;
+}
+
+.sidebar-form .button{
+ letter-spacing:normal;
+ font-weight:600;
+ font-family:'Open Sans', sans-serif;
+}
+
+.sidebar-form em{
+ font-size:12px;
+ font-style:normal;
+ margin-bottom:20px;
+ display:block;
+ width:100%;
+ padding:0px!important;
+}
+
+.sidebar-form .contactField{
+ font-family:'Roboto', sans-serif;
+ height:40px!important;
+ line-height:40px!important;
+ background-color:transparent;
+ border:none;
+ border-bottom:solid 1px rgba(255,255,255,0.03);
+ margin-bottom:20px;
+ padding-left:10px;
+}
+
+.sidebar-form .contactField:hover{
+ color:#FFFFFF;
+}
+
+.sidebar-form .contactField:focus{
+ color:#FFFFFF;
+ border-left:none;
+ border-right:none;
+ border-top:none;
+ border-bottom:solid 1px #2cc36b!important;
+}
+
+.sidebar-form .contactTextarea{
+ height:40px;
+ background-color:transparent;
+ border:none;
+ border-bottom:solid 1px rgba(255,255,255,0.03)!important;
+ margin-bottom:40px;
+ padding-left:10px;
+ transition:all 400ms ease;
+}
+
+.sidebar-form .contactTextarea:hover{
+ color:#FFFFFF;
+}
+
+.sidebar-form .contactTextarea:focus{
+ font-family:'Roboto', sans-serif;
+ color:#FFFFFF;
+ border-left:none;
+ border-right:none;
+ border-top:none;
+ border-bottom:solid 1px #2cc36b!important;
+ height:100px;
+ transition:all 400ms ease;
+}
+
+.snap-drawer-right::-webkit-scrollbar { width: 0 !important }
+
+/*Sidebar Updates*/
+
+.red-update i{ background-color:#c0392b; }
+.green-update i{ background-color:#27ae60; }
+.blue-update i{ background-color:#2980b9; }
+.yellow-update i{ background-color:#f39c12; }
+.orange-update i{ background-color:#d35400; }
+.magenta-update i{ background-color:#8e44ad; }
+
+
+.update-item{
+ padding-left:20px;
+ padding-right:20px;
+ height:60px;
+ margin-top:20px
+}
+
+.update-item i{
+ width:40px;
+ height:40px;
+ float:left;
+ border-radius:40px;
+ line-height:40px;
+ text-align:center;
+ color:#FFFFFF;
+ font-size:16px;
+ margin-top:10px;
+}
+
+.update-item strong{
+ font-family:'Roboto', sans-serif;
+ width:185px;
+ float:left;
+ padding-left:20px;
+ color:#d2d3d3;
+ font-weight:500;
+ font-size:13px;
+ margin-bottom:-5px;
+}
+
+.update-item em{
+ width:185px;
+ float:left;
+ font-size:11px;
+ padding-left:20px;
+ font-style:normal;
+}
+
+
+.sidebar-form .formValidationError{
+ font-size:11px!important;
+ text-align:center;
+ background-color:#c0392b;
+ color:#FFFFFF;
+ margin-bottom:20px;
+}
+
+.formSuccessMessageWrap{
+ font-size:11px!important;
+ text-align:center;
+ background-color:#27ae60;
+ color:#FFFFFF;
+ margin-bottom:20px;
+}
+
+/*Homepage Styles*/
+
+.image-column-left{margin-bottom:30px; padding-right:30px;}
+.image-column-left:after{
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
+}
+
+.image-column-left img{
+ width:115px;
+ height:115px;
+ border-top-left-radius:0px;
+ border-top-right-radius:100px;
+ border-bottom-right-radius:100px;
+ border-bottom-left-radius:0px;
+ margin-left:-50px;
+ float:left;
+ margin-right:20px;
+}
+
+.image-column-left strong{
+ display:block;
+ width:100%;
+ font-size:16px;
+}
+
+.image-column-left em{
+ font-size:14px;
+ font-style:normal;
+}
+
+.image-column-right{margin-bottom:30px; padding-left:30px;}
+.image-column-right:after{
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
+}
+
+.image-column-right img{
+ width:115px;
+ height:115px;
+ border-top-left-radius:100px;
+ border-top-right-radius:0px;
+ border-bottom-right-radius:0px;
+ border-bottom-left-radius:100px;
+ float:right;
+ margin-left:20px;
+ margin-right:-50px;
+}
+
+.image-column-right strong{
+ display:block;
+ width:100%;
+ font-size:16px;
+}
+
+.image-column-right em{
+ font-size:14px;
+ font-style:normal;
+}
+
+.footer-icons{
+ width:265px;
+ margin-left:auto;
+ margin-right:auto;
+ margin-bottom:30px;
+}
+
+.footer-icons a{
+ width:40px;
+ height:40px;
+ line-height:40px;
+ margin-left:5px;
+ margin-right:5px;
+}
+
+.footer-icons a i{
+ width:40px;
+ height:40px;
+ line-height:40px;
+ font-size:12px;
+}
+
+.footer p{
+ margin-bottom:15px;
+}
+
+
+.tutorial{
+ display:none;
+}
+
+.tutorial-overlay{
+ background-color:rgba(0,0,0,0.80);
+ position:fixed;
+ top:0px;
+ left:0px;
+ right:0px;
+ bottom:0px;
+ width:100%;
+ height:100%;
+ z-index:999;
+}
+
+.tutorial .fa-star{
+ position:absolute;
+ z-index:9999;
+ width:50px;
+ height:60px;
+ line-height:60px;
+ top:0px;
+ right:100px;
+ text-align:center;
+ color:#27ae60;
+}
+
+.tutorial p{
+ position:absolute;
+ z-index:9999;
+ width:230px;
+ right:60px;
+ text-align:center;
+ top:100px;
+ color:rgba(255,255,255,0.8);
+ font-size:12px;
+}
+
+.tutorial img{
+ position:absolute;
+ z-index:99999;
+ width:200px;
+ right:130px;
+ top:40px;
+ width:60px;
+ opacity:0.5;
+}
+
+.tutorial a{
+ position:absolute;
+ z-index:9999;
+ width:200px;
+ right:75px;
+ text-align:center;
+ top:200px;
+ color:#27ae60;
+ font-size:12px;
+}
+
+.left
+{
+ float: left
+}
+.right
+{
+ float: right
+}
+blockquote
+{
+ margin-top: 5px;
+ padding: 5px;
+ border-left: 1px solid #597da3;
+ background-color: #e8ebf0;
+}
+blockquote p
+{
+ margin: 0;
+ line-height: normal
+}
+
+.hide, .none, .spoiler_main
+{
+ display: none
+}
+.inline
+{
+ display: inline
+}
+
+.change_map
+{
+ display: none;
+ position: absolute;
+ max-height: 40%;
+ overflow: hidden;
+ overflow-y: scroll;
+ background-color: #fff;
+ z-index: 999;
+ width: 100%;
+ text-align: center;
+ margin-left: -5%
+}
+.change_close
+{
+ display: none;
+ cursor: pointer;
+ position: absolute;
+ padding: 5px 10px;
+ height: 30px;
+ line-height: 17px;
+ background-color: #F24141;
+ color: #FFF;
+ z-index: 1000;
+ border-top: 2px solid #000;
+ right: 5%
+}
+.change_map div
+{
+ cursor: pointer;
+ padding: 4px;
+ border: 1px solid #fff;
+ display: inline-block
+}
+.change_map div > i
+{
+ width: 20px;
+ float: right;
+ height: 20px;
+ line-height: 20px;
+ text-align: center;
+ background-color: #222;
+ color: #fff;
+ display: block;
+ box-sizing: border-box;
+ position: absolute;
+ margin-left: 100px
+}
+.change_map span
+{
+ display: block;
+ text-align: center;
+ padding: 5px;
+ color: #000;
+ font-size: 12px;
+ max-width: 120px;
+ height: 20px;
+ line-height: 10px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap
+}
+.clear
+{
+ clear: both
+}
+.inline-block
+{
+ display: inline-block;
+ width: 100%
+}
+.mini-text
+{
+ font-size: 10px
+}
+.hostname
+{
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ max-width: 85%
+}
\ No newline at end of file
diff --git a/template/megp/fonts/FontAwesome.otf b/template/megp/fonts/FontAwesome.otf
new file mode 100644
index 0000000..401ec0f
Binary files /dev/null and b/template/megp/fonts/FontAwesome.otf differ
diff --git a/template/megp/fonts/fontawesome-webfont.eot b/template/megp/fonts/fontawesome-webfont.eot
new file mode 100644
index 0000000..e9f60ca
Binary files /dev/null and b/template/megp/fonts/fontawesome-webfont.eot differ
diff --git a/template/megp/fonts/fontawesome-webfont.svg b/template/megp/fonts/fontawesome-webfont.svg
new file mode 100644
index 0000000..855c845
--- /dev/null
+++ b/template/megp/fonts/fontawesome-webfont.svg
@@ -0,0 +1,2671 @@
+
+
+
+
+Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016
+ By ,,,
+Copyright Dave Gandy 2016. All rights reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/megp/fonts/fontawesome-webfont.ttf b/template/megp/fonts/fontawesome-webfont.ttf
new file mode 100644
index 0000000..35acda2
Binary files /dev/null and b/template/megp/fonts/fontawesome-webfont.ttf differ
diff --git a/template/megp/fonts/fontawesome-webfont.woff b/template/megp/fonts/fontawesome-webfont.woff
new file mode 100644
index 0000000..400014a
Binary files /dev/null and b/template/megp/fonts/fontawesome-webfont.woff differ
diff --git a/template/megp/fonts/fontawesome-webfont.woff2 b/template/megp/fonts/fontawesome-webfont.woff2
new file mode 100644
index 0000000..4d13fc6
Binary files /dev/null and b/template/megp/fonts/fontawesome-webfont.woff2 differ
diff --git a/template/megp/images/arrow.png b/template/megp/images/arrow.png
new file mode 100644
index 0000000..ab19979
Binary files /dev/null and b/template/megp/images/arrow.png differ
diff --git a/template/megp/images/demo_img.png b/template/megp/images/demo_img.png
new file mode 100644
index 0000000..4256516
Binary files /dev/null and b/template/megp/images/demo_img.png differ
diff --git a/template/megp/images/detector/android.png b/template/megp/images/detector/android.png
new file mode 100644
index 0000000..33e84cb
Binary files /dev/null and b/template/megp/images/detector/android.png differ
diff --git a/template/megp/images/detector/appstore.png b/template/megp/images/detector/appstore.png
new file mode 100644
index 0000000..b7db9f8
Binary files /dev/null and b/template/megp/images/detector/appstore.png differ
diff --git a/template/megp/images/detector/blackberry.png b/template/megp/images/detector/blackberry.png
new file mode 100644
index 0000000..bee098e
Binary files /dev/null and b/template/megp/images/detector/blackberry.png differ
diff --git a/template/megp/images/detector/none.png b/template/megp/images/detector/none.png
new file mode 100644
index 0000000..015e048
Binary files /dev/null and b/template/megp/images/detector/none.png differ
diff --git a/template/megp/images/detector/windows.png b/template/megp/images/detector/windows.png
new file mode 100644
index 0000000..c1644d4
Binary files /dev/null and b/template/megp/images/detector/windows.png differ
diff --git a/template/megp/images/framework/checkbox.png b/template/megp/images/framework/checkbox.png
new file mode 100644
index 0000000..25559a4
Binary files /dev/null and b/template/megp/images/framework/checkbox.png differ
diff --git a/template/megp/images/framework/checkboxc.png b/template/megp/images/framework/checkboxc.png
new file mode 100644
index 0000000..9baeeee
Binary files /dev/null and b/template/megp/images/framework/checkboxc.png differ
diff --git a/template/megp/images/framework/icons.png b/template/megp/images/framework/icons.png
new file mode 100644
index 0000000..7a79f7a
Binary files /dev/null and b/template/megp/images/framework/icons.png differ
diff --git a/template/megp/images/framework/page-loader.gif b/template/megp/images/framework/page-loader.gif
new file mode 100644
index 0000000..3cb9003
Binary files /dev/null and b/template/megp/images/framework/page-loader.gif differ
diff --git a/template/megp/images/framework/radio.png b/template/megp/images/framework/radio.png
new file mode 100644
index 0000000..ebd56c3
Binary files /dev/null and b/template/megp/images/framework/radio.png differ
diff --git a/template/megp/images/framework/radioc.png b/template/megp/images/framework/radioc.png
new file mode 100644
index 0000000..a190f1e
Binary files /dev/null and b/template/megp/images/framework/radioc.png differ
diff --git a/template/megp/images/home-cover-1.jpg b/template/megp/images/home-cover-1.jpg
new file mode 100644
index 0000000..4286776
Binary files /dev/null and b/template/megp/images/home-cover-1.jpg differ
diff --git a/template/megp/images/home-cover-2.jpg b/template/megp/images/home-cover-2.jpg
new file mode 100644
index 0000000..e8103a2
Binary files /dev/null and b/template/megp/images/home-cover-2.jpg differ
diff --git a/template/megp/images/home-cover-3.jpg b/template/megp/images/home-cover-3.jpg
new file mode 100644
index 0000000..2c26ef7
Binary files /dev/null and b/template/megp/images/home-cover-3.jpg differ
diff --git a/template/megp/images/logo-light.png b/template/megp/images/logo-light.png
new file mode 100644
index 0000000..3102554
Binary files /dev/null and b/template/megp/images/logo-light.png differ
diff --git a/template/megp/images/pages-logo-dark.png b/template/megp/images/pages-logo-dark.png
new file mode 100644
index 0000000..33548ff
Binary files /dev/null and b/template/megp/images/pages-logo-dark.png differ
diff --git a/template/megp/images/pages-logo-light.png b/template/megp/images/pages-logo-light.png
new file mode 100644
index 0000000..66dd784
Binary files /dev/null and b/template/megp/images/pages-logo-light.png differ
diff --git a/template/megp/images/pictures/1.jpg b/template/megp/images/pictures/1.jpg
new file mode 100644
index 0000000..ce15471
Binary files /dev/null and b/template/megp/images/pictures/1.jpg differ
diff --git a/template/megp/images/pictures/1s.jpg b/template/megp/images/pictures/1s.jpg
new file mode 100644
index 0000000..cd83924
Binary files /dev/null and b/template/megp/images/pictures/1s.jpg differ
diff --git a/template/megp/images/pictures/1t.jpg b/template/megp/images/pictures/1t.jpg
new file mode 100644
index 0000000..dec092d
Binary files /dev/null and b/template/megp/images/pictures/1t.jpg differ
diff --git a/template/megp/images/pictures/1w.jpg b/template/megp/images/pictures/1w.jpg
new file mode 100644
index 0000000..e1cd204
Binary files /dev/null and b/template/megp/images/pictures/1w.jpg differ
diff --git a/template/megp/images/pictures/1ww.jpg b/template/megp/images/pictures/1ww.jpg
new file mode 100644
index 0000000..13599b8
Binary files /dev/null and b/template/megp/images/pictures/1ww.jpg differ
diff --git a/template/megp/images/pictures/2.jpg b/template/megp/images/pictures/2.jpg
new file mode 100644
index 0000000..229048d
Binary files /dev/null and b/template/megp/images/pictures/2.jpg differ
diff --git a/template/megp/images/pictures/2s.jpg b/template/megp/images/pictures/2s.jpg
new file mode 100644
index 0000000..0a2f8bb
Binary files /dev/null and b/template/megp/images/pictures/2s.jpg differ
diff --git a/template/megp/images/pictures/2t.jpg b/template/megp/images/pictures/2t.jpg
new file mode 100644
index 0000000..49a1922
Binary files /dev/null and b/template/megp/images/pictures/2t.jpg differ
diff --git a/template/megp/images/pictures/2w.jpg b/template/megp/images/pictures/2w.jpg
new file mode 100644
index 0000000..9585d59
Binary files /dev/null and b/template/megp/images/pictures/2w.jpg differ
diff --git a/template/megp/images/pictures/2ww.jpg b/template/megp/images/pictures/2ww.jpg
new file mode 100644
index 0000000..772c2f1
Binary files /dev/null and b/template/megp/images/pictures/2ww.jpg differ
diff --git a/template/megp/images/pictures/3.jpg b/template/megp/images/pictures/3.jpg
new file mode 100644
index 0000000..8e2c952
Binary files /dev/null and b/template/megp/images/pictures/3.jpg differ
diff --git a/template/megp/images/pictures/3s.jpg b/template/megp/images/pictures/3s.jpg
new file mode 100644
index 0000000..9e04aa6
Binary files /dev/null and b/template/megp/images/pictures/3s.jpg differ
diff --git a/template/megp/images/pictures/3w.jpg b/template/megp/images/pictures/3w.jpg
new file mode 100644
index 0000000..cb1db87
Binary files /dev/null and b/template/megp/images/pictures/3w.jpg differ
diff --git a/template/megp/images/pictures/3ww.jpg b/template/megp/images/pictures/3ww.jpg
new file mode 100644
index 0000000..982eff4
Binary files /dev/null and b/template/megp/images/pictures/3ww.jpg differ
diff --git a/template/megp/images/pictures/4.jpg b/template/megp/images/pictures/4.jpg
new file mode 100644
index 0000000..0948ca4
Binary files /dev/null and b/template/megp/images/pictures/4.jpg differ
diff --git a/template/megp/images/pictures/4s.jpg b/template/megp/images/pictures/4s.jpg
new file mode 100644
index 0000000..90c3a47
Binary files /dev/null and b/template/megp/images/pictures/4s.jpg differ
diff --git a/template/megp/images/pictures/4w.jpg b/template/megp/images/pictures/4w.jpg
new file mode 100644
index 0000000..c53b4e0
Binary files /dev/null and b/template/megp/images/pictures/4w.jpg differ
diff --git a/template/megp/images/pictures/5.jpg b/template/megp/images/pictures/5.jpg
new file mode 100644
index 0000000..97f2b59
Binary files /dev/null and b/template/megp/images/pictures/5.jpg differ
diff --git a/template/megp/images/pictures/5s.jpg b/template/megp/images/pictures/5s.jpg
new file mode 100644
index 0000000..d0edbf4
Binary files /dev/null and b/template/megp/images/pictures/5s.jpg differ
diff --git a/template/megp/images/pictures/5t.jpg b/template/megp/images/pictures/5t.jpg
new file mode 100644
index 0000000..cb724ef
Binary files /dev/null and b/template/megp/images/pictures/5t.jpg differ
diff --git a/template/megp/images/pictures/5w.jpg b/template/megp/images/pictures/5w.jpg
new file mode 100644
index 0000000..5b11b0d
Binary files /dev/null and b/template/megp/images/pictures/5w.jpg differ
diff --git a/template/megp/images/pictures/5ww.jpg b/template/megp/images/pictures/5ww.jpg
new file mode 100644
index 0000000..c5ea7ac
Binary files /dev/null and b/template/megp/images/pictures/5ww.jpg differ
diff --git a/template/megp/images/pictures/6.jpg b/template/megp/images/pictures/6.jpg
new file mode 100644
index 0000000..965c364
Binary files /dev/null and b/template/megp/images/pictures/6.jpg differ
diff --git a/template/megp/images/pictures/6s.jpg b/template/megp/images/pictures/6s.jpg
new file mode 100644
index 0000000..4d1f781
Binary files /dev/null and b/template/megp/images/pictures/6s.jpg differ
diff --git a/template/megp/images/pictures/6t.jpg b/template/megp/images/pictures/6t.jpg
new file mode 100644
index 0000000..84bf0fc
Binary files /dev/null and b/template/megp/images/pictures/6t.jpg differ
diff --git a/template/megp/images/pictures/6w.jpg b/template/megp/images/pictures/6w.jpg
new file mode 100644
index 0000000..e61ef99
Binary files /dev/null and b/template/megp/images/pictures/6w.jpg differ
diff --git a/template/megp/images/pictures/7.jpg b/template/megp/images/pictures/7.jpg
new file mode 100644
index 0000000..fce4949
Binary files /dev/null and b/template/megp/images/pictures/7.jpg differ
diff --git a/template/megp/images/pictures/7s.jpg b/template/megp/images/pictures/7s.jpg
new file mode 100644
index 0000000..21f894b
Binary files /dev/null and b/template/megp/images/pictures/7s.jpg differ
diff --git a/template/megp/images/pictures/7w.jpg b/template/megp/images/pictures/7w.jpg
new file mode 100644
index 0000000..3aa65ca
Binary files /dev/null and b/template/megp/images/pictures/7w.jpg differ
diff --git a/template/megp/images/pictures/7ww.jpg b/template/megp/images/pictures/7ww.jpg
new file mode 100644
index 0000000..0297884
Binary files /dev/null and b/template/megp/images/pictures/7ww.jpg differ
diff --git a/template/megp/images/pictures/8.jpg b/template/megp/images/pictures/8.jpg
new file mode 100644
index 0000000..d80bbef
Binary files /dev/null and b/template/megp/images/pictures/8.jpg differ
diff --git a/template/megp/images/pictures/8s.jpg b/template/megp/images/pictures/8s.jpg
new file mode 100644
index 0000000..253d0f3
Binary files /dev/null and b/template/megp/images/pictures/8s.jpg differ
diff --git a/template/megp/images/pictures/8w.jpg b/template/megp/images/pictures/8w.jpg
new file mode 100644
index 0000000..f7c5079
Binary files /dev/null and b/template/megp/images/pictures/8w.jpg differ
diff --git a/template/megp/images/preload-logo.png b/template/megp/images/preload-logo.png
new file mode 100644
index 0000000..327e914
Binary files /dev/null and b/template/megp/images/preload-logo.png differ
diff --git a/template/megp/images/splash/android-chrome-192x192.png b/template/megp/images/splash/android-chrome-192x192.png
new file mode 100644
index 0000000..75860dc
Binary files /dev/null and b/template/megp/images/splash/android-chrome-192x192.png differ
diff --git a/template/megp/images/splash/apple-touch-icon-114x114.png b/template/megp/images/splash/apple-touch-icon-114x114.png
new file mode 100644
index 0000000..2228ac9
Binary files /dev/null and b/template/megp/images/splash/apple-touch-icon-114x114.png differ
diff --git a/template/megp/images/splash/apple-touch-icon-120x120.png b/template/megp/images/splash/apple-touch-icon-120x120.png
new file mode 100644
index 0000000..dc4853e
Binary files /dev/null and b/template/megp/images/splash/apple-touch-icon-120x120.png differ
diff --git a/template/megp/images/splash/apple-touch-icon-144x144.png b/template/megp/images/splash/apple-touch-icon-144x144.png
new file mode 100644
index 0000000..2ccb628
Binary files /dev/null and b/template/megp/images/splash/apple-touch-icon-144x144.png differ
diff --git a/template/megp/images/splash/apple-touch-icon-152x152.png b/template/megp/images/splash/apple-touch-icon-152x152.png
new file mode 100644
index 0000000..036fb15
Binary files /dev/null and b/template/megp/images/splash/apple-touch-icon-152x152.png differ
diff --git a/template/megp/images/splash/apple-touch-icon-180x180.png b/template/megp/images/splash/apple-touch-icon-180x180.png
new file mode 100644
index 0000000..b12e959
Binary files /dev/null and b/template/megp/images/splash/apple-touch-icon-180x180.png differ
diff --git a/template/megp/images/splash/apple-touch-icon-196x196.png b/template/megp/images/splash/apple-touch-icon-196x196.png
new file mode 100644
index 0000000..60befe9
Binary files /dev/null and b/template/megp/images/splash/apple-touch-icon-196x196.png differ
diff --git a/template/megp/images/splash/apple-touch-icon-57x57.png b/template/megp/images/splash/apple-touch-icon-57x57.png
new file mode 100644
index 0000000..6550a2d
Binary files /dev/null and b/template/megp/images/splash/apple-touch-icon-57x57.png differ
diff --git a/template/megp/images/splash/apple-touch-icon-60x60.png b/template/megp/images/splash/apple-touch-icon-60x60.png
new file mode 100644
index 0000000..7b94736
Binary files /dev/null and b/template/megp/images/splash/apple-touch-icon-60x60.png differ
diff --git a/template/megp/images/splash/apple-touch-icon-72x72.png b/template/megp/images/splash/apple-touch-icon-72x72.png
new file mode 100644
index 0000000..b34174d
Binary files /dev/null and b/template/megp/images/splash/apple-touch-icon-72x72.png differ
diff --git a/template/megp/images/splash/apple-touch-icon-76x76.png b/template/megp/images/splash/apple-touch-icon-76x76.png
new file mode 100644
index 0000000..9e7c1f5
Binary files /dev/null and b/template/megp/images/splash/apple-touch-icon-76x76.png differ
diff --git a/template/megp/images/splash/favicon-16x16.png b/template/megp/images/splash/favicon-16x16.png
new file mode 100644
index 0000000..4dbb111
Binary files /dev/null and b/template/megp/images/splash/favicon-16x16.png differ
diff --git a/template/megp/images/splash/favicon-32x32.png b/template/megp/images/splash/favicon-32x32.png
new file mode 100644
index 0000000..6fcc72d
Binary files /dev/null and b/template/megp/images/splash/favicon-32x32.png differ
diff --git a/template/megp/images/splash/favicon-96x96.png b/template/megp/images/splash/favicon-96x96.png
new file mode 100644
index 0000000..9486a71
Binary files /dev/null and b/template/megp/images/splash/favicon-96x96.png differ
diff --git a/template/megp/images/splash/favicon.ico b/template/megp/images/splash/favicon.ico
new file mode 100644
index 0000000..ff39e2c
Binary files /dev/null and b/template/megp/images/splash/favicon.ico differ
diff --git a/template/megp/index.html b/template/megp/index.html
new file mode 100644
index 0000000..52cb662
--- /dev/null
+++ b/template/megp/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
Баланс: [balance] [cur]
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/js/auth.js b/template/megp/js/auth.js
new file mode 100644
index 0000000..d6b23f5
--- /dev/null
+++ b/template/megp/js/auth.js
@@ -0,0 +1,39 @@
+$('#auth').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить",
+ }]
+ );
+
+ document.getElementById("captcha_img").src = home+'auth/captcha?'+Math.random();
+
+ $('#captcha').val('');
+ }
+
+ if(i == 'i')
+ {
+ bootbox.dialog('Внимание '+val,
+ [{
+ "label" : "Продолжить",
+ }]
+ );
+
+ $('#security_code').css('display', 'table-row');
+
+ document.getElementById("captcha_img").src = home+'auth/captcha?'+Math.random();
+
+ $('#captcha').val('');
+ }
+
+ if(i == 's')
+ location.reload();
+ });
+ }
+});
\ No newline at end of file
diff --git a/template/megp/js/bootbox.js b/template/megp/js/bootbox.js
new file mode 100644
index 0000000..e78f8d7
--- /dev/null
+++ b/template/megp/js/bootbox.js
@@ -0,0 +1,6 @@
+/**
+ * bootbox.js v3.3.0
+ *
+ * http://bootboxjs.com/license.txt
+ */
+var bootbox=window.bootbox||function(a,b){function c(a,b){return"undefined"==typeof b&&(b=d),"string"==typeof m[b][a]?m[b][a]:b!=e?c(a,e):a}var d="en",e="en",f=!0,g="static",h="javascript:;",i="",j={},k={},l={};l.setLocale=function(a){for(var b in m)if(b==a)return d=a,void 0;throw new Error("Invalid locale: "+a)},l.addLocale=function(a,b){"undefined"==typeof m[a]&&(m[a]={});for(var c in b)m[a][c]=b[c]},l.setIcons=function(a){k=a,("object"!=typeof k||null===k)&&(k={})},l.setBtnClasses=function(a){j=a,("object"!=typeof j||null===j)&&(j={})},l.alert=function(){var a="",b=c("OK"),d=null;switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"function"==typeof arguments[1]?d=arguments[1]:b=arguments[1];break;case 3:a=arguments[0],b=arguments[1],d=arguments[2];break;default:throw new Error("Incorrect number of arguments: expected 1-3")}return l.dialog(a,{label:b,icon:k.OK,"class":j.OK,callback:d},{onEscape:d||!0})},l.confirm=function(){var a="",b=c("CANCEL"),d=c("CONFIRM"),e=null;switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"function"==typeof arguments[1]?e=arguments[1]:b=arguments[1];break;case 3:a=arguments[0],b=arguments[1],"function"==typeof arguments[2]?e=arguments[2]:d=arguments[2];break;case 4:a=arguments[0],b=arguments[1],d=arguments[2],e=arguments[3];break;default:throw new Error("Incorrect number of arguments: expected 1-4")}var f=function(){return"function"==typeof e?e(!1):void 0},g=function(){return"function"==typeof e?e(!0):void 0};return l.dialog(a,[{label:b,icon:k.CANCEL,"class":j.CANCEL,callback:f},{label:d,icon:k.CONFIRM,"class":j.CONFIRM,callback:g}],{onEscape:f})},l.prompt=function(){var a="",d=c("CANCEL"),e=c("CONFIRM"),f=null,g="";switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"function"==typeof arguments[1]?f=arguments[1]:d=arguments[1];break;case 3:a=arguments[0],d=arguments[1],"function"==typeof arguments[2]?f=arguments[2]:e=arguments[2];break;case 4:a=arguments[0],d=arguments[1],e=arguments[2],f=arguments[3];break;case 5:a=arguments[0],d=arguments[1],e=arguments[2],f=arguments[3],g=arguments[4];break;default:throw new Error("Incorrect number of arguments: expected 1-5")}var h=a,i=b(" ");i.append(" ");var m=function(){return"function"==typeof f?f(null):void 0},n=function(){return"function"==typeof f?f(i.find("input[type=text]").val()):void 0},o=l.dialog(i,[{label:d,icon:k.CANCEL,"class":j.CANCEL,callback:m},{label:e,icon:k.CONFIRM,"class":j.CONFIRM,callback:n}],{header:h,show:!1,onEscape:m});return o.on("shown",function(){i.find("input[type=text]").focus(),i.on("submit",function(a){a.preventDefault(),o.find(".btn-primary").click()})}),o.modal("show"),o},l.dialog=function(c,d,e){function j(){var a=null;"function"==typeof e.onEscape&&(a=e.onEscape()),a!==!1&&x.modal("hide")}var k="",l=[];e||(e={}),"undefined"==typeof d?d=[]:"undefined"==typeof d.length&&(d=[d]);for(var m=d.length;m--;){var n=null,o=null,p=null,q="",r=null;if("undefined"==typeof d[m].label&&"undefined"==typeof d[m]["class"]&&"undefined"==typeof d[m].callback){var s=0,t=null;for(var u in d[m])if(t=u,++s>1)break;1==s&&"function"==typeof d[m][u]&&(d[m].label=t,d[m].callback=d[m][u])}"function"==typeof d[m].callback&&(r=d[m].callback),d[m]["class"]?p=d[m]["class"]:m==d.length-1&&d.length<=2&&(p="btn-primary"),d[m].link!==!0&&(p="btn "+p),n=d[m].label?d[m].label:"Option "+(m+1),d[m].icon&&(q=" "),o=d[m].href?d[m].href:h,k=""+q+n+" "+k,l[m]=r}var v=[""];if(e.header){var w="";("undefined"==typeof e.headerCloseButton||e.headerCloseButton)&&(w="
× "),v.push("")}v.push("
"),k&&v.push(""),v.push("
");var x=b(v.join("\n")),y="undefined"==typeof e.animate?f:e.animate;y&&x.addClass("fade");var z="undefined"==typeof e.classes?i:e.classes;return z&&x.addClass(z),x.find(".modal-body").html(c),x.on("keyup.dismiss.modal",function(a){27===a.which&&e.onEscape&&j("escape")}),x.on("click","a.close",function(a){a.preventDefault(),j("close")}),x.on("shown",function(){x.find("a.btn-primary:first").focus()}),x.on("hidden",function(a){a.target===this&&x.remove()}),x.on("click",".modal-footer a",function(a){var c=b(this).data("handler"),e=l[c],f=null;("undefined"==typeof c||"undefined"==typeof d[c].href)&&(a.preventDefault(),"function"==typeof e&&(f=e(a)),f!==!1&&x.modal("hide"))}),b("body").append(x),x.modal({backdrop:"undefined"==typeof e.backdrop?g:e.backdrop,keyboard:!1,show:!1}),x.on("show",function(){b(a).off("focusin.modal")}),("undefined"==typeof e.show||e.show===!0)&&x.modal("show"),x},l.modal=function(){var a,c,d,e={onEscape:null,keyboard:!0,backdrop:g};switch(arguments.length){case 1:a=arguments[0];break;case 2:a=arguments[0],"object"==typeof arguments[1]?d=arguments[1]:c=arguments[1];break;case 3:a=arguments[0],c=arguments[1],d=arguments[2];break;default:throw new Error("Incorrect number of arguments: expected 1-3")}return e.header=c,d="object"==typeof d?b.extend(e,d):e,l.dialog(a,[],d)},l.hideAll=function(){b(".bootbox").modal("hide")},l.animate=function(a){f=a},l.backdrop=function(a){g=a},l.classes=function(a){i=a};var m={br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},zh_CN:{OK:"OK",CANCEL:"å–消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"å–消",CONFIRM:"確èª"}};return l}(document,window.jQuery);window.bootbox=bootbox;
\ No newline at end of file
diff --git a/template/megp/js/bootstrap.js b/template/megp/js/bootstrap.js
new file mode 100644
index 0000000..f9cbdae
--- /dev/null
+++ b/template/megp/js/bootstrap.js
@@ -0,0 +1,6 @@
+/*!
+* Bootstrap.js by @fat & @mdo
+* Copyright 2012 Twitter, Inc.
+* http://www.apache.org/licenses/LICENSE-2.0.txt
+*/
+!function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(".dropdown-backdrop").remove(),e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||("ontouchstart"in document.documentElement&&e('
').insertBefore(e(this)).on("click",r),s.toggleClass("open")),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f ').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:''}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:' ',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);
\ No newline at end of file
diff --git a/template/megp/js/custom.js b/template/megp/js/custom.js
new file mode 100644
index 0000000..52d1ded
--- /dev/null
+++ b/template/megp/js/custom.js
@@ -0,0 +1,950 @@
+$(window).load(function() {
+ $("#status").fadeOut(); // will first fade out the loading animation
+ $("#preloader").delay(100).fadeOut("slow"); // will fade out the white DIV that covers the website.
+});
+
+
+$( document ).ready(function() {
+
+ $('.header-menu').click(function(){
+ $('.navigation').toggleClass('show-menu');
+ $(this).find('.fa').toggleClass('rotate-star');
+ return false;
+ });
+
+ $('.close-header-menu').click(function(){
+ $('.navigation').toggleClass('show-menu');
+ $(this).parent().parent().find('.fa').removeClass('rotate-star');
+ return false;
+ });
+
+
+ /*Cookie Notification*/
+
+ if (typeof window.sessionStorage != undefined) {
+ if (!sessionStorage.getItem('enabled_cookie')) {
+ $('.tutorial').show();
+ sessionStorage.setItem('enabled_cookie', true);
+ sessionStorage.setItem('storedWhen', (new Date()).getTime());
+ }
+ }
+
+ $('.tutorial').click(function(){
+ $(this).fadeOut(500);
+ });
+
+
+
+
+ /*Image Sliders*/
+
+ //Note. Every image slider must be placed within the timeout function.//
+ //Image sliders put a lot of load on mobile devices and slow the performance of other animations//
+ //But adding a timeout event, even for a microsecond gives a great boost in performance (41% boost to be exact)
+
+ setTimeout(function() {
+ //Simple Slider
+
+ var owl = $('.simple-slider');
+ owl.owlCarousel({
+ animateOut: 'fadeOut',
+ animateIn: 'fadeIn',
+ items:1,
+ loop:true,
+ margin:5,
+ autoplay:true,
+ autoplayTimeout:3000,
+ autoplayHoverPause:true
+ });
+
+ //Coverpage Slider
+
+ $('.coverpage-slider').owlCarousel({
+ loop:true,
+ margin:-2,
+ nav:false,
+ dots:true,
+ items:1
+ });
+
+ //Demo Slider inside Quotes
+
+ $('.demo-slider').owlCarousel({
+ loop:true,
+ margin:200,
+ nav:false,
+ autoHeight:true,
+ responsive:{
+ 0:{
+ items:1
+ },
+ 600:{
+ items:2
+ }
+ }
+ });
+
+ $('.next-demo').click(function() {$('.demo-slider').trigger('next.owl.carousel'); return false;});
+ $('.prev-demo').click(function() {$('.demo-slider').trigger('prev.owl.carousel'); return false;});
+
+
+ //Homepage Slider No Transitions
+ $('.homepage-slider-no-transition').owlCarousel({
+ autoplay:true,
+ autoplayTimeout:5000,
+ autoplayHoverPause:true,
+ loop:true,
+ margin:10,
+ nav:false,
+ dots:false,
+ items:1
+ });
+
+ //Homepage Slider With Transition
+ $('.homepage-slider-transition').owlCarousel({
+ autoplay:true,
+ autoplayTimeout:5000,
+ autoplayHoverPause:true,
+ animateOut: 'fadeOut',
+ animateIn: 'fadein',
+ loop:true,
+ margin:10,
+ nav:false,
+ dots:false,
+ items:1
+ });
+
+
+ //Homepage Slider With Transition 2
+ $('.homepage-slider-transition-2').owlCarousel({
+ autoplay:true,
+ autoplayTimeout:5000,
+ autoplayHoverPause:true,
+ animateOut: 'slideOutDown',
+ animateIn: 'slideInUp',
+ loop:true,
+ margin:10,
+ nav:false,
+ dots:false,
+ items:1
+ });
+
+
+ //Homepage Slider With Transition 2
+ $('.homepage-slider-transition-3').owlCarousel({
+ autoplay:true,
+ autoplayTimeout:5000,
+ autoplayHoverPause:true,
+ animateOut: 'rollOut',
+ animateIn: 'rollIn',
+ loop:true,
+ margin:10,
+ nav:false,
+ dots:false,
+ items:1
+ });
+
+ $('.homepage-cover-slider').owlCarousel({
+ autoplay:true,
+ autoplayTimeout:5000,
+ autoplayHoverPause:true,
+ animateOut: 'fadeOut',
+ animateIn: 'fadeIn',
+ loop:true,
+ margin:10,
+ nav:false,
+ dots:false,
+ items:1
+ });
+
+ $('.next-home-slider').click(function() {$('.homepage-slider-transition, .homepage-slider-transition-2, .homepage-slider-transition-3, .homepage-slider-no-transition').trigger('next.owl.carousel'); return false;});
+ $('.prev-home-slider').click(function() {$('.homepage-slider-transition, .homepage-slider-transition-2, .homepage-slider-transition-3, .homepage-slider-no-transition').trigger('prev.owl.carousel'); return false;});
+
+
+ //Staff Slider No Transition
+ $('.staff-slider-no-transition').owlCarousel({
+ autoplay:true,
+ autoplayTimeout:5000,
+ autoplayHoverPause:true,
+ lazyLoad:true,
+ loop:true,
+ margin:10,
+ nav:false,
+ dots:false,
+ responsive:{
+ 0:{
+ items:1
+ },
+ 600:{
+ items:2
+ },
+ 1000:{
+ items:3
+ }
+ }
+ });
+
+ //Staff Slider With Transition
+ $('.staff-slider-transition').owlCarousel({
+ autoplay:true,
+ autoplayTimeout:5000,
+ autoplayHoverPause:true,
+ animateOut: 'fadeOut',
+ animateIn: 'fadeIn',
+ lazyLoad:true,
+ loop:true,
+ margin:10,
+ nav:false,
+ dots:false,
+ responsive:{
+ 0:{
+ items:1
+ },
+ 600:{
+ items:2
+ },
+ 1000:{
+ items:3
+ }
+ }
+ });
+
+ $('.next-staff-slider').click(function() {$('.staff-slider-no-transition, .staff-slider-transition').trigger('next.owl.carousel'); return false;});
+ $('.prev-staff-slider').click(function() {$('.staff-slider-no-transition, .staff-slider-transition').trigger('prev.owl.carousel'); return false;});
+
+
+ //Quote Slider No Transition
+ $('.quote-slider-no-transition').owlCarousel({
+ autoHeight:true,
+ autoplay:true,
+ autoplayTimeout:5000,
+ autoplayHoverPause:true,
+ lazyLoad:true,
+ loop:true,
+ margin:10,
+ nav:false,
+ dots:false,
+ responsive:{
+ 0:{
+ items:1
+ },
+ 600:{
+ items:1
+ },
+ 1000:{
+ items:1
+ }
+ }
+ });
+
+ //Quote Slider No Transition
+ $('.quote-slider-transition').owlCarousel({
+ autoHeight:true,
+ autoplay:true,
+ autoplayTimeout:5000,
+ autoplayHoverPause:true,
+ animateOut: 'fadeOut',
+ animateIn: 'fadeIn',
+ lazyLoad:true,
+ loop:true,
+ margin:10,
+ nav:false,
+ dots:false,
+ responsive:{
+ 0:{
+ items:1
+ },
+ 600:{
+ items:1
+ },
+ 1000:{
+ items:1
+ }
+ }
+ });
+
+ $('.next-quote-slider').click(function() {$('.quote-slider-no-transition, .quote-slider-transition').trigger('next.owl.carousel'); return false;});
+ $('.prev-quote-slider').click(function() {$('.quote-slider-no-transition, .quote-slider-transition').trigger('prev.owl.carousel'); return false;});
+
+ //Placing the Dots if Needed
+ function slider_dots(){
+ var dots_width = (-($('.owl-dots').width()/2));
+ $('.owl-dots').css('position', 'absolute');
+ $('.owl-dots').css('left', '50%');
+ $('.owl-dots').css('margin-left', dots_width);
+ }
+ slider_dots();
+
+ }, 1);
+
+ //Detect if iOS WebApp Engaged and permit navigation without deploying Safari
+ (function(a,b,c){if(c in b&&b[c]){var d,e=a.location,f=/^(a|html)$/i;a.addEventListener("click",function(a){d=a.target;while(!f.test(d.nodeName))d=d.parentNode;"href"in d&&(d.href.indexOf("http")||~d.href.indexOf(e.host))&&(a.preventDefault(),e.href=d.href)},!1)}})(document,window.navigator,"standalone")
+
+ //Fast Click - Removing 300ms delay when clicking for instant response time
+
+ $(function() {
+ FastClick.attach(document.body);
+ });
+
+ //Lazy Load | Preloading Image
+
+ $(function() {
+ $(".preload-image").lazyload({
+ threshold : 200,
+ effect : "fadeIn"
+ });
+ $("img.lazy").show().lazyload();
+ });
+
+ //Page Chapters Activation
+
+ $('.show-page-chapters, .hide-chapters').click(function(){
+ $('.page-chapters').toggleClass('page-chapters-active');
+ });
+
+ $('.page-chapters a').click(function(){
+ $('.page-chapters a').removeClass('active-chapter');
+ $(this).addClass('active-chapter');
+ });
+
+ //Countdown timer
+
+ var endDate = "June 7, 2015 15:03:25";
+ $(function() {
+ $('.countdown-class').countdown({
+ date: "June 7, 2087 15:03:26"
+ });
+ });
+
+ //Pie Charts
+
+ if ($('body').hasClass('has-charts')){
+ var pieData = [
+ { value: 25, color: "#c0392b", highlight: "#c0392b", label: "Red" },
+ { value: 20, color: "#27ae60", highlight: "#27ae60", label: "Green" },
+ { value: 15, color: "#f39c12", highlight: "#f39c12", label: "Yellow" },
+ { value: 30, color: "#2980b9", highlight: "#34495e", label: "Dark Blue" }
+ ];
+ var barChartData = {
+ labels : ["One","Two","Three","Four","Five", "Six"],
+ datasets : [{
+ fillColor : "rgba(0,0,0,0.1)",
+ strokeColor : "rgba(0,0,0,0.2)",
+ highlightFill: "rgba(0,0,0,0.25)",
+ highlightStroke: "rgba(0,0,0,0.25)",
+ data : [20,10,40,30,10, 80]
+ }]
+ }
+ window.onload = function(){
+ var pie_chart_1 = document.getElementById("generate-pie-chart").getContext("2d");
+ window.pie_chart_1 = new Chart(pie_chart_1).Pie(pieData);
+
+ var bar_chart_1 = document.getElementById("generate-bar-chart").getContext("2d");
+ window.bar_chart_1 = new Chart(bar_chart_1).Bar(barChartData);
+ };
+ }
+
+ //Tabs
+
+ $('ul.tabs li').click(function(){
+ var tab_id = $(this).attr('data-tab');
+
+ $('ul.tabs li').removeClass('active-tab');
+ $('.tab-content').slideUp(200);
+
+ $(this).addClass('active-tab');
+ $("#"+tab_id).slideToggle(200);
+ })
+
+ //Accordion
+
+ $('.accordion').find('.accordion-toggle').click(function(){
+ //Expand or collapse this panel
+ $(this).next().slideDown(250);
+ $('.accordion').find('i').removeClass('rotate-180');
+ $(this).find('i').addClass('rotate-180');
+
+ //Hide the other panels
+ $(".accordion-content").not($(this).next()).slideUp(200);
+ });
+
+ //Classic Toggles
+
+ $('.toggle-title').click(function(){
+ $(this).parent().find('.toggle-content').slideToggle(200);
+ $(this).find('i').toggleClass('rotate-toggle');
+ return false;
+ });
+
+ //Notifications
+
+ $('.static-notification-close').click(function(){
+ $(this).parent().slideUp(200);
+ return false;
+ });
+
+ $('.tap-dismiss').click(function(){
+ $(this).slideUp(200);
+ return false;
+ });
+
+ //Modal Launchers
+
+ $('.modal-close').click(function(){return false;});
+
+ $('.simple-modal').click(function() {
+ $('.simple-modal-content').modal();
+ });
+
+ $('.social-login-modal').click(function() {
+ $('.social-login-modal-content').modal();
+ });
+
+ $('.simple-login-modal').click(function() {
+ $('.simple-login-modal-content').modal();
+ });
+
+ $('.social-profile-modal').click(function() {
+ $('.social-profile-modal-content').modal();
+ });
+
+ //Sharebox Settings
+
+ $('.show-share-bottom, .show-share-box').click(function(){
+ $('.share-bottom').toggleClass('active-share-bottom');
+ $.modal.close()
+ return false;
+ });
+
+ $('.close-share-bottom').click(function(){
+ $('.share-bottom').removeClass('active-share-bottom');
+ return false;
+ });
+
+ //Fixed Notifications
+
+ //top
+ $('.close-top-notification').click(function(){
+ $('.top-notification').slideUp(200);
+ return false;
+ });
+
+ $('.show-top-notification-1').click(function(){
+ $('.top-notification, .bottom-notification, .timeout-notification').slideUp(200);
+ $('.top-notification-1').slideDown(200);
+ });
+
+ $('.show-top-notification-2').click(function(){
+ $('.top-notification, .bottom-notification, .timeout-notification').slideUp(200);
+ $('.top-notification-2').slideDown(200);
+ });
+
+ $('.show-top-notification-3').click(function(){
+ $('.top-notification, .bottom-notification, .timeout-notification').slideUp(200);
+ $('.top-notification-3').slideDown(200);
+ });
+
+ //bottom
+ $('.close-bottom-notification').click(function(){
+ $('.bottom-notification').slideUp(200);
+ clearTimeout(notification_timer);
+ return false;
+ });
+
+ $('.show-bottom-notification-1').click(function(){
+ $('.top-notification, .bottom-notification, .timeout-notification').slideUp(200);
+ $('.bottom-notification-1').slideDown(200);
+ return false;
+ });
+
+ $('.show-bottom-notification-2').click(function(){
+ $('.top-notification, .bottom-notification, .timeout-notification').slideUp(200);
+ $('.bottom-notification-2').slideDown(200);
+ return false;
+ });
+
+ $('.show-bottom-notification-3').click(function(){
+ $('.top-notification, .bottom-notification, .timeout-notification').slideUp(200);
+ $('.bottom-notification-3').slideDown(200);
+ return false;
+ });
+
+ //Timeout
+
+ $('.timer-notification').click(function(){
+ var notification_timer;
+ notification_timer = setTimeout(function(){ $('.timeout-notification').slideUp(250); },5000);
+ });
+
+ //Switches
+
+ $('.switch-1').click(function(){
+ $(this).toggleClass('switch-1-on');
+ return false;
+ });
+
+ $('.switch-2').click(function(){
+ $(this).toggleClass('switch-2-on');
+ return false;
+ });
+
+ $('.switch-3').click(function(){
+ $(this).toggleClass('switch-3-on');
+ return false;
+ });
+
+ $('.switch, .switch-icon').click(function(){
+ $(this).parent().find('.switch-box-content').slideToggle(200);
+ $(this).parent().find('.switch-box-subtitle').slideToggle(200);
+ return false;
+ });
+
+ //Reminders & Checklists & Tasklists
+
+ $('.reminder-check-square').click(function(){
+ $(this).toggleClass('reminder-check-square-selected');
+ return false;
+ });
+
+ $('.reminder-check-round').click(function(){
+ $(this).toggleClass('reminder-check-round-selected');
+ return false;
+ });
+
+ $('.checklist-square').click(function(){
+ $(this).toggleClass('checklist-square-selected');
+ return false;
+ });
+
+ $('.checklist-round').click(function(){
+ $(this).toggleClass('checklist-round-selected');
+ return false;
+ });
+
+ $('.tasklist-incomplete').click(function(){
+ $(this).removeClass('tasklist-incomplete');
+ $(this).addClass('tasklist-completed');
+ });
+
+ $('.tasklist-item').click(function(){
+ $(this).toggleClass('tasklist-completed');
+ });
+
+ //Activity Item Toggle
+
+ $('.activity-item').click(function(){
+ $(this).find('.activity-item-detail').slideToggle(200);
+ });
+
+ //Detecting Mobiles//
+
+ var isMobile = {
+ Android: function() {
+ return navigator.userAgent.match(/Android/i);
+ },
+ BlackBerry: function() {
+ return navigator.userAgent.match(/BlackBerry/i);
+ },
+ iOS: function() {
+ return navigator.userAgent.match(/iPhone|iPad|iPod/i);
+ },
+ Opera: function() {
+ return navigator.userAgent.match(/Opera Mini/i);
+ },
+ Windows: function() {
+ return navigator.userAgent.match(/IEMobile/i);
+ },
+ any: function() {
+ return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
+ }
+ };
+
+ if(isMobile.any()) {
+ //Settings for all mobiles
+ $('head').append(' ');
+ }
+
+ if( !isMobile.any() ){
+ $('.show-blackberry, .show-ios, .show-windows, .show-android').hide(0);
+ $('show-no-detection').show(0);
+
+ $('#content').bind('mousewheel', function(event) {
+ event.preventDefault();
+ var scrollTop = this.scrollTop;
+ this.scrollTop = (scrollTop + ((event.deltaY * event.deltaFactor) * -2));
+ //console.log(event.deltaY, event.deltaFactor, event.originalEvent.deltaMode, event.originalEvent.wheelDelta);
+ });
+ $("#content").css("overflow-y","hidden");
+ }
+
+ if(isMobile.Android()) {
+ $('.show-android').show(0);
+ $('.show-blackberry, .show-ios, .show-windows').hide(0);
+ }
+
+ if(isMobile.BlackBerry()) {
+ $('.show-blackberry').show(0);
+ $('.show-android, .show-ios, .show-windows').hide(0);
+ }
+
+ if(isMobile.iOS()) {
+ $('.show-ios').show(0);
+ $('.show-blackberry, .show-android, .show-windows').hide(0);
+ }
+
+ if(isMobile.Windows()) {
+ $('.show-windows').show(0);
+ $('.show-blackberry, .show-ios, .show-android').hide(0);
+ }
+
+ $('.back-to-top-badge, .back-to-top').click(function() {
+ $('#content').animate({
+ scrollTop:0
+ }, 500, 'easeInOutQuad');
+ return false;
+ });
+
+ //Show Back To Home When Scrolling
+
+ $('#content').on('scroll', function () {
+ var total_scroll_height = $('#content')[0].scrollHeight
+ var inside_header = ($(this).scrollTop() <= 150);
+ var passed_header = ($(this).scrollTop() >= 0); //250
+ var footer_reached = ($(this).scrollTop() >= (total_scroll_height - ($(window).height() +100 )));
+
+ if (inside_header == true) {
+ $('.back-to-top-badge').removeClass('back-to-top-badge-visible');
+ } else if (passed_header == true) {
+ $('.back-to-top-badge').addClass('back-to-top-badge-visible');
+ }
+ if (footer_reached == true){
+ $('.back-to-top-badge').removeClass('back-to-top-badge-visible');
+ }
+ });
+
+ //Make contianer fullscreen//
+
+ function create_paddings(){
+ var no_padding = $(window).width();
+ function mobile_paddings(){
+ $('.content').css('padding-left', '20px');
+ $('.content').css('padding-right', '20px');
+ $('.container-fullscreen, .image-fullscreen').css('margin-left', '-20px');
+ $('.container-fullscreen, .image-fullscreen').css('width', no_padding +2);
+ }
+
+ function tablet_paddings(){
+ $('.content').css('padding-left', '50px');
+ $('.content').css('padding-right', '50px');
+ $('.container-fullscreen, .image-fullscreen').css('margin-left', '-50px');
+ $('.container-fullscreen, .image-fullscreen').css('width', no_padding +2);
+ }
+
+ if($(window).width() < 766){
+ mobile_paddings()
+ }
+ if($(window).width() > 766){
+ tablet_paddings()
+ }
+ }
+
+ $(window).resize(function() {
+ create_paddings();
+ });
+
+ create_paddings();
+
+ //Morph Headings
+
+ $(".infinite-text").Morphext({
+ // The [in] animation type. Refer to Animate.css for a list of available animations.
+ animation: "flipInX",
+ // An array of phrases to rotate are created based on this separator. Change it if you wish to separate the phrases differently (e.g. So Simple | Very Doge | Much Wow | Such Cool).
+ separator: "|",
+ // The delay between the changing of each phrase in milliseconds.
+ speed: 2000,
+ complete: function () {
+ // Called after the entrance animation is executed.
+ }
+ });
+
+ //Set inputs to today's date by adding class set-day
+
+ var set_input_now = new Date();
+ var set_input_month = (set_input_now.getMonth() + 1);
+ var set_input_day = set_input_now.getDate();
+ if(set_input_month < 10)
+ set_input_month = "0" + set_input_month;
+ if(set_input_day < 10)
+ set_input_day = "0" + set_input_day;
+ var set_input_today = set_input_now.getFullYear() + '-' + set_input_month + '-' + set_input_day;
+ $('.set-today').val(set_input_today);
+
+
+ //Portfolios and Gallerties
+
+ $('.adaptive-one').click(function(){
+ $('.portfolio-switch').removeClass('active-adaptive');
+ $(this).addClass('active-adaptive');
+ $('.portfolio-adaptive').removeClass('portfolio-adaptive-two portfolio-adaptive-three');
+ $('.portfolio-adaptive').addClass('portfolio-adaptive-one');
+ return false;
+ });
+
+ $('.adaptive-two').click(function(){
+ $('.portfolio-switch').removeClass('active-adaptive');
+ $(this).addClass('active-adaptive');
+ $('.portfolio-adaptive').removeClass('portfolio-adaptive-one portfolio-adaptive-three');
+ $('.portfolio-adaptive').addClass('portfolio-adaptive-two');
+ return false;
+ });
+
+ $('.adaptive-three').click(function(){
+ $('.portfolio-switch').removeClass('active-adaptive');
+ $(this).addClass('active-adaptive');
+ $('.portfolio-adaptive').removeClass('portfolio-adaptive-two portfolio-adaptive-one');
+ $('.portfolio-adaptive').addClass('portfolio-adaptive-three');
+ return false;
+ });
+
+ //Wide Portfolio
+
+ $('.show-wide-text').click(function(){
+ $(this).parent().find('.wide-text').slideToggle(200);
+ return false;
+ });
+
+ $('.portfolio-close').click(function(){
+ $(this).parent().parent().find('.wide-text').slideToggle(200);
+ return false;
+ });
+
+ $('.show-gallery, .show-gallery-1, .show-gallery-2, .show-gallery-3, .show-gallery-4, .show-gallery-5, .add-gallery a').swipebox();
+
+ function apply_gallery_justification(){
+ var screen_widths = $(window).width();
+ if( screen_widths < 768){
+ $('.gallery-justified').justifiedGallery({
+ rowHeight : 70,
+ maxRowHeight : 370,
+ margins : 5,
+ fixedHeight:false
+ });
+ };
+
+ if( screen_widths > 768){
+ $('.gallery-justified').justifiedGallery({
+ rowHeight : 150,
+ maxRowHeight : 370,
+ margins : 5,
+ fixedHeight:false
+ });
+ };
+ };
+ apply_gallery_justification();
+
+ //Filterable Gallery
+
+ var selectedClass = "";
+ $(".filter-category").click(function(){
+ $('.portfolio-filter-categories a').removeClass('selected-filter');
+ $(this).addClass('selected-filter');
+ selectedClass = $(this).attr("data-rel");
+ $(".portfolio-filter-wrapper").show(250);
+ $(".portfolio-filter-wrapper div").not("."+selectedClass).delay(100).hide(250);
+ setTimeout(function() {
+ $("."+selectedClass).show(250);
+ $(".portfolio-filter-wrapper").show(250);
+ }, 0);
+ });
+
+ if($('body').hasClass('dual-sidebar')){ dual_sidebar(); }
+ if($('body').hasClass('left-sidebar')){ left_sidebar(); }
+ if($('body').hasClass('right-sidebar')){ right_sidebar(); }
+ if($('body').hasClass('no-sidebar')){ no_sidebar(); }
+
+ function dual_sidebar(){
+ var $div = $('
').appendTo('body');
+ $div.attr('id', 'footer-fixed');
+ $div.attr('class', 'not-active');
+ var snapper = new Snap({
+ element: document.getElementById('content'),
+ elementMirror: document.getElementById('header'),
+ elementMirror2: document.getElementById('footer-fixed'),
+ disable: 'none',
+ tapToClose: true,
+ touchToDrag: true,
+ maxPosition: 266,
+ minPosition: -266
+ });
+ $('.close-sidebar').click(function(){snapper.close(); return false;});
+ $('.open-left-sidebar').click(function() {
+ //$(this).toggleClass('remove-sidebar');
+ if( snapper.state().state=="left" ){
+ snapper.close();
+ } else {
+ snapper.open('left');
+ }
+ return false;
+ });
+ $('.open-right-sidebar').click(function() {
+ //$(this).toggleClass('remove-sidebar');
+ if( snapper.state().state=="right" ){
+ snapper.close();
+ } else {
+ snapper.open('right');
+ }
+ return false;
+ });
+ snapper.on('open', function(){$('.back-to-top-badge').removeClass('back-to-top-badge-visible');});
+ };
+
+ function left_sidebar(){
+ var $div = $('
').appendTo('body');
+ $div.attr('id', 'footer-fixed');
+ $div.attr('class', 'not-active');
+ var snapper = new Snap({
+ element: document.getElementById('content'),
+ elementMirror: document.getElementById('header'),
+ elementMirror2: document.getElementById('footer-fixed'),
+ disable: 'right',
+ tapToClose: true,
+ touchToDrag: true,
+ maxPosition: 266,
+ minPosition: -266
+ });
+ $('.close-sidebar').click(function(){snapper.close(); return false;});
+ $('.open-left-sidebar').click(function() {
+ //$(this).toggleClass('remove-sidebar');
+ if( snapper.state().state=="left" ){
+ snapper.close();
+ } else {
+ snapper.open('left');
+ }
+ return false;
+ });
+ snapper.on('open', function(){$('.back-to-top-badge').removeClass('back-to-top-badge-visible');});
+ };
+
+ function right_sidebar(){
+ var $div = $('
').appendTo('body');
+ $div.attr('id', 'footer-fixed');
+ $div.attr('class', 'not-active');
+ var snapper = new Snap({
+ element: document.getElementById('content'),
+ elementMirror: document.getElementById('header'),
+ elementMirror2: document.getElementById('footer-fixed'),
+ disable: 'left',
+ tapToClose: true,
+ touchToDrag: true,
+ maxPosition: 266,
+ minPosition: -266
+ });
+ $('.close-sidebar').click(function(){snapper.close(); return false;});
+ $('.open-right-sidebar').click(function() {
+ //$(this).toggleClass('remove-sidebar');
+ if( snapper.state().state=="right" ){
+ snapper.close();
+ } else {
+ snapper.open('right');
+ }
+ return false;
+ });
+ snapper.on('open', function(){$('.back-to-top-badge').removeClass('back-to-top-badge-visible');});
+ };
+
+ function no_sidebar(){
+ var snapper = new Snap({
+ element: document.getElementById('content'),
+ elementMirror: document.getElementById('header'),
+ elementMirror2: document.getElementById('footer-fixed'),
+ disable: 'none',
+ tapToClose: false,
+ touchToDrag: false
+ });
+ };
+
+ //Fullscreen Map
+
+ $('.map-text, .map-overlay').click(function(){
+ $('.map-text, .map-overlay').fadeOut(200);
+ $('.deactivate-map').fadeIn(200);
+ });
+
+ $('.deactivate-map').click(function(){
+ $('.map-text, .map-overlay').fadeIn(200);
+ $('.deactivate-map').fadeOut(200);
+ });
+
+ function generate_map(){
+ var map_width = $(window).width();
+ var map_height = $(window).height();
+
+ $('.map-fullscreen iframe').css('width', map_width);
+ $('.map-fullscreen iframe').css('height', map_height);
+ };
+ generate_map();
+
+ //-------------------Generate Cover Screen Elements--------------------//
+ //Global Settings for Fullscreen Pages, PageApps and Coverscreen Slider//
+
+ function align_cover_elements(){
+ var cover_width = $(window).width();
+ var cover_height = $(window).height();
+ var cover_vertical = -($('.cover-center').height())/2;
+ var cover_horizontal = -($('.cover-center').width())/2;
+
+ $('.cover-screen').css('width', cover_width);
+ $('.cover-screen').css('height', cover_height);
+ $('.cover-screen .overlay').css('width', cover_width);
+ $('.cover-screen .overlay').css('height', cover_height);
+
+ $('.cover-center').css('margin-left', cover_horizontal);
+ $('.cover-center').css('margin-top', cover_vertical + 30);
+ $('.cover-left').css('margin-top', cover_vertical);
+ $('.cover-right').css('margin-top', cover_vertical);
+
+ $('.homepage-cover, .homepage-cover-slider').css('height', cover_height);
+ $('.homepage-cover, .homepage-cover-slider').css('width', cover_width);
+
+ };
+ align_cover_elements();
+
+ //Resize Functions//
+
+ $(window).resize(function(){
+ apply_gallery_justification();
+ align_cover_elements();
+ generate_map();
+ });
+
+ //Swipebox Image Gallery//
+ //SVG Usage is not recommended due to poor compatibility with older Android / Windows Mobile Devices//
+
+ $(".swipebox").swipebox({
+ useCSS : true,
+ hideBarsDelay : 3000 // 0 to always show caption and action bar
+ });
+
+
+
+});
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/megp/js/form.js b/template/megp/js/form.js
new file mode 100644
index 0000000..c67fc33
--- /dev/null
+++ b/template/megp/js/form.js
@@ -0,0 +1,1277 @@
+/*!
+ * jQuery Form Plugin
+ * version: 3.51.0-2014.06.20
+ * Requires jQuery v1.5 or later
+ * Copyright (c) 2014 M. Alsup
+ * Examples and documentation at: http://malsup.com/jquery/form/
+ * Project repository: https://github.com/malsup/form
+ * Dual licensed under the MIT and GPL licenses.
+ * https://github.com/malsup/form#copyright-and-license
+ */
+/*global ActiveXObject */
+
+// AMD support
+(function (factory) {
+ "use strict";
+ if (typeof define === 'function' && define.amd) {
+ // using AMD; register as anon module
+ define(['jquery'], factory);
+ } else {
+ // no AMD; invoke directly
+ factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
+ }
+}
+
+(function($) {
+"use strict";
+
+/*
+ Usage Note:
+ -----------
+ Do not use both ajaxSubmit and ajaxForm on the same form. These
+ functions are mutually exclusive. Use ajaxSubmit if you want
+ to bind your own submit handler to the form. For example,
+
+ $(document).ready(function() {
+ $('#myForm').on('submit', function(e) {
+ e.preventDefault(); // <-- important
+ $(this).ajaxSubmit({
+ target: '#output'
+ });
+ });
+ });
+
+ Use ajaxForm when you want the plugin to manage all the event binding
+ for you. For example,
+
+ $(document).ready(function() {
+ $('#myForm').ajaxForm({
+ target: '#output'
+ });
+ });
+
+ You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
+ form does not have to exist when you invoke ajaxForm:
+
+ $('#myForm').ajaxForm({
+ delegation: true,
+ target: '#output'
+ });
+
+ When using ajaxForm, the ajaxSubmit function will be invoked for you
+ at the appropriate time.
+*/
+
+/**
+ * Feature detection
+ */
+var feature = {};
+feature.fileapi = $(" ").get(0).files !== undefined;
+feature.formdata = window.FormData !== undefined;
+
+var hasProp = !!$.fn.prop;
+
+// attr2 uses prop when it can but checks the return type for
+// an expected string. this accounts for the case where a form
+// contains inputs with names like "action" or "method"; in those
+// cases "prop" returns the element
+$.fn.attr2 = function() {
+ if ( ! hasProp ) {
+ return this.attr.apply(this, arguments);
+ }
+ var val = this.prop.apply(this, arguments);
+ if ( ( val && val.jquery ) || typeof val === 'string' ) {
+ return val;
+ }
+ return this.attr.apply(this, arguments);
+};
+
+/**
+ * ajaxSubmit() provides a mechanism for immediately submitting
+ * an HTML form using AJAX.
+ */
+$.fn.ajaxSubmit = function(options) {
+ /*jshint scripturl:true */
+
+ // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
+ if (!this.length) {
+ log('ajaxSubmit: skipping submit process - no element selected');
+ return this;
+ }
+
+ var method, action, url, $form = this;
+
+ if (typeof options == 'function') {
+ options = { success: options };
+ }
+ else if ( options === undefined ) {
+ options = {};
+ }
+
+ method = options.type || this.attr2('method');
+ action = options.url || this.attr2('action');
+
+ url = (typeof action === 'string') ? $.trim(action) : '';
+ url = url || window.location.href || '';
+ if (url) {
+ // clean url (don't include hash vaue)
+ url = (url.match(/^([^#]+)/)||[])[1];
+ }
+
+ options = $.extend(true, {
+ url: url,
+ success: $.ajaxSettings.success,
+ type: method || $.ajaxSettings.type,
+ iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
+ }, options);
+
+ // hook for manipulating the form data before it is extracted;
+ // convenient for use with rich editors like tinyMCE or FCKEditor
+ var veto = {};
+ this.trigger('form-pre-serialize', [this, options, veto]);
+ if (veto.veto) {
+ log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
+ return this;
+ }
+
+ // provide opportunity to alter form data before it is serialized
+ if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
+ log('ajaxSubmit: submit aborted via beforeSerialize callback');
+ return this;
+ }
+
+ var traditional = options.traditional;
+ if ( traditional === undefined ) {
+ traditional = $.ajaxSettings.traditional;
+ }
+
+ var elements = [];
+ var qx, a = this.formToArray(options.semantic, elements);
+ if (options.data) {
+ options.extraData = options.data;
+ qx = $.param(options.data, traditional);
+ }
+
+ // give pre-submit callback an opportunity to abort the submit
+ if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
+ log('ajaxSubmit: submit aborted via beforeSubmit callback');
+ return this;
+ }
+
+ // fire vetoable 'validate' event
+ this.trigger('form-submit-validate', [a, this, options, veto]);
+ if (veto.veto) {
+ log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
+ return this;
+ }
+
+ var q = $.param(a, traditional);
+ if (qx) {
+ q = ( q ? (q + '&' + qx) : qx );
+ }
+ if (options.type.toUpperCase() == 'GET') {
+ options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
+ options.data = null; // data is null for 'get'
+ }
+ else {
+ options.data = q; // data is the query string for 'post'
+ }
+
+ var callbacks = [];
+ if (options.resetForm) {
+ callbacks.push(function() { $form.resetForm(); });
+ }
+ if (options.clearForm) {
+ callbacks.push(function() { $form.clearForm(options.includeHidden); });
+ }
+
+ // perform a load on the target only if dataType is not provided
+ if (!options.dataType && options.target) {
+ var oldSuccess = options.success || function(){};
+ callbacks.push(function(data) {
+ var fn = options.replaceTarget ? 'replaceWith' : 'html';
+ $(options.target)[fn](data).each(oldSuccess, arguments);
+ });
+ }
+ else if (options.success) {
+ callbacks.push(options.success);
+ }
+
+ options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
+ var context = options.context || this ; // jQuery 1.4+ supports scope context
+ for (var i=0, max=callbacks.length; i < max; i++) {
+ callbacks[i].apply(context, [data, status, xhr || $form, $form]);
+ }
+ };
+
+ if (options.error) {
+ var oldError = options.error;
+ options.error = function(xhr, status, error) {
+ var context = options.context || this;
+ oldError.apply(context, [xhr, status, error, $form]);
+ };
+ }
+
+ if (options.complete) {
+ var oldComplete = options.complete;
+ options.complete = function(xhr, status) {
+ var context = options.context || this;
+ oldComplete.apply(context, [xhr, status, $form]);
+ };
+ }
+
+ // are there files to upload?
+
+ // [value] (issue #113), also see comment:
+ // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
+ var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; });
+
+ var hasFileInputs = fileInputs.length > 0;
+ var mp = 'multipart/form-data';
+ var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
+
+ var fileAPI = feature.fileapi && feature.formdata;
+ log("fileAPI :" + fileAPI);
+ var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
+
+ var jqxhr;
+
+ // options.iframe allows user to force iframe mode
+ // 06-NOV-09: now defaulting to iframe mode if file input is detected
+ if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
+ // hack to fix Safari hang (thanks to Tim Molendijk for this)
+ // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
+ if (options.closeKeepAlive) {
+ $.get(options.closeKeepAlive, function() {
+ jqxhr = fileUploadIframe(a);
+ });
+ }
+ else {
+ jqxhr = fileUploadIframe(a);
+ }
+ }
+ else if ((hasFileInputs || multipart) && fileAPI) {
+ jqxhr = fileUploadXhr(a);
+ }
+ else {
+ jqxhr = $.ajax(options);
+ }
+
+ $form.removeData('jqxhr').data('jqxhr', jqxhr);
+
+ // clear element array
+ for (var k=0; k < elements.length; k++) {
+ elements[k] = null;
+ }
+
+ // fire 'notify' event
+ this.trigger('form-submit-notify', [this, options]);
+ return this;
+
+ // utility fn for deep serialization
+ function deepSerialize(extraData){
+ var serialized = $.param(extraData, options.traditional).split('&');
+ var len = serialized.length;
+ var result = [];
+ var i, part;
+ for (i=0; i < len; i++) {
+ // #252; undo param space replacement
+ serialized[i] = serialized[i].replace(/\+/g,' ');
+ part = serialized[i].split('=');
+ // #278; use array instead of object storage, favoring array serializations
+ result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
+ }
+ return result;
+ }
+
+ // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
+ function fileUploadXhr(a) {
+ var formdata = new FormData();
+
+ for (var i=0; i < a.length; i++) {
+ formdata.append(a[i].name, a[i].value);
+ }
+
+ if (options.extraData) {
+ var serializedData = deepSerialize(options.extraData);
+ for (i=0; i < serializedData.length; i++) {
+ if (serializedData[i]) {
+ formdata.append(serializedData[i][0], serializedData[i][1]);
+ }
+ }
+ }
+
+ options.data = null;
+
+ var s = $.extend(true, {}, $.ajaxSettings, options, {
+ contentType: false,
+ processData: false,
+ cache: false,
+ type: method || 'POST'
+ });
+
+ if (options.uploadProgress) {
+ // workaround because jqXHR does not expose upload property
+ s.xhr = function() {
+ var xhr = $.ajaxSettings.xhr();
+ if (xhr.upload) {
+ xhr.upload.addEventListener('progress', function(event) {
+ var percent = 0;
+ var position = event.loaded || event.position; /*event.position is deprecated*/
+ var total = event.total;
+ if (event.lengthComputable) {
+ percent = Math.ceil(position / total * 100);
+ }
+ options.uploadProgress(event, position, total, percent);
+ }, false);
+ }
+ return xhr;
+ };
+ }
+
+ s.data = null;
+ var beforeSend = s.beforeSend;
+ s.beforeSend = function(xhr, o) {
+ //Send FormData() provided by user
+ if (options.formData) {
+ o.data = options.formData;
+ }
+ else {
+ o.data = formdata;
+ }
+ if(beforeSend) {
+ beforeSend.call(this, xhr, o);
+ }
+ };
+ return $.ajax(s);
+ }
+
+ // private function for handling file uploads (hat tip to YAHOO!)
+ function fileUploadIframe(a) {
+ var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
+ var deferred = $.Deferred();
+
+ // #341
+ deferred.abort = function(status) {
+ xhr.abort(status);
+ };
+
+ if (a) {
+ // ensure that every serialized input is still enabled
+ for (i=0; i < elements.length; i++) {
+ el = $(elements[i]);
+ if ( hasProp ) {
+ el.prop('disabled', false);
+ }
+ else {
+ el.removeAttr('disabled');
+ }
+ }
+ }
+
+ s = $.extend(true, {}, $.ajaxSettings, options);
+ s.context = s.context || s;
+ id = 'jqFormIO' + (new Date().getTime());
+ if (s.iframeTarget) {
+ $io = $(s.iframeTarget);
+ n = $io.attr2('name');
+ if (!n) {
+ $io.attr2('name', id);
+ }
+ else {
+ id = n;
+ }
+ }
+ else {
+ $io = $('');
+ $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
+ }
+ io = $io[0];
+
+
+ xhr = { // mock object
+ aborted: 0,
+ responseText: null,
+ responseXML: null,
+ status: 0,
+ statusText: 'n/a',
+ getAllResponseHeaders: function() {},
+ getResponseHeader: function() {},
+ setRequestHeader: function() {},
+ abort: function(status) {
+ var e = (status === 'timeout' ? 'timeout' : 'aborted');
+ log('aborting upload... ' + e);
+ this.aborted = 1;
+
+ try { // #214, #257
+ if (io.contentWindow.document.execCommand) {
+ io.contentWindow.document.execCommand('Stop');
+ }
+ }
+ catch(ignore) {}
+
+ $io.attr('src', s.iframeSrc); // abort op in progress
+ xhr.error = e;
+ if (s.error) {
+ s.error.call(s.context, xhr, e, status);
+ }
+ if (g) {
+ $.event.trigger("ajaxError", [xhr, s, e]);
+ }
+ if (s.complete) {
+ s.complete.call(s.context, xhr, e);
+ }
+ }
+ };
+
+ g = s.global;
+ // trigger ajax global events so that activity/block indicators work like normal
+ if (g && 0 === $.active++) {
+ $.event.trigger("ajaxStart");
+ }
+ if (g) {
+ $.event.trigger("ajaxSend", [xhr, s]);
+ }
+
+ if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
+ if (s.global) {
+ $.active--;
+ }
+ deferred.reject();
+ return deferred;
+ }
+ if (xhr.aborted) {
+ deferred.reject();
+ return deferred;
+ }
+
+ // add submitting element to data if we know it
+ sub = form.clk;
+ if (sub) {
+ n = sub.name;
+ if (n && !sub.disabled) {
+ s.extraData = s.extraData || {};
+ s.extraData[n] = sub.value;
+ if (sub.type == "image") {
+ s.extraData[n+'.x'] = form.clk_x;
+ s.extraData[n+'.y'] = form.clk_y;
+ }
+ }
+ }
+
+ var CLIENT_TIMEOUT_ABORT = 1;
+ var SERVER_ABORT = 2;
+
+ function getDoc(frame) {
+ /* it looks like contentWindow or contentDocument do not
+ * carry the protocol property in ie8, when running under ssl
+ * frame.document is the only valid response document, since
+ * the protocol is know but not on the other two objects. strange?
+ * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
+ */
+
+ var doc = null;
+
+ // IE8 cascading access check
+ try {
+ if (frame.contentWindow) {
+ doc = frame.contentWindow.document;
+ }
+ } catch(err) {
+ // IE8 access denied under ssl & missing protocol
+ log('cannot get iframe.contentWindow document: ' + err);
+ }
+
+ if (doc) { // successful getting content
+ return doc;
+ }
+
+ try { // simply checking may throw in ie8 under ssl or mismatched protocol
+ doc = frame.contentDocument ? frame.contentDocument : frame.document;
+ } catch(err) {
+ // last attempt
+ log('cannot get iframe.contentDocument: ' + err);
+ doc = frame.document;
+ }
+ return doc;
+ }
+
+ // Rails CSRF hack (thanks to Yvan Barthelemy)
+ var csrf_token = $('meta[name=csrf-token]').attr('content');
+ var csrf_param = $('meta[name=csrf-param]').attr('content');
+ if (csrf_param && csrf_token) {
+ s.extraData = s.extraData || {};
+ s.extraData[csrf_param] = csrf_token;
+ }
+
+ // take a breath so that pending repaints get some cpu time before the upload starts
+ function doSubmit() {
+ // make sure form attrs are set
+ var t = $form.attr2('target'),
+ a = $form.attr2('action'),
+ mp = 'multipart/form-data',
+ et = $form.attr('enctype') || $form.attr('encoding') || mp;
+
+ // update form attrs in IE friendly way
+ form.setAttribute('target',id);
+ if (!method || /post/i.test(method) ) {
+ form.setAttribute('method', 'POST');
+ }
+ if (a != s.url) {
+ form.setAttribute('action', s.url);
+ }
+
+ // ie borks in some cases when setting encoding
+ if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
+ $form.attr({
+ encoding: 'multipart/form-data',
+ enctype: 'multipart/form-data'
+ });
+ }
+
+ // support timout
+ if (s.timeout) {
+ timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
+ }
+
+ // look for server aborts
+ function checkState() {
+ try {
+ var state = getDoc(io).readyState;
+ log('state = ' + state);
+ if (state && state.toLowerCase() == 'uninitialized') {
+ setTimeout(checkState,50);
+ }
+ }
+ catch(e) {
+ log('Server abort: ' , e, ' (', e.name, ')');
+ cb(SERVER_ABORT);
+ if (timeoutHandle) {
+ clearTimeout(timeoutHandle);
+ }
+ timeoutHandle = undefined;
+ }
+ }
+
+ // add "extra" data to form if provided in options
+ var extraInputs = [];
+ try {
+ if (s.extraData) {
+ for (var n in s.extraData) {
+ if (s.extraData.hasOwnProperty(n)) {
+ // if using the $.param format that allows for multiple values with the same name
+ if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
+ extraInputs.push(
+ $(' ').val(s.extraData[n].value)
+ .appendTo(form)[0]);
+ } else {
+ extraInputs.push(
+ $(' ').val(s.extraData[n])
+ .appendTo(form)[0]);
+ }
+ }
+ }
+ }
+
+ if (!s.iframeTarget) {
+ // add iframe to doc and submit the form
+ $io.appendTo('body');
+ }
+ if (io.attachEvent) {
+ io.attachEvent('onload', cb);
+ }
+ else {
+ io.addEventListener('load', cb, false);
+ }
+ setTimeout(checkState,15);
+
+ try {
+ form.submit();
+ } catch(err) {
+ // just in case form has element with name/id of 'submit'
+ var submitFn = document.createElement('form').submit;
+ submitFn.apply(form);
+ }
+ }
+ finally {
+ // reset attrs and remove "extra" input elements
+ form.setAttribute('action',a);
+ form.setAttribute('enctype', et); // #380
+ if(t) {
+ form.setAttribute('target', t);
+ } else {
+ $form.removeAttr('target');
+ }
+ $(extraInputs).remove();
+ }
+ }
+
+ if (s.forceSync) {
+ doSubmit();
+ }
+ else {
+ setTimeout(doSubmit, 10); // this lets dom updates render
+ }
+
+ var data, doc, domCheckCount = 50, callbackProcessed;
+
+ function cb(e) {
+ if (xhr.aborted || callbackProcessed) {
+ return;
+ }
+
+ doc = getDoc(io);
+ if(!doc) {
+ log('cannot access response document');
+ e = SERVER_ABORT;
+ }
+ if (e === CLIENT_TIMEOUT_ABORT && xhr) {
+ xhr.abort('timeout');
+ deferred.reject(xhr, 'timeout');
+ return;
+ }
+ else if (e == SERVER_ABORT && xhr) {
+ xhr.abort('server abort');
+ deferred.reject(xhr, 'error', 'server abort');
+ return;
+ }
+
+ if (!doc || doc.location.href == s.iframeSrc) {
+ // response not received yet
+ if (!timedOut) {
+ return;
+ }
+ }
+ if (io.detachEvent) {
+ io.detachEvent('onload', cb);
+ }
+ else {
+ io.removeEventListener('load', cb, false);
+ }
+
+ var status = 'success', errMsg;
+ try {
+ if (timedOut) {
+ throw 'timeout';
+ }
+
+ var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
+ log('isXml='+isXml);
+ if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
+ if (--domCheckCount) {
+ // in some browsers (Opera) the iframe DOM is not always traversable when
+ // the onload callback fires, so we loop a bit to accommodate
+ log('requeing onLoad callback, DOM not available');
+ setTimeout(cb, 250);
+ return;
+ }
+ // let this fall through because server response could be an empty document
+ //log('Could not access iframe DOM after mutiple tries.');
+ //throw 'DOMException: not available';
+ }
+
+ //log('response detected');
+ var docRoot = doc.body ? doc.body : doc.documentElement;
+ xhr.responseText = docRoot ? docRoot.innerHTML : null;
+ xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
+ if (isXml) {
+ s.dataType = 'xml';
+ }
+ xhr.getResponseHeader = function(header){
+ var headers = {'content-type': s.dataType};
+ return headers[header.toLowerCase()];
+ };
+ // support for XHR 'status' & 'statusText' emulation :
+ if (docRoot) {
+ xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
+ xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
+ }
+
+ var dt = (s.dataType || '').toLowerCase();
+ var scr = /(json|script|text)/.test(dt);
+ if (scr || s.textarea) {
+ // see if user embedded response in textarea
+ var ta = doc.getElementsByTagName('textarea')[0];
+ if (ta) {
+ xhr.responseText = ta.value;
+ // support for XHR 'status' & 'statusText' emulation :
+ xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
+ xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
+ }
+ else if (scr) {
+ // account for browsers injecting pre around json response
+ var pre = doc.getElementsByTagName('pre')[0];
+ var b = doc.getElementsByTagName('body')[0];
+ if (pre) {
+ xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
+ }
+ else if (b) {
+ xhr.responseText = b.textContent ? b.textContent : b.innerText;
+ }
+ }
+ }
+ else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
+ xhr.responseXML = toXml(xhr.responseText);
+ }
+
+ try {
+ data = httpData(xhr, dt, s);
+ }
+ catch (err) {
+ status = 'parsererror';
+ xhr.error = errMsg = (err || status);
+ }
+ }
+ catch (err) {
+ log('error caught: ',err);
+ status = 'error';
+ xhr.error = errMsg = (err || status);
+ }
+
+ if (xhr.aborted) {
+ log('upload aborted');
+ status = null;
+ }
+
+ if (xhr.status) { // we've set xhr.status
+ status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
+ }
+
+ // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
+ if (status === 'success') {
+ if (s.success) {
+ s.success.call(s.context, data, 'success', xhr);
+ }
+ deferred.resolve(xhr.responseText, 'success', xhr);
+ if (g) {
+ $.event.trigger("ajaxSuccess", [xhr, s]);
+ }
+ }
+ else if (status) {
+ if (errMsg === undefined) {
+ errMsg = xhr.statusText;
+ }
+ if (s.error) {
+ s.error.call(s.context, xhr, status, errMsg);
+ }
+ deferred.reject(xhr, 'error', errMsg);
+ if (g) {
+ $.event.trigger("ajaxError", [xhr, s, errMsg]);
+ }
+ }
+
+ if (g) {
+ $.event.trigger("ajaxComplete", [xhr, s]);
+ }
+
+ if (g && ! --$.active) {
+ $.event.trigger("ajaxStop");
+ }
+
+ if (s.complete) {
+ s.complete.call(s.context, xhr, status);
+ }
+
+ callbackProcessed = true;
+ if (s.timeout) {
+ clearTimeout(timeoutHandle);
+ }
+
+ // clean up
+ setTimeout(function() {
+ if (!s.iframeTarget) {
+ $io.remove();
+ }
+ else { //adding else to clean up existing iframe response.
+ $io.attr('src', s.iframeSrc);
+ }
+ xhr.responseXML = null;
+ }, 100);
+ }
+
+ var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
+ if (window.ActiveXObject) {
+ doc = new ActiveXObject('Microsoft.XMLDOM');
+ doc.async = 'false';
+ doc.loadXML(s);
+ }
+ else {
+ doc = (new DOMParser()).parseFromString(s, 'text/xml');
+ }
+ return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
+ };
+ var parseJSON = $.parseJSON || function(s) {
+ /*jslint evil:true */
+ return window['eval']('(' + s + ')');
+ };
+
+ var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
+
+ var ct = xhr.getResponseHeader('content-type') || '',
+ xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if (xml && data.documentElement.nodeName === 'parsererror') {
+ if ($.error) {
+ $.error('parsererror');
+ }
+ }
+ if (s && s.dataFilter) {
+ data = s.dataFilter(data, type);
+ }
+ if (typeof data === 'string') {
+ if (type === 'json' || !type && ct.indexOf('json') >= 0) {
+ data = parseJSON(data);
+ } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
+ $.globalEval(data);
+ }
+ }
+ return data;
+ };
+
+ return deferred;
+ }
+};
+
+/**
+ * ajaxForm() provides a mechanism for fully automating form submission.
+ *
+ * The advantages of using this method instead of ajaxSubmit() are:
+ *
+ * 1: This method will include coordinates for elements (if the element
+ * is used to submit the form).
+ * 2. This method will include the submit element's name/value data (for the element that was
+ * used to submit the form).
+ * 3. This method binds the submit() method to the form for you.
+ *
+ * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
+ * passes the options argument along after properly binding events for submit elements and
+ * the form itself.
+ */
+$.fn.ajaxForm = function(options) {
+ options = options || {};
+ options.delegation = options.delegation && $.isFunction($.fn.on);
+
+ // in jQuery 1.3+ we can fix mistakes with the ready state
+ if (!options.delegation && this.length === 0) {
+ var o = { s: this.selector, c: this.context };
+ if (!$.isReady && o.s) {
+ log('DOM not ready, queuing ajaxForm');
+ $(function() {
+ $(o.s,o.c).ajaxForm(options);
+ });
+ return this;
+ }
+ // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
+ log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
+ return this;
+ }
+
+ if ( options.delegation ) {
+ $(document)
+ .off('submit.form-plugin', this.selector, doAjaxSubmit)
+ .off('click.form-plugin', this.selector, captureSubmittingElement)
+ .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
+ .on('click.form-plugin', this.selector, options, captureSubmittingElement);
+ return this;
+ }
+
+ return this.ajaxFormUnbind()
+ .bind('submit.form-plugin', options, doAjaxSubmit)
+ .bind('click.form-plugin', options, captureSubmittingElement);
+};
+
+// private event handlers
+function doAjaxSubmit(e) {
+ /*jshint validthis:true */
+ var options = e.data;
+ if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
+ e.preventDefault();
+ $(e.target).ajaxSubmit(options); // #365
+ }
+}
+
+function captureSubmittingElement(e) {
+ /*jshint validthis:true */
+ var target = e.target;
+ var $el = $(target);
+ if (!($el.is("[type=submit],[type=image]"))) {
+ // is this a child element of the submit el? (ex: a span within a button)
+ var t = $el.closest('[type=submit]');
+ if (t.length === 0) {
+ return;
+ }
+ target = t[0];
+ }
+ var form = this;
+ form.clk = target;
+ if (target.type == 'image') {
+ if (e.offsetX !== undefined) {
+ form.clk_x = e.offsetX;
+ form.clk_y = e.offsetY;
+ } else if (typeof $.fn.offset == 'function') {
+ var offset = $el.offset();
+ form.clk_x = e.pageX - offset.left;
+ form.clk_y = e.pageY - offset.top;
+ } else {
+ form.clk_x = e.pageX - target.offsetLeft;
+ form.clk_y = e.pageY - target.offsetTop;
+ }
+ }
+ // clear form vars
+ setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
+}
+
+
+// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
+$.fn.ajaxFormUnbind = function() {
+ return this.unbind('submit.form-plugin click.form-plugin');
+};
+
+/**
+ * formToArray() gathers form element data into an array of objects that can
+ * be passed to any of the following ajax functions: $.get, $.post, or load.
+ * Each object in the array has both a 'name' and 'value' property. An example of
+ * an array for a simple login form might be:
+ *
+ * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
+ *
+ * It is this array that is passed to pre-submit callback functions provided to the
+ * ajaxSubmit() and ajaxForm() methods.
+ */
+$.fn.formToArray = function(semantic, elements) {
+ var a = [];
+ if (this.length === 0) {
+ return a;
+ }
+
+ var form = this[0];
+ var formId = this.attr('id');
+ var els = semantic ? form.getElementsByTagName('*') : form.elements;
+ var els2;
+
+ if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
+ els = $(els).get(); // convert to standard array
+ }
+
+ // #386; account for inputs outside the form which use the 'form' attribute
+ if ( formId ) {
+ els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
+ if ( els2.length ) {
+ els = (els || []).concat(els2);
+ }
+ }
+
+ if (!els || !els.length) {
+ return a;
+ }
+
+ var i,j,n,v,el,max,jmax;
+ for(i=0, max=els.length; i < max; i++) {
+ el = els[i];
+ n = el.name;
+ if (!n || el.disabled) {
+ continue;
+ }
+
+ if (semantic && form.clk && el.type == "image") {
+ // handle image inputs on the fly when semantic == true
+ if(form.clk == el) {
+ a.push({name: n, value: $(el).val(), type: el.type });
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
+ }
+ continue;
+ }
+
+ v = $.fieldValue(el, true);
+ if (v && v.constructor == Array) {
+ if (elements) {
+ elements.push(el);
+ }
+ for(j=0, jmax=v.length; j < jmax; j++) {
+ a.push({name: n, value: v[j]});
+ }
+ }
+ else if (feature.fileapi && el.type == 'file') {
+ if (elements) {
+ elements.push(el);
+ }
+ var files = el.files;
+ if (files.length) {
+ for (j=0; j < files.length; j++) {
+ a.push({name: n, value: files[j], type: el.type});
+ }
+ }
+ else {
+ // #180
+ a.push({ name: n, value: '', type: el.type });
+ }
+ }
+ else if (v !== null && typeof v != 'undefined') {
+ if (elements) {
+ elements.push(el);
+ }
+ a.push({name: n, value: v, type: el.type, required: el.required});
+ }
+ }
+
+ if (!semantic && form.clk) {
+ // input type=='image' are not found in elements array! handle it here
+ var $input = $(form.clk), input = $input[0];
+ n = input.name;
+ if (n && !input.disabled && input.type == 'image') {
+ a.push({name: n, value: $input.val()});
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
+ }
+ }
+ return a;
+};
+
+/**
+ * Serializes form data into a 'submittable' string. This method will return a string
+ * in the format: name1=value1&name2=value2
+ */
+$.fn.formSerialize = function(semantic) {
+ //hand off to jQuery.param for proper encoding
+ return $.param(this.formToArray(semantic));
+};
+
+/**
+ * Serializes all field elements in the jQuery object into a query string.
+ * This method will return a string in the format: name1=value1&name2=value2
+ */
+$.fn.fieldSerialize = function(successful) {
+ var a = [];
+ this.each(function() {
+ var n = this.name;
+ if (!n) {
+ return;
+ }
+ var v = $.fieldValue(this, successful);
+ if (v && v.constructor == Array) {
+ for (var i=0,max=v.length; i < max; i++) {
+ a.push({name: n, value: v[i]});
+ }
+ }
+ else if (v !== null && typeof v != 'undefined') {
+ a.push({name: this.name, value: v});
+ }
+ });
+ //hand off to jQuery.param for proper encoding
+ return $.param(a);
+};
+
+/**
+ * Returns the value(s) of the element in the matched set. For example, consider the following form:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * var v = $('input[type=text]').fieldValue();
+ * // if no values are entered into the text inputs
+ * v == ['','']
+ * // if values entered into the text inputs are 'foo' and 'bar'
+ * v == ['foo','bar']
+ *
+ * var v = $('input[type=checkbox]').fieldValue();
+ * // if neither checkbox is checked
+ * v === undefined
+ * // if both checkboxes are checked
+ * v == ['B1', 'B2']
+ *
+ * var v = $('input[type=radio]').fieldValue();
+ * // if neither radio is checked
+ * v === undefined
+ * // if first radio is checked
+ * v == ['C1']
+ *
+ * The successful argument controls whether or not the field element must be 'successful'
+ * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
+ * The default value of the successful argument is true. If this value is false the value(s)
+ * for each element is returned.
+ *
+ * Note: This method *always* returns an array. If no valid value can be determined the
+ * array will be empty, otherwise it will contain one or more values.
+ */
+$.fn.fieldValue = function(successful) {
+ for (var val=[], i=0, max=this.length; i < max; i++) {
+ var el = this[i];
+ var v = $.fieldValue(el, successful);
+ if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
+ continue;
+ }
+ if (v.constructor == Array) {
+ $.merge(val, v);
+ }
+ else {
+ val.push(v);
+ }
+ }
+ return val;
+};
+
+/**
+ * Returns the value of the field element.
+ */
+$.fieldValue = function(el, successful) {
+ var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
+ if (successful === undefined) {
+ successful = true;
+ }
+
+ if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
+ (t == 'checkbox' || t == 'radio') && !el.checked ||
+ (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
+ tag == 'select' && el.selectedIndex == -1)) {
+ return null;
+ }
+
+ if (tag == 'select') {
+ var index = el.selectedIndex;
+ if (index < 0) {
+ return null;
+ }
+ var a = [], ops = el.options;
+ var one = (t == 'select-one');
+ var max = (one ? index+1 : ops.length);
+ for(var i=(one ? index : 0); i < max; i++) {
+ var op = ops[i];
+ if (op.selected) {
+ var v = op.value;
+ if (!v) { // extra pain for IE...
+ v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
+ }
+ if (one) {
+ return v;
+ }
+ a.push(v);
+ }
+ }
+ return a;
+ }
+ return $(el).val();
+};
+
+/**
+ * Clears the form data. Takes the following actions on the form's input fields:
+ * - input text fields will have their 'value' property set to the empty string
+ * - select elements will have their 'selectedIndex' property set to -1
+ * - checkbox and radio inputs will have their 'checked' property set to false
+ * - inputs of type submit, button, reset, and hidden will *not* be effected
+ * - button elements will *not* be effected
+ */
+$.fn.clearForm = function(includeHidden) {
+ return this.each(function() {
+ $('input,select,textarea', this).clearFields(includeHidden);
+ });
+};
+
+/**
+ * Clears the selected form elements.
+ */
+$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
+ var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
+ return this.each(function() {
+ var t = this.type, tag = this.tagName.toLowerCase();
+ if (re.test(t) || tag == 'textarea') {
+ this.value = '';
+ }
+ else if (t == 'checkbox' || t == 'radio') {
+ this.checked = false;
+ }
+ else if (tag == 'select') {
+ this.selectedIndex = -1;
+ }
+ else if (t == "file") {
+ if (/MSIE/.test(navigator.userAgent)) {
+ $(this).replaceWith($(this).clone(true));
+ } else {
+ $(this).val('');
+ }
+ }
+ else if (includeHidden) {
+ // includeHidden can be the value true, or it can be a selector string
+ // indicating a special test; for example:
+ // $('#myForm').clearForm('.special:hidden')
+ // the above would clean hidden inputs that have the class of 'special'
+ if ( (includeHidden === true && /hidden/.test(t)) ||
+ (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) {
+ this.value = '';
+ }
+ }
+ });
+};
+
+/**
+ * Resets the form data. Causes all form elements to be reset to their original value.
+ */
+$.fn.resetForm = function() {
+ return this.each(function() {
+ // guard against an input with the name of 'reset'
+ // note that IE reports the reset function as an 'object'
+ if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
+ this.reset();
+ }
+ });
+};
+
+/**
+ * Enables or disables any matching elements.
+ */
+$.fn.enable = function(b) {
+ if (b === undefined) {
+ b = true;
+ }
+ return this.each(function() {
+ this.disabled = !b;
+ });
+};
+
+/**
+ * Checks/unchecks any matching checkboxes or radio buttons and
+ * selects/deselects and matching option elements.
+ */
+$.fn.selected = function(select) {
+ if (select === undefined) {
+ select = true;
+ }
+ return this.each(function() {
+ var t = this.type;
+ if (t == 'checkbox' || t == 'radio') {
+ this.checked = select;
+ }
+ else if (this.tagName.toLowerCase() == 'option') {
+ var $sel = $(this).parent('select');
+ if (select && $sel[0] && $sel[0].type == 'select-one') {
+ // deselect all other options
+ $sel.find('option').selected(false);
+ }
+ this.selected = select;
+ }
+ });
+};
+
+// expose debug var
+$.fn.ajaxSubmit.debug = false;
+
+// helper fn for console logging
+function log() {
+ if (!$.fn.ajaxSubmit.debug) {
+ return;
+ }
+ var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
+ if (window.console && window.console.log) {
+ window.console.log(msg);
+ }
+ else if (window.opera && window.opera.postError) {
+ window.opera.postError(msg);
+ }
+}
+
+}));
\ No newline at end of file
diff --git a/template/megp/js/framework-plugins.js b/template/megp/js/framework-plugins.js
new file mode 100644
index 0000000..ab08443
--- /dev/null
+++ b/template/megp/js/framework-plugins.js
@@ -0,0 +1,1441 @@
+ /*
+
+ Template Name: Enabled Mobile & Tablet Templates
+ Theme URL: http://enableds.com
+ Author: Enabled
+ Author URI: http://themeforest.net/user/Enabled?ref=Enabled
+ Author Facebook: https://www.facebook.com/enabled.labs
+ Author Twitter: https://twitter.com/iEnabled
+ Version: 4.0
+ Envato License: Regular or Extended via ThemeForest
+ Plugin Licenses: GPL / MIT - Redistribution Allowed
+ Each Plugin has it's indivudal license attached
+
+ Description: The framework plugins is built into one single JS file to allow the
+ template universal, fast access to all the items. As -webkit- browsers
+ cache the JS on load, this asures best loading times on all platforms
+ and at the same time asures you will find everything you need in one
+ single place.
+
+ The custom.js file is where all scripts should be imported for usage
+ throughout the template. If you wish to import scripts inline that's
+ completly up to you, but for a perfect function on all mobile devices.
+ Mobile devices such as Nokia and Blackberrry handle JS better if it's
+ implemented globally rather than inline throughout items.
+
+ Please Note! Not following the structure presented in the documentation
+ or altering custom.js and framework.plugins without proper experience
+ can lead to the item malfunctioning. We are not responsible for any
+ custom alterations and edits you make outside the as is item.
+
+ Warning: All the plugins in the pack have been tested on numerous mobile devices
+ running different versions of their Native OS and differen browsers.
+ Some of the plugins have been heavily altered to increase performance
+ or fix issues with differnet devices. We strongly recommend NOT to update
+ these plugins to newer versions. We constantly check and modify and update
+ them when a new, stable, proper quality version is released.
+
+ Structure:
+
+ 01. FastClick -- Version: 1.0.0
+ 02. Swipebox -- Version: 1.1.2
+ 03. OWL Carousel -- Version: 2.0.0
+ 04. Snap.JS -- Version: 1.9.3
+ 05. CountdownJS -- Version: 3.0.0
+ 06. ChartsJS -- Version: 1.0.2
+ 07. SimpleModal -- Version: 1.4.4
+ 08. MouseWheel -- Version: 3.1.2
+ 09. MorphText -- Version: 2.3.4
+ 10. JustifiedGallery -- Version: 3.6.0
+ 11. AddToHomeScreen -- Version: 3.2.2
+ 12. Contact Form -- Version: 4.0.0
+
+*/
+
+(function($){
+ /*
+ * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
+ *
+ * @codingstandard ftlabs-jsv2
+ * @copyright The Financial Times Limited [All Rights Reserved]
+ * @license MIT License (see LICENSE.txt)
+ */
+
+ !function(){"use strict";function t(e,o){function i(t,e){return function(){return t.apply(e,arguments)}}var r;if(o=o||{},this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.touchBoundary=o.touchBoundary||10,this.layer=e,this.tapDelay=o.tapDelay||200,this.tapTimeout=o.tapTimeout||700,!t.notNeeded(e)){for(var a=["onMouse","onClick","onTouchStart","onTouchMove","onTouchEnd","onTouchCancel"],c=this,s=0,u=a.length;u>s;s++)c[a[s]]=i(c[a[s]],c);n&&(e.addEventListener("mouseover",this.onMouse,!0),e.addEventListener("mousedown",this.onMouse,!0),e.addEventListener("mouseup",this.onMouse,!0)),e.addEventListener("click",this.onClick,!0),e.addEventListener("touchstart",this.onTouchStart,!1),e.addEventListener("touchmove",this.onTouchMove,!1),e.addEventListener("touchend",this.onTouchEnd,!1),e.addEventListener("touchcancel",this.onTouchCancel,!1),Event.prototype.stopImmediatePropagation||(e.removeEventListener=function(t,n,o){var i=Node.prototype.removeEventListener;"click"===t?i.call(e,t,n.hijacked||n,o):i.call(e,t,n,o)},e.addEventListener=function(t,n,o){var i=Node.prototype.addEventListener;"click"===t?i.call(e,t,n.hijacked||(n.hijacked=function(t){t.propagationStopped||n(t)}),o):i.call(e,t,n,o)}),"function"==typeof e.onclick&&(r=e.onclick,e.addEventListener("click",function(t){r(t)},!1),e.onclick=null)}}var e=navigator.userAgent.indexOf("Windows Phone")>=0,n=navigator.userAgent.indexOf("Android")>0&&!e,o=/iP(ad|hone|od)/.test(navigator.userAgent)&&!e,i=o&&/OS 4_\d(_\d)?/.test(navigator.userAgent),r=o&&/OS [6-7]_\d/.test(navigator.userAgent),a=navigator.userAgent.indexOf("BB10")>0;t.prototype.needsClick=function(t){switch(t.nodeName.toLowerCase()){case"button":case"select":case"textarea":if(t.disabled)return!0;break;case"input":if(o&&"file"===t.type||t.disabled)return!0;break;case"label":case"iframe":case"video":return!0}return/\bneedsclick\b/.test(t.className)},t.prototype.needsFocus=function(t){switch(t.nodeName.toLowerCase()){case"textarea":return!0;case"select":return!n;case"input":switch(t.type){case"button":case"checkbox":case"file":case"image":case"radio":case"submit":return!1}return!t.disabled&&!t.readOnly;default:return/\bneedsfocus\b/.test(t.className)}},t.prototype.sendClick=function(t,e){var n,o;document.activeElement&&document.activeElement!==t&&document.activeElement.blur(),o=e.changedTouches[0],n=document.createEvent("MouseEvents"),n.initMouseEvent(this.determineEventType(t),!0,!0,window,1,o.screenX,o.screenY,o.clientX,o.clientY,!1,!1,!1,!1,0,null),n.forwardedTouchEvent=!0,t.dispatchEvent(n)},t.prototype.determineEventType=function(t){return n&&"select"===t.tagName.toLowerCase()?"mousedown":"click"},t.prototype.focus=function(t){var e;o&&t.setSelectionRange&&0!==t.type.indexOf("date")&&"time"!==t.type&&"month"!==t.type?(e=t.value.length,t.setSelectionRange(e,e)):t.focus()},t.prototype.updateScrollParent=function(t){var e,n;if(e=t.fastClickScrollParent,!e||!e.contains(t)){n=t;do{if(n.scrollHeight>n.offsetHeight){e=n,t.fastClickScrollParent=n;break}n=n.parentElement}while(n)}e&&(e.fastClickLastScrollTop=e.scrollTop)},t.prototype.getTargetElementFromEventTarget=function(t){return t.nodeType===Node.TEXT_NODE?t.parentNode:t},t.prototype.onTouchStart=function(t){var e,n,r;if(t.targetTouches.length>1)return!0;if(e=this.getTargetElementFromEventTarget(t.target),n=t.targetTouches[0],o){if(r=window.getSelection(),r.rangeCount&&!r.isCollapsed)return!0;if(!i){if(n.identifier&&n.identifier===this.lastTouchIdentifier)return t.preventDefault(),!1;this.lastTouchIdentifier=n.identifier,this.updateScrollParent(e)}}return this.trackingClick=!0,this.trackingClickStart=t.timeStamp,this.targetElement=e,this.touchStartX=n.pageX,this.touchStartY=n.pageY,t.timeStamp-this.lastClickTimen||Math.abs(e.pageY-this.touchStartY)>n?!0:!1},t.prototype.onTouchMove=function(t){return this.trackingClick?((this.targetElement!==this.getTargetElementFromEventTarget(t.target)||this.touchHasMoved(t))&&(this.trackingClick=!1,this.targetElement=null),!0):!0},t.prototype.findControl=function(t){return void 0!==t.control?t.control:t.htmlFor?document.getElementById(t.htmlFor):t.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")},t.prototype.onTouchEnd=function(t){var e,a,c,s,u,l=this.targetElement;if(!this.trackingClick)return!0;if(t.timeStamp-this.lastClickTimethis.tapTimeout)return!0;if(this.cancelNextClick=!1,this.lastClickTime=t.timeStamp,a=this.trackingClickStart,this.trackingClick=!1,this.trackingClickStart=0,r&&(u=t.changedTouches[0],l=document.elementFromPoint(u.pageX-window.pageXOffset,u.pageY-window.pageYOffset)||l,l.fastClickScrollParent=this.targetElement.fastClickScrollParent),c=l.tagName.toLowerCase(),"label"===c){if(e=this.findControl(l)){if(this.focus(l),n)return!1;l=e}}else if(this.needsFocus(l))return t.timeStamp-a>100||o&&window.top!==window&&"input"===c?(this.targetElement=null,!1):(this.focus(l),this.sendClick(l,t),o&&"select"===c||(this.targetElement=null,t.preventDefault()),!1);return o&&!i&&(s=l.fastClickScrollParent,s&&s.fastClickLastScrollTop!==s.scrollTop)?!0:(this.needsClick(l)||(t.preventDefault(),this.sendClick(l,t)),!1)},t.prototype.onTouchCancel=function(){this.trackingClick=!1,this.targetElement=null},t.prototype.onMouse=function(t){return this.targetElement?t.forwardedTouchEvent?!0:t.cancelable&&(!this.needsClick(this.targetElement)||this.cancelNextClick)?(t.stopImmediatePropagation?t.stopImmediatePropagation():t.propagationStopped=!0,t.stopPropagation(),t.preventDefault(),!1):!0:!0},t.prototype.onClick=function(t){var e;return this.trackingClick?(this.targetElement=null,this.trackingClick=!1,!0):"submit"===t.target.type&&0===t.detail?!0:(e=this.onMouse(t),e||(this.targetElement=null),e)},t.prototype.destroy=function(){var t=this.layer;n&&(t.removeEventListener("mouseover",this.onMouse,!0),t.removeEventListener("mousedown",this.onMouse,!0),t.removeEventListener("mouseup",this.onMouse,!0)),t.removeEventListener("click",this.onClick,!0),t.removeEventListener("touchstart",this.onTouchStart,!1),t.removeEventListener("touchmove",this.onTouchMove,!1),t.removeEventListener("touchend",this.onTouchEnd,!1),t.removeEventListener("touchcancel",this.onTouchCancel,!1)},t.notNeeded=function(t){var e,o,i,r;if("undefined"==typeof window.ontouchstart)return!0;if(o=+(/Chrome\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]){if(!n)return!0;if(e=document.querySelector("meta[name=viewport]")){if(-1!==e.content.indexOf("user-scalable=no"))return!0;if(o>31&&document.documentElement.scrollWidth<=window.outerWidth)return!0}}if(a&&(i=navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/),i[1]>=10&&i[2]>=3&&(e=document.querySelector("meta[name=viewport]")))){if(-1!==e.content.indexOf("user-scalable=no"))return!0;if(document.documentElement.scrollWidth<=window.outerWidth)return!0}return"none"===t.style.msTouchAction||"manipulation"===t.style.touchAction?!0:(r=+(/Firefox\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1],r>=27&&(e=document.querySelector("meta[name=viewport]"),e&&(-1!==e.content.indexOf("user-scalable=no")||document.documentElement.scrollWidth<=window.outerWidth))?!0:"none"===t.style.touchAction||"manipulation"===t.style.touchAction?!0:!1)},t.attach=function(e,n){return new t(e,n)},"function"==typeof define&&"object"==typeof define.amd&&define.amd?define(function(){return t}):"undefined"!=typeof module&&module.exports?(module.exports=t.attach,module.exports.FastClick=t):window.FastClick=t}();
+}(jQuery));
+
+
+/*
+ *@author Constantin Saguin - @brutaldesign
+ *@link http://bsign.co
+ *@github http://github.com/brutaldesign/swipebox
+ *@version 1.1.2
+ *@license MIT License
+*/
+
+
+(function($){
+
+(function(e, t, n, r) {
+ n.swipebox = function(i, s) {
+ var o = {
+ useCSS: true,
+ hideBarsDelay: 3e3
+ },
+ u = this,
+ a = n(i),
+ i = i,
+ f = i.selector,
+ l = n(f),
+ c = t.createTouch !== r || "ontouchstart" in e || "onmsgesturechange" in e || navigator.msMaxTouchPoints,
+ h = !!e.SVGSVGElement,
+ p = '';
+ u.settings = {};
+ u.init = function() {
+ u.settings = n.extend({}, o, s);
+ l.click(function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ index = a.index(n(this));
+ d.target = n(e.target);
+ d.init(index)
+ })
+ };
+ var d = {
+ init: function(e) {
+ this.target.trigger("swipebox-start");
+ this.build();
+ this.openSlide(e);
+ this.openImg(e);
+ this.preloadImg(e + 1);
+ this.preloadImg(e - 1)
+ },
+ build: function() {
+ var t = this;
+ n("body").append(p);
+ if (t.doCssTrans()) {
+ n("#swipebox-slider").css({
+ "-webkit-transition": "left 0.4s ease",
+ "-moz-transition": "left 0.4s ease",
+ "-o-transition": "left 0.4s ease",
+ "-khtml-transition": "left 0.4s ease",
+ transition: "left 0.4s ease"
+ });
+ n("#swipebox-overlay").css({
+ "-webkit-transition": "opacity 1s ease",
+ "-moz-transition": "opacity 1s ease",
+ "-o-transition": "opacity 1s ease",
+ "-khtml-transition": "opacity 1s ease",
+ transition: "opacity 1s ease"
+ });
+ n("#swipebox-action, #swipebox-caption").css({
+ "-webkit-transition": "0.5s",
+ "-moz-transition": "0.5s",
+ "-o-transition": "0.5s",
+ "-khtml-transition": "0.5s",
+ transition: "0.5s"
+ })
+ }
+ if (h) {
+ var r = n("#swipebox-action #swipebox-close").css("background-image");
+ r = r.replace("png", "svg");
+ n("#swipebox-action #swipebox-prev,#swipebox-action #swipebox-next,#swipebox-action #swipebox-close").css({
+ "background-image": r
+ })
+ }
+ a.each(function() {
+ n("#swipebox-slider").append('
')
+ });
+ t.setDim();
+ t.actions();
+ t.keyboard();
+ t.gesture();
+ t.animBars();
+ n(e).resize(function() {
+ t.setDim()
+ }).resize()
+ },
+ setDim: function() {
+ var t = {
+ width: n(e).width(),
+ height: e.innerHeight ? e.innerHeight : n(e).height()
+ };
+ n("#swipebox-overlay").css(t)
+ },
+ supportTransition: function() {
+ var e = "transition WebkitTransition MozTransition OTransition msTransition KhtmlTransition".split(" ");
+ for (var n = 0; n < e.length; n++) {
+ if (t.createElement("div").style[e[n]] !== r) {
+ return e[n]
+ }
+ }
+ return false
+ },
+ doCssTrans: function() {
+ if (u.settings.useCSS && this.supportTransition()) {
+ return true
+ }
+ },
+ gesture: function() {
+ if (c) {
+ var e = this,
+ t = null,
+ r = 10,
+ i = {},
+ s = {};
+ var o = n("#swipebox-caption, #swipebox-action");
+ o.addClass("visible-bars");
+ e.setTimeout();
+ n("body").bind("touchstart", function(e) {
+ n(this).addClass("touching");
+ s = e.originalEvent.targetTouches[0];
+ i.pageX = e.originalEvent.targetTouches[0].pageX;
+ n(".touching").bind("touchmove", function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ s = e.originalEvent.targetTouches[0]
+ });
+ return false
+ }).bind("touchend", function(u) {
+ u.preventDefault();
+ u.stopPropagation();
+ t = s.pageX - i.pageX;
+ if (t >= r) {
+ e.getPrev()
+ } else if (t <= -r) {
+ e.getNext()
+ } else {
+ if (!o.hasClass("visible-bars")) {
+ e.showBars();
+ e.setTimeout()
+ } else {
+ e.clearTimeout();
+ e.hideBars()
+ }
+ }
+ n(".touching").off("touchmove").removeClass("touching")
+ })
+ }
+ },
+ setTimeout: function() {
+ if (u.settings.hideBarsDelay > 0) {
+ var t = this;
+ t.clearTimeout();
+ t.timeout = e.setTimeout(function() {
+ t.hideBars()
+ }, u.settings.hideBarsDelay)
+ }
+ },
+ clearTimeout: function() {
+ e.clearTimeout(this.timeout);
+ this.timeout = null
+ },
+ showBars: function() {
+ var e = n("#swipebox-caption, #swipebox-action");
+ if (this.doCssTrans()) {
+ e.addClass("visible-bars")
+ } else {
+ n("#swipebox-caption").animate({
+ top: 0
+ }, 500);
+ n("#swipebox-action").animate({
+ bottom: 0
+ }, 500);
+ setTimeout(function() {
+ e.addClass("visible-bars")
+ }, 1e3)
+ }
+ },
+ hideBars: function() {
+ var e = n("#swipebox-caption, #swipebox-action");
+ if (this.doCssTrans()) {
+ e.removeClass("visible-bars")
+ } else {
+ n("#swipebox-caption").animate({
+ top: "-50px"
+ }, 500);
+ n("#swipebox-action").animate({
+ bottom: "-50px"
+ }, 500);
+ setTimeout(function() {
+ e.removeClass("visible-bars")
+ }, 1e3)
+ }
+ },
+ animBars: function() {
+ var e = this;
+ var t = n("#swipebox-caption, #swipebox-action");
+ t.addClass("visible-bars");
+ e.setTimeout();
+ n("#swipebox-slider").click(function(n) {
+ if (!t.hasClass("visible-bars")) {
+ e.showBars();
+ e.setTimeout()
+ }
+ });
+ n("#swipebox-action").hover(function() {
+ e.showBars();
+ t.addClass("force-visible-bars");
+ e.clearTimeout()
+ }, function() {
+ t.removeClass("force-visible-bars");
+ e.setTimeout()
+ })
+ },
+ keyboard: function() {
+ var t = this;
+ n(e).bind("keyup", function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ if (e.keyCode == 37) {
+ t.getPrev()
+ } else if (e.keyCode == 39) {
+ t.getNext()
+ } else if (e.keyCode == 27) {
+ t.closeSlide()
+ }
+ })
+ },
+ actions: function() {
+ var e = this;
+ if (a.length < 2) {
+ n("#swipebox-prev, #swipebox-next").hide()
+ } else {
+ n("#swipebox-prev").bind("click touchend", function(t) {
+ t.preventDefault();
+ t.stopPropagation();
+ e.getPrev();
+ e.setTimeout()
+ });
+ n("#swipebox-next").bind("click touchend", function(t) {
+ t.preventDefault();
+ t.stopPropagation();
+ e.getNext();
+ e.setTimeout()
+ })
+ }
+ n("#swipebox-close").bind("click touchend", function(t) {
+ e.closeSlide()
+ $('.gallery-fix').delay(1).fadeOut(0);
+ })
+ },
+ setSlide: function(e, t) {
+ t = t || false;
+ var r = n("#swipebox-slider");
+ if (this.doCssTrans()) {
+ r.css({
+ left: -e * 100 + "%"
+ })
+ } else {
+ r.animate({
+ left: -e * 100 + "%"
+ })
+ }
+ n("#swipebox-slider .slide").removeClass("current");
+ n("#swipebox-slider .slide").eq(e).addClass("current");
+ this.setTitle(e);
+ if (t) {
+ r.fadeIn()
+ }
+ n("#swipebox-prev, #swipebox-next").removeClass("disabled");
+ if (e == 0) {
+ n("#swipebox-prev").addClass("disabled")
+ } else if (e == a.length - 1) {
+ n("#swipebox-next").addClass("disabled")
+ }
+ },
+ openSlide: function(t) {
+ n("html").addClass("swipebox");
+ n(e).trigger("resize");
+ this.setSlide(t, true)
+ },
+ preloadImg: function(e) {
+ var t = this;
+ setTimeout(function() {
+ t.openImg(e)
+ }, 1e3)
+ },
+ openImg: function(e) {
+ var t = this;
+ if (e < 0 || e >= a.length) {
+ return false
+ }
+ t.loadImg(a.eq(e).attr("href"), function() {
+ n("#swipebox-slider .slide").eq(e).html(this)
+ })
+ },
+ setTitle: function(e, t) {
+ n("#swipebox-caption").empty();
+ if (a.eq(e).attr("title")) {
+ n("#swipebox-caption").append(a.eq(e).attr("title"))
+ }
+ },
+ loadImg: function(e, t) {
+ var r = n(" ").on("load", function() {
+ t.call(r)
+ });
+ r.attr("src", e)
+ },
+ getNext: function() {
+ var e = this;
+ index = n("#swipebox-slider .slide").index(n("#swipebox-slider .slide.current"));
+ if (index + 1 < a.length) {
+ index++;
+ e.setSlide(index);
+ e.preloadImg(index + 1)
+ } else {
+ n("#swipebox-slider").addClass("rightSpring");
+ setTimeout(function() {
+ n("#swipebox-slider").removeClass("rightSpring")
+ }, 500)
+ }
+ },
+ getPrev: function() {
+ var e = this;
+ index = n("#swipebox-slider .slide").index(n("#swipebox-slider .slide.current"));
+ if (index > 0) {
+ index--;
+ e.setSlide(index);
+ e.preloadImg(index - 1)
+ } else {
+ n("#swipebox-slider").addClass("leftSpring");
+ setTimeout(function() {
+ n("#swipebox-slider").removeClass("leftSpring")
+ }, 500)
+ }
+ },
+ closeSlide: function() {
+ var t = this;
+ n(e).trigger("resize");
+ n("html").removeClass("swipebox");
+ t.destroy()
+ },
+ destroy: function() {
+ var t = this;
+ n(e).unbind("keyup");
+ n("body").unbind("touchstart");
+ n("body").unbind("touchmove");
+ n("body").unbind("touchend");
+ n("#swipebox-slider").unbind();
+ n("#swipebox-overlay").remove();
+ a.removeData("_swipebox");
+ t.target.trigger("swipebox-destroy")
+ }
+ };
+ u.init()
+ };
+
+
+
+ n.fn.swipebox = function(e) {
+ if (!n.data(this, "_swipebox")) {
+ var t = new n.swipebox(this, e);
+ this.data("_swipebox", t)
+ }
+ }
+})(window, document, jQuery)
+
+
+
+}(jQuery));
+
+
+
+(function($){
+ /**
+ * Owl carousel
+ * @version 2.0.0
+ * @author Bartosz Wojciechowski
+ * @license The MIT License (MIT)
+ */
+
+ !function(a,b,c,d){function e(b,c){this.settings=null,this.options=a.extend({},e.Defaults,c),this.$element=a(b),this.drag=a.extend({},m),this.state=a.extend({},n),this.e=a.extend({},o),this._plugins={},this._supress={},this._current=null,this._speed=null,this._coordinates=[],this._breakpoint=null,this._width=null,this._items=[],this._clones=[],this._mergers=[],this._invalidated={},this._pipe=[],a.each(e.Plugins,a.proxy(function(a,b){this._plugins[a[0].toLowerCase()+a.slice(1)]=new b(this)},this)),a.each(e.Pipe,a.proxy(function(b,c){this._pipe.push({filter:c.filter,run:a.proxy(c.run,this)})},this)),this.setup(),this.initialize()}function f(a){if(a.touches!==d)return{x:a.touches[0].pageX,y:a.touches[0].pageY};if(a.touches===d){if(a.pageX!==d)return{x:a.pageX,y:a.pageY};if(a.pageX===d)return{x:a.clientX,y:a.clientY}}}function g(a){var b,d,e=c.createElement("div"),f=a;for(b in f)if(d=f[b],"undefined"!=typeof e.style[d])return e=null,[d,b];return[!1]}function h(){return g(["transition","WebkitTransition","MozTransition","OTransition"])[1]}function i(){return g(["transform","WebkitTransform","MozTransform","OTransform","msTransform"])[0]}function j(){return g(["perspective","webkitPerspective","MozPerspective","OPerspective","MsPerspective"])[0]}function k(){return"ontouchstart"in b||!!navigator.msMaxTouchPoints}function l(){return b.navigator.msPointerEnabled}var m,n,o;m={start:0,startX:0,startY:0,current:0,currentX:0,currentY:0,offsetX:0,offsetY:0,distance:null,startTime:0,endTime:0,updatedX:0,targetEl:null},n={isTouch:!1,isScrolling:!1,isSwiping:!1,direction:!1,inMotion:!1},o={_onDragStart:null,_onDragMove:null,_onDragEnd:null,_transitionEnd:null,_resizer:null,_responsiveCall:null,_goToLoop:null,_checkVisibile:null},e.Defaults={items:3,loop:!1,center:!1,mouseDrag:!0,touchDrag:!0,pullDrag:!0,freeDrag:!1,margin:0,stagePadding:0,merge:!1,mergeFit:!0,autoWidth:!1,startPosition:0,rtl:!1,smartSpeed:250,fluidSpeed:!1,dragEndSpeed:!1,responsive:{},responsiveRefreshRate:200,responsiveBaseElement:b,responsiveClass:!1,fallbackEasing:"swing",info:!1,nestedItemSelector:!1,itemElement:"div",stageElement:"div",themeClass:"owl-theme",baseClass:"owl-carousel",itemClass:"owl-item",centerClass:"center",activeClass:"active"},e.Width={Default:"default",Inner:"inner",Outer:"outer"},e.Plugins={},e.Pipe=[{filter:["width","items","settings"],run:function(a){a.current=this._items&&this._items[this.relative(this._current)]}},{filter:["items","settings"],run:function(){var a=this._clones,b=this.$stage.children(".cloned");(b.length!==a.length||!this.settings.loop&&a.length>0)&&(this.$stage.children(".cloned").remove(),this._clones=[])}},{filter:["items","settings"],run:function(){var a,b,c=this._clones,d=this._items,e=this.settings.loop?c.length-Math.max(2*this.settings.items,4):0;for(a=0,b=Math.abs(e/2);b>a;a++)e>0?(this.$stage.children().eq(d.length+c.length-1).remove(),c.pop(),this.$stage.children().eq(0).remove(),c.pop()):(c.push(c.length/2),this.$stage.append(d[c[c.length-1]].clone().addClass("cloned")),c.push(d.length-1-(c.length-1)/2),this.$stage.prepend(d[c[c.length-1]].clone().addClass("cloned")))}},{filter:["width","items","settings"],run:function(){var a,b,c,d=this.settings.rtl?1:-1,e=(this.width()/this.settings.items).toFixed(3),f=0;for(this._coordinates=[],b=0,c=this._clones.length+this._items.length;c>b;b++)a=this._mergers[this.relative(b)],a=this.settings.mergeFit&&Math.min(a,this.settings.items)||a,f+=(this.settings.autoWidth?this._items[this.relative(b)].width()+this.settings.margin:e*a)*d,this._coordinates.push(f)}},{filter:["width","items","settings"],run:function(){var b,c,d=(this.width()/this.settings.items).toFixed(3),e={width:Math.abs(this._coordinates[this._coordinates.length-1])+2*this.settings.stagePadding,"padding-left":this.settings.stagePadding||"","padding-right":this.settings.stagePadding||""};if(this.$stage.css(e),e={width:this.settings.autoWidth?"auto":d-this.settings.margin},e[this.settings.rtl?"margin-left":"margin-right"]=this.settings.margin,!this.settings.autoWidth&&a.grep(this._mergers,function(a){return a>1}).length>0)for(b=0,c=this._coordinates.length;c>b;b++)e.width=Math.abs(this._coordinates[b])-Math.abs(this._coordinates[b-1]||0)-this.settings.margin,this.$stage.children().eq(b).css(e);else this.$stage.children().css(e)}},{filter:["width","items","settings"],run:function(a){a.current&&this.reset(this.$stage.children().index(a.current))}},{filter:["position"],run:function(){this.animate(this.coordinates(this._current))}},{filter:["width","position","items","settings"],run:function(){var a,b,c,d,e=this.settings.rtl?1:-1,f=2*this.settings.stagePadding,g=this.coordinates(this.current())+f,h=g+this.width()*e,i=[];for(c=0,d=this._coordinates.length;d>c;c++)a=this._coordinates[c-1]||0,b=Math.abs(this._coordinates[c])+f*e,(this.op(a,"<=",g)&&this.op(a,">",h)||this.op(b,"<",g)&&this.op(b,">",h))&&i.push(c);this.$stage.children("."+this.settings.activeClass).removeClass(this.settings.activeClass),this.$stage.children(":eq("+i.join("), :eq(")+")").addClass(this.settings.activeClass),this.settings.center&&(this.$stage.children("."+this.settings.centerClass).removeClass(this.settings.centerClass),this.$stage.children().eq(this.current()).addClass(this.settings.centerClass))}}],e.prototype.initialize=function(){if(this.trigger("initialize"),this.$element.addClass(this.settings.baseClass).addClass(this.settings.themeClass).toggleClass("owl-rtl",this.settings.rtl),this.browserSupport(),this.settings.autoWidth&&this.state.imagesLoaded!==!0){var b,c,e;if(b=this.$element.find("img"),c=this.settings.nestedItemSelector?"."+this.settings.nestedItemSelector:d,e=this.$element.children(c).width(),b.length&&0>=e)return this.preloadAutoWidthImages(b),!1}this.$element.addClass("owl-loading"),this.$stage=a("<"+this.settings.stageElement+' class="owl-stage"/>').wrap(''),this.$element.append(this.$stage.parent()),this.replace(this.$element.children().not(this.$stage.parent())),this._width=this.$element.width(),this.refresh(),this.$element.removeClass("owl-loading").addClass("owl-loaded"),this.eventsCall(),this.internalEvents(),this.addTriggerableEvents(),this.trigger("initialized")},e.prototype.setup=function(){var b=this.viewport(),c=this.options.responsive,d=-1,e=null;c?(a.each(c,function(a){b>=a&&a>d&&(d=Number(a))}),e=a.extend({},this.options,c[d]),delete e.responsive,e.responsiveClass&&this.$element.attr("class",function(a,b){return b.replace(/\b owl-responsive-\S+/g,"")}).addClass("owl-responsive-"+d)):e=a.extend({},this.options),(null===this.settings||this._breakpoint!==d)&&(this.trigger("change",{property:{name:"settings",value:e}}),this._breakpoint=d,this.settings=e,this.invalidate("settings"),this.trigger("changed",{property:{name:"settings",value:this.settings}}))},e.prototype.optionsLogic=function(){this.$element.toggleClass("owl-center",this.settings.center),this.settings.loop&&this._items.length
").addClass(this.settings.itemClass).append(b)),this.trigger("prepared",{content:c.data}),c.data},e.prototype.update=function(){for(var b=0,c=this._pipe.length,d=a.proxy(function(a){return this[a]},this._invalidated),e={};c>b;)(this._invalidated.all||a.grep(this._pipe[b].filter,d).length>0)&&this._pipe[b].run(e),b++;this._invalidated={}},e.prototype.width=function(a){switch(a=a||e.Width.Default){case e.Width.Inner:case e.Width.Outer:return this._width;default:return this._width-2*this.settings.stagePadding+this.settings.margin}},e.prototype.refresh=function(){if(0===this._items.length)return!1;(new Date).getTime();this.trigger("refresh"),this.setup(),this.optionsLogic(),this.$stage.addClass("owl-refresh"),this.update(),this.$stage.removeClass("owl-refresh"),this.state.orientation=b.orientation,this.watchVisibility(),this.trigger("refreshed")},e.prototype.eventsCall=function(){this.e._onDragStart=a.proxy(function(a){this.onDragStart(a)},this),this.e._onDragMove=a.proxy(function(a){this.onDragMove(a)},this),this.e._onDragEnd=a.proxy(function(a){this.onDragEnd(a)},this),this.e._onResize=a.proxy(function(a){this.onResize(a)},this),this.e._transitionEnd=a.proxy(function(a){this.transitionEnd(a)},this),this.e._preventClick=a.proxy(function(a){this.preventClick(a)},this)},e.prototype.onThrottledResize=function(){b.clearTimeout(this.resizeTimer),this.resizeTimer=b.setTimeout(this.e._onResize,this.settings.responsiveRefreshRate)},e.prototype.onResize=function(){return this._items.length?this._width===this.$element.width()?!1:this.trigger("resize").isDefaultPrevented()?!1:(this._width=this.$element.width(),this.invalidate("width"),this.refresh(),void this.trigger("resized")):!1},e.prototype.eventsRouter=function(a){var b=a.type;"mousedown"===b||"touchstart"===b?this.onDragStart(a):"mousemove"===b||"touchmove"===b?this.onDragMove(a):"mouseup"===b||"touchend"===b?this.onDragEnd(a):"touchcancel"===b&&this.onDragEnd(a)},e.prototype.internalEvents=function(){var c=(k(),l());this.settings.mouseDrag?(this.$stage.on("mousedown",a.proxy(function(a){this.eventsRouter(a)},this)),this.$stage.on("dragstart",function(){return!1}),this.$stage.get(0).onselectstart=function(){return!1}):this.$element.addClass("owl-text-select-on"),this.settings.touchDrag&&!c&&this.$stage.on("touchstart touchcancel",a.proxy(function(a){this.eventsRouter(a)},this)),this.transitionEndVendor&&this.on(this.$stage.get(0),this.transitionEndVendor,this.e._transitionEnd,!1),this.settings.responsive!==!1&&this.on(b,"resize",a.proxy(this.onThrottledResize,this))},e.prototype.onDragStart=function(d){var e,g,h,i;if(e=d.originalEvent||d||b.event,3===e.which||this.state.isTouch)return!1;if("mousedown"===e.type&&this.$stage.addClass("owl-grab"),this.trigger("drag"),this.drag.startTime=(new Date).getTime(),this.speed(0),this.state.isTouch=!0,this.state.isScrolling=!1,this.state.isSwiping=!1,this.drag.distance=0,g=f(e).x,h=f(e).y,this.drag.offsetX=this.$stage.position().left,this.drag.offsetY=this.$stage.position().top,this.settings.rtl&&(this.drag.offsetX=this.$stage.position().left+this.$stage.width()-this.width()+this.settings.margin),this.state.inMotion&&this.support3d)i=this.getTransformProperty(),this.drag.offsetX=i,this.animate(i),this.state.inMotion=!0;else if(this.state.inMotion&&!this.support3d)return this.state.inMotion=!1,!1;this.drag.startX=g-this.drag.offsetX,this.drag.startY=h-this.drag.offsetY,this.drag.start=g-this.drag.startX,this.drag.targetEl=e.target||e.srcElement,this.drag.updatedX=this.drag.start,("IMG"===this.drag.targetEl.tagName||"A"===this.drag.targetEl.tagName)&&(this.drag.targetEl.draggable=!1),a(c).on("mousemove.owl.dragEvents mouseup.owl.dragEvents touchmove.owl.dragEvents touchend.owl.dragEvents",a.proxy(function(a){this.eventsRouter(a)},this))},e.prototype.onDragMove=function(a){var c,e,g,h,i,j;this.state.isTouch&&(this.state.isScrolling||(c=a.originalEvent||a||b.event,e=f(c).x,g=f(c).y,this.drag.currentX=e-this.drag.startX,this.drag.currentY=g-this.drag.startY,this.drag.distance=this.drag.currentX-this.drag.offsetX,this.drag.distance<0?this.state.direction=this.settings.rtl?"right":"left":this.drag.distance>0&&(this.state.direction=this.settings.rtl?"left":"right"),this.settings.loop?this.op(this.drag.currentX,">",this.coordinates(this.minimum()))&&"right"===this.state.direction?this.drag.currentX-=(this.settings.center&&this.coordinates(0))-this.coordinates(this._items.length):this.op(this.drag.currentX,"<",this.coordinates(this.maximum()))&&"left"===this.state.direction&&(this.drag.currentX+=(this.settings.center&&this.coordinates(0))-this.coordinates(this._items.length)):(h=this.coordinates(this.settings.rtl?this.maximum():this.minimum()),i=this.coordinates(this.settings.rtl?this.minimum():this.maximum()),j=this.settings.pullDrag?this.drag.distance/5:0,this.drag.currentX=Math.max(Math.min(this.drag.currentX,h+j),i+j)),(this.drag.distance>8||this.drag.distance<-8)&&(c.preventDefault!==d?c.preventDefault():c.returnValue=!1,this.state.isSwiping=!0),this.drag.updatedX=this.drag.currentX,(this.drag.currentY>16||this.drag.currentY<-16)&&this.state.isSwiping===!1&&(this.state.isScrolling=!0,this.drag.updatedX=this.drag.start),this.animate(this.drag.updatedX)))},e.prototype.onDragEnd=function(b){var d,e,f;if(this.state.isTouch){if("mouseup"===b.type&&this.$stage.removeClass("owl-grab"),this.trigger("dragged"),this.drag.targetEl.removeAttribute("draggable"),this.state.isTouch=!1,this.state.isScrolling=!1,this.state.isSwiping=!1,0===this.drag.distance&&this.state.inMotion!==!0)return this.state.inMotion=!1,!1;this.drag.endTime=(new Date).getTime(),d=this.drag.endTime-this.drag.startTime,e=Math.abs(this.drag.distance),(e>3||d>300)&&this.removeClick(this.drag.targetEl),f=this.closest(this.drag.updatedX),this.speed(this.settings.dragEndSpeed||this.settings.smartSpeed),this.current(f),this.invalidate("position"),this.update(),this.settings.pullDrag||this.drag.updatedX!==this.coordinates(f)||this.transitionEnd(),this.drag.distance=0,a(c).off(".owl.dragEvents")}},e.prototype.removeClick=function(c){this.drag.targetEl=c,a(c).on("click.preventClick",this.e._preventClick),b.setTimeout(function(){a(c).off("click.preventClick")},300)},e.prototype.preventClick=function(b){b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation&&b.stopPropagation(),a(b.target).off("click.preventClick")},e.prototype.getTransformProperty=function(){var a,c;return a=b.getComputedStyle(this.$stage.get(0),null).getPropertyValue(this.vendorName+"transform"),a=a.replace(/matrix(3d)?\(|\)/g,"").split(","),c=16===a.length,c!==!0?a[4]:a[12]},e.prototype.closest=function(b){var c=-1,d=30,e=this.width(),f=this.coordinates();return this.settings.freeDrag||a.each(f,a.proxy(function(a,g){return b>g-d&&g+d>b?c=a:this.op(b,"<",g)&&this.op(b,">",f[a+1]||g-e)&&(c="left"===this.state.direction?a+1:a),-1===c},this)),this.settings.loop||(this.op(b,">",f[this.minimum()])?c=b=this.minimum():this.op(b,"<",f[this.maximum()])&&(c=b=this.maximum())),c},e.prototype.animate=function(b){this.trigger("translate"),this.state.inMotion=this.speed()>0,this.support3d?this.$stage.css({transform:"translate3d("+b+"px,0px, 0px)",transition:this.speed()/1e3+"s"}):this.state.isTouch?this.$stage.css({left:b+"px"}):this.$stage.animate({left:b},this.speed()/1e3,this.settings.fallbackEasing,a.proxy(function(){this.state.inMotion&&this.transitionEnd()},this))},e.prototype.current=function(a){if(a===d)return this._current;if(0===this._items.length)return d;if(a=this.normalize(a),this._current!==a){var b=this.trigger("change",{property:{name:"position",value:a}});b.data!==d&&(a=this.normalize(b.data)),this._current=a,this.invalidate("position"),this.trigger("changed",{property:{name:"position",value:this._current}})}return this._current},e.prototype.invalidate=function(a){this._invalidated[a]=!0},e.prototype.reset=function(a){a=this.normalize(a),a!==d&&(this._speed=0,this._current=a,this.suppress(["translate","translated"]),this.animate(this.coordinates(a)),this.release(["translate","translated"]))},e.prototype.normalize=function(b,c){var e=c?this._items.length:this._items.length+this._clones.length;return!a.isNumeric(b)||1>e?d:b=this._clones.length?(b%e+e)%e:Math.max(this.minimum(c),Math.min(this.maximum(c),b))},e.prototype.relative=function(a){return a=this.normalize(a),a-=this._clones.length/2,this.normalize(a,!0)},e.prototype.maximum=function(a){var b,c,d,e=0,f=this.settings;if(a)return this._items.length-1;if(!f.loop&&f.center)b=this._items.length-1;else if(f.loop||f.center)if(f.loop||f.center)b=this._items.length+f.items;else{if(!f.autoWidth&&!f.merge)throw"Can not detect maximum absolute position.";for(revert=f.rtl?1:-1,c=this.$stage.width()-this.$element.width();(d=this.coordinates(e))&&!(d*revert>=c);)b=++e}else b=this._items.length-f.items;return b},e.prototype.minimum=function(a){return a?0:this._clones.length/2},e.prototype.items=function(a){return a===d?this._items.slice():(a=this.normalize(a,!0),this._items[a])},e.prototype.mergers=function(a){return a===d?this._mergers.slice():(a=this.normalize(a,!0),this._mergers[a])},e.prototype.clones=function(b){var c=this._clones.length/2,e=c+this._items.length,f=function(a){return a%2===0?e+a/2:c-(a+1)/2};return b===d?a.map(this._clones,function(a,b){return f(b)}):a.map(this._clones,function(a,c){return a===b?f(c):null})},e.prototype.speed=function(a){return a!==d&&(this._speed=a),this._speed},e.prototype.coordinates=function(b){var c=null;return b===d?a.map(this._coordinates,a.proxy(function(a,b){return this.coordinates(b)},this)):(this.settings.center?(c=this._coordinates[b],c+=(this.width()-c+(this._coordinates[b-1]||0))/2*(this.settings.rtl?-1:1)):c=this._coordinates[b-1]||0,c)},e.prototype.duration=function(a,b,c){return Math.min(Math.max(Math.abs(b-a),1),6)*Math.abs(c||this.settings.smartSpeed)},e.prototype.to=function(c,d){if(this.settings.loop){var e=c-this.relative(this.current()),f=this.current(),g=this.current(),h=this.current()+e,i=0>g-h?!0:!1,j=this._clones.length+this._items.length;h
=j-this.settings.items&&i===!0&&(f=g-this._items.length,this.reset(f)),b.clearTimeout(this.e._goToLoop),this.e._goToLoop=b.setTimeout(a.proxy(function(){this.speed(this.duration(this.current(),f+e,d)),this.current(f+e),this.update()},this),30)}else this.speed(this.duration(this.current(),c,d)),this.current(c),this.update()},e.prototype.next=function(a){a=a||!1,this.to(this.relative(this.current())+1,a)},e.prototype.prev=function(a){a=a||!1,this.to(this.relative(this.current())-1,a)},e.prototype.transitionEnd=function(a){return a!==d&&(a.stopPropagation(),(a.target||a.srcElement||a.originalTarget)!==this.$stage.get(0))?!1:(this.state.inMotion=!1,void this.trigger("translated"))},e.prototype.viewport=function(){var d;if(this.options.responsiveBaseElement!==b)d=a(this.options.responsiveBaseElement).width();else if(b.innerWidth)d=b.innerWidth;else{if(!c.documentElement||!c.documentElement.clientWidth)throw"Can not detect viewport width.";d=c.documentElement.clientWidth}return d},e.prototype.replace=function(b){this.$stage.empty(),this._items=[],b&&(b=b instanceof jQuery?b:a(b)),this.settings.nestedItemSelector&&(b=b.find("."+this.settings.nestedItemSelector)),b.filter(function(){return 1===this.nodeType}).each(a.proxy(function(a,b){b=this.prepare(b),this.$stage.append(b),this._items.push(b),this._mergers.push(1*b.find("[data-merge]").andSelf("[data-merge]").attr("data-merge")||1)},this)),this.reset(a.isNumeric(this.settings.startPosition)?this.settings.startPosition:0),this.invalidate("items")},e.prototype.add=function(a,b){b=b===d?this._items.length:this.normalize(b,!0),this.trigger("add",{content:a,position:b}),0===this._items.length||b===this._items.length?(this.$stage.append(a),this._items.push(a),this._mergers.push(1*a.find("[data-merge]").andSelf("[data-merge]").attr("data-merge")||1)):(this._items[b].before(a),this._items.splice(b,0,a),this._mergers.splice(b,0,1*a.find("[data-merge]").andSelf("[data-merge]").attr("data-merge")||1)),this.invalidate("items"),this.trigger("added",{content:a,position:b})},e.prototype.remove=function(a){a=this.normalize(a,!0),a!==d&&(this.trigger("remove",{content:this._items[a],position:a}),this._items[a].remove(),this._items.splice(a,1),this._mergers.splice(a,1),this.invalidate("items"),this.trigger("removed",{content:null,position:a}))},e.prototype.addTriggerableEvents=function(){var b=a.proxy(function(b,c){return a.proxy(function(a){a.relatedTarget!==this&&(this.suppress([c]),b.apply(this,[].slice.call(arguments,1)),this.release([c]))},this)},this);a.each({next:this.next,prev:this.prev,to:this.to,destroy:this.destroy,refresh:this.refresh,replace:this.replace,add:this.add,remove:this.remove},a.proxy(function(a,c){this.$element.on(a+".owl.carousel",b(c,a+".owl.carousel"))},this))},e.prototype.watchVisibility=function(){function c(a){return a.offsetWidth>0&&a.offsetHeight>0}function d(){c(this.$element.get(0))&&(this.$element.removeClass("owl-hidden"),this.refresh(),b.clearInterval(this.e._checkVisibile))}c(this.$element.get(0))||(this.$element.addClass("owl-hidden"),b.clearInterval(this.e._checkVisibile),this.e._checkVisibile=b.setInterval(a.proxy(d,this),500))},e.prototype.preloadAutoWidthImages=function(b){var c,d,e,f;c=0,d=this,b.each(function(g,h){e=a(h),f=new Image,f.onload=function(){c++,e.attr("src",f.src),e.css("opacity",1),c>=b.length&&(d.state.imagesLoaded=!0,d.initialize())},f.src=e.attr("src")||e.attr("data-src")||e.attr("data-src-retina")})},e.prototype.destroy=function(){this.$element.hasClass(this.settings.themeClass)&&this.$element.removeClass(this.settings.themeClass),this.settings.responsive!==!1&&a(b).off("resize.owl.carousel"),this.transitionEndVendor&&this.off(this.$stage.get(0),this.transitionEndVendor,this.e._transitionEnd);for(var d in this._plugins)this._plugins[d].destroy();(this.settings.mouseDrag||this.settings.touchDrag)&&(this.$stage.off("mousedown touchstart touchcancel"),a(c).off(".owl.dragEvents"),this.$stage.get(0).onselectstart=function(){},this.$stage.off("dragstart",function(){return!1})),this.$element.off(".owl"),this.$stage.children(".cloned").remove(),this.e=null,this.$element.removeData("owlCarousel"),this.$stage.children().contents().unwrap(),this.$stage.children().unwrap(),this.$stage.unwrap()},e.prototype.op=function(a,b,c){var d=this.settings.rtl;switch(b){case"<":return d?a>c:c>a;case">":return d?c>a:a>c;case">=":return d?c>=a:a>=c;case"<=":return d?a>=c:c>=a}},e.prototype.on=function(a,b,c,d){a.addEventListener?a.addEventListener(b,c,d):a.attachEvent&&a.attachEvent("on"+b,c)},e.prototype.off=function(a,b,c,d){a.removeEventListener?a.removeEventListener(b,c,d):a.detachEvent&&a.detachEvent("on"+b,c)},e.prototype.trigger=function(b,c,d){var e={item:{count:this._items.length,index:this.current()}},f=a.camelCase(a.grep(["on",b,d],function(a){return a}).join("-").toLowerCase()),g=a.Event([b,"owl",d||"carousel"].join(".").toLowerCase(),a.extend({relatedTarget:this},e,c));return this._supress[b]||(a.each(this._plugins,function(a,b){b.onTrigger&&b.onTrigger(g)}),this.$element.trigger(g),this.settings&&"function"==typeof this.settings[f]&&this.settings[f].apply(this,g)),g},e.prototype.suppress=function(b){a.each(b,a.proxy(function(a,b){this._supress[b]=!0},this))},e.prototype.release=function(b){a.each(b,a.proxy(function(a,b){delete this._supress[b]},this))},e.prototype.browserSupport=function(){if(this.support3d=j(),this.support3d){this.transformVendor=i();var a=["transitionend","webkitTransitionEnd","transitionend","oTransitionEnd"];this.transitionEndVendor=a[h()],this.vendorName=this.transformVendor.replace(/Transform/i,""),this.vendorName=""!==this.vendorName?"-"+this.vendorName.toLowerCase()+"-":""}this.state.orientation=b.orientation},a.fn.owlCarousel=function(b){return this.each(function(){a(this).data("owlCarousel")||a(this).data("owlCarousel",new e(this,b))})},a.fn.owlCarousel.Constructor=e}(window.Zepto||window.jQuery,window,document),function(a,b){var c=function(b){this._core=b,this._loaded=[],this._handlers={"initialized.owl.carousel change.owl.carousel":a.proxy(function(b){if(b.namespace&&this._core.settings&&this._core.settings.lazyLoad&&(b.property&&"position"==b.property.name||"initialized"==b.type))for(var c=this._core.settings,d=c.center&&Math.ceil(c.items/2)||c.items,e=c.center&&-1*d||0,f=(b.property&&b.property.value||this._core.current())+e,g=this._core.clones().length,h=a.proxy(function(a,b){this.load(b)},this);e++-1||(e.each(a.proxy(function(c,d){var e,f=a(d),g=b.devicePixelRatio>1&&f.attr("data-src-retina")||f.attr("data-src");this._core.trigger("load",{element:f,url:g},"lazy"),f.is("img")?f.one("load.owl.lazy",a.proxy(function(){f.css("opacity",1),this._core.trigger("loaded",{element:f,url:g},"lazy")},this)).attr("src",g):(e=new Image,e.onload=a.proxy(function(){f.css({"background-image":"url("+g+")",opacity:"1"}),this._core.trigger("loaded",{element:f,url:g},"lazy")},this),e.src=g)},this)),this._loaded.push(d.get(0)))},c.prototype.destroy=function(){var a,b;for(a in this.handlers)this._core.$element.off(a,this.handlers[a]);for(b in Object.getOwnPropertyNames(this))"function"!=typeof this[b]&&(this[b]=null)},a.fn.owlCarousel.Constructor.Plugins.Lazy=c}(window.Zepto||window.jQuery,window,document),function(a){var b=function(c){this._core=c,this._handlers={"initialized.owl.carousel":a.proxy(function(){this._core.settings.autoHeight&&this.update()},this),"changed.owl.carousel":a.proxy(function(a){this._core.settings.autoHeight&&"position"==a.property.name&&this.update()},this),"loaded.owl.lazy":a.proxy(function(a){this._core.settings.autoHeight&&a.element.closest("."+this._core.settings.itemClass)===this._core.$stage.children().eq(this._core.current())&&this.update()},this)},this._core.options=a.extend({},b.Defaults,this._core.options),this._core.$element.on(this._handlers)};b.Defaults={autoHeight:!1,autoHeightClass:"owl-height"},b.prototype.update=function(){this._core.$stage.parent().height(this._core.$stage.children().eq(this._core.current()).height()).addClass(this._core.settings.autoHeightClass)},b.prototype.destroy=function(){var a,b;for(a in this._handlers)this._core.$element.off(a,this._handlers[a]);for(b in Object.getOwnPropertyNames(this))"function"!=typeof this[b]&&(this[b]=null)},a.fn.owlCarousel.Constructor.Plugins.AutoHeight=b}(window.Zepto||window.jQuery,window,document),function(a,b,c){var d=function(b){this._core=b,this._videos={},this._playing=null,this._fullscreen=!1,this._handlers={"resize.owl.carousel":a.proxy(function(a){this._core.settings.video&&!this.isInFullScreen()&&a.preventDefault()},this),"refresh.owl.carousel changed.owl.carousel":a.proxy(function(){this._playing&&this.stop()},this),"prepared.owl.carousel":a.proxy(function(b){var c=a(b.content).find(".owl-video");c.length&&(c.css("display","none"),this.fetch(c,a(b.content)))},this)},this._core.options=a.extend({},d.Defaults,this._core.options),this._core.$element.on(this._handlers),this._core.$element.on("click.owl.video",".owl-video-play-icon",a.proxy(function(a){this.play(a)},this))};d.Defaults={video:!1,videoHeight:!1,videoWidth:!1},d.prototype.fetch=function(a,b){var c=a.attr("data-vimeo-id")?"vimeo":"youtube",d=a.attr("data-vimeo-id")||a.attr("data-youtube-id"),e=a.attr("data-width")||this._core.settings.videoWidth,f=a.attr("data-height")||this._core.settings.videoHeight,g=a.attr("href");if(!g)throw new Error("Missing video URL.");if(d=g.match(/(http:|https:|)\/\/(player.|www.)?(vimeo\.com|youtu(be\.com|\.be|be\.googleapis\.com))\/(video\/|embed\/|watch\?v=|v\/)?([A-Za-z0-9._%-]*)(\&\S+)?/),d[3].indexOf("youtu")>-1)c="youtube";else{if(!(d[3].indexOf("vimeo")>-1))throw new Error("Video URL not supported.");c="vimeo"}d=d[6],this._videos[g]={type:c,id:d,width:e,height:f},b.attr("data-video",g),this.thumbnail(a,this._videos[g])},d.prototype.thumbnail=function(b,c){var d,e,f,g=c.width&&c.height?'style="width:'+c.width+"px;height:"+c.height+'px;"':"",h=b.find("img"),i="src",j="",k=this._core.settings,l=function(a){e='
',d=k.lazyLoad?'
':'
',b.after(d),b.after(e)};return b.wrap('
"),this._core.settings.lazyLoad&&(i="data-src",j="owl-lazy"),h.length?(l(h.attr(i)),h.remove(),!1):void("youtube"===c.type?(f="http://img.youtube.com/vi/"+c.id+"/hqdefault.jpg",l(f)):"vimeo"===c.type&&a.ajax({type:"GET",url:"http://vimeo.com/api/v2/video/"+c.id+".json",jsonp:"callback",dataType:"jsonp",success:function(a){f=a[0].thumbnail_large,l(f)}}))},d.prototype.stop=function(){this._core.trigger("stop",null,"video"),this._playing.find(".owl-video-frame").remove(),this._playing.removeClass("owl-video-playing"),this._playing=null},d.prototype.play=function(b){this._core.trigger("play",null,"video"),this._playing&&this.stop();var c,d,e=a(b.target||b.srcElement),f=e.closest("."+this._core.settings.itemClass),g=this._videos[f.attr("data-video")],h=g.width||"100%",i=g.height||this._core.$stage.height();"youtube"===g.type?c='VIDEO ':"vimeo"===g.type&&(c=''),f.addClass("owl-video-playing"),this._playing=f,d=a(''+c+"
"),e.after(d)},d.prototype.isInFullScreen=function(){var d=c.fullscreenElement||c.mozFullScreenElement||c.webkitFullscreenElement;return d&&a(d).parent().hasClass("owl-video-frame")&&(this._core.speed(0),this._fullscreen=!0),d&&this._fullscreen&&this._playing?!1:this._fullscreen?(this._fullscreen=!1,!1):this._playing&&this._core.state.orientation!==b.orientation?(this._core.state.orientation=b.orientation,!1):!0},d.prototype.destroy=function(){var a,b;this._core.$element.off("click.owl.video");for(a in this._handlers)this._core.$element.off(a,this._handlers[a]);for(b in Object.getOwnPropertyNames(this))"function"!=typeof this[b]&&(this[b]=null)},a.fn.owlCarousel.Constructor.Plugins.Video=d}(window.Zepto||window.jQuery,window,document),function(a,b,c,d){var e=function(b){this.core=b,this.core.options=a.extend({},e.Defaults,this.core.options),this.swapping=!0,this.previous=d,this.next=d,this.handlers={"change.owl.carousel":a.proxy(function(a){"position"==a.property.name&&(this.previous=this.core.current(),this.next=a.property.value)},this),"drag.owl.carousel dragged.owl.carousel translated.owl.carousel":a.proxy(function(a){this.swapping="translated"==a.type},this),"translate.owl.carousel":a.proxy(function(){this.swapping&&(this.core.options.animateOut||this.core.options.animateIn)&&this.swap()},this)},this.core.$element.on(this.handlers)};e.Defaults={animateOut:!1,animateIn:!1},e.prototype.swap=function(){if(1===this.core.settings.items&&this.core.support3d){this.core.speed(0);var b,c=a.proxy(this.clear,this),d=this.core.$stage.children().eq(this.previous),e=this.core.$stage.children().eq(this.next),f=this.core.settings.animateIn,g=this.core.settings.animateOut;this.core.current()!==this.previous&&(g&&(b=this.core.coordinates(this.previous)-this.core.coordinates(this.next),d.css({left:b+"px"}).addClass("animated owl-animated-out").addClass(g).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",c)),f&&e.addClass("animated owl-animated-in").addClass(f).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",c))}},e.prototype.clear=function(b){a(b.target).css({left:""}).removeClass("animated owl-animated-out owl-animated-in").removeClass(this.core.settings.animateIn).removeClass(this.core.settings.animateOut),this.core.transitionEnd()},e.prototype.destroy=function(){var a,b;for(a in this.handlers)this.core.$element.off(a,this.handlers[a]);for(b in Object.getOwnPropertyNames(this))"function"!=typeof this[b]&&(this[b]=null)},a.fn.owlCarousel.Constructor.Plugins.Animate=e}(window.Zepto||window.jQuery,window,document),function(a,b,c){var d=function(b){this.core=b,this.core.options=a.extend({},d.Defaults,this.core.options),this.handlers={"translated.owl.carousel refreshed.owl.carousel":a.proxy(function(){this.autoplay()
+ },this),"play.owl.autoplay":a.proxy(function(a,b,c){this.play(b,c)},this),"stop.owl.autoplay":a.proxy(function(){this.stop()},this),"mouseover.owl.autoplay":a.proxy(function(){this.core.settings.autoplayHoverPause&&this.pause()},this),"mouseleave.owl.autoplay":a.proxy(function(){this.core.settings.autoplayHoverPause&&this.autoplay()},this)},this.core.$element.on(this.handlers)};d.Defaults={autoplay:!1,autoplayTimeout:5e3,autoplayHoverPause:!1,autoplaySpeed:!1},d.prototype.autoplay=function(){this.core.settings.autoplay&&!this.core.state.videoPlay?(b.clearInterval(this.interval),this.interval=b.setInterval(a.proxy(function(){this.play()},this),this.core.settings.autoplayTimeout)):b.clearInterval(this.interval)},d.prototype.play=function(){return c.hidden===!0||this.core.state.isTouch||this.core.state.isScrolling||this.core.state.isSwiping||this.core.state.inMotion?void 0:this.core.settings.autoplay===!1?void b.clearInterval(this.interval):void this.core.next(this.core.settings.autoplaySpeed)},d.prototype.stop=function(){b.clearInterval(this.interval)},d.prototype.pause=function(){b.clearInterval(this.interval)},d.prototype.destroy=function(){var a,c;b.clearInterval(this.interval);for(a in this.handlers)this.core.$element.off(a,this.handlers[a]);for(c in Object.getOwnPropertyNames(this))"function"!=typeof this[c]&&(this[c]=null)},a.fn.owlCarousel.Constructor.Plugins.autoplay=d}(window.Zepto||window.jQuery,window,document),function(a){"use strict";var b=function(c){this._core=c,this._initialized=!1,this._pages=[],this._controls={},this._templates=[],this.$element=this._core.$element,this._overrides={next:this._core.next,prev:this._core.prev,to:this._core.to},this._handlers={"prepared.owl.carousel":a.proxy(function(b){this._core.settings.dotsData&&this._templates.push(a(b.content).find("[data-dot]").andSelf("[data-dot]").attr("data-dot"))},this),"add.owl.carousel":a.proxy(function(b){this._core.settings.dotsData&&this._templates.splice(b.position,0,a(b.content).find("[data-dot]").andSelf("[data-dot]").attr("data-dot"))},this),"remove.owl.carousel prepared.owl.carousel":a.proxy(function(a){this._core.settings.dotsData&&this._templates.splice(a.position,1)},this),"change.owl.carousel":a.proxy(function(a){if("position"==a.property.name&&!this._core.state.revert&&!this._core.settings.loop&&this._core.settings.navRewind){var b=this._core.current(),c=this._core.maximum(),d=this._core.minimum();a.data=a.property.value>c?b>=c?d:c:a.property.value").addClass(d.dotClass).append(a("")).prop("outerHTML")]),d.navContainer&&d.dotsContainer||(this._controls.$container=a("").addClass(d.controlsClass).appendTo(this.$element)),this._controls.$indicators=d.dotsContainer?a(d.dotsContainer):a("
").hide().addClass(d.dotsClass).appendTo(this._controls.$container),this._controls.$indicators.on("click","div",a.proxy(function(b){var c=a(b.target).parent().is(this._controls.$indicators)?a(b.target).index():a(b.target).parent().index();b.preventDefault(),this.to(c,d.dotsSpeed)},this)),b=d.navContainer?a(d.navContainer):a("
").addClass(d.navContainerClass).prependTo(this._controls.$container),this._controls.$next=a("<"+d.navElement+">"),this._controls.$previous=this._controls.$next.clone(),this._controls.$previous.addClass(d.navClass[0]).html(d.navText[0]).hide().prependTo(b).on("click",a.proxy(function(){this.prev(d.navSpeed)},this)),this._controls.$next.addClass(d.navClass[1]).html(d.navText[1]).hide().appendTo(b).on("click",a.proxy(function(){this.next(d.navSpeed)},this));for(c in this._overrides)this._core[c]=a.proxy(this[c],this)},b.prototype.destroy=function(){var a,b,c,d;for(a in this._handlers)this.$element.off(a,this._handlers[a]);for(b in this._controls)this._controls[b].remove();for(d in this.overides)this._core[d]=this._overrides[d];for(c in Object.getOwnPropertyNames(this))"function"!=typeof this[c]&&(this[c]=null)},b.prototype.update=function(){var a,b,c,d=this._core.settings,e=this._core.clones().length/2,f=e+this._core.items().length,g=d.center||d.autoWidth||d.dotData?1:d.dotsEach||d.items;if("page"!==d.slideBy&&(d.slideBy=Math.min(d.slideBy,d.items)),d.dots||"page"==d.slideBy)for(this._pages=[],a=e,b=0,c=0;f>a;a++)(b>=g||0===b)&&(this._pages.push({start:a-e,end:a-e+g-1}),b=0,++c),b+=this._core.mergers(this._core.relative(a))},b.prototype.draw=function(){var b,c,d="",e=this._core.settings,f=(this._core.$stage.children(),this._core.relative(this._core.current()));if(!e.nav||e.loop||e.navRewind||(this._controls.$previous.toggleClass("disabled",0>=f),this._controls.$next.toggleClass("disabled",f>=this._core.maximum())),this._controls.$previous.toggle(e.nav),this._controls.$next.toggle(e.nav),e.dots){if(b=this._pages.length-this._controls.$indicators.children().length,e.dotData&&0!==b){for(c=0;c
0?(d=new Array(b+1).join(this._templates[0]),this._controls.$indicators.append(d)):0>b&&this._controls.$indicators.children().slice(b).remove();this._controls.$indicators.find(".active").removeClass("active"),this._controls.$indicators.children().eq(a.inArray(this.current(),this._pages)).addClass("active")}this._controls.$indicators.toggle(e.dots)},b.prototype.onTrigger=function(b){var c=this._core.settings;b.page={index:a.inArray(this.current(),this._pages),count:this._pages.length,size:c&&(c.center||c.autoWidth||c.dotData?1:c.dotsEach||c.items)}},b.prototype.current=function(){var b=this._core.relative(this._core.current());return a.grep(this._pages,function(a){return a.start<=b&&a.end>=b}).pop()},b.prototype.getPosition=function(b){var c,d,e=this._core.settings;return"page"==e.slideBy?(c=a.inArray(this.current(),this._pages),d=this._pages.length,b?++c:--c,c=this._pages[(c%d+d)%d].start):(c=this._core.relative(this._core.current()),d=this._core.items().length,b?c+=e.slideBy:c-=e.slideBy),c},b.prototype.next=function(b){a.proxy(this._overrides.to,this._core)(this.getPosition(!0),b)},b.prototype.prev=function(b){a.proxy(this._overrides.to,this._core)(this.getPosition(!1),b)},b.prototype.to=function(b,c,d){var e;d?a.proxy(this._overrides.to,this._core)(b,c):(e=this._pages.length,a.proxy(this._overrides.to,this._core)(this._pages[(b%e+e)%e].start,c))},a.fn.owlCarousel.Constructor.Plugins.Navigation=b}(window.Zepto||window.jQuery,window,document),function(a,b){"use strict";var c=function(d){this._core=d,this._hashes={},this.$element=this._core.$element,this._handlers={"initialized.owl.carousel":a.proxy(function(){"URLHash"==this._core.settings.startPosition&&a(b).trigger("hashchange.owl.navigation")},this),"prepared.owl.carousel":a.proxy(function(b){var c=a(b.content).find("[data-hash]").andSelf("[data-hash]").attr("data-hash");this._hashes[c]=b.content},this)},this._core.options=a.extend({},c.Defaults,this._core.options),this.$element.on(this._handlers),a(b).on("hashchange.owl.navigation",a.proxy(function(){var a=b.location.hash.substring(1),c=this._core.$stage.children(),d=this._hashes[a]&&c.index(this._hashes[a])||0;return a?void this._core.to(d,!1,!0):!1},this))};c.Defaults={URLhashListener:!1},c.prototype.destroy=function(){var c,d;a(b).off("hashchange.owl.navigation");for(c in this._handlers)this._core.$element.off(c,this._handlers[c]);for(d in Object.getOwnPropertyNames(this))"function"!=typeof this[d]&&(this[d]=null)},a.fn.owlCarousel.Constructor.Plugins.Hash=c}(window.Zepto||window.jQuery,window,document);
+
+}(jQuery));
+
+
+(function($){
+ /*! Lazy Load 1.9.5 - MIT license - Copyright 2010-2015 Mika Tuupola */
+ !function(a,b,c,d){var e=a(b);a.fn.lazyload=function(f){function g(){var b=0;i.each(function(){var c=a(this);if(!j.skip_invisible||c.is(":visible"))if(a.abovethetop(this,j)||a.leftofbegin(this,j));else if(a.belowthefold(this,j)||a.rightoffold(this,j)){if(++b>j.failure_limit)return!1}else c.trigger("appear"),b=0})}var h,i=this,j={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!1,appear:null,load:null,placeholder:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"};return f&&(d!==f.failurelimit&&(f.failure_limit=f.failurelimit,delete f.failurelimit),d!==f.effectspeed&&(f.effect_speed=f.effectspeed,delete f.effectspeed),a.extend(j,f)),h=j.container===d||j.container===b?e:a(j.container),0===j.event.indexOf("scroll")&&h.bind(j.event,function(){return g()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,(c.attr("src")===d||c.attr("src")===!1)&&c.is("img")&&c.attr("src",j.placeholder),c.one("appear",function(){if(!this.loaded){if(j.appear){var d=i.length;j.appear.call(b,d,j)}a(" ").bind("load",function(){var d=c.attr("data-"+j.data_attribute);c.hide(),c.is("img")?c.attr("src",d):c.css("background-image","url('"+d+"')"),c[j.effect](j.effect_speed),b.loaded=!0;var e=a.grep(i,function(a){return!a.loaded});if(i=a(e),j.load){var f=i.length;j.load.call(b,f,j)}}).attr("src",c.attr("data-"+j.data_attribute))}}),0!==j.event.indexOf("scroll")&&c.bind(j.event,function(){b.loaded||c.trigger("appear")})}),e.bind("resize",function(){g()}),/(?:iphone|ipod|ipad).*os 5/gi.test(navigator.appVersion)&&e.bind("pageshow",function(b){b.originalEvent&&b.originalEvent.persisted&&i.each(function(){a(this).trigger("appear")})}),a(c).ready(function(){g()}),this},a.belowthefold=function(c,f){var g;return g=f.container===d||f.container===b?(b.innerHeight?b.innerHeight:e.height())+e.scrollTop():a(f.container).offset().top+a(f.container).height(),g<=a(c).offset().top-f.threshold},a.rightoffold=function(c,f){var g;return g=f.container===d||f.container===b?e.width()+e.scrollLeft():a(f.container).offset().left+a(f.container).width(),g<=a(c).offset().left-f.threshold},a.abovethetop=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollTop():a(f.container).offset().top,g>=a(c).offset().top+f.threshold+a(c).height()},a.leftofbegin=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollLeft():a(f.container).offset().left,g>=a(c).offset().left+f.threshold+a(c).width()},a.inviewport=function(b,c){return!(a.rightoffold(b,c)||a.leftofbegin(b,c)||a.belowthefold(b,c)||a.abovethetop(b,c))},a.extend(a.expr[":"],{"below-the-fold":function(b){return a.belowthefold(b,{threshold:0})},"above-the-top":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-screen":function(b){return a.rightoffold(b,{threshold:0})},"left-of-screen":function(b){return!a.rightoffold(b,{threshold:0})},"in-viewport":function(b){return a.inviewport(b,{threshold:0})},"above-the-fold":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-fold":function(b){return a.rightoffold(b,{threshold:0})},"left-of-fold":function(b){return!a.rightoffold(b,{threshold:0})}})}(jQuery,window,document);
+
+}(jQuery));
+
+
+(function($){
+/*
+ * Snap.js
+ *
+ * Copyright 2013, Jacob Kelley - http://jakiestfu.com/
+ * Released under the MIT Licence
+ * http://opensource.org/licenses/MIT
+ *
+ * Github: http://github.com/jakiestfu/Snap.js/
+ * Version: 1.9.3 (with elementMirror for fixed navigation bars)
+ */
+/*jslint browser: true*/
+/*global define, module, ender*/
+(function(win, doc) {
+ 'use strict';
+ var Snap = Snap || function(userOpts) {
+ var settings = {
+ element: null,
+ elementMirror: null,
+ elementMirror2: null,
+ dragger: null,
+ disable: 'none',
+ addBodyClasses: true,
+ hyperextensible: true,
+ resistance: 0.5,
+ flickThreshold: 50,
+ transitionSpeed: 0.3,
+ easing: 'ease',
+ maxPosition: 266,
+ minPosition: -266,
+ tapToClose: true,
+ touchToDrag: true,
+ slideIntent: 40, // degrees
+ minDragDistance: 5
+ },
+ cache = {
+ simpleStates: {
+ opening: null,
+ towards: null,
+ hyperExtending: null,
+ halfway: null,
+ flick: null,
+ translation: {
+ absolute: 0,
+ relative: 0,
+ sinceDirectionChange: 0,
+ percentage: 0
+ }
+ }
+ },
+ eventList = {},
+ utils = {
+ hasTouch: ('ontouchstart' in doc.documentElement || win.navigator.msPointerEnabled),
+ eventType: function(action) {
+ var eventTypes = {
+ down: (utils.hasTouch ? 'touchstart' : 'mousedown'),
+ move: (utils.hasTouch ? 'touchmove' : 'mousemove'),
+ up: (utils.hasTouch ? 'touchend' : 'mouseup'),
+ out: (utils.hasTouch ? 'touchcancel' : 'mouseout')
+ };
+ return eventTypes[action];
+ },
+ page: function(t, e){
+ return (utils.hasTouch && e.touches.length && e.touches[0]) ? e.touches[0]['page'+t] : e['page'+t];
+ },
+ klass: {
+ has: function(el, name){
+ return (el.className).indexOf(name) !== -1;
+ },
+ add: function(el, name){
+ if(!utils.klass.has(el, name) && settings.addBodyClasses){
+ el.className += " "+name;
+ }
+ },
+ remove: function(el, name){
+ if(settings.addBodyClasses){
+ el.className = (el.className).replace(name, "").replace(/^\s+|\s+$/g, '');
+ }
+ }
+ },
+ dispatchEvent: function(type) {
+ if (typeof eventList[type] === 'function') {
+ return eventList[type].call();
+ }
+ },
+ vendor: function(){
+ var tmp = doc.createElement("div"),
+ prefixes = 'webkit Moz O ms'.split(' '),
+ i;
+ for (i in prefixes) {
+ if (typeof tmp.style[prefixes[i] + 'Transition'] !== 'undefined') {
+ return prefixes[i];
+ }
+ }
+ },
+ transitionCallback: function(){
+ return (cache.vendor==='Moz' || cache.vendor==='ms') ? 'transitionend' : cache.vendor+'TransitionEnd';
+ },
+ canTransform: function(){settings.element
+ return typeof settings.element.style[cache.vendor+'Transform'] !== 'undefined';
+ },
+ deepExtend: function(destination, source) {
+ var property;
+ for (property in source) {
+ if (source[property] && source[property].constructor && source[property].constructor === Object) {
+ destination[property] = destination[property] || {};
+ utils.deepExtend(destination[property], source[property]);
+ } else {
+ destination[property] = source[property];
+ }
+ }
+ return destination;
+ },
+ angleOfDrag: function(x, y) {
+ var degrees, theta;
+ // Calc Theta
+ theta = Math.atan2(-(cache.startDragY - y), (cache.startDragX - x));
+ if (theta < 0) {
+ theta += 2 * Math.PI;
+ }
+ // Calc Degrees
+ degrees = Math.floor(theta * (180 / Math.PI) - 180);
+ if (degrees < 0 && degrees > -180) {
+ degrees = 360 - Math.abs(degrees);
+ }
+ return Math.abs(degrees);
+ },
+ events: {
+ addEvent: function addEvent(element, eventName, func) {
+ if (element.addEventListener) {
+ return element.addEventListener(eventName, func, false);
+ } else if (element.attachEvent) {
+ return element.attachEvent("on" + eventName, func);
+ }
+ },
+ removeEvent: function addEvent(element, eventName, func) {
+ if (element.addEventListener) {
+ return element.removeEventListener(eventName, func, false);
+ } else if (element.attachEvent) {
+ return element.detachEvent("on" + eventName, func);
+ }
+ },
+ prevent: function(e) {
+ if (e.preventDefault) {
+ e.preventDefault();
+ } else {
+ e.returnValue = false;
+ }
+ }
+ },
+ parentUntil: function(el, attr) {
+ var isStr = typeof attr === 'string';
+ while (el.parentNode) {
+ if (isStr && el.getAttribute && el.getAttribute(attr)){
+ return el;
+ } else if(!isStr && el === attr){
+ return el;
+ }
+ el = el.parentNode;
+ }
+ return null;
+ }
+ },
+ action = {
+ translate: {
+ get: {
+ matrix: function(index) {
+
+ if( !utils.canTransform() ){
+ return parseInt(settings.element.style.left, 10);
+ } else {
+ var matrix = win.getComputedStyle(settings.element)[cache.vendor+'Transform'].match(/\((.*)\)/),
+ ieOffset = 8;
+ if (matrix) {
+ matrix = matrix[1].split(',');
+ if(matrix.length===16){
+ index+=ieOffset;
+ }
+ return parseInt(matrix[index], 10);
+ }
+ return 0;
+ }
+ }
+ },
+ easeCallback: function(){
+ settings.element.style[cache.vendor+'Transition'] = '';
+ settings.elementMirror.style[cache.vendor+'Transition'] = '';
+ settings.elementMirror2.style[cache.vendor+'Transition'] = '';
+ cache.translation = action.translate.get.matrix(4);
+ cache.easing = false;
+ clearInterval(cache.animatingInterval);
+
+ if(cache.easingTo===0){
+ utils.klass.remove(doc.body, 'snapjs-right');
+ utils.klass.remove(doc.body, 'snapjs-left');
+ }
+
+ utils.dispatchEvent('animated');
+ utils.events.removeEvent(settings.element, utils.transitionCallback(), action.translate.easeCallback);
+ },
+ easeTo: function(n) {
+
+ if( !utils.canTransform() ){
+ cache.translation = n;
+ action.translate.x(n);
+ } else {
+ cache.easing = true;
+ cache.easingTo = n;
+
+ settings.element.style[cache.vendor+'Transition'] = 'all ' + settings.transitionSpeed + 's ' + settings.easing;
+ settings.elementMirror.style[cache.vendor+'Transition'] = 'all ' + settings.transitionSpeed + 's ' + settings.easing;
+ settings.elementMirror2.style[cache.vendor+'Transition'] = 'all ' + settings.transitionSpeed + 's ' + settings.easing;
+
+ cache.animatingInterval = setInterval(function() {
+ utils.dispatchEvent('animating');
+ }, 1);
+
+ utils.events.addEvent(settings.element, utils.transitionCallback(), action.translate.easeCallback);
+ action.translate.x(n);
+ }
+ if(n===0){
+ settings.element.style[cache.vendor+'Transform'] = '';
+ settings.elementMirror.style[cache.vendor+'Transform'] = '';
+ settings.elementMirror2.style[cache.vendor+'Transform'] = '';
+ }
+ },
+ x: function(n) {
+ if( (settings.disable==='left' && n>0) ||
+ (settings.disable==='right' && n<0)
+ ){ return; }
+
+ if( !settings.hyperextensible ){
+ if( n===settings.maxPosition || n>settings.maxPosition ){
+ n=settings.maxPosition;
+ } else if( n===settings.minPosition || n 0,
+ translateTo = whileDragX,
+ diff;
+
+ // Shown no intent already
+ if((cache.intentChecked && !cache.hasIntent)){
+ return;
+ }
+
+ if(settings.addBodyClasses){
+ if((absoluteTranslation)>0){
+ utils.klass.add(doc.body, 'snapjs-left');
+ utils.klass.remove(doc.body, 'snapjs-right');
+ } else if((absoluteTranslation)<0){
+ utils.klass.add(doc.body, 'snapjs-right');
+ utils.klass.remove(doc.body, 'snapjs-left');
+ }
+ }
+
+ if (cache.hasIntent === false || cache.hasIntent === null) {
+ var deg = utils.angleOfDrag(thePageX, thePageY),
+ inRightRange = (deg >= 0 && deg <= settings.slideIntent) || (deg <= 360 && deg > (360 - settings.slideIntent)),
+ inLeftRange = (deg >= 180 && deg <= (180 + settings.slideIntent)) || (deg <= 180 && deg >= (180 - settings.slideIntent));
+ if (!inLeftRange && !inRightRange) {
+ cache.hasIntent = false;
+ } else {
+ cache.hasIntent = true;
+ }
+ cache.intentChecked = true;
+ }
+
+ if (
+ (settings.minDragDistance>=Math.abs(thePageX-cache.startDragX)) || // Has user met minimum drag distance?
+ (cache.hasIntent === false)
+ ) {
+ return;
+ }
+
+ utils.events.prevent(e);
+ utils.dispatchEvent('drag');
+
+ cache.dragWatchers.current = thePageX;
+ // Determine which direction we are going
+ if (cache.dragWatchers.last > thePageX) {
+ if (cache.dragWatchers.state !== 'left') {
+ cache.dragWatchers.state = 'left';
+ cache.dragWatchers.hold = thePageX;
+ }
+ cache.dragWatchers.last = thePageX;
+ } else if (cache.dragWatchers.last < thePageX) {
+ if (cache.dragWatchers.state !== 'right') {
+ cache.dragWatchers.state = 'right';
+ cache.dragWatchers.hold = thePageX;
+ }
+ cache.dragWatchers.last = thePageX;
+ }
+ if (openingLeft) {
+ // Pulling too far to the right
+ if (settings.maxPosition < absoluteTranslation) {
+ diff = (absoluteTranslation - settings.maxPosition) * settings.resistance;
+ translateTo = whileDragX - diff;
+ }
+ cache.simpleStates = {
+ opening: 'left',
+ towards: cache.dragWatchers.state,
+ hyperExtending: settings.maxPosition < absoluteTranslation,
+ halfway: absoluteTranslation > (settings.maxPosition / 2),
+ flick: Math.abs(cache.dragWatchers.current - cache.dragWatchers.hold) > settings.flickThreshold,
+ translation: {
+ absolute: absoluteTranslation,
+ relative: whileDragX,
+ sinceDirectionChange: (cache.dragWatchers.current - cache.dragWatchers.hold),
+ percentage: (absoluteTranslation/settings.maxPosition)*100
+ }
+ };
+ } else {
+ // Pulling too far to the left
+ if (settings.minPosition > absoluteTranslation) {
+ diff = (absoluteTranslation - settings.minPosition) * settings.resistance;
+ translateTo = whileDragX - diff;
+ }
+ cache.simpleStates = {
+ opening: 'right',
+ towards: cache.dragWatchers.state,
+ hyperExtending: settings.minPosition > absoluteTranslation,
+ halfway: absoluteTranslation < (settings.minPosition / 2),
+ flick: Math.abs(cache.dragWatchers.current - cache.dragWatchers.hold) > settings.flickThreshold,
+ translation: {
+ absolute: absoluteTranslation,
+ relative: whileDragX,
+ sinceDirectionChange: (cache.dragWatchers.current - cache.dragWatchers.hold),
+ percentage: (absoluteTranslation/settings.minPosition)*100
+ }
+ };
+ }
+ action.translate.x(translateTo + translated);
+ }
+ },
+ endDrag: function(e) {
+ if (cache.isDragging) {
+ utils.dispatchEvent('end');
+ var translated = action.translate.get.matrix(4);
+
+ // Tap Close
+ if (cache.dragWatchers.current === 0 && translated !== 0 && settings.tapToClose) {
+ utils.dispatchEvent('close');
+ utils.events.prevent(e);
+ action.translate.easeTo(0);
+ cache.isDragging = false;
+ cache.startDragX = 0;
+ return;
+ }
+
+ // Revealing Left
+ if (cache.simpleStates.opening === 'left') {
+ // Halfway, Flicking, or Too Far Out
+ if ((cache.simpleStates.halfway || cache.simpleStates.hyperExtending || cache.simpleStates.flick)) {
+ if (cache.simpleStates.flick && cache.simpleStates.towards === 'left') { // Flicking Closed
+ action.translate.easeTo(0);
+ } else if (
+ (cache.simpleStates.flick && cache.simpleStates.towards === 'right') || // Flicking Open OR
+ (cache.simpleStates.halfway || cache.simpleStates.hyperExtending) // At least halfway open OR hyperextending
+ ) {
+ action.translate.easeTo(settings.maxPosition); // Open Left
+ }
+ } else {
+ action.translate.easeTo(0); // Close Left
+ }
+ // Revealing Right
+ } else if (cache.simpleStates.opening === 'right') {
+ // Halfway, Flicking, or Too Far Out
+ if ((cache.simpleStates.halfway || cache.simpleStates.hyperExtending || cache.simpleStates.flick)) {
+ if (cache.simpleStates.flick && cache.simpleStates.towards === 'right') { // Flicking Closed
+ action.translate.easeTo(0);
+ } else if (
+ (cache.simpleStates.flick && cache.simpleStates.towards === 'left') || // Flicking Open OR
+ (cache.simpleStates.halfway || cache.simpleStates.hyperExtending) // At least halfway open OR hyperextending
+ ) {
+ action.translate.easeTo(settings.minPosition); // Open Right
+ }
+ } else {
+ action.translate.easeTo(0); // Close Right
+ }
+ }
+ cache.isDragging = false;
+ cache.startDragX = utils.page('X', e);
+ }
+ }
+ }
+ },
+ init = function(opts) {
+ if (opts.element) {
+ utils.deepExtend(settings, opts);
+ cache.vendor = utils.vendor();
+ action.drag.listen();
+ }
+ };
+ /*
+ * Public
+ */
+ this.open = function(side) {
+ utils.dispatchEvent('open');
+ utils.klass.remove(doc.body, 'snapjs-expand-left');
+ utils.klass.remove(doc.body, 'snapjs-expand-right');
+
+ if (side === 'left') {
+ cache.simpleStates.opening = 'left';
+ cache.simpleStates.towards = 'right';
+ utils.klass.add(doc.body, 'snapjs-left');
+ utils.klass.remove(doc.body, 'snapjs-right');
+ action.translate.easeTo(settings.maxPosition);
+ } else if (side === 'right') {
+ cache.simpleStates.opening = 'right';
+ cache.simpleStates.towards = 'left';
+ utils.klass.remove(doc.body, 'snapjs-left');
+ utils.klass.add(doc.body, 'snapjs-right');
+ action.translate.easeTo(settings.minPosition);
+ }
+ };
+ this.close = function() {
+ utils.dispatchEvent('close');
+ action.translate.easeTo(0);
+ };
+ this.expand = function(side){
+ var to = win.innerWidth || doc.documentElement.clientWidth;
+
+ if(side==='left'){
+ utils.dispatchEvent('expandLeft');
+ utils.klass.add(doc.body, 'snapjs-expand-left');
+ utils.klass.remove(doc.body, 'snapjs-expand-right');
+ } else {
+ utils.dispatchEvent('expandRight');
+ utils.klass.add(doc.body, 'snapjs-expand-right');
+ utils.klass.remove(doc.body, 'snapjs-expand-left');
+ to *= -1;
+ }
+ action.translate.easeTo(to);
+ };
+
+ this.on = function(evt, fn) {
+ eventList[evt] = fn;
+ return this;
+ };
+ this.off = function(evt) {
+ if (eventList[evt]) {
+ eventList[evt] = false;
+ }
+ };
+
+ this.enable = function() {
+ utils.dispatchEvent('enable');
+ action.drag.listen();
+ };
+ this.disable = function() {
+ utils.dispatchEvent('disable');
+ action.drag.stopListening();
+ };
+
+ this.settings = function(opts){
+ utils.deepExtend(settings, opts);
+ };
+
+ this.state = function() {
+ var state,
+ fromLeft = action.translate.get.matrix(4);
+ if (fromLeft === settings.maxPosition) {
+ state = 'left';
+ } else if (fromLeft === settings.minPosition) {
+ state = 'right';
+ } else {
+ state = 'closed';
+ }
+ return {
+ state: state,
+ info: cache.simpleStates
+ };
+ };
+ init(userOpts);
+ };
+ if ((typeof module !== 'undefined') && module.exports) {
+ module.exports = Snap;
+ }
+ if (typeof ender === 'undefined') {
+ this.Snap = Snap;
+ }
+ if ((typeof define === "function") && define.amd) {
+ define("snap", [], function() {
+ return Snap;
+ });
+ }
+}).call(this, window, document);
+}(jQuery));// JavaScript Document
+
+(function($){
+ /*License MIT & GPL 3.0 Licenses.*/
+ /* https://github.com/rendro/countdown */
+
+ !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e.Countdown=t()}}(function(){return function t(e,i,n){function s(r,a){if(!i[r]){if(!e[r]){var d="function"==typeof require&&require;if(!a&&d)return d(r,!0);if(o)return o(r,!0);var f=new Error("Cannot find module '"+r+"'");throw f.code="MODULE_NOT_FOUND",f}var h=i[r]={exports:{}};e[r][0].call(h.exports,function(t){var i=e[r][1][t];return s(i?i:t)},h,h.exports,t,e,i,n)}return i[r].exports}for(var o="function"==typeof require&&require,r=0;r"+t.years+"years "+t.days+"days
"+this.leadingZeros(t.hours)+"hours
"+this.leadingZeros(t.min)+"minutes
"+this.leadingZeros(t.sec)+"seconds
"}},n=function(t,e){this.el=t,this.options={},this.interval=!1;for(var n in i)i.hasOwnProperty(n)&&(this.options[n]="undefined"!=typeof e[n]?e[n]:i[n],"date"===n&&"object"!=typeof this.options.date&&(this.options.date=new Date(this.options.date)),"function"==typeof this.options[n]&&(this.options[n]=this.options[n].bind(this)));this.getDiffDate=function(){var t=(this.options.date.getTime()-Date.now()+this.options.offset)/1e3,e={years:0,days:0,hours:0,min:0,sec:0,millisec:0};return 0>=t?(this.interval&&(this.stop(),this.options.onEnd()),e):(t>=31557600&&(e.years=Math.floor(t/31557600),t-=365.25*e.years*86400),t>=86400&&(e.days=Math.floor(t/86400),t-=86400*e.days),t>=3600&&(e.hours=Math.floor(t/3600),t-=3600*e.hours),t>=60&&(e.min=Math.floor(t/60),t-=60*e.min),e.sec=Math.round(t),e.millisec=t%1*1e3,e)}.bind(this),this.leadingZeros=function(t,e){return e=e||2,t=String(t),t.length>e?t:(Array(e+1).join("0")+t).substr(-e)},this.update=function(t){return"object"!=typeof t&&(t=new Date(t)),this.options.date=t,this.render(),this}.bind(this),this.stop=function(){return this.interval&&(clearInterval(this.interval),this.interval=!1),this}.bind(this),this.render=function(){return this.options.render(this.getDiffDate()),this}.bind(this),this.start=function(){return this.interval?void 0:(this.render(),this.options.refresh&&(this.interval=setInterval(this.render,this.options.refresh)),this)}.bind(this),this.updateOffset=function(t){return this.options.offset=t,this}.bind(this),this.start()};e.exports=n},{}],2:[function(t,e){var i=t("./countdown.js"),n="countdown",s="date";jQuery.fn.countdown=function(t){return $.each(this,function(e,o){var r=$(o);r.data(n)||(r.data(s)&&(t.date=r.data(s)),r.data(n,new i(o,t)))})},e.exports=i},{"./countdown.js":1}]},{},[2])(2)});
+}(jQuery));
+
+
+(function($){
+ /*!
+ * Chart.js
+ * http://chartjs.org/
+ * Version: 1.0.2
+ *
+ * Copyright 2015 Nick Downie
+ * Released under the MIT license
+ * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
+ */
+ (function(){"use strict";var t=this,i=t.Chart,e=function(t){this.canvas=t.canvas,this.ctx=t;var i=function(t,i){return t["offset"+i]?t["offset"+i]:document.defaultView.getComputedStyle(t).getPropertyValue(i)},e=this.width=i(t.canvas,"Width"),n=this.height=i(t.canvas,"Height");t.canvas.width=e,t.canvas.height=n;var e=this.width=t.canvas.width,n=this.height=t.canvas.height;return this.aspectRatio=this.width/this.height,s.retinaScale(this),this};e.defaults={global:{animation:false,animationSteps:60,animationEasing:"easeOutQuart",showScale:!0,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleIntegersOnly:!0,scaleBeginAtZero:!1,scaleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",responsive:true,maintainAspectRatio:true,showTooltips:!0,customTooltips:!1,tooltipEvents:["mousemove","touchstart","touchmove","mouseout"],tooltipFillColor:"rgba(0,0,0,0.8)",tooltipFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipFontSize:14,tooltipFontStyle:"normal",tooltipFontColor:"#fff",tooltipTitleFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",tooltipTitleFontSize:14,tooltipTitleFontStyle:"bold",tooltipTitleFontColor:"#fff",tooltipYPadding:6,tooltipXPadding:6,tooltipCaretSize:8,tooltipCornerRadius:6,tooltipXOffset:10,tooltipTemplate:"<%if (label){%><%=label%>: <%}%><%= value %>",multiTooltipTemplate:"<%= value %>",multiTooltipKeyBackground:"#fff",onAnimationProgress:function(){},onAnimationComplete:function(){}}},e.types={};var s=e.helpers={},n=s.each=function(t,i,e){var s=Array.prototype.slice.call(arguments,3);if(t)if(t.length===+t.length){var n;for(n=0;n
=0;s--){var n=t[s];if(i(n))return n}},s.inherits=function(t){var i=this,e=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return i.apply(this,arguments)},s=function(){this.constructor=e};return s.prototype=i.prototype,e.prototype=new s,e.extend=r,t&&a(e.prototype,t),e.__super__=i.prototype,e}),c=s.noop=function(){},u=s.uid=function(){var t=0;return function(){return"chart-"+t++}}(),d=s.warn=function(t){window.console&&"function"==typeof window.console.warn&&console.warn(t)},p=s.amd="function"==typeof define&&define.amd,f=s.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},g=s.max=function(t){return Math.max.apply(Math,t)},m=s.min=function(t){return Math.min.apply(Math,t)},v=(s.cap=function(t,i,e){if(f(i)){if(t>i)return i}else if(f(e)&&e>t)return e;return t},s.getDecimalPlaces=function(t){return t%1!==0&&f(t)?t.toString().split(".")[1].length:0}),S=s.radians=function(t){return t*(Math.PI/180)},x=(s.getAngleFromPoint=function(t,i){var e=i.x-t.x,s=i.y-t.y,n=Math.sqrt(e*e+s*s),o=2*Math.PI+Math.atan2(s,e);return 0>e&&0>s&&(o+=2*Math.PI),{angle:o,distance:n}},s.aliasPixel=function(t){return t%2===0?0:.5}),y=(s.splineCurve=function(t,i,e,s){var n=Math.sqrt(Math.pow(i.x-t.x,2)+Math.pow(i.y-t.y,2)),o=Math.sqrt(Math.pow(e.x-i.x,2)+Math.pow(e.y-i.y,2)),a=s*n/(n+o),h=s*o/(n+o);return{inner:{x:i.x-a*(e.x-t.x),y:i.y-a*(e.y-t.y)},outer:{x:i.x+h*(e.x-t.x),y:i.y+h*(e.y-t.y)}}},s.calculateOrderOfMagnitude=function(t){return Math.floor(Math.log(t)/Math.LN10)}),C=(s.calculateScaleRange=function(t,i,e,s,n){var o=2,a=Math.floor(i/(1.5*e)),h=o>=a,l=g(t),r=m(t);l===r&&(l+=.5,r>=.5&&!s?r-=.5:l+=.5);for(var c=Math.abs(l-r),u=y(c),d=Math.ceil(l/(1*Math.pow(10,u)))*Math.pow(10,u),p=s?0:Math.floor(r/(1*Math.pow(10,u)))*Math.pow(10,u),f=d-p,v=Math.pow(10,u),S=Math.round(f/v);(S>a||a>2*S)&&!h;)if(S>a)v*=2,S=Math.round(f/v),S%1!==0&&(h=!0);else if(n&&u>=0){if(v/2%1!==0)break;v/=2,S=Math.round(f/v)}else v/=2,S=Math.round(f/v);return h&&(S=o,v=f/S),{steps:S,stepValue:v,min:p,max:p+S*v}},s.template=function(t,i){function e(t,i){var e=/\W/.test(t)?new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+t.replace(/[\r\t\n]/g," ").split("<%").join(" ").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split(" ").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');"):s[t]=s[t];return i?e(i):e}if(t instanceof Function)return t(i);var s={};return e(t,i)}),w=(s.generateLabels=function(t,i,e,s){var o=new Array(i);return labelTemplateString&&n(o,function(i,n){o[n]=C(t,{value:e+s*(n+1)})}),o},s.easingEffects={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-1*t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-0.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return 1*((t=t/1-1)*t*t+1)},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-1*((t=t/1-1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-0.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return 1*(t/=1)*t*t*t*t},easeOutQuint:function(t){return 1*((t=t/1-1)*t*t*t*t+1)},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return-1*Math.cos(t/1*(Math.PI/2))+1},easeOutSine:function(t){return 1*Math.sin(t/1*(Math.PI/2))},easeInOutSine:function(t){return-0.5*(Math.cos(Math.PI*t/1)-1)},easeInExpo:function(t){return 0===t?1:1*Math.pow(2,10*(t/1-1))},easeOutExpo:function(t){return 1===t?1:1*(-Math.pow(2,-10*t/1)+1)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(-Math.pow(2,-10*--t)+2)},easeInCirc:function(t){return t>=1?t:-1*(Math.sqrt(1-(t/=1)*t)-1)},easeOutCirc:function(t){return 1*Math.sqrt(1-(t=t/1-1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-0.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var i=1.70158,e=0,s=1;return 0===t?0:1==(t/=1)?1:(e||(e=.3),st?-.5*s*Math.pow(2,10*(t-=1))*Math.sin(2*(1*t-i)*Math.PI/e):s*Math.pow(2,-10*(t-=1))*Math.sin(2*(1*t-i)*Math.PI/e)*.5+1)},easeInBack:function(t){var i=1.70158;return 1*(t/=1)*t*((i+1)*t-i)},easeOutBack:function(t){var i=1.70158;return 1*((t=t/1-1)*t*((i+1)*t+i)+1)},easeInOutBack:function(t){var i=1.70158;return(t/=.5)<1?.5*t*t*(((i*=1.525)+1)*t-i):.5*((t-=2)*t*(((i*=1.525)+1)*t+i)+2)},easeInBounce:function(t){return 1-w.easeOutBounce(1-t)},easeOutBounce:function(t){return(t/=1)<1/2.75?7.5625*t*t:2/2.75>t?1*(7.5625*(t-=1.5/2.75)*t+.75):2.5/2.75>t?1*(7.5625*(t-=2.25/2.75)*t+.9375):1*(7.5625*(t-=2.625/2.75)*t+.984375)},easeInOutBounce:function(t){return.5>t?.5*w.easeInBounce(2*t):.5*w.easeOutBounce(2*t-1)+.5}}),b=s.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}(),P=s.cancelAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(t){return window.clearTimeout(t,1e3/60)}}(),L=(s.animationLoop=function(t,i,e,s,n,o){var a=0,h=w[e]||w.linear,l=function(){a++;var e=a/i,r=h(e);t.call(o,r,e,a),s.call(o,r,e),i>a?o.animationFrame=b(l):n.apply(o)};b(l)},s.getRelativePosition=function(t){var i,e,s=t.originalEvent||t,n=t.currentTarget||t.srcElement,o=n.getBoundingClientRect();return s.touches?(i=s.touches[0].clientX-o.left,e=s.touches[0].clientY-o.top):(i=s.clientX-o.left,e=s.clientY-o.top),{x:i,y:e}},s.addEvent=function(t,i,e){t.addEventListener?t.addEventListener(i,e):t.attachEvent?t.attachEvent("on"+i,e):t["on"+i]=e}),k=s.removeEvent=function(t,i,e){t.removeEventListener?t.removeEventListener(i,e,!1):t.detachEvent?t.detachEvent("on"+i,e):t["on"+i]=c},F=(s.bindEvents=function(t,i,e){t.events||(t.events={}),n(i,function(i){t.events[i]=function(){e.apply(t,arguments)},L(t.chart.canvas,i,t.events[i])})},s.unbindEvents=function(t,i){n(i,function(i,e){k(t.chart.canvas,e,i)})}),R=s.getMaximumWidth=function(t){var i=t.parentNode;return i.clientWidth},T=s.getMaximumHeight=function(t){var i=t.parentNode;return i.clientHeight},A=(s.getMaximumSize=s.getMaximumWidth,s.retinaScale=function(t){var i=t.ctx,e=t.canvas.width,s=t.canvas.height;window.devicePixelRatio&&(i.canvas.style.width=e+"px",i.canvas.style.height=s+"px",i.canvas.height=s*window.devicePixelRatio,i.canvas.width=e*window.devicePixelRatio,i.scale(window.devicePixelRatio,window.devicePixelRatio))}),M=s.clear=function(t){t.ctx.clearRect(0,0,t.width,t.height)},W=s.fontString=function(t,i,e){return i+" "+t+"px "+e},z=s.longestText=function(t,i,e){t.font=i;var s=0;return n(e,function(i){var e=t.measureText(i).width;s=e>s?e:s}),s},B=s.drawRoundedRectangle=function(t,i,e,s,n,o){t.beginPath(),t.moveTo(i+o,e),t.lineTo(i+s-o,e),t.quadraticCurveTo(i+s,e,i+s,e+o),t.lineTo(i+s,e+n-o),t.quadraticCurveTo(i+s,e+n,i+s-o,e+n),t.lineTo(i+o,e+n),t.quadraticCurveTo(i,e+n,i,e+n-o),t.lineTo(i,e+o),t.quadraticCurveTo(i,e,i+o,e),t.closePath()};e.instances={},e.Type=function(t,i,s){this.options=i,this.chart=s,this.id=u(),e.instances[this.id]=this,i.responsive&&this.resize(),this.initialize.call(this,t)},a(e.Type.prototype,{initialize:function(){return this},clear:function(){return M(this.chart),this},stop:function(){return P(this.animationFrame),this},resize:function(t){this.stop();var i=this.chart.canvas,e=R(this.chart.canvas),s=this.options.maintainAspectRatio?e/this.chart.aspectRatio:T(this.chart.canvas);return i.width=this.chart.width=e,i.height=this.chart.height=s,A(this.chart),"function"==typeof t&&t.apply(this,Array.prototype.slice.call(arguments,1)),this},reflow:c,render:function(t){return t&&this.reflow(),this.options.animation&&!t?s.animationLoop(this.draw,this.options.animationSteps,this.options.animationEasing,this.options.onAnimationProgress,this.options.onAnimationComplete,this):(this.draw(),this.options.onAnimationComplete.call(this)),this},generateLegend:function(){return C(this.options.legendTemplate,this)},destroy:function(){this.clear(),F(this,this.events);var t=this.chart.canvas;t.width=this.chart.width,t.height=this.chart.height,t.style.removeProperty?(t.style.removeProperty("width"),t.style.removeProperty("height")):(t.style.removeAttribute("width"),t.style.removeAttribute("height")),delete e.instances[this.id]},showTooltip:function(t,i){"undefined"==typeof this.activeElements&&(this.activeElements=[]);var o=function(t){var i=!1;return t.length!==this.activeElements.length?i=!0:(n(t,function(t,e){t!==this.activeElements[e]&&(i=!0)},this),i)}.call(this,t);if(o||i){if(this.activeElements=t,this.draw(),this.options.customTooltips&&this.options.customTooltips(!1),t.length>0)if(this.datasets&&this.datasets.length>1){for(var a,h,r=this.datasets.length-1;r>=0&&(a=this.datasets[r].points||this.datasets[r].bars||this.datasets[r].segments,h=l(a,t[0]),-1===h);r--);var c=[],u=[],d=function(){var t,i,e,n,o,a=[],l=[],r=[];return s.each(this.datasets,function(i){t=i.points||i.bars||i.segments,t[h]&&t[h].hasValue()&&a.push(t[h])}),s.each(a,function(t){l.push(t.x),r.push(t.y),c.push(s.template(this.options.multiTooltipTemplate,t)),u.push({fill:t._saved.fillColor||t.fillColor,stroke:t._saved.strokeColor||t.strokeColor})},this),o=m(r),e=g(r),n=m(l),i=g(l),{x:n>this.chart.width/2?n:i,y:(o+e)/2}}.call(this,h);new e.MultiTooltip({x:d.x,y:d.y,xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,xOffset:this.options.tooltipXOffset,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,titleTextColor:this.options.tooltipTitleFontColor,titleFontFamily:this.options.tooltipTitleFontFamily,titleFontStyle:this.options.tooltipTitleFontStyle,titleFontSize:this.options.tooltipTitleFontSize,cornerRadius:this.options.tooltipCornerRadius,labels:c,legendColors:u,legendColorBackground:this.options.multiTooltipKeyBackground,title:t[0].label,chart:this.chart,ctx:this.chart.ctx,custom:this.options.customTooltips}).draw()}else n(t,function(t){var i=t.tooltipPosition();new e.Tooltip({x:Math.round(i.x),y:Math.round(i.y),xPadding:this.options.tooltipXPadding,yPadding:this.options.tooltipYPadding,fillColor:this.options.tooltipFillColor,textColor:this.options.tooltipFontColor,fontFamily:this.options.tooltipFontFamily,fontStyle:this.options.tooltipFontStyle,fontSize:this.options.tooltipFontSize,caretHeight:this.options.tooltipCaretSize,cornerRadius:this.options.tooltipCornerRadius,text:C(this.options.tooltipTemplate,t),chart:this.chart,custom:this.options.customTooltips}).draw()},this);return this}},toBase64Image:function(){return this.chart.canvas.toDataURL.apply(this.chart.canvas,arguments)}}),e.Type.extend=function(t){var i=this,s=function(){return i.apply(this,arguments)};if(s.prototype=o(i.prototype),a(s.prototype,t),s.extend=e.Type.extend,t.name||i.prototype.name){var n=t.name||i.prototype.name,l=e.defaults[i.prototype.name]?o(e.defaults[i.prototype.name]):{};e.defaults[n]=a(l,t.defaults),e.types[n]=s,e.prototype[n]=function(t,i){var o=h(e.defaults.global,e.defaults[n],i||{});return new s(t,o,this)}}else d("Name not provided for this chart, so it hasn't been registered");return i},e.Element=function(t){a(this,t),this.initialize.apply(this,arguments),this.save()},a(e.Element.prototype,{initialize:function(){},restore:function(t){return t?n(t,function(t){this[t]=this._saved[t]},this):a(this,this._saved),this},save:function(){return this._saved=o(this),delete this._saved._saved,this},update:function(t){return n(t,function(t,i){this._saved[i]=this[i],this[i]=t},this),this},transition:function(t,i){return n(t,function(t,e){this[e]=(t-this._saved[e])*i+this._saved[e]},this),this},tooltipPosition:function(){return{x:this.x,y:this.y}},hasValue:function(){return f(this.value)}}),e.Element.extend=r,e.Point=e.Element.extend({display:!0,inRange:function(t,i){var e=this.hitDetectionRadius+this.radius;return Math.pow(t-this.x,2)+Math.pow(i-this.y,2)=this.startAngle&&e.angle<=this.endAngle,o=e.distance>=this.innerRadius&&e.distance<=this.outerRadius;return n&&o},tooltipPosition:function(){var t=this.startAngle+(this.endAngle-this.startAngle)/2,i=(this.outerRadius-this.innerRadius)/2+this.innerRadius;return{x:this.x+Math.cos(t)*i,y:this.y+Math.sin(t)*i}},draw:function(t){var i=this.ctx;i.beginPath(),i.arc(this.x,this.y,this.outerRadius,this.startAngle,this.endAngle),i.arc(this.x,this.y,this.innerRadius,this.endAngle,this.startAngle,!0),i.closePath(),i.strokeStyle=this.strokeColor,i.lineWidth=this.strokeWidth,i.fillStyle=this.fillColor,i.fill(),i.lineJoin="bevel",this.showStroke&&i.stroke()}}),e.Rectangle=e.Element.extend({draw:function(){var t=this.ctx,i=this.width/2,e=this.x-i,s=this.x+i,n=this.base-(this.base-this.y),o=this.strokeWidth/2;this.showStroke&&(e+=o,s-=o,n+=o),t.beginPath(),t.fillStyle=this.fillColor,t.strokeStyle=this.strokeColor,t.lineWidth=this.strokeWidth,t.moveTo(e,this.base),t.lineTo(e,n),t.lineTo(s,n),t.lineTo(s,this.base),t.fill(),this.showStroke&&t.stroke()},height:function(){return this.base-this.y},inRange:function(t,i){return t>=this.x-this.width/2&&t<=this.x+this.width/2&&i>=this.y&&i<=this.base}}),e.Tooltip=e.Element.extend({draw:function(){var t=this.chart.ctx;t.font=W(this.fontSize,this.fontStyle,this.fontFamily),this.xAlign="center",this.yAlign="above";var i=this.caretPadding=2,e=t.measureText(this.text).width+2*this.xPadding,s=this.fontSize+2*this.yPadding,n=s+this.caretHeight+i;this.x+e/2>this.chart.width?this.xAlign="left":this.x-e/2<0&&(this.xAlign="right"),this.y-n<0&&(this.yAlign="below");var o=this.x-e/2,a=this.y-n;if(t.fillStyle=this.fillColor,this.custom)this.custom(this);else{switch(this.yAlign){case"above":t.beginPath(),t.moveTo(this.x,this.y-i),t.lineTo(this.x+this.caretHeight,this.y-(i+this.caretHeight)),t.lineTo(this.x-this.caretHeight,this.y-(i+this.caretHeight)),t.closePath(),t.fill();break;case"below":a=this.y+i+this.caretHeight,t.beginPath(),t.moveTo(this.x,this.y+i),t.lineTo(this.x+this.caretHeight,this.y+i+this.caretHeight),t.lineTo(this.x-this.caretHeight,this.y+i+this.caretHeight),t.closePath(),t.fill()}switch(this.xAlign){case"left":o=this.x-e+(this.cornerRadius+this.caretHeight);break;case"right":o=this.x-(this.cornerRadius+this.caretHeight)}B(t,o,a,e,s,this.cornerRadius),t.fill(),t.fillStyle=this.textColor,t.textAlign="center",t.textBaseline="middle",t.fillText(this.text,o+e/2,a+s/2)}}}),e.MultiTooltip=e.Element.extend({initialize:function(){this.font=W(this.fontSize,this.fontStyle,this.fontFamily),this.titleFont=W(this.titleFontSize,this.titleFontStyle,this.titleFontFamily),this.height=this.labels.length*this.fontSize+(this.labels.length-1)*(this.fontSize/2)+2*this.yPadding+1.5*this.titleFontSize,this.ctx.font=this.titleFont;var t=this.ctx.measureText(this.title).width,i=z(this.ctx,this.font,this.labels)+this.fontSize+3,e=g([i,t]);this.width=e+2*this.xPadding;var s=this.height/2;this.y-s<0?this.y=s:this.y+s>this.chart.height&&(this.y=this.chart.height-s),this.x>this.chart.width/2?this.x-=this.xOffset+this.width:this.x+=this.xOffset},getLineHeight:function(t){var i=this.y-this.height/2+this.yPadding,e=t-1;return 0===t?i+this.titleFontSize/2:i+(1.5*this.fontSize*e+this.fontSize/2)+1.5*this.titleFontSize},draw:function(){if(this.custom)this.custom(this);else{B(this.ctx,this.x,this.y-this.height/2,this.width,this.height,this.cornerRadius);var t=this.ctx;t.fillStyle=this.fillColor,t.fill(),t.closePath(),t.textAlign="left",t.textBaseline="middle",t.fillStyle=this.titleTextColor,t.font=this.titleFont,t.fillText(this.title,this.x+this.xPadding,this.getLineHeight(0)),t.font=this.font,s.each(this.labels,function(i,e){t.fillStyle=this.textColor,t.fillText(i,this.x+this.xPadding+this.fontSize+3,this.getLineHeight(e+1)),t.fillStyle=this.legendColorBackground,t.fillRect(this.x+this.xPadding,this.getLineHeight(e+1)-this.fontSize/2,this.fontSize,this.fontSize),t.fillStyle=this.legendColors[e].fill,t.fillRect(this.x+this.xPadding,this.getLineHeight(e+1)-this.fontSize/2,this.fontSize,this.fontSize)},this)}}}),e.Scale=e.Element.extend({initialize:function(){this.fit()},buildYLabels:function(){this.yLabels=[];for(var t=v(this.stepValue),i=0;i<=this.steps;i++)this.yLabels.push(C(this.templateString,{value:(this.min+i*this.stepValue).toFixed(t)}));this.yLabelWidth=this.display&&this.showLabels?z(this.ctx,this.font,this.yLabels):0},addXLabel:function(t){this.xLabels.push(t),this.valuesCount++,this.fit()},removeXLabel:function(){this.xLabels.shift(),this.valuesCount--,this.fit()},fit:function(){this.startPoint=this.display?this.fontSize:0,this.endPoint=this.display?this.height-1.5*this.fontSize-5:this.height,this.startPoint+=this.padding,this.endPoint-=this.padding;var t,i=this.endPoint-this.startPoint;for(this.calculateYRange(i),this.buildYLabels(),this.calculateXLabelRotation();i>this.endPoint-this.startPoint;)i=this.endPoint-this.startPoint,t=this.yLabelWidth,this.calculateYRange(i),this.buildYLabels(),tthis.yLabelWidth+10?e/2:this.yLabelWidth+10,this.xLabelRotation=0,this.display){var n,o=z(this.ctx,this.font,this.xLabels);this.xLabelWidth=o;for(var a=Math.floor(this.calculateX(1)-this.calculateX(0))-6;this.xLabelWidth>a&&0===this.xLabelRotation||this.xLabelWidth>a&&this.xLabelRotation<=90&&this.xLabelRotation>0;)n=Math.cos(S(this.xLabelRotation)),t=n*e,i=n*s,t+this.fontSize/2>this.yLabelWidth+8&&(this.xScalePaddingLeft=t+this.fontSize/2),this.xScalePaddingRight=this.fontSize/2,this.xLabelRotation++,this.xLabelWidth=n*o;this.xLabelRotation>0&&(this.endPoint-=Math.sin(S(this.xLabelRotation))*o+3)}else this.xLabelWidth=0,this.xScalePaddingRight=this.padding,this.xScalePaddingLeft=this.padding},calculateYRange:c,drawingArea:function(){return this.startPoint-this.endPoint},calculateY:function(t){var i=this.drawingArea()/(this.min-this.max);return this.endPoint-i*(t-this.min)},calculateX:function(t){var i=(this.xLabelRotation>0,this.width-(this.xScalePaddingLeft+this.xScalePaddingRight)),e=i/Math.max(this.valuesCount-(this.offsetGridLines?0:1),1),s=e*t+this.xScalePaddingLeft;return this.offsetGridLines&&(s+=e/2),Math.round(s)},update:function(t){s.extend(this,t),this.fit()},draw:function(){var t=this.ctx,i=(this.endPoint-this.startPoint)/this.steps,e=Math.round(this.xScalePaddingLeft);this.display&&(t.fillStyle=this.textColor,t.font=this.font,n(this.yLabels,function(n,o){var a=this.endPoint-i*o,h=Math.round(a),l=this.showHorizontalLines;t.textAlign="right",t.textBaseline="middle",this.showLabels&&t.fillText(n,e-10,a),0!==o||l||(l=!0),l&&t.beginPath(),o>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),h+=s.aliasPixel(t.lineWidth),l&&(t.moveTo(e,h),t.lineTo(this.width,h),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(e-5,h),t.lineTo(e,h),t.stroke(),t.closePath()},this),n(this.xLabels,function(i,e){var s=this.calculateX(e)+x(this.lineWidth),n=this.calculateX(e-(this.offsetGridLines?.5:0))+x(this.lineWidth),o=this.xLabelRotation>0,a=this.showVerticalLines;0!==e||a||(a=!0),a&&t.beginPath(),e>0?(t.lineWidth=this.gridLineWidth,t.strokeStyle=this.gridLineColor):(t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor),a&&(t.moveTo(n,this.endPoint),t.lineTo(n,this.startPoint-3),t.stroke(),t.closePath()),t.lineWidth=this.lineWidth,t.strokeStyle=this.lineColor,t.beginPath(),t.moveTo(n,this.endPoint),t.lineTo(n,this.endPoint+5),t.stroke(),t.closePath(),t.save(),t.translate(s,o?this.endPoint+12:this.endPoint+8),t.rotate(-1*S(this.xLabelRotation)),t.font=this.font,t.textAlign=o?"right":"center",t.textBaseline=o?"middle":"top",t.fillText(i,0,0),t.restore()},this))}}),e.RadialScale=e.Element.extend({initialize:function(){this.size=m([this.height,this.width]),this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2},calculateCenterOffset:function(t){var i=this.drawingArea/(this.max-this.min);return(t-this.min)*i},update:function(){this.lineArc?this.drawingArea=this.display?this.size/2-(this.fontSize/2+this.backdropPaddingY):this.size/2:this.setScaleSize(),this.buildYLabels()},buildYLabels:function(){this.yLabels=[];for(var t=v(this.stepValue),i=0;i<=this.steps;i++)this.yLabels.push(C(this.templateString,{value:(this.min+i*this.stepValue).toFixed(t)}))},getCircumference:function(){return 2*Math.PI/this.valuesCount},setScaleSize:function(){var t,i,e,s,n,o,a,h,l,r,c,u,d=m([this.height/2-this.pointLabelFontSize-5,this.width/2]),p=this.width,g=0;for(this.ctx.font=W(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),i=0;ip&&(p=t.x+s,n=i),t.x-sp&&(p=t.x+e,n=i):i>this.valuesCount/2&&t.x-e0){var s,n=e*(this.drawingArea/this.steps),o=this.yCenter-n;if(this.lineWidth>0)if(t.strokeStyle=this.lineColor,t.lineWidth=this.lineWidth,this.lineArc)t.beginPath(),t.arc(this.xCenter,this.yCenter,n,0,2*Math.PI),t.closePath(),t.stroke();else{t.beginPath();for(var a=0;a=0;i--){if(this.angleLineWidth>0){var e=this.getPointPosition(i,this.calculateCenterOffset(this.max));t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(e.x,e.y),t.stroke(),t.closePath()}var s=this.getPointPosition(i,this.calculateCenterOffset(this.max)+5);t.font=W(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily),t.fillStyle=this.pointLabelFontColor;var o=this.labels.length,a=this.labels.length/2,h=a/2,l=h>i||i>o-h,r=i===h||i===o-h;t.textAlign=0===i?"center":i===a?"center":a>i?"left":"right",t.textBaseline=r?"middle":l?"bottom":"top",t.fillText(this.labels[i],s.x,s.y)}}}}}),s.addEvent(window,"resize",function(){var t;return function(){clearTimeout(t),t=setTimeout(function(){n(e.instances,function(t){t.options.responsive&&t.resize(t.render,!0)})},50)}}()),p?define(function(){return e}):"object"==typeof module&&module.exports&&(module.exports=e),t.Chart=e,e.noConflict=function(){return t.Chart=i,e}}).call(this),function(){"use strict";var t=this,i=t.Chart,e=i.helpers,s={scaleBeginAtZero:!0,scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,scaleShowHorizontalLines:!0,scaleShowVerticalLines:!0,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,legendTemplate:'<% for (var i=0; i <%if(datasets[i].label){%><%=datasets[i].label%><%}%> <%}%> '};i.Type.extend({name:"Bar",defaults:s,initialize:function(t){var s=this.options;this.ScaleClass=i.Scale.extend({offsetGridLines:!0,calculateBarX:function(t,i,e){var n=this.calculateBaseWidth(),o=this.calculateX(e)-n/2,a=this.calculateBarWidth(t);return o+a*i+i*s.barDatasetSpacing+a/2},calculateBaseWidth:function(){return this.calculateX(1)-this.calculateX(0)-2*s.barValueSpacing},calculateBarWidth:function(t){var i=this.calculateBaseWidth()-(t-1)*s.barDatasetSpacing;return i/t}}),this.datasets=[],this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getBarsAtEvent(t):[];this.eachBars(function(t){t.restore(["fillColor","strokeColor"])}),e.each(i,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(i)}),this.BarClass=i.Rectangle.extend({strokeWidth:this.options.barStrokeWidth,showStroke:this.options.barShowStroke,ctx:this.chart.ctx}),e.each(t.datasets,function(i){var s={label:i.label||null,fillColor:i.fillColor,strokeColor:i.strokeColor,bars:[]};this.datasets.push(s),e.each(i.data,function(e,n){s.bars.push(new this.BarClass({value:e,label:t.labels[n],datasetLabel:i.label,strokeColor:i.strokeColor,fillColor:i.fillColor,highlightFill:i.highlightFill||i.fillColor,highlightStroke:i.highlightStroke||i.strokeColor}))},this)},this),this.buildScale(t.labels),this.BarClass.prototype.base=this.scale.endPoint,this.eachBars(function(t,i,s){e.extend(t,{width:this.scale.calculateBarWidth(this.datasets.length),x:this.scale.calculateBarX(this.datasets.length,s,i),y:this.scale.endPoint}),t.save()},this),this.render()},update:function(){this.scale.update(),e.each(this.activeElements,function(t){t.restore(["fillColor","strokeColor"])}),this.eachBars(function(t){t.save()}),this.render()},eachBars:function(t){e.each(this.datasets,function(i,s){e.each(i.bars,t,this,s)},this)},getBarsAtEvent:function(t){for(var i,s=[],n=e.getRelativePosition(t),o=function(t){s.push(t.bars[i])},a=0;a<% for (var i=0; i <%if(segments[i].label){%><%=segments[i].label%><%}%> <%}%>'};i.Type.extend({name:"Doughnut",defaults:s,initialize:function(t){this.segments=[],this.outerRadius=(e.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,this.SegmentArc=i.Arc.extend({ctx:this.chart.ctx,x:this.chart.width/2,y:this.chart.height/2}),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];e.each(this.segments,function(t){t.restore(["fillColor"])}),e.each(i,function(t){t.fillColor=t.highlightColor}),this.showTooltip(i)}),this.calculateTotal(t),e.each(t,function(t,i){this.addData(t,i,!0)},this),this.render()},getSegmentsAtEvent:function(t){var i=[],s=e.getRelativePosition(t);return e.each(this.segments,function(t){t.inRange(s.x,s.y)&&i.push(t)},this),i},addData:function(t,i,e){var s=i||this.segments.length;this.segments.splice(s,0,new this.SegmentArc({value:t.value,outerRadius:this.options.animateScale?0:this.outerRadius,innerRadius:this.options.animateScale?0:this.outerRadius/100*this.options.percentageInnerCutout,fillColor:t.color,highlightColor:t.highlight||t.color,showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,startAngle:1.5*Math.PI,circumference:this.options.animateRotate?0:this.calculateCircumference(t.value),label:t.label})),e||(this.reflow(),this.update())},calculateCircumference:function(t){return 2*Math.PI*(Math.abs(t)/this.total)},calculateTotal:function(t){this.total=0,e.each(t,function(t){this.total+=Math.abs(t.value)},this)},update:function(){this.calculateTotal(this.segments),e.each(this.activeElements,function(t){t.restore(["fillColor"])}),e.each(this.segments,function(t){t.save()}),this.render()},removeData:function(t){var i=e.isNumber(t)?t:this.segments.length-1;this.segments.splice(i,1),this.reflow(),this.update()},reflow:function(){e.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.outerRadius=(e.min([this.chart.width,this.chart.height])-this.options.segmentStrokeWidth/2)/2,e.each(this.segments,function(t){t.update({outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout})},this)},draw:function(t){var i=t?t:1;this.clear(),e.each(this.segments,function(t,e){t.transition({circumference:this.calculateCircumference(t.value),outerRadius:this.outerRadius,innerRadius:this.outerRadius/100*this.options.percentageInnerCutout},i),t.endAngle=t.startAngle+t.circumference,t.draw(),0===e&&(t.startAngle=1.5*Math.PI),e<% for (var i=0; i <%if(datasets[i].label){%><%=datasets[i].label%><%}%> <%}%>'};i.Type.extend({name:"Line",defaults:s,initialize:function(t){this.PointClass=i.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx,inRange:function(t){return Math.pow(t-this.x,2)0&&ithis.scale.endPoint?t.controlPoints.outer.y=this.scale.endPoint:t.controlPoints.outer.ythis.scale.endPoint?t.controlPoints.inner.y=this.scale.endPoint:t.controlPoints.inner.y0&&(s.lineTo(h[h.length-1].x,this.scale.endPoint),s.lineTo(h[0].x,this.scale.endPoint),s.fillStyle=t.fillColor,s.closePath(),s.fill()),e.each(h,function(t){t.draw()})},this)}})}.call(this),function(){"use strict";var t=this,i=t.Chart,e=i.helpers,s={scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBeginAtZero:!0,scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,scaleShowLine:!0,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,legendTemplate:'<% for (var i=0; i <%if(segments[i].label){%><%=segments[i].label%><%}%> <%}%> '};i.Type.extend({name:"PolarArea",defaults:s,initialize:function(t){this.segments=[],this.SegmentArc=i.Arc.extend({showStroke:this.options.segmentShowStroke,strokeWidth:this.options.segmentStrokeWidth,strokeColor:this.options.segmentStrokeColor,ctx:this.chart.ctx,innerRadius:0,x:this.chart.width/2,y:this.chart.height/2}),this.scale=new i.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,lineArc:!0,width:this.chart.width,height:this.chart.height,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,valuesCount:t.length}),this.updateScaleRange(t),this.scale.update(),e.each(t,function(t,i){this.addData(t,i,!0)},this),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getSegmentsAtEvent(t):[];e.each(this.segments,function(t){t.restore(["fillColor"])}),e.each(i,function(t){t.fillColor=t.highlightColor}),this.showTooltip(i)}),this.render()},getSegmentsAtEvent:function(t){var i=[],s=e.getRelativePosition(t);return e.each(this.segments,function(t){t.inRange(s.x,s.y)&&i.push(t)},this),i},addData:function(t,i,e){var s=i||this.segments.length;this.segments.splice(s,0,new this.SegmentArc({fillColor:t.color,highlightColor:t.highlight||t.color,label:t.label,value:t.value,outerRadius:this.options.animateScale?0:this.scale.calculateCenterOffset(t.value),circumference:this.options.animateRotate?0:this.scale.getCircumference(),startAngle:1.5*Math.PI})),e||(this.reflow(),this.update())},removeData:function(t){var i=e.isNumber(t)?t:this.segments.length-1;this.segments.splice(i,1),this.reflow(),this.update()},calculateTotal:function(t){this.total=0,e.each(t,function(t){this.total+=t.value},this),this.scale.valuesCount=this.segments.length},updateScaleRange:function(t){var i=[];e.each(t,function(t){i.push(t.value)});var s=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:e.calculateScaleRange(i,e.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);e.extend(this.scale,s,{size:e.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2})},update:function(){this.calculateTotal(this.segments),e.each(this.segments,function(t){t.save()}),this.reflow(),this.render()},reflow:function(){e.extend(this.SegmentArc.prototype,{x:this.chart.width/2,y:this.chart.height/2}),this.updateScaleRange(this.segments),this.scale.update(),e.extend(this.scale,{xCenter:this.chart.width/2,yCenter:this.chart.height/2}),e.each(this.segments,function(t){t.update({outerRadius:this.scale.calculateCenterOffset(t.value)})},this)},draw:function(t){var i=t||1;this.clear(),e.each(this.segments,function(t,e){t.transition({circumference:this.scale.getCircumference(),outerRadius:this.scale.calculateCenterOffset(t.value)},i),t.endAngle=t.startAngle+t.circumference,0===e&&(t.startAngle=1.5*Math.PI),e<% for (var i=0; i <%if(datasets[i].label){%><%=datasets[i].label%><%}%> <%}%>'},initialize:function(t){this.PointClass=i.Point.extend({strokeWidth:this.options.pointDotStrokeWidth,radius:this.options.pointDotRadius,display:this.options.pointDot,hitDetectionRadius:this.options.pointHitDetectionRadius,ctx:this.chart.ctx}),this.datasets=[],this.buildScale(t),this.options.showTooltips&&e.bindEvents(this,this.options.tooltipEvents,function(t){var i="mouseout"!==t.type?this.getPointsAtEvent(t):[];this.eachPoints(function(t){t.restore(["fillColor","strokeColor"])}),e.each(i,function(t){t.fillColor=t.highlightFill,t.strokeColor=t.highlightStroke}),this.showTooltip(i)}),e.each(t.datasets,function(i){var s={label:i.label||null,fillColor:i.fillColor,strokeColor:i.strokeColor,pointColor:i.pointColor,pointStrokeColor:i.pointStrokeColor,points:[]};this.datasets.push(s),e.each(i.data,function(e,n){var o;this.scale.animation||(o=this.scale.getPointPosition(n,this.scale.calculateCenterOffset(e))),s.points.push(new this.PointClass({value:e,label:t.labels[n],datasetLabel:i.label,x:this.options.animation?this.scale.xCenter:o.x,y:this.options.animation?this.scale.yCenter:o.y,strokeColor:i.pointStrokeColor,fillColor:i.pointColor,highlightFill:i.pointHighlightFill||i.pointColor,highlightStroke:i.pointHighlightStroke||i.pointStrokeColor}))},this)},this),this.render()},eachPoints:function(t){e.each(this.datasets,function(i){e.each(i.points,t,this)},this)},getPointsAtEvent:function(t){var i=e.getRelativePosition(t),s=e.getAngleFromPoint({x:this.scale.xCenter,y:this.scale.yCenter},i),n=2*Math.PI/this.scale.valuesCount,o=Math.round((s.angle-1.5*Math.PI)/n),a=[];return(o>=this.scale.valuesCount||0>o)&&(o=0),s.distance<=this.scale.drawingArea&&e.each(this.datasets,function(t){a.push(t.points[o])}),a},buildScale:function(t){this.scale=new i.RadialScale({display:this.options.showScale,fontStyle:this.options.scaleFontStyle,fontSize:this.options.scaleFontSize,fontFamily:this.options.scaleFontFamily,fontColor:this.options.scaleFontColor,showLabels:this.options.scaleShowLabels,showLabelBackdrop:this.options.scaleShowLabelBackdrop,backdropColor:this.options.scaleBackdropColor,backdropPaddingY:this.options.scaleBackdropPaddingY,backdropPaddingX:this.options.scaleBackdropPaddingX,lineWidth:this.options.scaleShowLine?this.options.scaleLineWidth:0,lineColor:this.options.scaleLineColor,angleLineColor:this.options.angleLineColor,angleLineWidth:this.options.angleShowLineOut?this.options.angleLineWidth:0,pointLabelFontColor:this.options.pointLabelFontColor,pointLabelFontSize:this.options.pointLabelFontSize,pointLabelFontFamily:this.options.pointLabelFontFamily,pointLabelFontStyle:this.options.pointLabelFontStyle,height:this.chart.height,width:this.chart.width,xCenter:this.chart.width/2,yCenter:this.chart.height/2,ctx:this.chart.ctx,templateString:this.options.scaleLabel,labels:t.labels,valuesCount:t.datasets[0].data.length}),this.scale.setScaleSize(),this.updateScaleRange(t.datasets),this.scale.buildYLabels()},updateScaleRange:function(t){var i=function(){var i=[];return e.each(t,function(t){t.data?i=i.concat(t.data):e.each(t.points,function(t){i.push(t.value)})}),i}(),s=this.options.scaleOverride?{steps:this.options.scaleSteps,stepValue:this.options.scaleStepWidth,min:this.options.scaleStartValue,max:this.options.scaleStartValue+this.options.scaleSteps*this.options.scaleStepWidth}:e.calculateScaleRange(i,e.min([this.chart.width,this.chart.height])/2,this.options.scaleFontSize,this.options.scaleBeginAtZero,this.options.scaleIntegersOnly);e.extend(this.scale,s)},addData:function(t,i){this.scale.valuesCount++,e.each(t,function(t,e){var s=this.scale.getPointPosition(this.scale.valuesCount,this.scale.calculateCenterOffset(t));this.datasets[e].points.push(new this.PointClass({value:t,label:i,x:s.x,y:s.y,strokeColor:this.datasets[e].pointStrokeColor,fillColor:this.datasets[e].pointColor}))},this),this.scale.labels.push(i),this.reflow(),this.update()},removeData:function(){this.scale.valuesCount--,this.scale.labels.shift(),e.each(this.datasets,function(t){t.points.shift()},this),this.reflow(),this.update()},update:function(){this.eachPoints(function(t){t.save()}),this.reflow(),this.render()},reflow:function(){e.extend(this.scale,{width:this.chart.width,height:this.chart.height,size:e.min([this.chart.width,this.chart.height]),xCenter:this.chart.width/2,yCenter:this.chart.height/2}),this.updateScaleRange(this.datasets),this.scale.setScaleSize(),this.scale.buildYLabels()},draw:function(t){var i=t||1,s=this.chart.ctx;this.clear(),this.scale.draw(),e.each(this.datasets,function(t){e.each(t.points,function(t,e){t.hasValue()&&t.transition(this.scale.getPointPosition(e,this.scale.calculateCenterOffset(t.value)),i)},this),s.lineWidth=this.options.datasetStrokeWidth,s.strokeStyle=t.strokeColor,s.beginPath(),e.each(t.points,function(t,i){0===i?s.moveTo(t.x,t.y):s.lineTo(t.x,t.y)},this),s.closePath(),s.stroke(),s.fillStyle=t.fillColor,s.fill(),e.each(t.points,function(t){t.hasValue()&&t.draw()})},this)}})}.call(this);
+
+}(jQuery));
+
+
+(function($){
+ //Deprecated//
+}(jQuery));
+
+
+(function($){
+ /*
+ * SimpleModal 1.4.4 - jQuery Plugin
+ * http://simplemodal.com/
+ * Copyright (c) 2013 Eric Martin
+ * Licensed under MIT and GPL
+ * Date: Sun, Jan 20 2013 15:58:56 -0800
+ */
+ (function(b) {
+ "function" === typeof define && define.amd ? define(["jquery"], b) : b(jQuery)
+ })(function(b) {
+ var j = [],
+ n = b(document),
+ k = navigator.userAgent.toLowerCase(),
+ l = b(window),
+ g = [],
+ o = null,
+ p = /msie/.test(k) && !/opera/.test(k),
+ q = /opera/.test(k),
+ m, r;
+ m = p && /msie 6./.test(k) && "object" !== typeof window.XMLHttpRequest;
+ r = p && /msie 7.0/.test(k);
+ b.modal = function(a, h) {
+ return b.modal.impl.init(a, h)
+ };
+ b.modal.close = function() {
+ b.modal.impl.close()
+ };
+ b.modal.focus = function(a) {
+ b.modal.impl.focus(a)
+ };
+ b.modal.setContainerDimensions =
+ function() {
+ b.modal.impl.setContainerDimensions()
+ };
+ b.modal.setPosition = function() {
+ b.modal.impl.setPosition()
+ };
+ b.modal.update = function(a, h) {
+ b.modal.impl.update(a, h)
+ };
+ b.fn.modal = function(a) {
+ return b.modal.impl.init(this, a)
+ };
+ b.modal.defaults = {
+ appendTo: "body",
+ focus: !1,
+ opacity: 80,
+ overlayId: "simplemodal-overlay",
+ overlayCss: {},
+ containerId: "simplemodal-container",
+ containerCss: {},
+ dataId: "simplemodal-data",
+ dataCss: {},
+ minHeight: null,
+ minWidth: null,
+ maxHeight: null,
+ maxWidth: null,
+ autoResize: !0,
+ autoPosition: !0,
+ zIndex: 99999,
+ close: !0,
+ closeHTML: ' ',
+ closeClass: "modal-close",
+ escClose: !0,
+ overlayClose: !0,
+ fixed: !0,
+ position: null,
+ persist: !1,
+ modal: !0,
+ onOpen: function (dialog) {
+ dialog.overlay.fadeIn(150);
+ dialog.data.fadeOut(250);
+ dialog.container.fadeIn(250);
+ dialog.data.fadeIn(250);
+ },
+ onShow: null,
+ onClose: function (dialog) {
+ dialog.overlay.fadeOut(500);
+ dialog.data.fadeOut(250);
+ dialog.container.fadeOut(250);
+ dialog.data.fadeOut('250',function(){
+ setTimeout(function() { $.modal.close(); }, 250);
+ });
+ }
+ };
+ b.modal.impl = {
+ d: {},
+ init: function(a, h) {
+ if (this.d.data) return !1;
+ o = p && !b.support.boxModel;
+ this.o = b.extend({}, b.modal.defaults, h);
+ this.zIndex = this.o.zIndex;
+ this.occb = !1;
+ if ("object" === typeof a) {
+ if (a = a instanceof b ? a : b(a), this.d.placeholder = !1, 0 < a.parent().parent().size() && (a.before(b(" ").attr("id",
+ "simplemodal-placeholder").css({
+ display: "none"
+ })), this.d.placeholder = !0, this.display = a.css("display"), !this.o.persist)) this.d.orig = a.clone(!0)
+ } else if ("string" === typeof a || "number" === typeof a) a = b("
").html(a);
+ else return alert("SimpleModal Error: Unsupported data type: " + typeof a), this;
+ this.create(a);
+ this.open();
+ b.isFunction(this.o.onShow) && this.o.onShow.apply(this, [this.d]);
+ return this
+ },
+ create: function(a) {
+ this.getDimensions();
+ if (this.o.modal && m) this.d.iframe = b('').css(b.extend(this.o.iframeCss, {
+ display: "none",
+ opacity: 0,
+ position: "fixed",
+ height: g[0],
+ width: g[1],
+ zIndex: this.o.zIndex,
+ top: 0,
+ left: 0
+ })).appendTo(this.o.appendTo);
+ this.d.overlay = b("
").attr("id", this.o.overlayId).addClass("simplemodal-overlay").css(b.extend(this.o.overlayCss, {
+ display: "none",
+ opacity: this.o.opacity / 100,
+ height: this.o.modal ? j[0] : 0,
+ width: this.o.modal ? j[1] : 0,
+ position: "fixed",
+ left: 0,
+ top: 0,
+ zIndex: this.o.zIndex + 1
+ })).appendTo(this.o.appendTo);
+ this.d.container = b("
").attr("id", this.o.containerId).addClass("simplemodal-container").css(b.extend({
+ position: this.o.fixed ?
+ "fixed" : "absolute"
+ }, this.o.containerCss, {
+ display: "none",
+ zIndex: this.o.zIndex + 2
+ })).append(this.o.close && this.o.closeHTML ? b(this.o.closeHTML).addClass(this.o.closeClass) : "").appendTo(this.o.appendTo);
+ this.d.wrap = b("
").attr("tabIndex", -1).addClass("simplemodal-wrap").css({
+ height: "100%",
+ outline: 0,
+ width: "100%"
+ }).appendTo(this.d.container);
+ this.d.data = a.attr("id", a.attr("id") || this.o.dataId).addClass("simplemodal-data").css(b.extend(this.o.dataCss, {
+ display: "none"
+ })).appendTo("body");
+ this.setContainerDimensions();
+ this.d.data.appendTo(this.d.wrap);
+ (m || o) && this.fixIE()
+ },
+ bindEvents: function() {
+ var a = this;
+ b("." + a.o.closeClass).bind("click.simplemodal", function(b) {
+ b.preventDefault();
+ a.close()
+ });
+ a.o.modal && a.o.close && a.o.overlayClose && a.d.overlay.bind("click.simplemodal", function(b) {
+ b.preventDefault();
+ a.close()
+ });
+ n.bind("keydown.simplemodal", function(b) {
+ a.o.modal && 9 === b.keyCode ? a.watchTab(b) : a.o.close && a.o.escClose && 27 === b.keyCode && (b.preventDefault(), a.close())
+ });
+ l.bind("resize.simplemodal orientationchange.simplemodal",
+ function() {
+ a.getDimensions();
+ a.o.autoResize ? a.setContainerDimensions() : a.o.autoPosition && a.setPosition();
+ m || o ? a.fixIE() : a.o.modal && (a.d.iframe && a.d.iframe.css({
+ height: g[0],
+ width: g[1]
+ }), a.d.overlay.css({
+ height: j[0],
+ width: j[1]
+ }))
+ })
+ },
+ unbindEvents: function() {
+ b("." + this.o.closeClass).unbind("click.simplemodal");
+ n.unbind("keydown.simplemodal");
+ l.unbind(".simplemodal");
+ this.d.overlay.unbind("click.simplemodal")
+ },
+ fixIE: function() {
+ var a = this.o.position;
+ b.each([this.d.iframe || null, !this.o.modal ? null : this.d.overlay,
+ "fixed" === this.d.container.css("position") ? this.d.container : null
+ ], function(b, e) {
+ if (e) {
+ var f = e[0].style;
+ f.position = "absolute";
+ if (2 > b) f.removeExpression("height"), f.removeExpression("width"), f.setExpression("height", 'document.body.scrollHeight > document.body.clientHeight ? document.body.scrollHeight : document.body.clientHeight + "px"'), f.setExpression("width", 'document.body.scrollWidth > document.body.clientWidth ? document.body.scrollWidth : document.body.clientWidth + "px"');
+ else {
+ var c, d;
+ a && a.constructor ===
+ Array ? (c = a[0] ? "number" === typeof a[0] ? a[0].toString() : a[0].replace(/px/, "") : e.css("top").replace(/px/, ""), c = -1 === c.indexOf("%") ? c + ' + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"' : parseInt(c.replace(/%/, "")) + ' * ((document.documentElement.clientHeight || document.body.clientHeight) / 100) + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"', a[1] && (d = "number" === typeof a[1] ?
+ a[1].toString() : a[1].replace(/px/, ""), d = -1 === d.indexOf("%") ? d + ' + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"' : parseInt(d.replace(/%/, "")) + ' * ((document.documentElement.clientWidth || document.body.clientWidth) / 100) + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"')) : (c = '(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"',
+ d = '(document.documentElement.clientWidth || document.body.clientWidth) / 2 - (this.offsetWidth / 2) + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"');
+ f.removeExpression("top");
+ f.removeExpression("left");
+ f.setExpression("top", c);
+ f.setExpression("left", d)
+ }
+ }
+ })
+ },
+ focus: function(a) {
+ var h = this,
+ a = a && -1 !== b.inArray(a, ["first", "last"]) ? a : "first",
+ e = b(":input:enabled:visible:" + a, h.d.wrap);
+ setTimeout(function() {
+ 0 < e.length ? e.focus() : h.d.wrap.focus()
+ },
+ 10)
+ },
+ getDimensions: function() {
+ var a = "undefined" === typeof window.innerHeight ? l.height() : window.innerHeight;
+ j = [n.height(), n.width()];
+ g = [a, l.width()]
+ },
+ getVal: function(a, b) {
+ return a ? "number" === typeof a ? a : "auto" === a ? 0 : 0 < a.indexOf("%") ? parseInt(a.replace(/%/, "")) / 100 * ("h" === b ? g[0] : g[1]) : parseInt(a.replace(/px/, "")) : null
+ },
+ update: function(a, b) {
+ if (!this.d.data) return !1;
+ this.d.origHeight = this.getVal(a, "h");
+ this.d.origWidth = this.getVal(b, "w");
+ this.d.data.hide();
+ a && this.d.container.css("height", a);
+ b && this.d.container.css("width",
+ b);
+ this.setContainerDimensions();
+ this.d.data.show();
+ this.o.focus && this.focus();
+ this.unbindEvents();
+ this.bindEvents()
+ },
+ setContainerDimensions: function() {
+ var a = m || r,
+ b = this.d.origHeight ? this.d.origHeight : q ? this.d.container.height() : this.getVal(a ? this.d.container[0].currentStyle.height : this.d.container.css("height"), "h"),
+ a = this.d.origWidth ? this.d.origWidth : q ? this.d.container.width() : this.getVal(a ? this.d.container[0].currentStyle.width : this.d.container.css("width"), "w"),
+ e = this.d.data.outerHeight(!0),
+ f =
+ this.d.data.outerWidth(!0);
+ this.d.origHeight = this.d.origHeight || b;
+ this.d.origWidth = this.d.origWidth || a;
+ var c = this.o.maxHeight ? this.getVal(this.o.maxHeight, "h") : null,
+ d = this.o.maxWidth ? this.getVal(this.o.maxWidth, "w") : null,
+ c = c && c < g[0] ? c : g[0],
+ d = d && d < g[1] ? d : g[1],
+ i = this.o.minHeight ? this.getVal(this.o.minHeight, "h") : "auto",
+ b = b ? this.o.autoResize && b > c ? c : b < i ? i : b : e ? e > c ? c : this.o.minHeight && "auto" !== i && e < i ? i : e : i,
+ c = this.o.minWidth ? this.getVal(this.o.minWidth, "w") : "auto",
+ a = a ? this.o.autoResize && a > d ? d : a < c ? c : a : f ?
+ f > d ? d : this.o.minWidth && "auto" !== c && f < c ? c : f : c;
+ this.d.container.css({
+ height: b,
+ width: a
+ });
+ this.d.wrap.css({
+ overflow: e > b || f > a ? "auto" : "visible"
+ });
+ this.o.autoPosition && this.setPosition()
+ },
+ setPosition: function() {
+ var a, b;
+ a = g[0] / 2 - this.d.container.outerHeight(!0) / 2;
+ b = g[1] / 2 - this.d.container.outerWidth(!0) / 2;
+ var e = "fixed" !== this.d.container.css("position") ? l.scrollTop() : 0;
+ this.o.position && "[object Array]" === Object.prototype.toString.call(this.o.position) ? (a = e + (this.o.position[0] || a), b = this.o.position[1] || b) :
+ a = e + a;
+ this.d.container.css({
+ left: b,
+ top: a
+ })
+ },
+ watchTab: function(a) {
+ if (0 < b(a.target).parents(".simplemodal-container").length) {
+ if (this.inputs = b(":input:enabled:visible:first, :input:enabled:visible:last", this.d.data[0]), !a.shiftKey && a.target === this.inputs[this.inputs.length - 1] || a.shiftKey && a.target === this.inputs[0] || 0 === this.inputs.length) a.preventDefault(), this.focus(a.shiftKey ? "last" : "first")
+ } else a.preventDefault(), this.focus()
+ },
+ open: function() {
+ this.d.iframe && this.d.iframe.show();
+ b.isFunction(this.o.onOpen) ?
+ this.o.onOpen.apply(this, [this.d]) : (this.d.overlay.show(), this.d.container.show(), this.d.data.show());
+ this.o.focus && this.focus();
+ this.bindEvents()
+ },
+ close: function() {
+ if (!this.d.data) return !1;
+ this.unbindEvents();
+ if (b.isFunction(this.o.onClose) && !this.occb) this.occb = !0, this.o.onClose.apply(this, [this.d]);
+ else {
+ if (this.d.placeholder) {
+ var a = b("#simplemodal-placeholder");
+ this.o.persist ? a.replaceWith(this.d.data.removeClass("simplemodal-data").css("display", this.display)) : (this.d.data.hide().remove(), a.replaceWith(this.d.orig))
+ } else this.d.data.hide().remove();
+ this.d.container.hide().remove();
+ this.d.overlay.hide();
+ this.d.iframe && this.d.iframe.hide().remove();
+ this.d.overlay.remove();
+ this.d = {}
+ }
+ }
+ }
+ });
+
+}(jQuery));
+
+
+(function($){
+ /*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
+ * Licensed under the MIT License.
+ *
+ * jquery.mousewheel.min.js
+ * Version: 3.1.12
+ *
+ * Requires: jQuery 1.2.2+
+ */
+ !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
+}(jQuery));
+
+
+(function($){
+ /*
+ Morphext - v2.3.4 - 2015-04-14
+ Morphext is licensed under the MIT license (http://ian.mit-license.org/)
+ */
+ !function(a){"use strict";function b(b,c){this.element=a(b),this.settings=a.extend({},d,c),this._defaults=d,this._init()}var c="Morphext",d={animation:"bounceIn",separator:",",speed:2e3,complete:a.noop};b.prototype={_init:function(){var b=this;this.phrases=[],this.element.addClass("morphext"),a.each(this.element.text().split(this.settings.separator),function(a,c){b.phrases.push(c.trim())}),this.index=-1,this.animate(),this.start()},animate:function(){this.index=++this.index%this.phrases.length,this.element[0].innerHTML=''+this.phrases[this.index]+" ",a.isFunction(this.settings.complete)&&this.settings.complete.call(this)},start:function(){var a=this;this._interval=setInterval(function(){a.animate()},this.settings.speed)},stop:function(){this._interval=clearInterval(this._interval)}},a.fn[c]=function(d){return this.each(function(){a.data(this,"plugin_"+c)||a.data(this,"plugin_"+c,new b(this,d))})}}(jQuery);
+}(jQuery));
+
+
+
+(function($){
+ /*!
+ * Justified Gallery - v3.6.0
+ * http://miromannino.github.io/Justified-Gallery/
+ * Copyright (c) 2015 Miro Mannino
+ * Licensed under the MIT license.
+ */
+ !function(t){var i=function(i,e){this.settings=e,this.checkSettings(),this.imgAnalyzerTimeout=null,this.entries=null,this.buildingRow={entriesBuff:[],width:0,aspectRatio:0},this.lastAnalyzedIndex=-1,this["yield"]={every:2,flushed:0},this.border=e.border>=0?e.border:e.margins,this.maxRowHeight=this.retrieveMaxRowHeight(),this.suffixRanges=this.retrieveSuffixRanges(),this.offY=this.border,this.spinner={phase:0,timeSlot:150,$el:t('
'),intervalId:null},this.checkWidthIntervalId=null,this.galleryWidth=i.width(),this.$gallery=i};i.prototype.getSuffix=function(t,i){var e,s;for(e=t>i?t:i,s=0;s img");return 0===i.length&&(i=t.find("> a > img")),0===i.length?null:i},i.prototype.captionFromEntry=function(t){var i=t.find("> .caption");return 0===i.length?null:i},i.prototype.displayEntry=function(i,e,s,n,r,a){i.width(n),i.height(a),i.css("top",s),i.css("left",e);var o=this.imgFromEntry(i);if(null!==o){o.css("width",n),o.css("height",r),o.css("margin-left",-n/2),o.css("margin-top",-r/2);var h=o.attr("src"),g=this.newSrc(h,n,r);o.one("error",function(){o.attr("src",o.data("jg.originalSrc"))});var l=function(){h!==g&&o.attr("src",g)};"skipped"===i.data("jg.loaded")?this.onImageEvent(h,t.proxy(function(){this.showImg(i,l),i.data("jg.loaded",!0)},this)):this.showImg(i,l)}else this.showImg(i);this.displayEntryCaption(i)},i.prototype.displayEntryCaption=function(i){var e=this.imgFromEntry(i);if(null!==e&&this.settings.captions){var s=this.captionFromEntry(i);if(null==s){var n=e.attr("alt");"undefined"==typeof n&&(n=i.attr("title")),"undefined"!=typeof n&&(s=t(''+n+"
"),i.append(s),i.data("jg.createdCaption",!0))}null!==s&&(this.settings.cssAnimation||s.stop().fadeTo(0,this.settings.captionSettings.nonVisibleOpacity),this.addCaptionEventsHandlers(i))}else this.removeCaptionEventsHandlers(i)},i.prototype.onEntryMouseEnterForCaption=function(i){var e=this.captionFromEntry(t(i.currentTarget));this.settings.cssAnimation?e.addClass("caption-visible").removeClass("caption-hidden"):e.stop().fadeTo(this.settings.captionSettings.animationDuration,this.settings.captionSettings.visibleOpacity)},i.prototype.onEntryMouseLeaveForCaption=function(i){var e=this.captionFromEntry(t(i.currentTarget));this.settings.cssAnimation?e.removeClass("caption-visible").removeClass("caption-hidden"):e.stop().fadeTo(this.settings.captionSettings.animationDuration,this.settings.captionSettings.nonVisibleOpacity)},i.prototype.addCaptionEventsHandlers=function(i){var e=i.data("jg.captionMouseEvents");"undefined"==typeof e&&(e={mouseenter:t.proxy(this.onEntryMouseEnterForCaption,this),mouseleave:t.proxy(this.onEntryMouseLeaveForCaption,this)},i.on("mouseenter",void 0,void 0,e.mouseenter),i.on("mouseleave",void 0,void 0,e.mouseleave),i.data("jg.captionMouseEvents",e))},i.prototype.removeCaptionEventsHandlers=function(t){var i=t.data("jg.captionMouseEvents");"undefined"!=typeof i&&(t.off("mouseenter",void 0,i.mouseenter),t.off("mouseleave",void 0,i.mouseleave),t.removeData("jg.captionMouseEvents"))},i.prototype.prepareBuildingRow=function(t){var i,e,s,n,r,a=!0,o=0,h=this.galleryWidth-2*this.border-(this.buildingRow.entriesBuff.length-1)*this.settings.margins,g=h/this.buildingRow.aspectRatio,l=this.buildingRow.width/h>this.settings.justifyThreshold;if(t&&"hide"===this.settings.lastRow&&!l){for(i=0;ir)&&(o=r);return this.settings.fixedHeight&&o>this.settings.rowHeight&&(o=this.settings.rowHeight),{minHeight:o,justify:a}},i.prototype.clearBuildingRow=function(){this.buildingRow.entriesBuff=[],this.buildingRow.aspectRatio=0,this.buildingRow.width=0},i.prototype.flushRow=function(t){var i,e,s,n=this.settings,r=this.border;if(s=this.prepareBuildingRow(t),e=s.minHeight,t&&"hide"===n.lastRow&&-1===e)return void this.clearBuildingRow();this.maxRowHeight.percentage?this.maxRowHeight.value*n.rowHeight0&&this.maxRowHeight.value0;i--)e=Math.floor(Math.random()*(i+1)),s=t[i],t[i]=t[e],t[e]=s;return this.insertToGallery(t),t},i.prototype.sortArray=function(t){return t.sort(this.settings.sort),this.insertToGallery(t),t},i.prototype.resetFilters=function(i){for(var e=0;e=this["yield"].every))return void this.startImgAnalyzer(i);this.buildingRow.entriesBuff.push(s),this.buildingRow.aspectRatio+=r,this.buildingRow.width+=r*this.settings.rowHeight,this.lastAnalyzedIndex=e}else if("error"!==s.data("jg.loaded"))return}this.buildingRow.entriesBuff.length>0&&this.flushRow(!0),this.isSpinnerActive()&&this.stopLoadingSpinnerAnimation(),this.stopImgAnalyzerStarter(),this.$gallery.trigger(i?"jg.resize":"jg.complete")},i.prototype.stopImgAnalyzerStarter=function(){this["yield"].flushed=0,null!==this.imgAnalyzerTimeout&&clearTimeout(this.imgAnalyzerTimeout)},i.prototype.startImgAnalyzer=function(t){var i=this;this.stopImgAnalyzerStarter(),this.imgAnalyzerTimeout=setTimeout(function(){i.analyzeImages(t)},.001)},i.prototype.onImageEvent=function(i,e,s){if(e||s){var n=new Image,r=t(n);e&&r.one("load",function(){r.off("load error"),e(n)}),s&&r.one("error",function(){r.off("load error"),s(n)}),n.src=i}},i.prototype.init=function(){var i=!1,e=!1,s=this;t.each(this.entries,function(n,r){var a=t(r),o=s.imgFromEntry(a);if(a.addClass("jg-entry"),a.data("jg.loaded")!==!0&&"skipped"!==a.data("jg.loaded"))if(null!==s.settings.rel&&a.attr("rel",s.settings.rel),null!==s.settings.target&&a.attr("target",s.settings.target),null!==o){var h=s.extractImgSrcFromImage(o);if(o.attr("src",h),s.settings.waitThumbnailsLoad===!1){var g=parseInt(o.attr("width"),10),l=parseInt(o.attr("height"),10);if(!isNaN(g)&&!isNaN(l))return a.data("jg.width",g),a.data("jg.height",l),a.data("jg.loaded","skipped"),e=!0,s.startImgAnalyzer(!1),!0}a.data("jg.loaded",!1),i=!0,s.isSpinnerActive()||s.startLoadingSpinnerAnimation(),s.onImageEvent(h,function(t){a.data("jg.width",t.width),a.data("jg.height",t.height),a.data("jg.loaded",!0),s.startImgAnalyzer(!1)},function(){a.data("jg.loaded","error"),s.startImgAnalyzer(!1)})}else a.data("jg.loaded",!0),a.data("jg.width",a.width()|a.css("width")|1),a.data("jg.height",a.height()|a.css("height")|1)}),i||e||this.startImgAnalyzer(!1),this.checkWidth()},i.prototype.checkOrConvertNumber=function(i,e){if("string"===t.type(i[e])&&(i[e]=parseFloat(i[e])),"number"!==t.type(i[e]))throw e+" must be a number";if(isNaN(i[e]))throw"invalid number for "+e},i.prototype.checkSizeRangesSuffixes=function(){if("object"!==t.type(this.settings.sizeRangeSuffixes))throw"sizeRangeSuffixes must be defined and must be an object";var i=[];for(var e in this.settings.sizeRangeSuffixes)this.settings.sizeRangeSuffixes.hasOwnProperty(e)&&i.push(e);for(var s={0:""},n=0;n0&&i.value1)throw"justifyThreshold must be in the interval [0,1]";if("boolean"!==t.type(this.settings.cssAnimation))throw"cssAnimation must be a boolean";if("boolean"!==t.type(this.settings.captions))throw"captions must be a boolean";if(this.checkOrConvertNumber(this.settings.captionSettings,"animationDuration"),this.checkOrConvertNumber(this.settings.captionSettings,"visibleOpacity"),this.settings.captionSettings.visibleOpacity<0||this.settings.captionSettings.visibleOpacity>1)throw"captionSettings.visibleOpacity must be in the interval [0, 1]";if(this.checkOrConvertNumber(this.settings.captionSettings,"nonVisibleOpacity"),this.settings.captionSettings.nonVisibleOpacity<0||this.settings.captionSettings.nonVisibleOpacity>1)throw"captionSettings.nonVisibleOpacity must be in the interval [0, 1]";if("boolean"!==t.type(this.settings.fixedHeight))throw"fixedHeight must be a boolean";if(this.checkOrConvertNumber(this.settings,"imagesAnimationDuration"),this.checkOrConvertNumber(this.settings,"refreshTime"),"boolean"!==t.type(this.settings.randomize))throw"randomize must be a boolean";if("string"!==t.type(this.settings.selector))throw"selector must be a string";if(this.settings.sort!==!1&&!t.isFunction(this.settings.sort))throw"sort must be false or a comparison function";if(this.settings.filter!==!1&&!t.isFunction(this.settings.sort)&&"string"!==t.type(this.settings.filter))throw"filter must be false, a string or a filter function"},i.prototype.retrieveSuffixRanges=function(){var t=[];for(var i in this.settings.sizeRangeSuffixes)this.settings.sizeRangeSuffixes.hasOwnProperty(i)&&t.push(parseInt(i,10));return t.sort(function(t,i){return t>i?1:i>t?-1:0}),t},i.prototype.updateSettings=function(i){this.settings=t.extend({},this.settings,i),this.checkSettings(),this.border=this.settings.border>=0?this.settings.border:this.settings.margins,this.maxRowHeight=this.retrieveMaxRowHeight(),this.suffixRanges=this.retrieveSuffixRanges()},t.fn.justifiedGallery=function(e){return this.each(function(s,n){var r=t(n);r.addClass("justified-gallery");var a=r.data("jg.controller");if("undefined"==typeof a){if("undefined"!=typeof e&&null!==e&&"object"!==t.type(e))throw"The argument must be an object";a=new i(r,t.extend({},t.fn.justifiedGallery.defaults,e)),r.data("jg.controller",a)}else if("norewind"===e)a.hideBuildingRowImages();else{if("destroy"===e)return void a.destroy();a.updateSettings(e),a.rewind()}a.updateEntries("norewind"===e)&&a.init()})},t.fn.justifiedGallery.defaults={sizeRangeSuffixes:{},rowHeight:120,maxRowHeight:"200%",margins:1,border:-1,lastRow:"nojustify",justifyThreshold:.75,fixedHeight:!1,waitThumbnailsLoad:!0,captions:!1,cssAnimation:!1,imagesAnimationDuration:500,captionSettings:{animationDuration:500,visibleOpacity:.7,nonVisibleOpacity:0},rel:null,target:null,extension:/\.[^.\\/]+$/,refreshTime:100,randomize:!1,sort:!1,filter:!1,selector:"> a, > div:not(.spinner)"}}(jQuery);
+
+}(jQuery));
+
+(function(){
+ /* Add to Homescreen v3.2.2 ~ (c) 2015 Matteo Spinelli ~ @license: http://cubiq.org/license */
+ !function(e,t){function i(){e.removeEventListener("load",i,!1),r=!0}function n(e){return l=l||new n.Class(e)}function o(e,t){for(var i in t)e[i]=t[i];return e}function s(){"#ath"==t.location.hash&&history.replaceState("",e.document.title,t.location.href.split("#")[0]),h.test(t.location.href)&&history.replaceState("",e.document.title,t.location.href.replace(h,"$1")),d.test(t.location.search)&&history.replaceState("",e.document.title,t.location.href.replace(d,"$2"))}var a="addEventListener"in e,r=!1;"complete"===t.readyState?r=!0:a&&e.addEventListener("load",i,!1);var l,h=/\/ath(\/)?$/,d=/([\?&]ath=[^&]*$|&ath=[^&]*(&))/;n.intl={de_de:{ios:"Um diese Web-App zum Home-Bildschirm hinzuzufügen, tippen Sie auf %icon und dann Zum Home-Bildschirm .",android:'Um diese Web-App zum Home-Bildschirm hinzuzufügen, öffnen Sie das Menü und tippen dann auf Zum Startbildschirm hinzufügen . Wenn Ihr Gerät eine Menütaste hat, lässt sich das Browsermenü über diese öffnen. Ansonsten tippen Sie auf icon . '},da_dk:{ios:"For at tilføje denne web app til hjemmeskærmen: Tryk %icon og derefter Føj til hjemmeskærm .",android:'For at tilføje denne web app til hjemmeskærmen, åbn browser egenskaber menuen og tryk på Føj til hjemmeskærm . Denne menu kan tilgås ved at trykke på menu knappen, hvis din enhed har en, eller ved at trykke på det øverste højre menu ikon icon . '},en_us:{ios:"To add this web app to the home screen: tap %icon and then Add to Home Screen .",android:'To add this web app to the home screen open the browser option menu and tap on Add to homescreen . The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon icon '},es_es:{ios:"Para añadir esta aplicación web a la pantalla de inicio: pulsa %icon y selecciona Añadir a pantalla de inicio .",android:'Para añadir esta aplicación web a la pantalla de inicio, abre las opciones y pulsa Añadir a pantalla inicio . El menú se puede acceder pulsando el botón táctil en caso de tenerlo, o bien el icono de la parte superior derecha de la pantalla icon . '},fi_fi:{ios:"Liitä tämä sovellus kotivalikkoon: klikkaa %icon ja tämän jälkeen Lisää kotivalikkoon .",android:'Lisätäksesi tämän sovelluksen aloitusnäytölle, avaa selaimen valikko ja klikkaa tähti -ikonia tai Lisää aloitusnäytölle tekstiä . Valikkoon pääsee myös painamalla menuvalikkoa, jos laitteessasi on sellainen tai koskettamalla oikealla yläkulmassa menu ikonia icon . '},fr_fr:{ios:"Pour ajouter cette application web sur l'écran d'accueil : Appuyez %icon et sélectionnez Ajouter sur l'écran d'accueil .",android:'Pour ajouter cette application web sur l\'écran d\'accueil : Appuyez sur le bouton "menu", puis sur Ajouter sur l\'écran d\'accueil . Le menu peut-être accessible en appyant sur le bouton "menu" du téléphone s\'il en possède un . Sinon, il se trouve probablement dans la coin supérieur droit du navigateur %icon. '},he_il:{ios:'להוספת האפליקציה למסך הבית: ללחוץ על %icon ואז הוסף למסך הבית . ',android:'To add this web app to the home screen open the browser option menu and tap on Add to homescreen . The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon icon . '},it_it:{ios:"Per aggiungere questa web app alla schermata iniziale: premi %icon e poi Aggiungi a Home .",android:'Per aggiungere questa web app alla schermata iniziale, apri il menu opzioni del browser e premi su Aggiungi alla homescreen . Puoi accedere al menu premendo il pulsante hardware delle opzioni se la tua device ne ha uno, oppure premendo l\'icona icon in alto a destra. '},ja_jp:{ios:"このウェプアプリをホーム画面に追加するために%iconを押してホーム画面に追加 。",android:'To add this web app to the home screen open the browser option menu and tap on Add to homescreen . The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon icon . '},ko_kr:{ios:"홈 화면에 바로가기 생성: %icon 을 클릭한 후 홈 화면에 추가 .",android:'브라우저 옵션 메뉴의 홈 화면에 추가 를 클릭하여 홈화면에 바로가기를 생성할 수 있습니다. 옵션 메뉴는 장치의 메뉴 버튼을 누르거나 오른쪽 상단의 메뉴 아이콘 icon 을 클릭하여 접근할 수 있습니다. '},nb_no:{ios:"For å installere denne appen på hjem-skjermen: trykk på %icon og deretter Legg til på Hjem-skjerm .",android:'For å legge til denne webappen på startsiden åpner en nettlesermenyen og velger Legg til på startsiden . Menyen åpnes ved å trykke på den fysiske menyknappen hvis enheten har det, eller ved å trykke på menyikonet øverst til høyre icon . '},pt_br:{ios:"Para adicionar este app à tela de início: clique %icon e então Tela de início .",android:'To add this web app to the home screen open the browser option menu and tap on Add to homescreen . The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon icon . '},pt_pt:{ios:"Para adicionar esta app ao ecrã principal: clique %icon e depois Ecrã principal .",android:'To add this web app to the home screen open the browser option menu and tap on Add to homescreen . The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon icon . '},nl_nl:{ios:"Om deze webapp op je telefoon te installeren, klik op %icon en dan Zet in beginscherm .",android:'To add this web app to the home screen open the browser option menu and tap on Add to homescreen . The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon icon . '},ru_ru:{ios:'Чтобы добавить этот сайт на свой домашний экран, нажмите на иконку %icon и затем На экран "Домой" .',android:'Чтобы добавить сайт на свой домашний экран, откройте меню браузера и нажмите на Добавить на главный экран . Меню можно вызвать, нажав на кнопку меню вашего телефона, если она есть. Или найдите иконку сверху справа иконка . '},sv_se:{ios:"För att lägga till denna webbapplikation på hemskärmen: tryck på %icon och därefter Lägg till på hemskärmen .",android:'För att lägga till den här webbappen på hemskärmen öppnar du webbläsarens alternativ-meny och väljer Lägg till på startskärmen . Man hittar menyn genom att trycka på hårdvaruknappen om din enhet har en sådan, eller genom att trycka på menyikonen högst upp till höger icon . '},zh_cn:{ios:"如要把应用程式加至主屏幕,请点击%icon, 然后加至主屏幕 ",android:'To add this web app to the home screen open the browser option menu and tap on Add to homescreen . The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon icon . '},zh_tw:{ios:"如要把應用程式加至主屏幕, 請點擊%icon, 然後加至主屏幕 .",android:'To add this web app to the home screen open the browser option menu and tap on Add to homescreen . The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon icon . '}};for(var p in n.intl)n.intl[p.substr(0,2)]=n.intl[p];n.defaults={appID:"org.cubiq.11addtohome",fontSize:15,debug:!1,logging:!1,modal:!1,mandatory:!1,autostart:!0,skipFirstVisit:!1,startDelay:1,lifespan:15,displayPace:1440,maxDisplayCount:0,icon:!0,message:"",validLocation:[],onInit:null,onShow:null,onRemove:null,onAdd:null,onPrivate:null,privateModeOverride:!1,detectHomescreen:!1};var c=e.navigator.userAgent,m=e.navigator;o(n,{hasToken:"#ath"==t.location.hash||h.test(t.location.href)||d.test(t.location.search),isRetina:e.devicePixelRatio&&e.devicePixelRatio>1,isIDevice:/iphone|ipod|ipad/i.test(c),isMobileChrome:c.indexOf("Android")>-1&&/Chrome\/[.0-9]*/.test(c)&&-1==c.indexOf("Version"),isMobileIE:c.indexOf("Windows Phone")>-1,language:m.language&&m.language.toLowerCase().replace("-","_")||""}),n.language=n.language&&n.language in n.intl?n.language:"en_us",n.isMobileSafari=n.isIDevice&&c.indexOf("Safari")>-1&&c.indexOf("CriOS")<0,n.OS=n.isIDevice?"ios":n.isMobileChrome?"android":n.isMobileIE?"windows":"unsupported",n.OSVersion=c.match(/(OS|Android) (\d+[_\.]\d+)/),n.OSVersion=n.OSVersion&&n.OSVersion[2]?+n.OSVersion[2].replace("_","."):0,n.isStandalone="standalone"in e.navigator&&e.navigator.standalone,n.isTablet=n.isMobileSafari&&c.indexOf("iPad")>-1||n.isMobileChrome&&c.indexOf("Mobile")<0,n.isCompatible=n.isMobileSafari&&n.OSVersion>=6||n.isMobileChrome;var u={lastDisplayTime:0,returningVisitor:!1,displayCount:0,optedout:!1,added:!1};n.removeSession=function(e){try{if(!localStorage)throw new Error("localStorage is not defined");localStorage.removeItem(e||n.defaults.appID)}catch(t){}},n.doLog=function(e){this.options.logging&&console.log(e)},n.Class=function(i){if(this.doLog=n.doLog,this.options=o({},n.defaults),o(this.options,i),i.debug&&"undefined"==typeof i.logging&&(this.options.logging=!0),a){if(this.options.mandatory=this.options.mandatory&&("standalone"in e.navigator||this.options.debug),this.options.modal=this.options.modal||this.options.mandatory,this.options.mandatory&&(this.options.startDelay=-.5),this.options.detectHomescreen=this.options.detectHomescreen===!0?"hash":this.options.detectHomescreen,this.options.debug&&(n.isCompatible=!0,n.OS="string"==typeof this.options.debug?this.options.debug:"unsupported"==n.OS?"android":n.OS,n.OSVersion="ios"==n.OS?"8":"4"),this.container=t.documentElement,this.session=this.getItem(this.options.appID),this.session=this.session?JSON.parse(this.session):void 0,!n.hasToken||n.isCompatible&&this.session||(n.hasToken=!1,s()),!n.isCompatible)return void this.doLog("Add to homescreen: not displaying callout because device not supported");this.session=this.session||u;try{if(!localStorage)throw new Error("localStorage is not defined");localStorage.setItem(this.options.appID,JSON.stringify(this.session)),n.hasLocalStorage=!0}catch(r){n.hasLocalStorage=!1,this.options.onPrivate&&this.options.onPrivate.call(this)}for(var l=!this.options.validLocation.length,h=this.options.validLocation.length;h--;)if(this.options.validLocation[h].test(t.location.href)){l=!0;break}if(this.getItem("addToHome")&&this.optOut(),this.session.optedout)return void this.doLog("Add to homescreen: not displaying callout because user opted out");if(this.session.added)return void this.doLog("Add to homescreen: not displaying callout because already added to the homescreen");if(!l)return void this.doLog("Add to homescreen: not displaying callout because not a valid location");if(n.isStandalone)return this.session.added||(this.session.added=!0,this.updateSession(),this.options.onAdd&&n.hasLocalStorage&&this.options.onAdd.call(this)),void this.doLog("Add to homescreen: not displaying callout because in standalone mode");if(this.options.detectHomescreen){if(n.hasToken)return s(),this.session.added||(this.session.added=!0,this.updateSession(),this.options.onAdd&&n.hasLocalStorage&&this.options.onAdd.call(this)),void this.doLog("Add to homescreen: not displaying callout because URL has token, so we are likely coming from homescreen");"hash"==this.options.detectHomescreen?history.replaceState("",e.document.title,t.location.href+"#ath"):"smartURL"==this.options.detectHomescreen?history.replaceState("",e.document.title,t.location.href.replace(/(\/)?$/,"/ath$1")):history.replaceState("",e.document.title,t.location.href+(t.location.search?"&":"?")+"ath=")}if(!this.session.returningVisitor&&(this.session.returningVisitor=!0,this.updateSession(),this.options.skipFirstVisit))return void this.doLog("Add to homescreen: not displaying callout because skipping first visit");if(!this.options.privateModeOverride&&!n.hasLocalStorage)return void this.doLog("Add to homescreen: not displaying callout because browser is in private mode");this.ready=!0,this.options.onInit&&this.options.onInit.call(this),this.options.autostart&&(this.doLog("Add to homescreen: autostart displaying callout"),this.show())}},n.Class.prototype={events:{load:"_delayedShow",error:"_delayedShow",orientationchange:"resize",resize:"resize",scroll:"resize",click:"remove",touchmove:"_preventDefault",transitionend:"_removeElements",webkitTransitionEnd:"_removeElements",MSTransitionEnd:"_removeElements"},handleEvent:function(e){var t=this.events[e.type];t&&this[t](e)},show:function(i){if(this.options.autostart&&!r)return void setTimeout(this.show.bind(this),50);if(this.shown)return void this.doLog("Add to homescreen: not displaying callout because already shown on screen");var o=Date.now(),s=this.session.lastDisplayTime;if(i!==!0){if(!this.ready)return void this.doLog("Add to homescreen: not displaying callout because not ready");if(o-s<6e4*this.options.displayPace)return void this.doLog("Add to homescreen: not displaying callout because displayed recently");if(this.options.maxDisplayCount&&this.session.displayCount>=this.options.maxDisplayCount)return void this.doLog("Add to homescreen: not displaying callout because displayed too many times already")}this.shown=!0,this.session.lastDisplayTime=o,this.session.displayCount++,this.updateSession(),this.applicationIcon||(this.applicationIcon=t.querySelector("ios"==n.OS?'head link[rel^=apple-touch-icon][sizes="152x152"],head link[rel^=apple-touch-icon][sizes="144x144"],head link[rel^=apple-touch-icon][sizes="120x120"],head link[rel^=apple-touch-icon][sizes="114x114"],head link[rel^=apple-touch-icon]':'head link[rel^="shortcut icon"][sizes="196x196"],head link[rel^=apple-touch-icon]'));var a="";"object"==typeof this.options.message&&n.language in this.options.message?a=this.options.message[n.language][n.OS]:"object"==typeof this.options.message&&n.OS in this.options.message?a=this.options.message[n.OS]:this.options.message in n.intl?a=n.intl[this.options.message][n.OS]:""!==this.options.message?a=this.options.message:n.OS in n.intl[n.language]&&(a=n.intl[n.language][n.OS]),a=""+a.replace("%icon",'icon ')+"
",this.viewport=t.createElement("div"),this.viewport.className="ath-viewport",this.options.modal&&(this.viewport.className+=" ath-modal"),this.options.mandatory&&(this.viewport.className+=" ath-mandatory"),this.viewport.style.position="absolute",this.element=t.createElement("div"),this.element.className="ath-container ath-"+n.OS+" ath-"+n.OS+(n.OSVersion+"").substr(0,1)+" ath-"+(n.isTablet?"tablet":"phone"),this.element.style.cssText="-webkit-transition-property:-webkit-transform,opacity;-webkit-transition-duration:0s;-webkit-transition-timing-function:ease-out;transition-property:transform,opacity;transition-duration:0s;transition-timing-function:ease-out;",this.element.style.webkitTransform="translate3d(0,-"+e.innerHeight+"px,0)",this.element.style.transform="translate3d(0,-"+e.innerHeight+"px,0)",this.options.icon&&this.applicationIcon&&(this.element.className+=" ath-icon",this.img=t.createElement("img"),this.img.className="ath-application-icon",this.img.addEventListener("load",this,!1),this.img.addEventListener("error",this,!1),this.img.src=this.applicationIcon.href,this.element.appendChild(this.img)),this.element.innerHTML+=a,this.viewport.style.left="-99999em",this.viewport.appendChild(this.element),this.container.appendChild(this.viewport),this.img?this.doLog("Add to homescreen: not displaying callout because waiting for img to load"):this._delayedShow()},_delayedShow:function(){setTimeout(this._show.bind(this),1e3*this.options.startDelay+500)},_show:function(){var i=this;this.updateViewport(),e.addEventListener("resize",this,!1),e.addEventListener("scroll",this,!1),e.addEventListener("orientationchange",this,!1),this.options.modal&&t.addEventListener("touchmove",this,!0),this.options.mandatory||setTimeout(function(){i.element.addEventListener("click",i,!0)},1e3),setTimeout(function(){i.element.style.webkitTransitionDuration="1.2s",i.element.style.transitionDuration="1.2s",i.element.style.webkitTransform="translate3d(0,0,0)",i.element.style.transform="translate3d(0,0,0)"},0),this.options.lifespan&&(this.removeTimer=setTimeout(this.remove.bind(this),1e3*this.options.lifespan)),this.options.onShow&&this.options.onShow.call(this)},remove:function(){clearTimeout(this.removeTimer),this.img&&(this.img.removeEventListener("load",this,!1),this.img.removeEventListener("error",this,!1)),e.removeEventListener("resize",this,!1),e.removeEventListener("scroll",this,!1),e.removeEventListener("orientationchange",this,!1),t.removeEventListener("touchmove",this,!0),this.element.removeEventListener("click",this,!0),this.element.addEventListener("transitionend",this,!1),this.element.addEventListener("webkitTransitionEnd",this,!1),this.element.addEventListener("MSTransitionEnd",this,!1),this.element.style.webkitTransitionDuration="0.3s",this.element.style.opacity="0"},_removeElements:function(){this.element.removeEventListener("transitionend",this,!1),this.element.removeEventListener("webkitTransitionEnd",this,!1),this.element.removeEventListener("MSTransitionEnd",this,!1),this.container.removeChild(this.viewport),this.shown=!1,this.options.onRemove&&this.options.onRemove.call(this)},updateViewport:function(){if(this.shown){this.viewport.style.width=e.innerWidth+"px",this.viewport.style.height=e.innerHeight+"px",this.viewport.style.left=e.scrollX+"px",this.viewport.style.top=e.scrollY+"px";var i=t.documentElement.clientWidth;this.orientation=i>t.documentElement.clientHeight?"landscape":"portrait";var o="ios"==n.OS?"portrait"==this.orientation?screen.width:screen.height:screen.width;this.scale=screen.width>i?1:o/e.innerWidth,this.element.style.fontSize=this.options.fontSize/this.scale+"px"}},resize:function(){clearTimeout(this.resizeTimer),this.resizeTimer=setTimeout(this.updateViewport.bind(this),100)},updateSession:function(){n.hasLocalStorage!==!1&&localStorage&&localStorage.setItem(this.options.appID,JSON.stringify(this.session))},clearSession:function(){this.session=u,this.updateSession()},getItem:function(e){try{if(!localStorage)throw new Error("localStorage is not defined");return localStorage.getItem(e)}catch(t){n.hasLocalStorage=!1}},optOut:function(){this.session.optedout=!0,this.updateSession()},optIn:function(){this.session.optedout=!1,this.updateSession()},clearDisplayCount:function(){this.session.displayCount=0,this.updateSession()},_preventDefault:function(e){e.preventDefault(),e.stopPropagation()}},e.addToHomescreen=n}(window,document);
+}(jQuery));
+
+/*
+ *jQuery Contact form developed by CosminCotor & Enabled
+ *Licensed to be used ONLY by CosminCotor & Enabled on the Envato Marketplaces
+ *DO NOT use in commercial projects outside Regular or Extended licenses for the marketplaces.
+*/
+
+(function($){
+ var formSubmitted="false";jQuery(document).ready(function(e){function t(t,n){formSubmitted="true";var r=e("#"+t).serialize();e.post(e("#"+t).attr("action"),r,function(n){e("#"+t).hide();e("#formSuccessMessageWrap").fadeIn(500)})}function n(n,r){e(".formValidationError").hide();e(".fieldHasError").removeClass("fieldHasError");e("#"+n+" .requiredField").each(function(i){if(e(this).val()==""||e(this).val()==e(this).attr("data-dummy")){e(this).val(e(this).attr("data-dummy"));e(this).focus();e(this).addClass("fieldHasError");e("#"+e(this).attr("id")+"Error").fadeIn(300);return false}if(e(this).hasClass("requiredEmailField")){var s=/^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;var o="#"+e(this).attr("id");if(!s.test(e(o).val())){e(o).focus();e(o).addClass("fieldHasError");e(o+"Error2").fadeIn(300);return false}}if(formSubmitted=="false"&&i==e("#"+n+" .requiredField").length-1){t(n,r)}})}e("#formSuccessMessageWrap").hide(0);e(".formValidationError").fadeOut(0);e('input[type="text"], input[type="password"], textarea').focus(function(){if(e(this).val()==e(this).attr("data-dummy")){e(this).val("")}});e("input, textarea").blur(function(){if(e(this).val()==""){e(this).val(e(this).attr("data-dummy"))}});e("#contactSubmitButton").click(function(){n(e(this).attr("data-formId"));return false})})
+}(jQuery));
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/js/functions.js b/template/megp/js/functions.js
new file mode 100644
index 0000000..8165181
--- /dev/null
+++ b/template/megp/js/functions.js
@@ -0,0 +1,112 @@
+function help_notice_check()
+{
+ $.getJSON(home+'help/section/notice/go',
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'empty')
+ {
+ hnt = false; help_notice_title(true);
+ $('.header-icon-1').css('animation', 'none');
+ }
+
+ if(i == 'reply')
+ {
+ if(val)
+ {
+ if(val > getCookie('help') || getCookie('help') == undefined)
+ {
+ setCookie('help', val, {expires: 86400});
+ help_notice_sound();
+ }
+ }
+
+ $('.header-icon-1').css('animation', 'fa-spin 2s infinite cubic-bezier(0, 0, 0.15, 0.96)');
+ }
+
+ if(i == 'notice')
+ {
+ help_notice_sound();
+ $('.header-icon-1').css('animation', 'fa-spin 2s infinite cubic-bezier(0, 0, 0.15, 0.96)');
+ }
+ });
+ });
+
+ setTimeout(function() {help_notice_check()}, 10000);
+}
+
+function help_notice_sound()
+{
+
+ hnt = true; help_notice_title();
+
+ var audio = new Audio();
+ audio.preload = 'auto';
+ audio.src = '/notice.wav';
+ audio.play();
+}
+
+function help_notice_title(stop)
+{
+ if(document.title == title && !stop)
+ document.title = 'Новое сообщение';
+ else
+ document.title = title;
+
+ if(hnt)
+ setTimeout(function() {help_notice_title()}, 1000);
+}
+
+function setCookie(name, value, options)
+{
+ options = options || {};
+
+ var expires = options.expires;
+
+ if(typeof expires == 'number' && expires)
+ {
+ var d = new Date();
+ d.setTime(d.getTime() + expires * 1000);
+ expires = options.expires = d;
+ }
+
+ if(expires && expires.toUTCString)
+ options.expires = expires.toUTCString();
+
+ value = encodeURIComponent(value);
+
+ var updatedCookie = name + '=' + value + '; path=/';
+
+ for (var propName in options)
+ {
+ updatedCookie += '; ' + propName;
+ var propValue = options[propName];
+
+ if(propValue !== true)
+ updatedCookie += '=' + propValue;
+ }
+
+ document.cookie = updatedCookie;
+}
+
+function getCookie(name)
+{
+ var matches = document.cookie.match(new RegExp(
+ "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
+ ));
+
+ return matches ? decodeURIComponent(matches[1]) : undefined;
+}
+
+function deleteCookie(name)
+{
+ setCookie(name, '', {expires: -1})
+}
+
+$(document).ready(function(){
+ $('.spoiler').click(function(){
+ $(this).parent().children('div.spoiler_main').toggle(0);
+ });
+ help_notice_check();
+});
\ No newline at end of file
diff --git a/template/megp/js/help.js b/template/megp/js/help.js
new file mode 100644
index 0000000..0d38f47
--- /dev/null
+++ b/template/megp/js/help.js
@@ -0,0 +1,238 @@
+$('#create').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ location.href=home+'help/section/dialog/id/'+val;
+ });
+
+ return false;
+ }
+});
+
+$('#reply').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 'с')
+ {
+ bootbox.dialog('Внимание '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ location.reload();
+ }
+
+ if(i == 'i')
+ {
+ bootbox.dialog('Внимание '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ dialog_update(false);
+ }
+
+ if(i == 's')
+ {
+ $('#text').val('');
+ $('#text').html('');
+ document.getElementById("text").removeAttribute("style");
+
+ dialog_update(false);
+ }
+ });
+
+ return false;
+ }
+});
+
+function help_open(id)
+{
+ $.getJSON(home+'help/section/close/action/open/id/'+id,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ location.href=home+'help/section/dialog/id/'+id;
+ });
+ });
+}
+
+function help_msg_del(id, msg)
+{
+ $.getJSON(home+'help/section/dialog/action/remove/id/'+id+'/msg/'+msg,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ dialog_update(false);
+ });
+ });
+
+ return false;
+}
+
+function help_close(id)
+{
+ $.getJSON(home+'help/section/open/action/close/id/'+id,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+ });
+}
+
+function help_readers()
+{
+ $.get(home+'help/section/dialog/action/read/id/'+help,
+ function(readers)
+ {
+ setTimeout(function() {help_readers()}, 9000);
+ });
+}
+
+function help_writers(now)
+{
+ write = '';
+
+ if($('#text').val() != '')
+ write = '/write/1';
+
+ $.get(home+'help/section/dialog/action/write/id/'+help+write,
+ function(writers)
+ {
+ if(!now)
+ setTimeout(function() {help_writers(false)}, 9000);
+ });
+}
+
+function dialog_update(go)
+{
+ if(go)
+ {
+ spoilers = $('.spoiler_main');
+ update = true;
+ for(var i = 0; i < spoilers.length; i++)
+ {
+ if(spoilers[i].style.display == 'block')
+ {
+ setTimeout(function() {dialog_update(true)}, 15000);
+
+ return false;
+ }
+ }
+ }
+
+ $.getJSON(home+'help/section/dialog/id/'+help+'/ajax',
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'dialog')
+ {
+ $('#dialog').html(val);
+
+ $('.spoiler').click(function(){
+ $(this).parent().children('div.spoiler_main').toggle(0);
+ });
+ }
+
+ if(i == 'status')
+ $('#help_status').html(val);
+ });
+
+ if(go)
+ setTimeout(function() {dialog_update(true)}, 15000);
+ });
+}
+
+// Переустановка сервера (подтверждение)
+function help_delete(id)
+{
+ bootbox.dialog('Внимание Вы уверены, что хотите удалить этот вопрос?',
+ [{
+ "label" : "Подтвердить",
+ "class" : "btn-small btn-primary",
+ callback: function() {help_delete_go(id)}
+ },{
+ "label" : "Отмена",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+}
+
+function help_delete_go(id)
+{
+ $.getJSON(home+'help/section/open/action/delete/id/'+id,
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+ });
+}
\ No newline at end of file
diff --git a/template/megp/js/jquery.js b/template/megp/js/jquery.js
new file mode 100644
index 0000000..0f60b7b
--- /dev/null
+++ b/template/megp/js/jquery.js
@@ -0,0 +1,5 @@
+/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML=" ",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML=" ","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML=" ",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;
+
+return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" a ",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="x ",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML=" ",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h ]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""," "],legend:[1,""," "],area:[1,""," "],param:[1,""," "],thead:[1,""],tr:[2,""],col:[2,""],td:[3,""],_default:k.htmlSerialize?[0,"",""]:[1,"X","
"]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("")).appendTo(b.documentElement),b=(Ca[0].contentWindow||Ca[0].contentDocument).document,b.write(),b.close(),c=Ea(a,b),Ca.detach()),Da[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Ga=/^margin/,Ha=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ia,Ja,Ka=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ia=function(b){return b.ownerDocument.defaultView.opener?b.ownerDocument.defaultView.getComputedStyle(b,null):a.getComputedStyle(b,null)},Ja=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ia(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Ha.test(g)&&Ga.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ia=function(a){return a.currentStyle},Ja=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ia(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Ha.test(g)&&!Ka.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function La(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" a ",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight),b.removeChild(i)),b.innerHTML="",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Ma=/alpha\([^)]*\)/i,Na=/opacity\s*=\s*([^)]*)/,Oa=/^(none|table(?!-c[ea]).+)/,Pa=new RegExp("^("+S+")(.*)$","i"),Qa=new RegExp("^([+-])=("+S+")","i"),Ra={position:"absolute",visibility:"hidden",display:"block"},Sa={letterSpacing:"0",fontWeight:"400"},Ta=["Webkit","O","Moz","ms"];function Ua(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Ta.length;while(e--)if(b=Ta[e]+c,b in a)return b;return d}function Va(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fa(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wa(a,b,c){var d=Pa.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xa(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Ya(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ia(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Ja(a,b,f),(0>e||null==e)&&(e=a.style[b]),Ha.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xa(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Ja(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ua(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qa.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ua(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Ja(a,b,d)),"normal"===f&&b in Sa&&(f=Sa[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Oa.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Ra,function(){return Ya(a,b,d)}):Ya(a,b,d):void 0},set:function(a,c,d){var e=d&&Ia(a);return Wa(a,c,d?Xa(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Na.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Ma,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Ma.test(f)?f.replace(Ma,e):f+" "+e)}}),m.cssHooks.marginRight=La(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Ja,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Ga.test(a)||(m.cssHooks[a+b].set=Wa)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ia(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Va(this,!0)},hide:function(){return Va(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Za(a,b,c,d,e){
+return new Za.prototype.init(a,b,c,d,e)}m.Tween=Za,Za.prototype={constructor:Za,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")},cur:function(){var a=Za.propHooks[this.prop];return a&&a.get?a.get(this):Za.propHooks._default.get(this)},run:function(a){var b,c=Za.propHooks[this.prop];return this.options.duration?this.pos=b=m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Za.propHooks._default.set(this),this}},Za.prototype.init.prototype=Za.prototype,Za.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Za.propHooks.scrollTop=Za.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Za.prototype.init,m.fx.step={};var $a,_a,ab=/^(?:toggle|show|hide)$/,bb=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cb=/queueHooks$/,db=[ib],eb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bb.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bb.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fb(){return setTimeout(function(){$a=void 0}),$a=m.now()}function gb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hb(a,b,c){for(var d,e=(eb[b]||[]).concat(eb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fa(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fa(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ab.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fa(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hb(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=db.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$a||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$a||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);g>f;f++)if(d=db[f].call(j,a,k,j.opts))return d;return m.map(k,hb,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kb,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],eb[c]=eb[c]||[],eb[c].unshift(b)},prefilter:function(a,b){b?db.unshift(a):db.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kb(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gb(b,!0),a,d,e)}}),m.each({slideDown:gb("show"),slideUp:gb("hide"),slideToggle:gb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($a=m.now();ca ",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lb=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lb,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mb,nb,ob=m.expr.attrHandle,pb=/^(?:checked|selected)$/i,qb=k.getSetAttribute,rb=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nb:mb)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rb&&qb||!pb.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qb?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nb={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rb&&qb||!pb.test(c)?a.setAttribute(!qb&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=ob[b]||m.find.attr;ob[b]=rb&&qb||!pb.test(b)?function(a,b,d){var e,f;return d||(f=ob[b],ob[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,ob[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rb&&qb||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mb&&mb.set(a,b,c)}}),qb||(mb={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},ob.id=ob.name=ob.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mb.set},m.attrHooks.contenteditable={set:function(a,b,c){mb.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sb=/^(?:input|select|textarea|button|object)$/i,tb=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sb.test(a.nodeName)||tb.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var ub=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ub," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ub," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ub," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vb=m.now(),wb=/\?/,xb=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xb,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yb,zb,Ab=/#.*$/,Bb=/([?&])_=[^&]*/,Cb=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Db=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Eb=/^(?:GET|HEAD)$/,Fb=/^\/\//,Gb=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hb={},Ib={},Jb="*/".concat("*");try{zb=location.href}catch(Kb){zb=y.createElement("a"),zb.href="",zb=zb.href}yb=Gb.exec(zb.toLowerCase())||[];function Lb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mb(a,b,c,d){var e={},f=a===Ib;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nb(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Ob(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zb,type:"GET",isLocal:Db.test(yb[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nb(Nb(a,m.ajaxSettings),b):Nb(m.ajaxSettings,a)},ajaxPrefilter:Lb(Hb),ajaxTransport:Lb(Ib),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cb.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zb)+"").replace(Ab,"").replace(Fb,yb[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gb.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yb[1]&&c[2]===yb[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yb[3]||("http:"===yb[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mb(Hb,k,b,v),2===t)return v;h=m.event&&k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Eb.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wb.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bb.test(e)?e.replace(Bb,"$1_="+vb++):e+(wb.test(e)?"&":"?")+"_="+vb++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jb+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mb(Ib,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Ob(k,v,c)),u=Pb(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qb=/%20/g,Rb=/\[\]$/,Sb=/\r?\n/g,Tb=/^(?:submit|button|image|reset|file)$/i,Ub=/^(?:input|select|textarea|keygen)/i;function Vb(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rb.test(a)?d(a,e):Vb(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vb(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vb(c,a[c],b,e);return d.join("&").replace(Qb,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Ub.test(this.nodeName)&&!Tb.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sb,"\r\n")}}):{name:b.name,value:c.replace(Sb,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zb()||$b()}:Zb;var Wb=0,Xb={},Yb=m.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in Xb)Xb[a](void 0,!0)}),k.cors=!!Yb&&"withCredentials"in Yb,Yb=k.ajax=!!Yb,Yb&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wb;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xb[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xb[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zb(){try{return new a.XMLHttpRequest}catch(b){}}function $b(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _b=[],ac=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_b.pop()||m.expando+"_"+vb++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ac.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ac.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ac,"$1"+e):b.jsonp!==!1&&(b.url+=(wb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_b.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bc=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bc)return bc.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cc=a.document.documentElement;function dc(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dc(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cc;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cc})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dc(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=La(k.pixelPosition,function(a,c){return c?(c=Ja(a,b),Ha.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ec=a.jQuery,fc=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fc),b&&a.jQuery===m&&(a.jQuery=ec),m},typeof b===K&&(a.jQuery=a.$=m),m});
diff --git a/template/megp/js/jqueryui.js b/template/megp/js/jqueryui.js
new file mode 100644
index 0000000..6819208
--- /dev/null
+++ b/template/megp/js/jqueryui.js
@@ -0,0 +1,6 @@
+/*! jQuery UI - v1.11.4 - 2015-06-15
+* http://jqueryui.com
+* Includes: core.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js
+* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
+
+(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,s){var n,a,o,r=t.nodeName.toLowerCase();return"area"===r?(n=t.parentNode,a=n.name,t.href&&a&&"map"===n.nodeName.toLowerCase()?(o=e("img[usemap='#"+a+"']")[0],!!o&&i(o)):!1):(/^(input|select|textarea|button|object)$/.test(r)?!t.disabled:"a"===r?t.href||s:s)&&i(t)}function i(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.4",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(t){var i=this.css("position"),s="absolute"===i,n=t?/(auto|scroll|hidden)/:/(auto|scroll)/,a=this.parents().filter(function(){var t=e(this);return s&&"static"===t.css("position")?!1:n.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==i&&a.length?a:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(i){return t(i,!isNaN(e.attr(i,"tabindex")))},tabbable:function(i){var s=e.attr(i,"tabindex"),n=isNaN(s);return(n||s>=0)&&t(i,!n)}}),e("
").outerWidth(1).jquery||e.each(["Width","Height"],function(t,i){function s(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+i]=function(t){return void 0===t?o["inner"+i].call(this):this.each(function(){e(this).css(a,s(this,t)+"px")})},e.fn["outer"+i]=function(t,n){return"number"!=typeof t?o["outer"+i].call(this,t):this.each(function(){e(this).css(a,s(this,t,!0,n)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e(" ").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var i,s,n=e(this[0]);n.length&&n[0]!==document;){if(i=n.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(s=parseInt(n.css("zIndex"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),e.ui.plugin={add:function(t,i,s){var n,a=e.ui[t].prototype;for(n in s)a.plugins[n]=a.plugins[n]||[],a.plugins[n].push([i,s[n]])},call:function(e,t,i,s){var n,a=e.plugins[t];if(a&&(s||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(n=0;a.length>n;n++)e.options[a[n][0]]&&a[n][1].apply(e.element,i)}};var s="ui-effects-",n=e;e.effects={effect:{}},function(e,t){function i(e,t,i){var s=d[t.type]||{};return null==e?i||!t.def?null:t.def:(e=s.floor?~~e:parseFloat(e),isNaN(e)?t.def:s.mod?(e+s.mod)%s.mod:0>e?0:e>s.max?s.max:e)}function s(i){var s=l(),n=s._rgba=[];return i=i.toLowerCase(),f(h,function(e,a){var o,r=a.re.exec(i),h=r&&a.parse(r),l=a.space||"rgba";return h?(o=s[l](h),s[u[l].cache]=o[u[l].cache],n=s._rgba=o._rgba,!1):t}),n.length?("0,0,0,0"===n.join()&&e.extend(n,a.transparent),s):a[i]}function n(e,t,i){return i=(i+1)%1,1>6*i?e+6*(t-e)*i:1>2*i?t:2>3*i?e+6*(t-e)*(2/3-i):e}var a,o="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,h=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[e[1],e[2],e[3],e[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[2.55*e[1],2.55*e[2],2.55*e[3],e[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(e){return[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(e){return[parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(e){return[e[1],e[2]/100,e[3]/100,e[4]]}}],l=e.Color=function(t,i,s,n){return new e.Color.fn.parse(t,i,s,n)},u={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},d={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},c=l.support={},p=e("")[0],f=e.each;p.style.cssText="background-color:rgba(1,1,1,.5)",c.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(u,function(e,t){t.cache="_"+e,t.props.alpha={idx:3,type:"percent",def:1}}),l.fn=e.extend(l.prototype,{parse:function(n,o,r,h){if(n===t)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=e(n).css(o),o=t);var d=this,c=e.type(n),p=this._rgba=[];return o!==t&&(n=[n,o,r,h],c="array"),"string"===c?this.parse(s(n)||a._default):"array"===c?(f(u.rgba.props,function(e,t){p[t.idx]=i(n[t.idx],t)}),this):"object"===c?(n instanceof l?f(u,function(e,t){n[t.cache]&&(d[t.cache]=n[t.cache].slice())}):f(u,function(t,s){var a=s.cache;f(s.props,function(e,t){if(!d[a]&&s.to){if("alpha"===e||null==n[e])return;d[a]=s.to(d._rgba)}d[a][t.idx]=i(n[e],t,!0)}),d[a]&&0>e.inArray(null,d[a].slice(0,3))&&(d[a][3]=1,s.from&&(d._rgba=s.from(d[a])))}),this):t},is:function(e){var i=l(e),s=!0,n=this;return f(u,function(e,a){var o,r=i[a.cache];return r&&(o=n[a.cache]||a.to&&a.to(n._rgba)||[],f(a.props,function(e,i){return null!=r[i.idx]?s=r[i.idx]===o[i.idx]:t})),s}),s},_space:function(){var e=[],t=this;return f(u,function(i,s){t[s.cache]&&e.push(i)}),e.pop()},transition:function(e,t){var s=l(e),n=s._space(),a=u[n],o=0===this.alpha()?l("transparent"):this,r=o[a.cache]||a.to(o._rgba),h=r.slice();return s=s[a.cache],f(a.props,function(e,n){var a=n.idx,o=r[a],l=s[a],u=d[n.type]||{};null!==l&&(null===o?h[a]=l:(u.mod&&(l-o>u.mod/2?o+=u.mod:o-l>u.mod/2&&(o-=u.mod)),h[a]=i((l-o)*t+o,n)))}),this[n](h)},blend:function(t){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=l(t)._rgba;return l(e.map(i,function(e,t){return(1-s)*n[t]+s*e}))},toRgbaString:function(){var t="rgba(",i=e.map(this._rgba,function(e,t){return null==e?t>2?1:0:e});return 1===i[3]&&(i.pop(),t="rgb("),t+i.join()+")"},toHslaString:function(){var t="hsla(",i=e.map(this.hsla(),function(e,t){return null==e&&(e=t>2?1:0),t&&3>t&&(e=Math.round(100*e)+"%"),e});return 1===i[3]&&(i.pop(),t="hsl("),t+i.join()+")"},toHexString:function(t){var i=this._rgba.slice(),s=i.pop();return t&&i.push(~~(255*s)),"#"+e.map(i,function(e){return e=(e||0).toString(16),1===e.length?"0"+e:e}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),l.fn.parse.prototype=l.fn,u.hsla.to=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t,i,s=e[0]/255,n=e[1]/255,a=e[2]/255,o=e[3],r=Math.max(s,n,a),h=Math.min(s,n,a),l=r-h,u=r+h,d=.5*u;return t=h===r?0:s===r?60*(n-a)/l+360:n===r?60*(a-s)/l+120:60*(s-n)/l+240,i=0===l?0:.5>=d?l/u:l/(2-u),[Math.round(t)%360,i,d,null==o?1:o]},u.hsla.from=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t=e[0]/360,i=e[1],s=e[2],a=e[3],o=.5>=s?s*(1+i):s+i-s*i,r=2*s-o;return[Math.round(255*n(r,o,t+1/3)),Math.round(255*n(r,o,t)),Math.round(255*n(r,o,t-1/3)),a]},f(u,function(s,n){var a=n.props,o=n.cache,h=n.to,u=n.from;l.fn[s]=function(s){if(h&&!this[o]&&(this[o]=h(this._rgba)),s===t)return this[o].slice();var n,r=e.type(s),d="array"===r||"object"===r?s:arguments,c=this[o].slice();return f(a,function(e,t){var s=d["object"===r?e:t.idx];null==s&&(s=c[t.idx]),c[t.idx]=i(s,t)}),u?(n=l(u(c)),n[o]=c,n):l(c)},f(a,function(t,i){l.fn[t]||(l.fn[t]=function(n){var a,o=e.type(n),h="alpha"===t?this._hsla?"hsla":"rgba":s,l=this[h](),u=l[i.idx];return"undefined"===o?u:("function"===o&&(n=n.call(this,u),o=e.type(n)),null==n&&i.empty?this:("string"===o&&(a=r.exec(n),a&&(n=u+parseFloat(a[2])*("+"===a[1]?1:-1))),l[i.idx]=n,this[h](l)))})})}),l.hook=function(t){var i=t.split(" ");f(i,function(t,i){e.cssHooks[i]={set:function(t,n){var a,o,r="";if("transparent"!==n&&("string"!==e.type(n)||(a=s(n)))){if(n=l(a||n),!c.rgba&&1!==n._rgba[3]){for(o="backgroundColor"===i?t.parentNode:t;(""===r||"transparent"===r)&&o&&o.style;)try{r=e.css(o,"backgroundColor"),o=o.parentNode}catch(h){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{t.style[i]=n}catch(h){}}},e.fx.step[i]=function(t){t.colorInit||(t.start=l(t.elem,i),t.end=l(t.end),t.colorInit=!0),e.cssHooks[i].set(t.elem,t.start.transition(t.end,t.pos))}})},l.hook(o),e.cssHooks.borderColor={expand:function(e){var t={};return f(["Top","Right","Bottom","Left"],function(i,s){t["border"+s+"Color"]=e}),t}},a=e.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(n),function(){function t(t){var i,s,n=t.ownerDocument.defaultView?t.ownerDocument.defaultView.getComputedStyle(t,null):t.currentStyle,a={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(a[e.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(a[i]=n[i]);return a}function i(t,i){var s,n,o={};for(s in i)n=i[s],t[s]!==n&&(a[s]||(e.fx.step[s]||!isNaN(parseFloat(n)))&&(o[s]=n));return o}var s=["add","remove","toggle"],a={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};e.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(t,i){e.fx.step[i]=function(e){("none"!==e.end&&!e.setAttr||1===e.pos&&!e.setAttr)&&(n.style(e.elem,i,e.end),e.setAttr=!0)}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e.effects.animateClass=function(n,a,o,r){var h=e.speed(a,o,r);return this.queue(function(){var a,o=e(this),r=o.attr("class")||"",l=h.children?o.find("*").addBack():o;l=l.map(function(){var i=e(this);return{el:i,start:t(this)}}),a=function(){e.each(s,function(e,t){n[t]&&o[t+"Class"](n[t])})},a(),l=l.map(function(){return this.end=t(this.el[0]),this.diff=i(this.start,this.end),this}),o.attr("class",r),l=l.map(function(){var t=this,i=e.Deferred(),s=e.extend({},h,{queue:!1,complete:function(){i.resolve(t)}});return this.el.animate(this.diff,s),i.promise()}),e.when.apply(e,l.get()).done(function(){a(),e.each(arguments,function(){var t=this.el;e.each(this.diff,function(e){t.css(e,"")})}),h.complete.call(o[0])})})},e.fn.extend({addClass:function(t){return function(i,s,n,a){return s?e.effects.animateClass.call(this,{add:i},s,n,a):t.apply(this,arguments)}}(e.fn.addClass),removeClass:function(t){return function(i,s,n,a){return arguments.length>1?e.effects.animateClass.call(this,{remove:i},s,n,a):t.apply(this,arguments)}}(e.fn.removeClass),toggleClass:function(t){return function(i,s,n,a,o){return"boolean"==typeof s||void 0===s?n?e.effects.animateClass.call(this,s?{add:i}:{remove:i},n,a,o):t.apply(this,arguments):e.effects.animateClass.call(this,{toggle:i},s,n,a)}}(e.fn.toggleClass),switchClass:function(t,i,s,n,a){return e.effects.animateClass.call(this,{add:i,remove:t},s,n,a)}})}(),function(){function t(t,i,s,n){return e.isPlainObject(t)&&(i=t,t=t.effect),t={effect:t},null==i&&(i={}),e.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||e.fx.speeds[i])&&(n=s,s=i,i={}),e.isFunction(s)&&(n=s,s=null),i&&e.extend(t,i),s=s||i.duration,t.duration=e.fx.off?0:"number"==typeof s?s:s in e.fx.speeds?e.fx.speeds[s]:e.fx.speeds._default,t.complete=n||i.complete,t}function i(t){return!t||"number"==typeof t||e.fx.speeds[t]?!0:"string"!=typeof t||e.effects.effect[t]?e.isFunction(t)?!0:"object"!=typeof t||t.effect?!1:!0:!0}e.extend(e.effects,{version:"1.11.4",save:function(e,t){for(var i=0;t.length>i;i++)null!==t[i]&&e.data(s+t[i],e[0].style[t[i]])},restore:function(e,t){var i,n;for(n=0;t.length>n;n++)null!==t[n]&&(i=e.data(s+t[n]),void 0===i&&(i=""),e.css(t[n],i))},setMode:function(e,t){return"toggle"===t&&(t=e.is(":hidden")?"show":"hide"),t},getBaseline:function(e,t){var i,s;switch(e[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=e[0]/t.height}switch(e[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=e[1]/t.width}return{x:s,y:i}},createWrapper:function(t){if(t.parent().is(".ui-effects-wrapper"))return t.parent();var i={width:t.outerWidth(!0),height:t.outerHeight(!0),"float":t.css("float")},s=e("
").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:t.width(),height:t.height()},a=document.activeElement;try{a.id}catch(o){a=document.body}return t.wrap(s),(t[0]===a||e.contains(t[0],a))&&e(a).focus(),s=t.parent(),"static"===t.css("position")?(s.css({position:"relative"}),t.css({position:"relative"})):(e.extend(i,{position:t.css("position"),zIndex:t.css("z-index")}),e.each(["top","left","bottom","right"],function(e,s){i[s]=t.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),t.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),t.css(n),s.css(i).show()},removeWrapper:function(t){var i=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),(t[0]===i||e.contains(t[0],i))&&e(i).focus()),t},setTransition:function(t,i,s,n){return n=n||{},e.each(i,function(e,i){var a=t.cssUnit(i);a[0]>0&&(n[i]=a[0]*s+a[1])}),n}}),e.fn.extend({effect:function(){function i(t){function i(){e.isFunction(a)&&a.call(n[0]),e.isFunction(t)&&t()}var n=e(this),a=s.complete,r=s.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),i()):o.call(n[0],s,i)}var s=t.apply(this,arguments),n=s.mode,a=s.queue,o=e.effects.effect[s.effect];return e.fx.off||!o?n?this[n](s.duration,s.complete):this.each(function(){s.complete&&s.complete.call(this)}):a===!1?this.each(i):this.queue(a||"fx",i)},show:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="show",this.effect.call(this,n)}}(e.fn.show),hide:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="hide",this.effect.call(this,n)}}(e.fn.hide),toggle:function(e){return function(s){if(i(s)||"boolean"==typeof s)return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)}}(e.fn.toggle),cssUnit:function(t){var i=this.css(t),s=[];return e.each(["em","px","%","pt"],function(e,t){i.indexOf(t)>0&&(s=[parseFloat(i),t])}),s}})}(),function(){var t={};e.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,i){t[i]=function(t){return Math.pow(t,e+2)}}),e.extend(t,{Sine:function(e){return 1-Math.cos(e*Math.PI/2)},Circ:function(e){return 1-Math.sqrt(1-e*e)},Elastic:function(e){return 0===e||1===e?e:-Math.pow(2,8*(e-1))*Math.sin((80*(e-1)-7.5)*Math.PI/15)},Back:function(e){return e*e*(3*e-2)},Bounce:function(e){for(var t,i=4;((t=Math.pow(2,--i))-1)/11>e;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*t-2)/22-e,2)}}),e.each(t,function(t,i){e.easing["easeIn"+t]=i,e.easing["easeOut"+t]=function(e){return 1-i(1-e)},e.easing["easeInOut"+t]=function(e){return.5>e?i(2*e)/2:1-i(-2*e+2)/2}})}(),e.effects,e.effects.effect.blind=function(t,i){var s,n,a,o=e(this),r=/up|down|vertical/,h=/up|left|vertical|horizontal/,l=["position","top","bottom","left","right","height","width"],u=e.effects.setMode(o,t.mode||"hide"),d=t.direction||"up",c=r.test(d),p=c?"height":"width",f=c?"top":"left",m=h.test(d),g={},v="show"===u;o.parent().is(".ui-effects-wrapper")?e.effects.save(o.parent(),l):e.effects.save(o,l),o.show(),s=e.effects.createWrapper(o).css({overflow:"hidden"}),n=s[p](),a=parseFloat(s.css(f))||0,g[p]=v?n:0,m||(o.css(c?"bottom":"right",0).css(c?"top":"left","auto").css({position:"absolute"}),g[f]=v?a:n+a),v&&(s.css(p,0),m||s.css(f,a+n)),s.animate(g,{duration:t.duration,easing:t.easing,queue:!1,complete:function(){"hide"===u&&o.hide(),e.effects.restore(o,l),e.effects.removeWrapper(o),i()}})},e.effects.effect.bounce=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(o,t.mode||"effect"),l="hide"===h,u="show"===h,d=t.direction||"up",c=t.distance,p=t.times||5,f=2*p+(u||l?1:0),m=t.duration/f,g=t.easing,v="up"===d||"down"===d?"top":"left",y="up"===d||"left"===d,b=o.queue(),_=b.length;for((u||l)&&r.push("opacity"),e.effects.save(o,r),o.show(),e.effects.createWrapper(o),c||(c=o["top"===v?"outerHeight":"outerWidth"]()/3),u&&(a={opacity:1},a[v]=0,o.css("opacity",0).css(v,y?2*-c:2*c).animate(a,m,g)),l&&(c/=Math.pow(2,p-1)),a={},a[v]=0,s=0;p>s;s++)n={},n[v]=(y?"-=":"+=")+c,o.animate(n,m,g).animate(a,m,g),c=l?2*c:c/2;l&&(n={opacity:0},n[v]=(y?"-=":"+=")+c,o.animate(n,m,g)),o.queue(function(){l&&o.hide(),e.effects.restore(o,r),e.effects.removeWrapper(o),i()}),_>1&&b.splice.apply(b,[1,0].concat(b.splice(_,f+1))),o.dequeue()},e.effects.effect.clip=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(o,t.mode||"hide"),l="show"===h,u=t.direction||"vertical",d="vertical"===u,c=d?"height":"width",p=d?"top":"left",f={};e.effects.save(o,r),o.show(),s=e.effects.createWrapper(o).css({overflow:"hidden"}),n="IMG"===o[0].tagName?s:o,a=n[c](),l&&(n.css(c,0),n.css(p,a/2)),f[c]=l?a:0,f[p]=l?0:a/2,n.animate(f,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){l||o.hide(),e.effects.restore(o,r),e.effects.removeWrapper(o),i()}})},e.effects.effect.drop=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","opacity","height","width"],o=e.effects.setMode(n,t.mode||"hide"),r="show"===o,h=t.direction||"left",l="up"===h||"down"===h?"top":"left",u="up"===h||"left"===h?"pos":"neg",d={opacity:r?1:0};e.effects.save(n,a),n.show(),e.effects.createWrapper(n),s=t.distance||n["top"===l?"outerHeight":"outerWidth"](!0)/2,r&&n.css("opacity",0).css(l,"pos"===u?-s:s),d[l]=(r?"pos"===u?"+=":"-=":"pos"===u?"-=":"+=")+s,n.animate(d,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}})},e.effects.effect.explode=function(t,i){function s(){b.push(this),b.length===d*c&&n()}function n(){p.css({visibility:"visible"}),e(b).remove(),m||p.hide(),i()}var a,o,r,h,l,u,d=t.pieces?Math.round(Math.sqrt(t.pieces)):3,c=d,p=e(this),f=e.effects.setMode(p,t.mode||"hide"),m="show"===f,g=p.show().css("visibility","hidden").offset(),v=Math.ceil(p.outerWidth()/c),y=Math.ceil(p.outerHeight()/d),b=[];for(a=0;d>a;a++)for(h=g.top+a*y,u=a-(d-1)/2,o=0;c>o;o++)r=g.left+o*v,l=o-(c-1)/2,p.clone().appendTo("body").wrap("
").css({position:"absolute",visibility:"visible",left:-o*v,top:-a*y}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:v,height:y,left:r+(m?l*v:0),top:h+(m?u*y:0),opacity:m?0:1}).animate({left:r+(m?0:l*v),top:h+(m?0:u*y),opacity:m?1:0},t.duration||500,t.easing,s)},e.effects.effect.fade=function(t,i){var s=e(this),n=e.effects.setMode(s,t.mode||"toggle");s.animate({opacity:n},{queue:!1,duration:t.duration,easing:t.easing,complete:i})},e.effects.effect.fold=function(t,i){var s,n,a=e(this),o=["position","top","bottom","left","right","height","width"],r=e.effects.setMode(a,t.mode||"hide"),h="show"===r,l="hide"===r,u=t.size||15,d=/([0-9]+)%/.exec(u),c=!!t.horizFirst,p=h!==c,f=p?["width","height"]:["height","width"],m=t.duration/2,g={},v={};e.effects.save(a,o),a.show(),s=e.effects.createWrapper(a).css({overflow:"hidden"}),n=p?[s.width(),s.height()]:[s.height(),s.width()],d&&(u=parseInt(d[1],10)/100*n[l?0:1]),h&&s.css(c?{height:0,width:u}:{height:u,width:0}),g[f[0]]=h?n[0]:u,v[f[1]]=h?n[1]:0,s.animate(g,m,t.easing).animate(v,m,t.easing,function(){l&&a.hide(),e.effects.restore(a,o),e.effects.removeWrapper(a),i()})},e.effects.effect.highlight=function(t,i){var s=e(this),n=["backgroundImage","backgroundColor","opacity"],a=e.effects.setMode(s,t.mode||"show"),o={backgroundColor:s.css("backgroundColor")};"hide"===a&&(o.opacity=0),e.effects.save(s,n),s.show().css({backgroundImage:"none",backgroundColor:t.color||"#ffff99"}).animate(o,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===a&&s.hide(),e.effects.restore(s,n),i()}})},e.effects.effect.size=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","width","height","overflow","opacity"],h=["position","top","bottom","left","right","overflow","opacity"],l=["width","height","overflow"],u=["fontSize"],d=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],c=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=e.effects.setMode(o,t.mode||"effect"),f=t.restore||"effect"!==p,m=t.scale||"both",g=t.origin||["middle","center"],v=o.css("position"),y=f?r:h,b={height:0,width:0,outerHeight:0,outerWidth:0};"show"===p&&o.show(),s={height:o.height(),width:o.width(),outerHeight:o.outerHeight(),outerWidth:o.outerWidth()},"toggle"===t.mode&&"show"===p?(o.from=t.to||b,o.to=t.from||s):(o.from=t.from||("show"===p?b:s),o.to=t.to||("hide"===p?b:s)),a={from:{y:o.from.height/s.height,x:o.from.width/s.width},to:{y:o.to.height/s.height,x:o.to.width/s.width}},("box"===m||"both"===m)&&(a.from.y!==a.to.y&&(y=y.concat(d),o.from=e.effects.setTransition(o,d,a.from.y,o.from),o.to=e.effects.setTransition(o,d,a.to.y,o.to)),a.from.x!==a.to.x&&(y=y.concat(c),o.from=e.effects.setTransition(o,c,a.from.x,o.from),o.to=e.effects.setTransition(o,c,a.to.x,o.to))),("content"===m||"both"===m)&&a.from.y!==a.to.y&&(y=y.concat(u).concat(l),o.from=e.effects.setTransition(o,u,a.from.y,o.from),o.to=e.effects.setTransition(o,u,a.to.y,o.to)),e.effects.save(o,y),o.show(),e.effects.createWrapper(o),o.css("overflow","hidden").css(o.from),g&&(n=e.effects.getBaseline(g,s),o.from.top=(s.outerHeight-o.outerHeight())*n.y,o.from.left=(s.outerWidth-o.outerWidth())*n.x,o.to.top=(s.outerHeight-o.to.outerHeight)*n.y,o.to.left=(s.outerWidth-o.to.outerWidth)*n.x),o.css(o.from),("content"===m||"both"===m)&&(d=d.concat(["marginTop","marginBottom"]).concat(u),c=c.concat(["marginLeft","marginRight"]),l=r.concat(d).concat(c),o.find("*[width]").each(function(){var i=e(this),s={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()};f&&e.effects.save(i,l),i.from={height:s.height*a.from.y,width:s.width*a.from.x,outerHeight:s.outerHeight*a.from.y,outerWidth:s.outerWidth*a.from.x},i.to={height:s.height*a.to.y,width:s.width*a.to.x,outerHeight:s.height*a.to.y,outerWidth:s.width*a.to.x},a.from.y!==a.to.y&&(i.from=e.effects.setTransition(i,d,a.from.y,i.from),i.to=e.effects.setTransition(i,d,a.to.y,i.to)),a.from.x!==a.to.x&&(i.from=e.effects.setTransition(i,c,a.from.x,i.from),i.to=e.effects.setTransition(i,c,a.to.x,i.to)),i.css(i.from),i.animate(i.to,t.duration,t.easing,function(){f&&e.effects.restore(i,l)})})),o.animate(o.to,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){0===o.to.opacity&&o.css("opacity",o.from.opacity),"hide"===p&&o.hide(),e.effects.restore(o,y),f||("static"===v?o.css({position:"relative",top:o.to.top,left:o.to.left}):e.each(["top","left"],function(e,t){o.css(t,function(t,i){var s=parseInt(i,10),n=e?o.to.left:o.to.top;return"auto"===i?n+"px":s+n+"px"})})),e.effects.removeWrapper(o),i()}})},e.effects.effect.scale=function(t,i){var s=e(this),n=e.extend(!0,{},t),a=e.effects.setMode(s,t.mode||"effect"),o=parseInt(t.percent,10)||(0===parseInt(t.percent,10)?0:"hide"===a?0:100),r=t.direction||"both",h=t.origin,l={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()},u={y:"horizontal"!==r?o/100:1,x:"vertical"!==r?o/100:1};n.effect="size",n.queue=!1,n.complete=i,"effect"!==a&&(n.origin=h||["middle","center"],n.restore=!0),n.from=t.from||("show"===a?{height:0,width:0,outerHeight:0,outerWidth:0}:l),n.to={height:l.height*u.y,width:l.width*u.x,outerHeight:l.outerHeight*u.y,outerWidth:l.outerWidth*u.x},n.fade&&("show"===a&&(n.from.opacity=0,n.to.opacity=1),"hide"===a&&(n.from.opacity=1,n.to.opacity=0)),s.effect(n)},e.effects.effect.puff=function(t,i){var s=e(this),n=e.effects.setMode(s,t.mode||"hide"),a="hide"===n,o=parseInt(t.percent,10)||150,r=o/100,h={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()};e.extend(t,{effect:"scale",queue:!1,fade:!0,mode:n,complete:i,percent:a?o:100,from:a?h:{height:h.height*r,width:h.width*r,outerHeight:h.outerHeight*r,outerWidth:h.outerWidth*r}}),s.effect(t)},e.effects.effect.pulsate=function(t,i){var s,n=e(this),a=e.effects.setMode(n,t.mode||"show"),o="show"===a,r="hide"===a,h=o||"hide"===a,l=2*(t.times||5)+(h?1:0),u=t.duration/l,d=0,c=n.queue(),p=c.length;for((o||!n.is(":visible"))&&(n.css("opacity",0).show(),d=1),s=1;l>s;s++)n.animate({opacity:d},u,t.easing),d=1-d;n.animate({opacity:d},u,t.easing),n.queue(function(){r&&n.hide(),i()}),p>1&&c.splice.apply(c,[1,0].concat(c.splice(p,l+1))),n.dequeue()},e.effects.effect.shake=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","height","width"],o=e.effects.setMode(n,t.mode||"effect"),r=t.direction||"left",h=t.distance||20,l=t.times||3,u=2*l+1,d=Math.round(t.duration/u),c="up"===r||"down"===r?"top":"left",p="up"===r||"left"===r,f={},m={},g={},v=n.queue(),y=v.length;for(e.effects.save(n,a),n.show(),e.effects.createWrapper(n),f[c]=(p?"-=":"+=")+h,m[c]=(p?"+=":"-=")+2*h,g[c]=(p?"-=":"+=")+2*h,n.animate(f,d,t.easing),s=1;l>s;s++)n.animate(m,d,t.easing).animate(g,d,t.easing);n.animate(m,d,t.easing).animate(f,d/2,t.easing).queue(function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}),y>1&&v.splice.apply(v,[1,0].concat(v.splice(y,u+1))),n.dequeue()},e.effects.effect.slide=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","width","height"],o=e.effects.setMode(n,t.mode||"show"),r="show"===o,h=t.direction||"left",l="up"===h||"down"===h?"top":"left",u="up"===h||"left"===h,d={};e.effects.save(n,a),n.show(),s=t.distance||n["top"===l?"outerHeight":"outerWidth"](!0),e.effects.createWrapper(n).css({overflow:"hidden"}),r&&n.css(l,u?isNaN(s)?"-"+s:-s:s),d[l]=(r?u?"+=":"-=":u?"-=":"+=")+s,n.animate(d,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}})},e.effects.effect.transfer=function(t,i){var s=e(this),n=e(t.to),a="fixed"===n.css("position"),o=e("body"),r=a?o.scrollTop():0,h=a?o.scrollLeft():0,l=n.offset(),u={top:l.top-r,left:l.left-h,height:n.innerHeight(),width:n.innerWidth()},d=s.offset(),c=e("
").appendTo(document.body).addClass(t.className).css({top:d.top-r,left:d.left-h,height:s.innerHeight(),width:s.innerWidth(),position:a?"fixed":"absolute"}).animate(u,t.duration,t.easing,function(){c.remove(),i()})}});
\ No newline at end of file
diff --git a/template/megp/js/recovery.js b/template/megp/js/recovery.js
new file mode 100644
index 0000000..bde155e
--- /dev/null
+++ b/template/megp/js/recovery.js
@@ -0,0 +1,32 @@
+$('#recovery').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ {
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить",
+ }]
+ );
+
+ document.getElementById("captcha_img").src = home+'user/section/recovery/captcha?'+Math.random();
+
+ $('#captcha').val('');
+ }
+
+ if(i == 's')
+ bootbox.dialog('Внимание '+val,
+ [{
+ "label" : "Продолжить",
+ callback: function()
+ {
+ location.href="http://"+data['mail'];
+ }
+ }]
+ );
+ });
+ }
+});
\ No newline at end of file
diff --git a/template/megp/js/servers.js b/template/megp/js/servers.js
new file mode 100644
index 0000000..42a834c
--- /dev/null
+++ b/template/megp/js/servers.js
@@ -0,0 +1,42 @@
+// Обновление информации сервера
+function update_info(id, go = false)
+{
+ if(wait[id] == true)
+ {
+ if(go)
+ setTimeout(function() {update_info(id, true)}, 3000);
+
+ return false;
+ }
+
+ $.getJSON(home+'servers/section/scan/id/'+id+'/mon', function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'time_end' && $('#time_end_'+id).html() != val && val != '')
+ $('#time_end_'+id).html(val);
+
+ if(i == 'name' && $('#name_'+id).html() != val)
+ $('#name_'+id).html(val);
+
+ if(i == 'status' && $('#status_'+id).html() != val)
+ $('#status_'+id).html(val);
+
+ if(i == 'online' && $('#online_'+id).html() != val)
+ $('#online_'+id).html(val);
+ });
+
+ if(go)
+ setTimeout(function() {update_info(id, true)}, 2000);
+ });
+}
+
+// Проверка статуса сервера
+function update_status(id, go = false)
+{
+ $.get(home+'servers/section/scan/id/'+id+'/status', function(data)
+ {
+ if(go)
+ setTimeout(function() {update_status(id, true)}, 2000);
+ });
+}
\ No newline at end of file
diff --git a/template/megp/js/servers/boost.js b/template/megp/js/servers/boost.js
new file mode 100644
index 0000000..6df7e83
--- /dev/null
+++ b/template/megp/js/servers/boost.js
@@ -0,0 +1,61 @@
+function buy_boost(boost)
+{
+ switch(boost)
+ {
+ case 'turboboost':
+ site = 'https://turbo-boost.ru/';
+ break;
+
+ case 'csboost':
+ site = 'http://csboost.net/';
+ break;
+
+ case 'zmcs':
+ site = 'http://zmcs.ru/';
+ break;
+
+ case 'vipms':
+ site = 'http://vipms-boost.ru/';
+ }
+
+ $.ajax({
+ url: home+'servers/id/'+server+'/section/boost/site/'+boost+'/service/'+$('#period_'+boost).val()+'/go',
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e' && val != '')
+ {
+ bootbox.dialog(val,
+ [{
+ "label" : "Продолжить",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+ }
+
+ if(i == 's')
+ {
+ bootbox.dialog('Вы успешно купили раскрутку.',
+ [{
+ "label" : "Продолжить",
+ "class" : "btn-small btn-primary",
+ callback: function() {
+ location.href = home+'servers/id/'+server;
+ }
+ },{
+ "label" : "Мониторинг",
+ "class" : "btn-small btn-primary",
+ callback: function() {
+ location.href = site;
+ }
+ }]
+ );
+ }
+ });
+ }
+ });
+
+ return false;
+}
\ No newline at end of file
diff --git a/template/megp/js/servers/console.js b/template/megp/js/servers/console.js
new file mode 100644
index 0000000..264722e
--- /dev/null
+++ b/template/megp/js/servers/console.js
@@ -0,0 +1,93 @@
+$('#form_console').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ {
+ if(!update)
+ update_switch();
+
+ update_console();
+ }
+ });
+ }
+});
+
+function set_command(command)
+{
+ $.ajax({
+ type: 'POST',
+ url: '/servers/section/console/id/'+server+'/go',
+ data: 'command='+command,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ {
+ if(!update)
+ update_switch();
+
+ update_console();
+ }
+ });
+ }
+ });
+}
+
+var update = true;
+
+function update_console(go = false)
+{
+
+ if(!update)
+ return false;
+
+ $.get(home+"servers/section/console/go/1/id/"+server, function(data)
+ {
+ var console = document.getElementById("console");
+
+ if(data)
+ {
+ console.innerHTML = data
+ console.scrollTop = console.scrollHeight;
+ }
+
+ if(go && update)
+ setTimeout(function() {update_console(true)}, 2500);
+ });
+}
+
+function update_switch()
+{
+ if($('#console_update').html() == ' ')
+ {
+ $('#console_update').html(' ');
+
+ update = false;
+ }else{
+ $('#console_update').html(' ');
+
+ update = true;
+ update_console();
+ }
+}
diff --git a/template/megp/js/servers/index.js b/template/megp/js/servers/index.js
new file mode 100644
index 0000000..fbc31ca
--- /dev/null
+++ b/template/megp/js/servers/index.js
@@ -0,0 +1,302 @@
+// Запуск сервера
+function server_start(id)
+{
+ wait = true;
+
+ $('#status').html('Выполняется...');
+
+ $.getJSON(home+'servers/section/action/id/'+id+'/action/start',
+ function(data)
+ {
+ wait = false;
+
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ update_info(id, false);
+ update_status(id, false);
+ update_resources(id, false);
+ });
+ });
+}
+
+// Перезапуск сервера
+function server_restart(id)
+{
+ wait = true;
+
+ $('#status').html('Выполняется...');
+
+ $.getJSON(home+'servers/section/action/id/'+id+'/action/restart',
+ function(data)
+ {
+ wait = false;
+
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ update_info(id, false);
+ update_status(id, false);
+ update_resources(id, false);
+ });
+ });
+}
+
+// Выключение сервера
+function server_stop(id)
+{
+ wait = true;
+
+ $('#status').html('Выполняется...');
+
+ $.getJSON(home+'servers/section/action/id/'+id+'/action/stop',
+ function(data)
+ {
+ wait = false;
+
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ update_info(id, false);
+ update_status(id, false);
+ update_resources(id, false);
+ });
+ });
+}
+
+// Смена карты (получение списка)
+function server_change(id)
+{
+ if($('#maps_list').html() != '')
+ {
+ $('#maps_close').css('display', 'block');
+ $('#maps_list').css('display', 'block');
+ }else{
+ $.getJSON(home+'servers/section/action/id/'+id+'/action/change',
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 'maps')
+ {
+ $('#maps_close').css('display', 'block');
+ $('#maps_list').css('display', 'block');
+ $('#maps_list').html(val);
+ }
+ });
+ });
+ }
+}
+
+// Смена карты
+function server_change_map(id, map)
+{
+ wait = true;
+
+ $('#status').html('Выполняется...');
+
+ server_change_close();
+
+ $.getJSON(home+'servers/section/action/id/'+id+'/action/change/change/'+map,
+ function(data)
+ {
+ wait = false;
+
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ update_info(id, false);
+ update_status(id, false);
+ update_resources(id, false);
+ });
+ });
+}
+
+// Скрытие списка карт
+function server_change_close()
+{
+ $('#maps_close').css('display', 'none');
+ $('#maps_list').css('display', 'none');
+}
+
+// Переустановка сервера (подтверждение)
+function server_reinstall(id)
+{
+ bootbox.dialog('Внимание После переустановки, все текущие файлы будут удалены.',
+ [{
+ "label" : "Подтвердить",
+ "class" : "btn-small btn-primary",
+ callback: function() {server_reinstall_go(id);}
+ },{
+ "label" : "Отмена",
+ "class" : "btn-small btn-primary",
+ }]
+
+ );
+}
+
+// Переустановка сервера
+function server_reinstall_go(id)
+{
+ wait = true;
+
+ $('#status').html('Выполняется...');
+
+ $.getJSON(home+'servers/section/action/id/'+id+'/action/reinstall', function(data)
+ {
+ wait = false;
+
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ update_info(id, false);
+ update_status(id, false);
+ update_resources(id, false);
+ });
+ });
+}
+
+// Обновление сервера
+function server_update(id)
+{
+ wait = true;
+
+ $('#status').html('Выполняется...');
+
+ $.getJSON(home+'servers/section/action/id/'+id+'/action/update', function(data)
+ {
+ wait = false;
+
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ update_info(id, false);
+ update_status(id, false);
+ update_resources(id, false);
+ });
+ });
+}
+
+// Обновление информации сервера
+function update_info(id, go)
+{
+ if(wait)
+ {
+ if(go)
+ setTimeout(function() {update_info(id, true)}, 3000);
+
+ return false;
+ }
+
+ $.getJSON(home+'servers/section/scan/id/'+id+'/fmon', function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'time_end' && $('#time_end').html() != val && val != '')
+ $('#time_end').html(val);
+
+ if(i == 'name' && $('#name').html() != val)
+ $('#name').html(val);
+
+ if(i == 'status' && $('#istatus').html() != val)
+ $('#istatus').html(val);
+
+ if(i == 'online' && $('#online').html() != val)
+ $('#online').html(val);
+
+ if(i == 'buttons' && $('#buttons').html() != val)
+ $('#buttons').html(val);
+
+ if(i == 'players' && $('#players').html() != val)
+ $('#players').html(val);
+ });
+
+ if(go)
+ setTimeout(function() {update_info(id, true)}, 2500);
+ });
+}
+
+// Обновление информации нагрузки
+function update_resources(id, go)
+{
+ $.getJSON(home+'servers/section/scan/id/'+id+'/resources', function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'usr')
+ $('#usr_load').html(val+'%');
+
+ if(i == 'cpu')
+ $('#cpu_load').html(val+'%');
+
+ if(i == 'ram')
+ $('#ram_load').html(val+'%');
+
+ if(i == 'hdd')
+ $('#hdd_load').html(val+'%');
+ });
+
+ if(go)
+ setTimeout(function() {update_resources(id)}, 5000);
+ });
+}
+
+// Проверка статуса сервера
+function update_status(id, go)
+{
+ $.get(home+'servers/section/scan/id/'+id+'/status', function(data)
+ {
+ if(go)
+ setTimeout(function() {update_status(id, true)}, 5000);
+ });
+}
\ No newline at end of file
diff --git a/template/megp/js/servers/settings.js b/template/megp/js/servers/settings.js
new file mode 100644
index 0000000..d58fcf2
--- /dev/null
+++ b/template/megp/js/servers/settings.js
@@ -0,0 +1,60 @@
+$('#form').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ bootbox.dialog('Внимание Внесенные изменения сохранены.',
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+ });
+ }
+});
+
+function repack(id)
+{
+ $.getJSON(home+'servers/id/'+id+'/section/settings/subsection/pack/pack/'+$('#packs').val(), function(arr)
+ {
+ $.each(arr, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить",
+ }]
+ );
+ if(i == 's')
+ location.reload();
+ });
+ });
+}
+
+function antiddos(id)
+{
+ $.getJSON(home+'servers/id/'+id+'/section/settings/subsection/antiddos/type/'+$('#rules').val()+'/go', function(arr)
+ {
+ $.each(arr, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить",
+ }]
+ );
+ if(i == 's')
+ location.reload();
+ });
+ });
+}
\ No newline at end of file
diff --git a/template/megp/js/servers/start.js b/template/megp/js/servers/start.js
new file mode 100644
index 0000000..126b35c
--- /dev/null
+++ b/template/megp/js/servers/start.js
@@ -0,0 +1,36 @@
+function settings_save(type)
+{
+ $.getJSON(home+'servers/id/'+server+'/section/settings/subsection/start/save/'+type+'/value/'+$('#'+type).val()+'/go/',
+ function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+ if(i == 's')
+ bootbox.dialog('Внимание Внесенные изменения сохранены.',
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+ });
+ });
+}
+
+function maplist()
+{
+ $.getJSON(home+'servers/id/'+server+'/section/settings/subsection/start/maps', function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'maps')
+ $('#map').html(val);
+ });
+ });
+}
\ No newline at end of file
diff --git a/template/megp/js/servers/tarif.js b/template/megp/js/servers/tarif.js
new file mode 100644
index 0000000..48680ce
--- /dev/null
+++ b/template/megp/js/servers/tarif.js
@@ -0,0 +1,200 @@
+$('#extend').ajaxForm({
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "OK",
+ "class" : "btn-small btn-primary",
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+ }
+});
+
+function upd_extend()
+{
+ time = $('#time').val();
+
+ if(time < 1)
+ {
+ $('#info_extend').html('0');
+
+ return false;
+ }
+
+ $.ajax({
+ type: 'POST',
+ url: home+'servers/id/'+server+'/section/tarif/subsection/extend',
+ data: 'time='+time,
+ dataType: 'json',
+ success: function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 's' && val != '')
+ $('#info_extend').html(val);
+ });
+
+ promo();
+ }
+ });
+}
+
+function promo()
+{
+ time = $('#time').val();
+
+ if(time < 1)
+ {
+ $('#info_extend').html('0');
+ $('#info_promo').css('display', 'none');
+ $('#info_extend').css('text-decoration', 'none');
+
+ return false;
+ }
+
+ if($('#promo').val() == '')
+ {
+ $('#info_promo').css('display', 'none');
+ $('#info_extend').css('text-decoration', 'none');
+ return false;
+ }
+
+ $.ajax({
+ type: 'POST',
+ url: home+'servers/id/'+server+'/section/tarif/subsection/extend/promo',
+ data: 'time='+time+'&promo='+$('#promo').val(),
+ dataType: 'json',
+ success: function(data)
+ {
+ $('#info_promo').css('display', 'inline-block');
+
+ if(data['e'] != undefined)
+ {
+ $('#info_promo').html(data['e']);
+ $('#info_extend').css('text-decoration', 'none');
+ }else{
+ if(data['discount'] == 1)
+ {
+ $('#info_extend').css('text-decoration', 'line-through');
+ $('#info_promo').html('Цена с учетом промо-кода: '+data['sum']+' '+data['cur']);
+ }else{
+ $('#info_extend').css('text-decoration', 'none');
+ $('#info_promo').html('Подарочные дни: '+data['days']);
+ }
+ }
+ }
+ });
+
+ return false
+}
+
+function upd_plan()
+{
+ plan = $('#tarif').val();
+
+ if(plan < 1)
+ {
+ $('#info_plan').html('');
+
+ return false;
+ }
+
+ $.getJSON(home+'servers/id/'+server+'/section/tarif/subsection/plan/plan/'+plan, function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 's' && val != '')
+ $('#info_plan').html('Сервер будет арендован до: '+val);
+ });
+ });
+}
+
+function upd_plan_go()
+{
+ plan = $('#tarif').val();
+
+ if(plan < 1)
+ return false;
+
+ $.getJSON(home+'servers/id/'+server+'/section/tarif/subsection/plan/plan/'+plan+'/go', function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить",
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+ });
+}
+
+function upd_slots()
+{
+ slots = $('#slots').val();
+
+ if(slots < 1)
+ return false;
+
+ $.getJSON(home+'servers/id/'+server+'/section/tarif/subsection/slots/slots/'+slots, function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 's')
+ $('#info_slots').html(val);
+ });
+ });
+}
+
+function upd_slots_add()
+{
+ slots = $('#slots').val();
+
+ if(slots < 1)
+ return false;
+
+ $.getJSON(home+'servers/id/'+server+'/section/tarif/subsection/slots/slots/'+slots, function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 's')
+ $('#info_slots').html(val);
+ });
+ });
+}
+
+function upd_slots_go()
+{
+ slots = $('#slots').val();
+
+ if(slots < 1)
+ return false;
+
+ $.getJSON(home+'servers/id/'+server+'/section/tarif/subsection/slots/slots/'+slots+'/go', function(data)
+ {
+ $.each(data, function(i, val)
+ {
+ if(i == 'e')
+ bootbox.dialog('Ошибка '+val,
+ [{
+ "label" : "Продолжить",
+ }]
+ );
+
+ if(i == 's')
+ location.reload();
+ });
+ });
+}
\ No newline at end of file
diff --git a/template/megp/logs/list.html b/template/megp/logs/list.html
new file mode 100644
index 0000000..aa41770
--- /dev/null
+++ b/template/megp/logs/list.html
@@ -0,0 +1,6 @@
+
+
+ Операция совершена: [date]
+ [text]
+
+
\ No newline at end of file
diff --git a/template/megp/noaccess.html b/template/megp/noaccess.html
new file mode 100644
index 0000000..349c03a
--- /dev/null
+++ b/template/megp/noaccess.html
@@ -0,0 +1,3 @@
+
+
Доступ к данному разделу во время [status] услуги недоступен.
+
\ No newline at end of file
diff --git a/template/megp/out.html b/template/megp/out.html
new file mode 100644
index 0000000..028b9b9
--- /dev/null
+++ b/template/megp/out.html
@@ -0,0 +1,146 @@
+
+
+
+ EGP Mobile
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Загрузка
+
Пожалуйста подождите.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Back to top
+
+
+
\ No newline at end of file
diff --git a/template/megp/overdue.html b/template/megp/overdue.html
new file mode 100644
index 0000000..9424af3
--- /dev/null
+++ b/template/megp/overdue.html
@@ -0,0 +1,3 @@
+
+
Доступ к данному разделу заблокирован, необходимо продлить аренду услуги.
+
\ No newline at end of file
diff --git a/template/megp/pages.html b/template/megp/pages.html
new file mode 100644
index 0000000..e69de29
diff --git a/template/megp/sections/help/close.html b/template/megp/sections/help/close.html
new file mode 100644
index 0000000..031381d
--- /dev/null
+++ b/template/megp/sections/help/close.html
@@ -0,0 +1,9 @@
+
+
+[question]
+
+
\ No newline at end of file
diff --git a/template/megp/sections/help/close/question.html b/template/megp/sections/help/close/question.html
new file mode 100644
index 0000000..e632ba7
--- /dev/null
+++ b/template/megp/sections/help/close/question.html
@@ -0,0 +1 @@
+ [name]
\ No newline at end of file
diff --git a/template/megp/sections/help/create.html b/template/megp/sections/help/create.html
new file mode 100644
index 0000000..0ed0dc4
--- /dev/null
+++ b/template/megp/sections/help/create.html
@@ -0,0 +1,24 @@
+
+
+
+ Услуга
+
+ Выберете услугу связанную с вопросом
+ Вопрос не связан с конкретной услугой
+ [services]
+
+ Заголовок
+
+ Сообщение
+
+ Создать
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/help/dialog.html b/template/megp/sections/help/dialog.html
new file mode 100644
index 0000000..723a61b
--- /dev/null
+++ b/template/megp/sections/help/dialog.html
@@ -0,0 +1,35 @@
+
+
+
Услуга: [service]
+
Создан: [date]
+
Статус: [status]
+|open|закрыть |_open|
+|close|открыть |_close|
+
+
+|open|
+
+ Сообщение
+
+ Отправить
+
+
+|_open|
+
+
[dialog]
+
+
+
+
+
+
diff --git a/template/megp/sections/help/dialog/attachment.html b/template/megp/sections/help/dialog/attachment.html
new file mode 100644
index 0000000..1845df4
--- /dev/null
+++ b/template/megp/sections/help/dialog/attachment.html
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/template/megp/sections/help/dialog/msg.html b/template/megp/sections/help/dialog/msg.html
new file mode 100644
index 0000000..7831ab3
--- /dev/null
+++ b/template/megp/sections/help/dialog/msg.html
@@ -0,0 +1,10 @@
+
+[text]
+|img|
+
[img]
+
+|_img|
+
[time]
\ No newline at end of file
diff --git a/template/megp/sections/help/noaccess.html b/template/megp/sections/help/noaccess.html
new file mode 100644
index 0000000..8809f3c
--- /dev/null
+++ b/template/megp/sections/help/noaccess.html
@@ -0,0 +1,4 @@
+
+
Вы нарушили права хостинга.
+
Доступ к данному разделу заблокирован.
+
\ No newline at end of file
diff --git a/template/megp/sections/help/notice.html b/template/megp/sections/help/notice.html
new file mode 100644
index 0000000..e69de29
diff --git a/template/megp/sections/help/open.html b/template/megp/sections/help/open.html
new file mode 100644
index 0000000..df29f7f
--- /dev/null
+++ b/template/megp/sections/help/open.html
@@ -0,0 +1,9 @@
+
+
+[question]
+
+
\ No newline at end of file
diff --git a/template/megp/sections/help/open/question.html b/template/megp/sections/help/open/question.html
new file mode 100644
index 0000000..e632ba7
--- /dev/null
+++ b/template/megp/sections/help/open/question.html
@@ -0,0 +1 @@
+
[name]
\ No newline at end of file
diff --git a/template/megp/sections/news/index.html b/template/megp/sections/news/index.html
new file mode 100644
index 0000000..4cb46d7
--- /dev/null
+++ b/template/megp/sections/news/index.html
@@ -0,0 +1,7 @@
+
+
Последние новости
+
+
+
+
+[list]
\ No newline at end of file
diff --git a/template/megp/sections/news/list.html b/template/megp/sections/news/list.html
new file mode 100644
index 0000000..e4d2bba
--- /dev/null
+++ b/template/megp/sections/news/list.html
@@ -0,0 +1,5 @@
+
[name]
+
[text]
+
[date] Подробнее
+
+
\ No newline at end of file
diff --git a/template/megp/sections/news/news.html b/template/megp/sections/news/news.html
new file mode 100644
index 0000000..fe7c610
--- /dev/null
+++ b/template/megp/sections/news/news.html
@@ -0,0 +1,8 @@
+
+
[text]
+
[date]
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/buttons/change.html b/template/megp/sections/servers/buttons/change.html
new file mode 100644
index 0000000..a1157e3
--- /dev/null
+++ b/template/megp/sections/servers/buttons/change.html
@@ -0,0 +1 @@
+
Поменять карту
\ No newline at end of file
diff --git a/template/megp/sections/servers/buttons/other.html b/template/megp/sections/servers/buttons/other.html
new file mode 100644
index 0000000..842e128
--- /dev/null
+++ b/template/megp/sections/servers/buttons/other.html
@@ -0,0 +1 @@
+
Включить
Обновить
Переустановить
\ No newline at end of file
diff --git a/template/megp/sections/servers/buttons/reinstall.html b/template/megp/sections/servers/buttons/reinstall.html
new file mode 100644
index 0000000..d4eacf1
--- /dev/null
+++ b/template/megp/sections/servers/buttons/reinstall.html
@@ -0,0 +1 @@
+
Переустановить
\ No newline at end of file
diff --git a/template/megp/sections/servers/buttons/restart.html b/template/megp/sections/servers/buttons/restart.html
new file mode 100644
index 0000000..22ff37c
--- /dev/null
+++ b/template/megp/sections/servers/buttons/restart.html
@@ -0,0 +1 @@
+
Перезапустить
\ No newline at end of file
diff --git a/template/megp/sections/servers/buttons/start.html b/template/megp/sections/servers/buttons/start.html
new file mode 100644
index 0000000..ffda308
--- /dev/null
+++ b/template/megp/sections/servers/buttons/start.html
@@ -0,0 +1 @@
+
Включить
\ No newline at end of file
diff --git a/template/megp/sections/servers/buttons/stop.html b/template/megp/sections/servers/buttons/stop.html
new file mode 100644
index 0000000..7583c75
--- /dev/null
+++ b/template/megp/sections/servers/buttons/stop.html
@@ -0,0 +1 @@
+
Выключить
\ No newline at end of file
diff --git a/template/megp/sections/servers/buttons/update.html b/template/megp/sections/servers/buttons/update.html
new file mode 100644
index 0000000..a7a0e86
--- /dev/null
+++ b/template/megp/sections/servers/buttons/update.html
@@ -0,0 +1 @@
+
Обновить
\ No newline at end of file
diff --git a/template/megp/sections/servers/crmp/console.html b/template/megp/sections/servers/crmp/console.html
new file mode 100644
index 0000000..b1be99d
--- /dev/null
+++ b/template/megp/sections/servers/crmp/console.html
@@ -0,0 +1,27 @@
+
+
+
+
Игровой сервер выключен.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/crmp/gmenu.html b/template/megp/sections/servers/crmp/gmenu.html
new file mode 100644
index 0000000..2ab78d0
--- /dev/null
+++ b/template/megp/sections/servers/crmp/gmenu.html
@@ -0,0 +1 @@
+[notice]
\ No newline at end of file
diff --git a/template/megp/sections/servers/crmp/index.html b/template/megp/sections/servers/crmp/index.html
new file mode 100644
index 0000000..5930075
--- /dev/null
+++ b/template/megp/sections/servers/crmp/index.html
@@ -0,0 +1,73 @@
+
+
Управление сервером
+
+
+
+
+
+
+
Адрес: [address]
+
Онлайн: [online] / [slots]
+
[status]
+
[time_end]
+
+
+
+
[btn]
+
+
x
+
+
+
+
+
+
+
+
+
Игра: [game]
+
Локация: [unit]
+
Тариф: [tarif]
+
Сборка: [pack]
+
Аренда до: [time]
+
Создан: [date]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/crmp/settings.html b/template/megp/sections/servers/crmp/settings.html
new file mode 100644
index 0000000..a9e8a0a
--- /dev/null
+++ b/template/megp/sections/servers/crmp/settings.html
@@ -0,0 +1,22 @@
+
+
Настройки сервера
+
+
+
+
+
+
+
Параметры запуска игрового сервера
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/crmp/settings/start.html b/template/megp/sections/servers/crmp/settings/start.html
new file mode 100644
index 0000000..2b65755
--- /dev/null
+++ b/template/megp/sections/servers/crmp/settings/start.html
@@ -0,0 +1,26 @@
+
+
Параметры запуска сервера
+
+
+
+
+
+
+
Количество слот
+
+
+
+
[slots]
+
+
+
+
Авторестарт при зависании
+
+
+
+
[autorestart]
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/crmp/vmenu.html b/template/megp/sections/servers/crmp/vmenu.html
new file mode 100644
index 0000000..cc775b3
--- /dev/null
+++ b/template/megp/sections/servers/crmp/vmenu.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Основное
+
+
+ |console_use|
+
+
+ Консоль
+
+
+ |_console_use|
+
+
+ Настройки
+
+
+
+
+ Тариф
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cs/boost.html b/template/megp/sections/servers/cs/boost.html
new file mode 100644
index 0000000..d8af44d
--- /dev/null
+++ b/template/megp/sections/servers/cs/boost.html
@@ -0,0 +1,26 @@
+
+
Раскрутка сервера
+
+
+
+
+
+
Раскрутка от zmcs.ru
+
+
+
+
Период К оплате: 0 [cur]
+
+ 1 круг / 10 руб.
+ 3 круга / 30 руб.
+ 5 кругов / 50 руб.
+ 15 кругов / 150 руб.
+
+Проверить игровой сервер в
мониторинге
+
Купить раскрутку
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cs/console.html b/template/megp/sections/servers/cs/console.html
new file mode 100644
index 0000000..cf6461c
--- /dev/null
+++ b/template/megp/sections/servers/cs/console.html
@@ -0,0 +1,48 @@
+
+
Интерактивная консоль
+
+
+
+
+
+
Игровой сервер выключен.
+
+
+
+
+
+
+ status
+ stats
+ amxx list
+ meta list
+ amx modules
+ amx_reloadadmins
+ amxx version
+ fast DL
+ version
+ Очистка консоли
+
+
+
+
+
+
+ Обновление
+
+ Отправить
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cs/gmenu.html b/template/megp/sections/servers/cs/gmenu.html
new file mode 100644
index 0000000..2ab78d0
--- /dev/null
+++ b/template/megp/sections/servers/cs/gmenu.html
@@ -0,0 +1 @@
+[notice]
\ No newline at end of file
diff --git a/template/megp/sections/servers/cs/index.html b/template/megp/sections/servers/cs/index.html
new file mode 100644
index 0000000..5930075
--- /dev/null
+++ b/template/megp/sections/servers/cs/index.html
@@ -0,0 +1,73 @@
+
+
Управление сервером
+
+
+
+
+
+
+
Адрес: [address]
+
Онлайн: [online] / [slots]
+
[status]
+
[time_end]
+
+
+
+
[btn]
+
+
x
+
+
+
+
+
+
+
+
+
Игра: [game]
+
Локация: [unit]
+
Тариф: [tarif]
+
Сборка: [pack]
+
Аренда до: [time]
+
Создан: [date]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cs/settings.html b/template/megp/sections/servers/cs/settings.html
new file mode 100644
index 0000000..abe23fc
--- /dev/null
+++ b/template/megp/sections/servers/cs/settings.html
@@ -0,0 +1,28 @@
+
+
Настройки сервера
+
+
+
+
+
+
+
Параметры запуска игрового сервера
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cs/settings/start.html b/template/megp/sections/servers/cs/settings/start.html
new file mode 100644
index 0000000..a97d32e
--- /dev/null
+++ b/template/megp/sections/servers/cs/settings/start.html
@@ -0,0 +1,71 @@
+
+
Параметры запуска сервера
+
+
+
+
+
+
+
Стартовая карта
+
+
+
+
[map]
+
+
+
+
Количество слот
+
+
+
+
[slots]
+
+
+
[vac]
+
+
+
+
Быстрая скачка (FastDL)
+
+
+
+
[fastdl]
+
+
+
+
Авторестарт при зависании
+
+
+
+
[autorestart]
+|fps|
+
+
[fps]
+|_fps|
+|pingboost|
+
+
+
Значение PINGBOOST
+
+
+
+
[pingboost]
+|_pingboost|
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cs/vmenu.html b/template/megp/sections/servers/cs/vmenu.html
new file mode 100644
index 0000000..ad90bc0
--- /dev/null
+++ b/template/megp/sections/servers/cs/vmenu.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+ Основное
+
+
+ |console_use|
+
+
+ Консоль
+
+
+ |_console_use|
+
+
+ Настройки
+
+
+
+
+ Тариф
+
+
+
+
+ Boost
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/csgo/console.html b/template/megp/sections/servers/csgo/console.html
new file mode 100644
index 0000000..8c06dbe
--- /dev/null
+++ b/template/megp/sections/servers/csgo/console.html
@@ -0,0 +1,45 @@
+
+
Интерактивная консоль
+
+
+
+
+
+
Игровой сервер выключен.
+
+
+
+
+
+
+ status
+ stats
+ sm plugins list
+ meta list
+ sm_reloadadmins
+ sm version
+ Очистка консоли
+
+
+
+
+
+
+ Обновление
+
+ Отправить
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/csgo/gmenu.html b/template/megp/sections/servers/csgo/gmenu.html
new file mode 100644
index 0000000..2ab78d0
--- /dev/null
+++ b/template/megp/sections/servers/csgo/gmenu.html
@@ -0,0 +1 @@
+[notice]
\ No newline at end of file
diff --git a/template/megp/sections/servers/csgo/index.html b/template/megp/sections/servers/csgo/index.html
new file mode 100644
index 0000000..5930075
--- /dev/null
+++ b/template/megp/sections/servers/csgo/index.html
@@ -0,0 +1,73 @@
+
+
Управление сервером
+
+
+
+
+
+
+
Адрес: [address]
+
Онлайн: [online] / [slots]
+
[status]
+
[time_end]
+
+
+
+
[btn]
+
+
x
+
+
+
+
+
+
+
+
+
Игра: [game]
+
Локация: [unit]
+
Тариф: [tarif]
+
Сборка: [pack]
+
Аренда до: [time]
+
Создан: [date]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/csgo/settings.html b/template/megp/sections/servers/csgo/settings.html
new file mode 100644
index 0000000..abe23fc
--- /dev/null
+++ b/template/megp/sections/servers/csgo/settings.html
@@ -0,0 +1,28 @@
+
+
Настройки сервера
+
+
+
+
+
+
+
Параметры запуска игрового сервера
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/csgo/settings/start.html b/template/megp/sections/servers/csgo/settings/start.html
new file mode 100644
index 0000000..e78737b
--- /dev/null
+++ b/template/megp/sections/servers/csgo/settings/start.html
@@ -0,0 +1,62 @@
+
+
Параметры запуска сервера
+
+
+
+
+
+
+
Стартовая карта
+
+
+
+
[map]
+
+
+
+
Количество слот
+
+
+
+
[slots]
+
+
+
[vac]
+
+
+
+
Быстрая скачка (FastDL)
+
+
+
+
[fastdl]
+
+
+
+
Авторестарт при зависании
+
+
+
+
[autorestart]
+|tickrate|
+
+
+
Значение TICKRATE
+
+
+
+
[tickrate]
+|_tickrate|
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/csgo/vmenu.html b/template/megp/sections/servers/csgo/vmenu.html
new file mode 100644
index 0000000..cc775b3
--- /dev/null
+++ b/template/megp/sections/servers/csgo/vmenu.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Основное
+
+
+ |console_use|
+
+
+ Консоль
+
+
+ |_console_use|
+
+
+ Настройки
+
+
+
+
+ Тариф
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/css/console.html b/template/megp/sections/servers/css/console.html
new file mode 100644
index 0000000..8c06dbe
--- /dev/null
+++ b/template/megp/sections/servers/css/console.html
@@ -0,0 +1,45 @@
+
+
Интерактивная консоль
+
+
+
+
+
+
Игровой сервер выключен.
+
+
+
+
+
+
+ status
+ stats
+ sm plugins list
+ meta list
+ sm_reloadadmins
+ sm version
+ Очистка консоли
+
+
+
+
+
+
+ Обновление
+
+ Отправить
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/css/gmenu.html b/template/megp/sections/servers/css/gmenu.html
new file mode 100644
index 0000000..2ab78d0
--- /dev/null
+++ b/template/megp/sections/servers/css/gmenu.html
@@ -0,0 +1 @@
+[notice]
\ No newline at end of file
diff --git a/template/megp/sections/servers/css/index.html b/template/megp/sections/servers/css/index.html
new file mode 100644
index 0000000..5930075
--- /dev/null
+++ b/template/megp/sections/servers/css/index.html
@@ -0,0 +1,73 @@
+
+
Управление сервером
+
+
+
+
+
+
+
Адрес: [address]
+
Онлайн: [online] / [slots]
+
[status]
+
[time_end]
+
+
+
+
[btn]
+
+
x
+
+
+
+
+
+
+
+
+
Игра: [game]
+
Локация: [unit]
+
Тариф: [tarif]
+
Сборка: [pack]
+
Аренда до: [time]
+
Создан: [date]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/css/settings.html b/template/megp/sections/servers/css/settings.html
new file mode 100644
index 0000000..abe23fc
--- /dev/null
+++ b/template/megp/sections/servers/css/settings.html
@@ -0,0 +1,28 @@
+
+
Настройки сервера
+
+
+
+
+
+
+
Параметры запуска игрового сервера
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/css/settings/start.html b/template/megp/sections/servers/css/settings/start.html
new file mode 100644
index 0000000..e78737b
--- /dev/null
+++ b/template/megp/sections/servers/css/settings/start.html
@@ -0,0 +1,62 @@
+
+
Параметры запуска сервера
+
+
+
+
+
+
+
Стартовая карта
+
+
+
+
[map]
+
+
+
+
Количество слот
+
+
+
+
[slots]
+
+
+
[vac]
+
+
+
+
Быстрая скачка (FastDL)
+
+
+
+
[fastdl]
+
+
+
+
Авторестарт при зависании
+
+
+
+
[autorestart]
+|tickrate|
+
+
+
Значение TICKRATE
+
+
+
+
[tickrate]
+|_tickrate|
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/css/vmenu.html b/template/megp/sections/servers/css/vmenu.html
new file mode 100644
index 0000000..cc775b3
--- /dev/null
+++ b/template/megp/sections/servers/css/vmenu.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Основное
+
+
+ |console_use|
+
+
+ Консоль
+
+
+ |_console_use|
+
+
+ Настройки
+
+
+
+
+ Тариф
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cssold/console.html b/template/megp/sections/servers/cssold/console.html
new file mode 100644
index 0000000..8c06dbe
--- /dev/null
+++ b/template/megp/sections/servers/cssold/console.html
@@ -0,0 +1,45 @@
+
+
Интерактивная консоль
+
+
+
+
+
+
Игровой сервер выключен.
+
+
+
+
+
+
+ status
+ stats
+ sm plugins list
+ meta list
+ sm_reloadadmins
+ sm version
+ Очистка консоли
+
+
+
+
+
+
+ Обновление
+
+ Отправить
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cssold/gmenu.html b/template/megp/sections/servers/cssold/gmenu.html
new file mode 100644
index 0000000..2ab78d0
--- /dev/null
+++ b/template/megp/sections/servers/cssold/gmenu.html
@@ -0,0 +1 @@
+[notice]
\ No newline at end of file
diff --git a/template/megp/sections/servers/cssold/index.html b/template/megp/sections/servers/cssold/index.html
new file mode 100644
index 0000000..5930075
--- /dev/null
+++ b/template/megp/sections/servers/cssold/index.html
@@ -0,0 +1,73 @@
+
+
Управление сервером
+
+
+
+
+
+
+
Адрес: [address]
+
Онлайн: [online] / [slots]
+
[status]
+
[time_end]
+
+
+
+
[btn]
+
+
x
+
+
+
+
+
+
+
+
+
Игра: [game]
+
Локация: [unit]
+
Тариф: [tarif]
+
Сборка: [pack]
+
Аренда до: [time]
+
Создан: [date]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cssold/settings.html b/template/megp/sections/servers/cssold/settings.html
new file mode 100644
index 0000000..abe23fc
--- /dev/null
+++ b/template/megp/sections/servers/cssold/settings.html
@@ -0,0 +1,28 @@
+
+
Настройки сервера
+
+
+
+
+
+
+
Параметры запуска игрового сервера
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cssold/settings/start.html b/template/megp/sections/servers/cssold/settings/start.html
new file mode 100644
index 0000000..221ce48
--- /dev/null
+++ b/template/megp/sections/servers/cssold/settings/start.html
@@ -0,0 +1,71 @@
+
+
Параметры запуска сервера
+
+
+
+
+
+
+
Стартовая карта
+
+
+
+
[map]
+
+
+
+
Количество слот
+
+
+
+
[slots]
+
+
+
[vac]
+
+
+
+
Быстрая скачка (FastDL)
+
+
+
+
[fastdl]
+
+
+
+
Авторестарт при зависании
+
+
+
+
[autorestart]
+|tickrate|
+
+
+
Значение TICKRATE
+
+
+
+
[tickrate]
+|_tickrate|
+|fps|
+
+
[fps]
+|_fps|
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/cssold/vmenu.html b/template/megp/sections/servers/cssold/vmenu.html
new file mode 100644
index 0000000..cc775b3
--- /dev/null
+++ b/template/megp/sections/servers/cssold/vmenu.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Основное
+
+
+ |console_use|
+
+
+ Консоль
+
+
+ |_console_use|
+
+
+ Настройки
+
+
+
+
+ Тариф
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/games/change_list.html b/template/megp/sections/servers/games/change_list.html
new file mode 100644
index 0000000..a064d55
--- /dev/null
+++ b/template/megp/sections/servers/games/change_list.html
@@ -0,0 +1 @@
+
[name]
\ No newline at end of file
diff --git a/template/megp/sections/servers/games/tarif.html b/template/megp/sections/servers/games/tarif.html
new file mode 100644
index 0000000..66934cd
--- /dev/null
+++ b/template/megp/sections/servers/games/tarif.html
@@ -0,0 +1,10 @@
+
+
+
+
+
Управление арендой
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/games/tarif/extend.html b/template/megp/sections/servers/games/tarif/extend.html
new file mode 100644
index 0000000..7450101
--- /dev/null
+++ b/template/megp/sections/servers/games/tarif/extend.html
@@ -0,0 +1,20 @@
+
+
Продление аренды сервера
+
+
+
+
Слот: [slots]шт.
+
Тариф: [tarif]
+
Осталось: [time]
+
[info]
+
+
+ Период К оплате: 0 [cur]
+ [options]
+
+
+
+ Продлить
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/games/tarif/extend_sp.html b/template/megp/sections/servers/games/tarif/extend_sp.html
new file mode 100644
index 0000000..216c453
--- /dev/null
+++ b/template/megp/sections/servers/games/tarif/extend_sp.html
@@ -0,0 +1,30 @@
+
+
Продление аренды сервера
+
+
+
+
Слот: [slots]шт.
+
Тариф: [tarif]
+
Осталось: [time]
+
[info]
+
+
+
+
+ [date]
+ Цена за месяц: [sum] [cur]
+
+
Система оплаты работает по расчетному периоду.
+
При оплате до 15-го числа взымается плата за остаток текущего месяца.
+
При оплате после 15-го числа взымается плата за остаток текущего месяца + следующий месяц.
+
+
+
+ К оплате: 0 [cur]
+
+ Продлить
+
+
+
+
+
diff --git a/template/megp/sections/servers/games/tarif/plan.html b/template/megp/sections/servers/games/tarif/plan.html
new file mode 100644
index 0000000..0a359f7
--- /dev/null
+++ b/template/megp/sections/servers/games/tarif/plan.html
@@ -0,0 +1,15 @@
+
+
Смена тарифного плана
+
+
+
+
Текущий: [tarif] / [info]
+
+
+
План
+
[options]
+
+
Внимание : при смене тарифного плана, вычитается дополнительный день.
+
Сменить
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/games/tarif/slots.html b/template/megp/sections/servers/games/tarif/slots.html
new file mode 100644
index 0000000..4c078db
--- /dev/null
+++ b/template/megp/sections/servers/games/tarif/slots.html
@@ -0,0 +1,14 @@
+
+
Смена количества слот
+
+
+
+
Текущее количество слот: [slots]шт.
+
+
Сменить на
+
[options]
+
+
Внимание : при смене количества игровых слот, вычитается один дополнительный день аренды.
+
Сменить слоты
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/games/tarif/slots_buy.html b/template/megp/sections/servers/games/tarif/slots_buy.html
new file mode 100644
index 0000000..8b517f2
--- /dev/null
+++ b/template/megp/sections/servers/games/tarif/slots_buy.html
@@ -0,0 +1,13 @@
+
+
+
Текущее количество слот: [slots]шт.
+
+
Добавить
+
[options]
+
+
Добавить слоты
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/list.html b/template/megp/sections/servers/list.html
new file mode 100644
index 0000000..abd9a2d
--- /dev/null
+++ b/template/megp/sections/servers/list.html
@@ -0,0 +1,13 @@
+
+
+
Адрес: [address]
+
Онлайн: [online] / [slots]
+
[status]
+
[time_end]
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/mc/console.html b/template/megp/sections/servers/mc/console.html
new file mode 100644
index 0000000..1d31b99
--- /dev/null
+++ b/template/megp/sections/servers/mc/console.html
@@ -0,0 +1,34 @@
+
+
Интерактивная консоль
+
+
+
+
+
+
Игровой сервер выключен.
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/mc/gmenu.html b/template/megp/sections/servers/mc/gmenu.html
new file mode 100644
index 0000000..2ab78d0
--- /dev/null
+++ b/template/megp/sections/servers/mc/gmenu.html
@@ -0,0 +1 @@
+[notice]
\ No newline at end of file
diff --git a/template/megp/sections/servers/mc/index.html b/template/megp/sections/servers/mc/index.html
new file mode 100644
index 0000000..5930075
--- /dev/null
+++ b/template/megp/sections/servers/mc/index.html
@@ -0,0 +1,73 @@
+
+
Управление сервером
+
+
+
+
+
+
+
Адрес: [address]
+
Онлайн: [online] / [slots]
+
[status]
+
[time_end]
+
+
+
+
[btn]
+
+
x
+
+
+
+
+
+
+
+
+
Игра: [game]
+
Локация: [unit]
+
Тариф: [tarif]
+
Сборка: [pack]
+
Аренда до: [time]
+
Создан: [date]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/mc/settings.html b/template/megp/sections/servers/mc/settings.html
new file mode 100644
index 0000000..a9e8a0a
--- /dev/null
+++ b/template/megp/sections/servers/mc/settings.html
@@ -0,0 +1,22 @@
+
+
Настройки сервера
+
+
+
+
+
+
+
Параметры запуска игрового сервера
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/mc/settings/start.html b/template/megp/sections/servers/mc/settings/start.html
new file mode 100644
index 0000000..2b65755
--- /dev/null
+++ b/template/megp/sections/servers/mc/settings/start.html
@@ -0,0 +1,26 @@
+
+
Параметры запуска сервера
+
+
+
+
+
+
+
Количество слот
+
+
+
+
[slots]
+
+
+
+
Авторестарт при зависании
+
+
+
+
[autorestart]
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/mc/vmenu.html b/template/megp/sections/servers/mc/vmenu.html
new file mode 100644
index 0000000..cc775b3
--- /dev/null
+++ b/template/megp/sections/servers/mc/vmenu.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Основное
+
+
+ |console_use|
+
+
+ Консоль
+
+
+ |_console_use|
+
+
+ Настройки
+
+
+
+
+ Тариф
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/mta/console.html b/template/megp/sections/servers/mta/console.html
new file mode 100644
index 0000000..1d31b99
--- /dev/null
+++ b/template/megp/sections/servers/mta/console.html
@@ -0,0 +1,34 @@
+
+
Интерактивная консоль
+
+
+
+
+
+
Игровой сервер выключен.
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/mta/gmenu.html b/template/megp/sections/servers/mta/gmenu.html
new file mode 100644
index 0000000..2ab78d0
--- /dev/null
+++ b/template/megp/sections/servers/mta/gmenu.html
@@ -0,0 +1 @@
+[notice]
\ No newline at end of file
diff --git a/template/megp/sections/servers/mta/index.html b/template/megp/sections/servers/mta/index.html
new file mode 100644
index 0000000..5930075
--- /dev/null
+++ b/template/megp/sections/servers/mta/index.html
@@ -0,0 +1,73 @@
+
+
Управление сервером
+
+
+
+
+
+
+
Адрес: [address]
+
Онлайн: [online] / [slots]
+
[status]
+
[time_end]
+
+
+
+
[btn]
+
+
x
+
+
+
+
+
+
+
+
+
Игра: [game]
+
Локация: [unit]
+
Тариф: [tarif]
+
Сборка: [pack]
+
Аренда до: [time]
+
Создан: [date]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/mta/settings.html b/template/megp/sections/servers/mta/settings.html
new file mode 100644
index 0000000..a9e8a0a
--- /dev/null
+++ b/template/megp/sections/servers/mta/settings.html
@@ -0,0 +1,22 @@
+
+
Настройки сервера
+
+
+
+
+
+
+
Параметры запуска игрового сервера
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/mta/settings/start.html b/template/megp/sections/servers/mta/settings/start.html
new file mode 100644
index 0000000..2b65755
--- /dev/null
+++ b/template/megp/sections/servers/mta/settings/start.html
@@ -0,0 +1,26 @@
+
+
Параметры запуска сервера
+
+
+
+
+
+
+
Количество слот
+
+
+
+
[slots]
+
+
+
+
Авторестарт при зависании
+
+
+
+
[autorestart]
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/mta/vmenu.html b/template/megp/sections/servers/mta/vmenu.html
new file mode 100644
index 0000000..cc775b3
--- /dev/null
+++ b/template/megp/sections/servers/mta/vmenu.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Основное
+
+
+ |console_use|
+
+
+ Консоль
+
+
+ |_console_use|
+
+
+ Настройки
+
+
+
+
+ Тариф
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/samp/console.html b/template/megp/sections/servers/samp/console.html
new file mode 100644
index 0000000..b1be99d
--- /dev/null
+++ b/template/megp/sections/servers/samp/console.html
@@ -0,0 +1,27 @@
+
+
+
+
Игровой сервер выключен.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/samp/gmenu.html b/template/megp/sections/servers/samp/gmenu.html
new file mode 100644
index 0000000..2ab78d0
--- /dev/null
+++ b/template/megp/sections/servers/samp/gmenu.html
@@ -0,0 +1 @@
+[notice]
\ No newline at end of file
diff --git a/template/megp/sections/servers/samp/index.html b/template/megp/sections/servers/samp/index.html
new file mode 100644
index 0000000..5930075
--- /dev/null
+++ b/template/megp/sections/servers/samp/index.html
@@ -0,0 +1,73 @@
+
+
Управление сервером
+
+
+
+
+
+
+
Адрес: [address]
+
Онлайн: [online] / [slots]
+
[status]
+
[time_end]
+
+
+
+
[btn]
+
+
x
+
+
+
+
+
+
+
+
+
Игра: [game]
+
Локация: [unit]
+
Тариф: [tarif]
+
Сборка: [pack]
+
Аренда до: [time]
+
Создан: [date]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/samp/settings.html b/template/megp/sections/servers/samp/settings.html
new file mode 100644
index 0000000..a9e8a0a
--- /dev/null
+++ b/template/megp/sections/servers/samp/settings.html
@@ -0,0 +1,22 @@
+
+
Настройки сервера
+
+
+
+
+
+
+
Параметры запуска игрового сервера
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/samp/settings/start.html b/template/megp/sections/servers/samp/settings/start.html
new file mode 100644
index 0000000..2b65755
--- /dev/null
+++ b/template/megp/sections/servers/samp/settings/start.html
@@ -0,0 +1,26 @@
+
+
Параметры запуска сервера
+
+
+
+
+
+
+
Количество слот
+
+
+
+
[slots]
+
+
+
+
Авторестарт при зависании
+
+
+
+
[autorestart]
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/samp/vmenu.html b/template/megp/sections/servers/samp/vmenu.html
new file mode 100644
index 0000000..cc775b3
--- /dev/null
+++ b/template/megp/sections/servers/samp/vmenu.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Основное
+
+
+ |console_use|
+
+
+ Консоль
+
+
+ |_console_use|
+
+
+ Настройки
+
+
+
+
+ Тариф
+
+
\ No newline at end of file
diff --git a/template/megp/sections/servers/servers.html b/template/megp/sections/servers/servers.html
new file mode 100644
index 0000000..b8fe743
--- /dev/null
+++ b/template/megp/sections/servers/servers.html
@@ -0,0 +1,17 @@
+
+
Список игровых серверов
+
+
+
+
+[list]
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/user/auth.html b/template/megp/sections/user/auth.html
new file mode 100644
index 0000000..b72f4a6
--- /dev/null
+++ b/template/megp/sections/user/auth.html
@@ -0,0 +1,34 @@
+
+
+
+ Логин
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Код подтверждения
+
+
+
+
+ Авторизоваться
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/user/recovery.html b/template/megp/sections/user/recovery.html
new file mode 100644
index 0000000..1a0a694
--- /dev/null
+++ b/template/megp/sections/user/recovery.html
@@ -0,0 +1,25 @@
+
+
Восстановление доступа
+
+
+
+
+
+ Логин
+
+
+
+
+
+
+
+
+
+ Восстановить
+ Перейти к авторизации
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/user/replenish.html b/template/megp/sections/user/replenish.html
new file mode 100644
index 0000000..5b92404
--- /dev/null
+++ b/template/megp/sections/user/replenish.html
@@ -0,0 +1,53 @@
+
+
Пополнение баланса
+
+
+
+
+
+ Сумма
+
+
+
+
+ UnitPay
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/megp/sections/user/replenish/list.html b/template/megp/sections/user/replenish/list.html
new file mode 100644
index 0000000..aa41770
--- /dev/null
+++ b/template/megp/sections/user/replenish/list.html
@@ -0,0 +1,6 @@
+
+
+ Операция совершена: [date]
+ [text]
+
+
\ No newline at end of file
diff --git a/template/nav.html b/template/nav.html
new file mode 100644
index 0000000..fc34938
--- /dev/null
+++ b/template/nav.html
@@ -0,0 +1,5 @@
+|link|
+
[name]
+
+|_link|
+|!link|[name]|_!link|
\ No newline at end of file
diff --git a/template/noaccess.html b/template/noaccess.html
new file mode 100644
index 0000000..0208a8e
--- /dev/null
+++ b/template/noaccess.html
@@ -0,0 +1 @@
+
Доступ к данному разделу во время [status] услуги недоступен.
\ No newline at end of file
diff --git a/template/out.html b/template/out.html
new file mode 100644
index 0000000..8f90849
--- /dev/null
+++ b/template/out.html
@@ -0,0 +1,15 @@
+
+
+
[title]
+
+
+
+
+
+
+
+
+ [text]
+
+
+
\ No newline at end of file
diff --git a/template/overdue.html b/template/overdue.html
new file mode 100644
index 0000000..1fb407e
--- /dev/null
+++ b/template/overdue.html
@@ -0,0 +1 @@
+
Доступ к данному разделу заблокирован, необходимо продлить аренду услуги.
\ No newline at end of file
diff --git a/template/page.html b/template/page.html
new file mode 100644
index 0000000..d58f83f
--- /dev/null
+++ b/template/page.html
@@ -0,0 +1 @@
+[content]
\ No newline at end of file
diff --git a/template/pages.html b/template/pages.html
new file mode 100644
index 0000000..486c2a8
--- /dev/null
+++ b/template/pages.html
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/template/partners.html b/template/partners.html
new file mode 100644
index 0000000..2053bda
--- /dev/null
+++ b/template/partners.html
@@ -0,0 +1,88 @@
+
+
+
+
Список партнеров
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/chat/dialog.html b/template/sections/chat/dialog.html
new file mode 100644
index 0000000..cfd9097
--- /dev/null
+++ b/template/sections/chat/dialog.html
@@ -0,0 +1,75 @@
+
Онлайн чат
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/chat/messages_all.html b/template/sections/chat/messages_all.html
new file mode 100644
index 0000000..42ef380
--- /dev/null
+++ b/template/sections/chat/messages_all.html
@@ -0,0 +1,21 @@
+
\ No newline at end of file
diff --git a/template/sections/chat/sound/chat_err.wav b/template/sections/chat/sound/chat_err.wav
new file mode 100644
index 0000000..2c05ea2
Binary files /dev/null and b/template/sections/chat/sound/chat_err.wav differ
diff --git a/template/sections/chat/sound/chat_notice.wav b/template/sections/chat/sound/chat_notice.wav
new file mode 100644
index 0000000..03677e5
Binary files /dev/null and b/template/sections/chat/sound/chat_notice.wav differ
diff --git a/template/sections/chat/sound/chat_send.wav b/template/sections/chat/sound/chat_send.wav
new file mode 100644
index 0000000..b8ffa41
Binary files /dev/null and b/template/sections/chat/sound/chat_send.wav differ
diff --git a/template/sections/check/check.html b/template/sections/check/check.html
new file mode 100644
index 0000000..f647ca3
--- /dev/null
+++ b/template/sections/check/check.html
@@ -0,0 +1,45 @@
+
Проверка плагинов на плохие команды
+
+|!auth|
+
+
Что бы проверить плагин, вам необходимо авторизоваться .
+
+|_!auth|
+|auth|
+
+
+
+
+
Результаты проверки
+
+|_auth|
+
+
Описание раздела
+
+
Многие из вас, администраторы игровых серверов Counter Strike 1.6 знают что устанавливая плагин без исходника , вы подвергаетесь риску свой игровой сервер, и по этому, устанавливать плагины на свой сервер нужно только при наличие файла с расширением - sma.
+
+
+Будьте очень внимательны когда скачиваете плагины, и обязательно проверяйте наличие исходного кода! Иначе вы можете подвергнуться взлому, так как плагин без исходника может содержать вредоносный код, который вы попросту не увидите, и увидеть его будет невозможно.
+
+
В свою очередь мы разработали для Вас удобный сервис, он поможет проверить исходник плагина на наличие в нем так называемых бэкдоров .
+
В любое время вы можете воспользоваться данным сервисом для проверки исходников с расширением SMA и быть уверены, что ваш сервер будет в безопасности.
+
+
+
\ No newline at end of file
diff --git a/template/sections/check/list.html b/template/sections/check/list.html
new file mode 100644
index 0000000..9521043
--- /dev/null
+++ b/template/sections/check/list.html
@@ -0,0 +1,4 @@
+
+ [n]
+ [text]
+
\ No newline at end of file
diff --git a/template/sections/help/close.html b/template/sections/help/close.html
new file mode 100644
index 0000000..baeb7cd
--- /dev/null
+++ b/template/sections/help/close.html
@@ -0,0 +1,28 @@
+
Открытые вопросы
+
Закрытые вопросы
+
+
Задать вопрос
+
+
+
+
Список закрытых вопросов
+
+
+
+
+ №
+ Информация
+ |support|Создатель |_support|
+ Состояние
+ Управление
+
+
+
+ [question]
+
+
+
+
+[pages]
+
+
\ No newline at end of file
diff --git a/template/sections/help/close/question.html b/template/sections/help/close/question.html
new file mode 100644
index 0000000..f6b854f
--- /dev/null
+++ b/template/sections/help/close/question.html
@@ -0,0 +1,25 @@
+
+ №[id]
+
+ [name]
+ Создан: [date]
+
+ |support|
+
+ #[uid] [login]
+ Активность: [time]
+
+ |_support|
+
+ [status]
+
+
+ |user|
+ Открыть
+ |_user|
+ |admin|
+ Открыть
+ Удалить
+ |_admin|
+
+
\ No newline at end of file
diff --git a/template/sections/help/create.html b/template/sections/help/create.html
new file mode 100644
index 0000000..c7024cb
--- /dev/null
+++ b/template/sections/help/create.html
@@ -0,0 +1,92 @@
+
Открытые вопросы
+
Закрытые вопросы
+
+
Задать вопрос
+
+
+
+
Форма создания вопроса
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/help/dialog.html b/template/sections/help/dialog.html
new file mode 100644
index 0000000..2a01f94
--- /dev/null
+++ b/template/sections/help/dialog.html
@@ -0,0 +1,137 @@
+
Открытые вопросы
+
Закрытые вопросы
+
+
Задать вопрос
+
+
+
+
Вопрос №[id]
+
+
+
+
+ Услуга:
+ [service]
+
+ |open|Закрыть
|_open|
+ |close|Открыть
|_close|
+
+
+
+ Создан:
+ [date]
+
+
+ Статус:
+ [status]
+
+
+
+
+|open|
+
+
+
+|!user|
+
Информация
+
+
+
+ Кто читает:
+ Кто пишет:
+
+
+ Определяется...
+ Определяется...
+
+
+
+
+
+|_!user|
+
+
Новое сообщение
+
+|_open|
+
+
+
+
Диалог
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/help/dialog/attachment.html b/template/sections/help/dialog/attachment.html
new file mode 100644
index 0000000..927b255
--- /dev/null
+++ b/template/sections/help/dialog/attachment.html
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/template/sections/help/dialog/msg.html b/template/sections/help/dialog/msg.html
new file mode 100644
index 0000000..d872ada
--- /dev/null
+++ b/template/sections/help/dialog/msg.html
@@ -0,0 +1,28 @@
+
+
+
+
+
[sender] |admin|(удалить соощение) |_admin|
+
[time]
+
+
+
+
+
+ [text]
+
+ |img|
+
+ |_img|
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/help/noaccess.html b/template/sections/help/noaccess.html
new file mode 100644
index 0000000..eb1541a
--- /dev/null
+++ b/template/sections/help/noaccess.html
@@ -0,0 +1 @@
+
Вы нарушили права хостинга. Доступ к данному разделу заблокирован.
\ No newline at end of file
diff --git a/template/sections/help/notice.html b/template/sections/help/notice.html
new file mode 100644
index 0000000..a66979e
--- /dev/null
+++ b/template/sections/help/notice.html
@@ -0,0 +1,6 @@
+
+
+
Новое сообщение [ago]
+
[text]
+
+
\ No newline at end of file
diff --git a/template/sections/help/open.html b/template/sections/help/open.html
new file mode 100644
index 0000000..29e9b4d
--- /dev/null
+++ b/template/sections/help/open.html
@@ -0,0 +1,28 @@
+
Открытые вопросы
+
Закрытые вопросы
+
+
Задать вопрос
+
+
+
+
Список открытых вопросов
+
+
+
+
+ №
+ Информация
+ |support|Создатель |_support|
+ Состояние
+ Управление
+
+
+
+ [question]
+
+
+
+
+[pages]
+
+
\ No newline at end of file
diff --git a/template/sections/help/open/question.html b/template/sections/help/open/question.html
new file mode 100644
index 0000000..bb0cf7a
--- /dev/null
+++ b/template/sections/help/open/question.html
@@ -0,0 +1,25 @@
+
+ №[id]
+
+ [name]
+ Создан: [date]
+
+ |support|
+
+ #[uid] [login]
+ Активность: [time]
+
+ |_support|
+
+ [status]
+
+
+ |user|
+ Закрыть
+ |_user|
+ |admin|
+ Закрыть
+ Удалить
+ |_admin|
+
+
\ No newline at end of file
diff --git a/template/sections/monitoring/all.html b/template/sections/monitoring/all.html
new file mode 100644
index 0000000..1297f1b
--- /dev/null
+++ b/template/sections/monitoring/all.html
@@ -0,0 +1,108 @@
+
Категории серверов
+
+
+
+
+
Список игровых серверов
+
+
+
+
+ #
+ Название сервера
+ Карта
+ Онлайн
+
+
+ IP адрес
+
+
+
+ [list]
+
+
+
+
+[pages]
+
+
\ No newline at end of file
diff --git a/template/sections/monitoring/list.html b/template/sections/monitoring/list.html
new file mode 100644
index 0000000..20d8b53
--- /dev/null
+++ b/template/sections/monitoring/list.html
@@ -0,0 +1,9 @@
+
+ [server]
+ [name]
+ [map]
+ [online] / [slots]
+
+
+ [address]
+
\ No newline at end of file
diff --git a/template/sections/monitoring/server.html b/template/sections/monitoring/server.html
new file mode 100644
index 0000000..cdf1d4b
--- /dev/null
+++ b/template/sections/monitoring/server.html
@@ -0,0 +1,178 @@
+
[name]
+
+
+
+
+
+
+ Адрес: [address]
+
+
+ Карта: [map]
+
+
+ Онлайн: [online] / [slots]
+
+
+ Локация: [unit]
+
+
+ Тариф: [tarif]
+
+
+ Создан: [create]
+
+
+
+
+
+
+
+
+
Игроки онлайн
+
Баннеры
+
+
+
+
+
+
+
+
+ #
+ Ник
+ Фраги
+ Время
+
+
+
+ [players]
+
+
+
+
+
+
Баннеры 160x248
+
+
+
+
+
+
Баннеры 560x95
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/news/all.html b/template/sections/news/all.html
new file mode 100644
index 0000000..598fc36
--- /dev/null
+++ b/template/sections/news/all.html
@@ -0,0 +1 @@
+[list]
\ No newline at end of file
diff --git a/template/sections/news/list.html b/template/sections/news/list.html
new file mode 100644
index 0000000..a355058
--- /dev/null
+++ b/template/sections/news/list.html
@@ -0,0 +1,12 @@
+
+
+
+
[text]
+
+
Теги: [tags]
+
+
\ No newline at end of file
diff --git a/template/sections/news/news.html b/template/sections/news/news.html
new file mode 100644
index 0000000..9a3b50d
--- /dev/null
+++ b/template/sections/news/news.html
@@ -0,0 +1,11 @@
+
+
[name] Опубликована: [date] Просмотров: [views]
+
+
[text]
+
+
Теги: [tags]
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/buttons/change.html b/template/sections/servers/buttons/change.html
new file mode 100644
index 0000000..a1157e3
--- /dev/null
+++ b/template/sections/servers/buttons/change.html
@@ -0,0 +1 @@
+
Поменять карту
\ No newline at end of file
diff --git a/template/sections/servers/buttons/other.html b/template/sections/servers/buttons/other.html
new file mode 100644
index 0000000..842e128
--- /dev/null
+++ b/template/sections/servers/buttons/other.html
@@ -0,0 +1 @@
+
Включить
Обновить
Переустановить
\ No newline at end of file
diff --git a/template/sections/servers/buttons/reinstall.html b/template/sections/servers/buttons/reinstall.html
new file mode 100644
index 0000000..d4eacf1
--- /dev/null
+++ b/template/sections/servers/buttons/reinstall.html
@@ -0,0 +1 @@
+
Переустановить
\ No newline at end of file
diff --git a/template/sections/servers/buttons/restart.html b/template/sections/servers/buttons/restart.html
new file mode 100644
index 0000000..22ff37c
--- /dev/null
+++ b/template/sections/servers/buttons/restart.html
@@ -0,0 +1 @@
+
Перезапустить
\ No newline at end of file
diff --git a/template/sections/servers/buttons/start.html b/template/sections/servers/buttons/start.html
new file mode 100644
index 0000000..ffda308
--- /dev/null
+++ b/template/sections/servers/buttons/start.html
@@ -0,0 +1 @@
+
Включить
\ No newline at end of file
diff --git a/template/sections/servers/buttons/stop.html b/template/sections/servers/buttons/stop.html
new file mode 100644
index 0000000..7583c75
--- /dev/null
+++ b/template/sections/servers/buttons/stop.html
@@ -0,0 +1 @@
+
Выключить
\ No newline at end of file
diff --git a/template/sections/servers/buttons/update.html b/template/sections/servers/buttons/update.html
new file mode 100644
index 0000000..a7a0e86
--- /dev/null
+++ b/template/sections/servers/buttons/update.html
@@ -0,0 +1 @@
+
Обновить
\ No newline at end of file
diff --git a/template/sections/servers/crmp/console.html b/template/sections/servers/crmp/console.html
new file mode 100644
index 0000000..9c195fd
--- /dev/null
+++ b/template/sections/servers/crmp/console.html
@@ -0,0 +1,109 @@
+
Консоль
+
+
+
+
+
...
+
+
+
+
+ Цвет консоли:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/crmp/copy.html b/template/sections/servers/crmp/copy.html
new file mode 100644
index 0000000..aaa3b76
--- /dev/null
+++ b/template/sections/servers/crmp/copy.html
@@ -0,0 +1,84 @@
+
Создание полной копии
+
+
+
+
+
+
+ *
+ Все содержимое игрового сервера
+
+
+ Создать полную резервную копию
+
+
+
+
+
+
+
+
+
Создание копии
+
+
+
+
+
+ [list]
+
+
+ Перед созданием резервной копии, убедитесь что игровой север выключен.
+ Создать резервную копию
+
+
+
+
+
+
+
+
+
+
Созданные копии
+
+
+
+
+
Информация
+
+
+
+
+ Полное восстановление - при восстановлении все директории в копии будут полностью заменена на имеющиеся.
+
+
+
+ Частичное восстановление - при восстановлении происходит замена одинаковых файлов, не удаляются другие файлы и папки.
+
+
+
+ Удаление копии - после удаление резервной копии, восстановить её будет невозможно.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/crmp/gmenu.html b/template/sections/servers/crmp/gmenu.html
new file mode 100644
index 0000000..bc7135e
--- /dev/null
+++ b/template/sections/servers/crmp/gmenu.html
@@ -0,0 +1,20 @@
+[notice]
+
+
\ No newline at end of file
diff --git a/template/sections/servers/crmp/index.html b/template/sections/servers/crmp/index.html
new file mode 100644
index 0000000..c25cba7
--- /dev/null
+++ b/template/sections/servers/crmp/index.html
@@ -0,0 +1,125 @@
+
Игровой сервер: #[id]
+
+
+
+
+
+
+
+ [name]
+ Игра: [game] (id: [id])
+ Создан: [date]
+
+
+ Адрес: [address]
+ Локация: [unit]
+ Арендован до: [time]
+
+
+ [status]
+ Сборка: [pack]
+ [time_end]
+
+
+
+ Онлайн: [online] / [slots]
+ x
+
+
+ Тариф: [tarif]
+
+
+ [btn]
+ Продлить аренду
+
+
+
+
+
+
+
+
+
+
+
Показатели нагрузки
+
+
+
+
+
+
+
Быстрый доступ
+
+
+
+
+
+ Основные настройки (server.cfg)
+ Перейти
+
+
+ Снимки консоли (StartLogs)
+ Перейти
+
+
+
+
+
+
+
+
+
+
Игроки онлайн
+
+
+
+
+
+ #
+ Ник
+ Фраги
+ Пинг
+
+
+
+ [players]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/crmp/owners.html b/template/sections/servers/crmp/owners.html
new file mode 100644
index 0000000..3dddc1d
--- /dev/null
+++ b/template/sections/servers/crmp/owners.html
@@ -0,0 +1,91 @@
+
+
Добавление совладельца
+
+
+
+
+
+
Список совладельцев
+
+
+
+
+
+
+
Выданные права
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/crmp/settings.html b/template/sections/servers/crmp/settings.html
new file mode 100644
index 0000000..046e0dd
--- /dev/null
+++ b/template/sections/servers/crmp/settings.html
@@ -0,0 +1,66 @@
+
Основное
+
+
+
+
+
+ Параметры запуска игрового сервера
+ Настроить
+
+
+ Основные настройки (SERVER.CFG )
+ Настроить
+
+
+ Блокировка на оборудовании (FireWall )
+ Настроить
+
+
+ Планировщик задач (CronTab )
+ Настроить
+
+
+ Сборка игрового сервера (После изменения сборки, сервер необходимо переустановить, чтобы все изменения вступили в силу)
+
+
+
+
+ [packs]
+
+
+ Сохранить
+
+
+
+
+
+
+
+
Логи
+
+
+
+
+
+ Снимки консоли (StartLogs )
+ Посмотреть
+
+
+
+
+
+|edits|
+
+
+
Редактируемые файлы
+
+
+|_edits|
+
+
\ No newline at end of file
diff --git a/template/sections/servers/crmp/settings/crontab.html b/template/sections/servers/crmp/settings/crontab.html
new file mode 100644
index 0000000..a67977a
--- /dev/null
+++ b/template/sections/servers/crmp/settings/crontab.html
@@ -0,0 +1,147 @@
+
Планировщик задач
+
+
+
+
+
+
Список добавленных задач
+
+
+
+
+
+ Задание
+ Время
+ Дни недели
+
+
+
+ [crontab]
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/crmp/settings/crontab/list.html b/template/sections/servers/crmp/settings/crontab/list.html
new file mode 100644
index 0000000..e69de29
diff --git a/template/sections/servers/crmp/settings/oldstart.html b/template/sections/servers/crmp/settings/oldstart.html
new file mode 100644
index 0000000..de601e4
--- /dev/null
+++ b/template/sections/servers/crmp/settings/oldstart.html
@@ -0,0 +1,5 @@
+
Снимок предудыщуего запуска игрового сервера
+
+
+ [log]
+
\ No newline at end of file
diff --git a/template/sections/servers/crmp/settings/servercfg.html b/template/sections/servers/crmp/settings/servercfg.html
new file mode 100644
index 0000000..fcadf5e
--- /dev/null
+++ b/template/sections/servers/crmp/settings/servercfg.html
@@ -0,0 +1,23 @@
+
Параметры server.cfg
+
+
+
+
diff --git a/template/sections/servers/crmp/settings/start.html b/template/sections/servers/crmp/settings/start.html
new file mode 100644
index 0000000..61dd421
--- /dev/null
+++ b/template/sections/servers/crmp/settings/start.html
@@ -0,0 +1,25 @@
+
Параметры запуска
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/crmp/vmenu.html b/template/sections/servers/crmp/vmenu.html
new file mode 100644
index 0000000..05a4292
--- /dev/null
+++ b/template/sections/servers/crmp/vmenu.html
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/crmp/web.html b/template/sections/servers/crmp/web.html
new file mode 100644
index 0000000..f99d03e
--- /dev/null
+++ b/template/sections/servers/crmp/web.html
@@ -0,0 +1 @@
+[web]
\ No newline at end of file
diff --git a/template/sections/servers/cs/console.html b/template/sections/servers/cs/console.html
new file mode 100644
index 0000000..e9f3ea6
--- /dev/null
+++ b/template/sections/servers/cs/console.html
@@ -0,0 +1,127 @@
+
Интерактивная консоль
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/copy.html b/template/sections/servers/cs/copy.html
new file mode 100644
index 0000000..a8ae57d
--- /dev/null
+++ b/template/sections/servers/cs/copy.html
@@ -0,0 +1,84 @@
+
Создание полной копии
+
+
+
+
+
+
+ cstrike
+ Все содержимое директории cstrike
+
+
+ Создать полную резервную копию
+
+
+
+
+
+
+
+
+
Создание копии
+
+
+
+
+
+ [list]
+
+
+ Перед созданием резервной копии, убедитесь что игровой север выключен.
+ Создать резервную копию
+
+
+
+
+
+
+
+
+
+
Созданные копии
+
+
+
+
+
Информация
+
+
+
+
+ Полное восстановление - при восстановлении все директории в копии будут полностью заменена на имеющиеся.
+
+
+
+ Частичное восстановление - при восстановлении происходит замена одинаковых файлов, не удаляются другие файлы и папки.
+
+
+
+ Удаление копии - после удаление резервной копии, восстановить её будет невозможно.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/cs/gmenu.html b/template/sections/servers/cs/gmenu.html
new file mode 100644
index 0000000..fec13aa
--- /dev/null
+++ b/template/sections/servers/cs/gmenu.html
@@ -0,0 +1,21 @@
+[notice]
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/index.html b/template/sections/servers/cs/index.html
new file mode 100644
index 0000000..ae2f4fc
--- /dev/null
+++ b/template/sections/servers/cs/index.html
@@ -0,0 +1,129 @@
+
Игровой сервер: #[id]
+
+
+
+
+
+
+
+ [name]
+ Игра: [game] (id: [id])
+ Создан: [date]
+
+
+ Адрес: [address]
+ Локация: [unit]
+ Арендован до: [time]
+
+
+ [status]
+ Сборка: [pack]
+ [time_end]
+
+
+
+ Онлайн: [online] / [slots]
+ x
+
+
+ Тариф: [tarif]
+
+
+ [btn]
+ Продлить аренду
+
+
+
+
+
+
+
+
+
+
+
Показатели нагрузки
+
+
+
+
+
+
+
Быстрый доступ
+
+
+
+
+
+ Админы на сервере (users.ini)
+ Перейти
+
+
+ Бан листы (banned.cfg / listip.cfg)
+ Перейти
+
+
+ Основные настройки (server.cfg)
+ Перейти
+
+
+ Снимки консоли (StartLogs)
+ Перейти
+
+
+
+
+
+
+
+
+
RCON УПРАВЛЕНИЕ
+
+
+
+
Игроки онлайн
+
+
+
+
+
+ #
+ Ник
+ Фраги
+ Время
+
+
+
+ [players]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/maps/listing.html b/template/sections/servers/cs/maps/listing.html
new file mode 100644
index 0000000..8ac715b
--- /dev/null
+++ b/template/sections/servers/cs/maps/listing.html
@@ -0,0 +1,65 @@
+
Настроить списки
+
+
Установить карты
+
+
+
+
+
+
Файл: maps.ini
+
+
+
+
+
+
+
+
Файл: mapcycle.txt
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/maps/types.html b/template/sections/servers/cs/maps/types.html
new file mode 100644
index 0000000..a2106ee
--- /dev/null
+++ b/template/sections/servers/cs/maps/types.html
@@ -0,0 +1,108 @@
+
Тип карт
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/rcon.html b/template/sections/servers/cs/rcon.html
new file mode 100644
index 0000000..1ab2cf2
--- /dev/null
+++ b/template/sections/servers/cs/rcon.html
@@ -0,0 +1,71 @@
+
Управление игроками
+
+
+
+
+
+
+ Выбранный игрок:
+ ____________
+
+
+ Обновить список
+
+
+ Убить
+
+
+ Кикнуть
+
+
+ Забанить
+
+
+ Заблокировать
+
+
+
+
+
+
+
+
+
+
+ #
+ Ник
+ SteamID
+ Время
+ Пинг
+ Адрес
+ Управление
+
+
+
+
+
+
+
+
+
+
+
+
Game Server Control Panel
+
+
+
Приложение для удобного управления игровыми серверами со своего смартфона.
+
+
С помощью него Вы всегда будете в курсе состояния вашего игрового сервера.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/rcon/player.html b/template/sections/servers/cs/rcon/player.html
new file mode 100644
index 0000000..577573c
--- /dev/null
+++ b/template/sections/servers/cs/rcon/player.html
@@ -0,0 +1,17 @@
+
+ [i]
+ [name]
+ [steamid]
+ [time]
+ [ping]мс
+
+
+ [ip]
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/settings.html b/template/sections/servers/cs/settings.html
new file mode 100644
index 0000000..648857e
--- /dev/null
+++ b/template/sections/servers/cs/settings.html
@@ -0,0 +1,121 @@
+
[start]
+
+
+
Основное
+
+
+
+
+
+ Основные настройки (SERVER.CFG )
+ Настроить
+
+
+ Администраторы на сервере (USERS.INI )
+ Настроить
+
+
+ Забаненные на сервере (banned.cfg / listip.cfg )
+ Настроить
+
+
+ Блокировка на оборудовании (FireWall )
+ Настроить
+
+
+ Планировщик задач (CronTab )
+ Настроить
+
+
+ Защита от DDoS атак Информация
+
+
+
+
+ [antiddos]
+
+
+ Сохранить
+
+
+ Сборка игрового сервера (После изменения сборки, сервер необходимо переустановить, чтобы все изменения вступили в силу)
+
+
+
+
+ [packs]
+
+
+ Сохранить
+
+
+ TOP15 (Если игровой сервер включен, то после выполнения сброса он будет перезагружен)
+
+
+
+ Обнуление статистики (/top15)
+
+ Обнулить
+
+
+ API
+
+
+
+ |!api|Для получения ключа, необходимо активировать API|_!api|
+ |api|Индивидуальный ключ: [api] API интерфейс
|_api|
+
+
+
+ |!api|Включить
|_!api|
+ |api|Отключить
|_api|
+
+
+
+
+
+
+
+
+
+
+
Логи
+
+
+
+
+
+ Снимки консоли (StartLogs )
+ Посмотреть
+
+
+ Краш сервера (debug )
+ Посмотреть
+
+
+ Логи игрового сервера (logs/*.log )
+ Посмотреть
+
+
+ Логи AmxModx (addons/amxmodx/logs/*.log )
+ Посмотреть
+
+
+
+
+
+|edits|
+
+
+
Редактируемые файлы
+
+
+|_edits|
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/settings/admins.html b/template/sections/servers/cs/settings/admins.html
new file mode 100644
index 0000000..2e21f0b
--- /dev/null
+++ b/template/sections/servers/cs/settings/admins.html
@@ -0,0 +1,40 @@
+
Управление администраторами на сервере
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/settings/admins/list.html b/template/sections/servers/cs/settings/admins/list.html
new file mode 100644
index 0000000..c52f4c8
--- /dev/null
+++ b/template/sections/servers/cs/settings/admins/list.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+ [type]
+
+
+ Удалить
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/settings/bans.html b/template/sections/servers/cs/settings/bans.html
new file mode 100644
index 0000000..505d6b2
--- /dev/null
+++ b/template/sections/servers/cs/settings/bans.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
Инструменты
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/settings/crontab.html b/template/sections/servers/cs/settings/crontab.html
new file mode 100644
index 0000000..16bb9f3
--- /dev/null
+++ b/template/sections/servers/cs/settings/crontab.html
@@ -0,0 +1,148 @@
+
Планировщик задач
+
+
+
+
+
+
Список добавленных задач
+
+
+
+
+
+ Задание
+ Время
+ Дни недели
+
+
+
+ [crontab]
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/settings/crontab/list.html b/template/sections/servers/cs/settings/crontab/list.html
new file mode 100644
index 0000000..e69de29
diff --git a/template/sections/servers/cs/settings/servercfg.html b/template/sections/servers/cs/settings/servercfg.html
new file mode 100644
index 0000000..fcadf5e
--- /dev/null
+++ b/template/sections/servers/cs/settings/servercfg.html
@@ -0,0 +1,23 @@
+
Параметры server.cfg
+
+
+
+
diff --git a/template/sections/servers/cs/settings/start.html b/template/sections/servers/cs/settings/start.html
new file mode 100644
index 0000000..d5a7da4
--- /dev/null
+++ b/template/sections/servers/cs/settings/start.html
@@ -0,0 +1,106 @@
+
Параметры запуска
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/top.html b/template/sections/servers/cs/top.html
new file mode 100644
index 0000000..1290ee9
--- /dev/null
+++ b/template/sections/servers/cs/top.html
@@ -0,0 +1,6 @@
+
+ [i]
+ [name]
+ [kills]
+ [deaths]
+
\ No newline at end of file
diff --git a/template/sections/servers/cs/vmenu.html b/template/sections/servers/cs/vmenu.html
new file mode 100644
index 0000000..bf56fa1
--- /dev/null
+++ b/template/sections/servers/cs/vmenu.html
@@ -0,0 +1,25 @@
+
+|plugins_use|
+
+|_plugins_use|
\ No newline at end of file
diff --git a/template/sections/servers/cs/web.html b/template/sections/servers/cs/web.html
new file mode 100644
index 0000000..f99d03e
--- /dev/null
+++ b/template/sections/servers/cs/web.html
@@ -0,0 +1 @@
+[web]
\ No newline at end of file
diff --git a/template/sections/servers/csgo/change_list.html b/template/sections/servers/csgo/change_list.html
new file mode 100644
index 0000000..b4b36e2
--- /dev/null
+++ b/template/sections/servers/csgo/change_list.html
@@ -0,0 +1 @@
+
|workshop|
|_workshop|
[name]
\ No newline at end of file
diff --git a/template/sections/servers/csgo/console.html b/template/sections/servers/csgo/console.html
new file mode 100644
index 0000000..b6109a3
--- /dev/null
+++ b/template/sections/servers/csgo/console.html
@@ -0,0 +1,122 @@
+
Интерактивная консоль
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/copy.html b/template/sections/servers/csgo/copy.html
new file mode 100644
index 0000000..7281ede
--- /dev/null
+++ b/template/sections/servers/csgo/copy.html
@@ -0,0 +1,84 @@
+
Создание полной копии
+
+
+
+
+
+
+ csgo
+ Все содержимое директории csgo
+
+
+ Создать полную резервную копию
+
+
+
+
+
+
+
+
+
Создание копии
+
+
+
+
+
+ [list]
+
+
+ Перед созданием резервной копии, убедитесь что игровой север выключен.
+ Создать резервную копию
+
+
+
+
+
+
+
+
+
+
Созданные копии
+
+
+
+
+
Информация
+
+
+
+
+ Полное восстановление - при восстановлении все директории в копии будут полностью заменена на имеющиеся.
+
+
+
+ Частичное восстановление - при восстановлении происходит замена одинаковых файлов, не удаляются другие файлы и папки.
+
+
+
+ Удаление копии - после удаление резервной копии, восстановить её будет невозможно.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/csgo/gmenu.html b/template/sections/servers/csgo/gmenu.html
new file mode 100644
index 0000000..fec13aa
--- /dev/null
+++ b/template/sections/servers/csgo/gmenu.html
@@ -0,0 +1,21 @@
+[notice]
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/index.html b/template/sections/servers/csgo/index.html
new file mode 100644
index 0000000..72d64de
--- /dev/null
+++ b/template/sections/servers/csgo/index.html
@@ -0,0 +1,129 @@
+
Игровой сервер: #[id]
+
+
+
+
+
+
+
+ [name]
+ Игра: [game] (id: [id])
+ Создан: [date]
+
+
+ Адрес: [address]
+ Локация: [unit]
+ Арендован до: [time]
+
+
+ [status]
+ Сборка: [pack]
+ [time_end]
+
+
+
+ Онлайн: [online] / [slots]
+ x
+
+
+ Тариф: [tarif]
+
+
+ [btn]
+ Продлить аренду
+
+
+
+
+
+
+
+
+
+
+
Показатели нагрузки
+
+
+
+
+
+
+
Быстрый доступ
+
+
+
+
+
+ Админы на сервере (admins_simple.ini)
+ Перейти
+
+
+ Бан листы (banned_user.cfg / banned_ip.cfg)
+ Перейти
+
+
+ Основные настройки (server.cfg)
+ Перейти
+
+
+ Снимки консоли (StartLogs)
+ Перейти
+
+
+
+
+
+
+
+
+
RCON УПРАВЛЕНИЕ
+
+
+
+
Игроки онлайн
+
+
+
+
+
+ #
+ Ник
+ Фраги
+ Время
+
+
+
+ [players]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/maps/listing.html b/template/sections/servers/csgo/maps/listing.html
new file mode 100644
index 0000000..21b397f
--- /dev/null
+++ b/template/sections/servers/csgo/maps/listing.html
@@ -0,0 +1,65 @@
+
Настроить списки
+
+
Установить карты
+
+
+
+
+
+
Файл: maplist.txt
+
+
+
+
+
+
+
+
Файл: mapcycle.txt
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/maps/map_server.html b/template/sections/servers/csgo/maps/map_server.html
new file mode 100644
index 0000000..fe1a524
--- /dev/null
+++ b/template/sections/servers/csgo/maps/map_server.html
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/maps/types.html b/template/sections/servers/csgo/maps/types.html
new file mode 100644
index 0000000..1114f2c
--- /dev/null
+++ b/template/sections/servers/csgo/maps/types.html
@@ -0,0 +1,63 @@
+
Тип карт
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/owners.html b/template/sections/servers/csgo/owners.html
new file mode 100644
index 0000000..cd1711b
--- /dev/null
+++ b/template/sections/servers/csgo/owners.html
@@ -0,0 +1,103 @@
+
+
Добавление совладельца
+
+
+
+
+
+
Список совладельцев
+
+
+
+
+
+
+
Выданные права
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/csgo/rcon.html b/template/sections/servers/csgo/rcon.html
new file mode 100644
index 0000000..b75a1fb
--- /dev/null
+++ b/template/sections/servers/csgo/rcon.html
@@ -0,0 +1,70 @@
+
Управление игроками
+
+
+
+
+
+
+ Выбранный игрок:
+ ____________
+
+
+ Обновить список
+
+
+ Убить
+
+
+ Кикнуть
+
+
+ Забанить
+
+
+ Заблокировать
+
+
+
+
+
+
+
+
+
+
+ #
+ Ник
+ SteamID
+ Время
+ Пинг
+ Адрес
+ Управление
+
+
+
+
+
+
+
+
+
+
+
Game Server Control Panel
+
+
+
Приложение для удобного управления игровыми серверами со своего смартфона.
+
+
С помощью него Вы всегда будете в курсе состояния вашего игрового сервера.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/rcon/player.html b/template/sections/servers/csgo/rcon/player.html
new file mode 100644
index 0000000..e5c796f
--- /dev/null
+++ b/template/sections/servers/csgo/rcon/player.html
@@ -0,0 +1,18 @@
+
+ [i]
+ [name]
+ [steamid]
+ [time]
+ [ping]мс
+
+
+ [ip]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/settings.html b/template/sections/servers/csgo/settings.html
new file mode 100644
index 0000000..38ff095
--- /dev/null
+++ b/template/sections/servers/csgo/settings.html
@@ -0,0 +1,109 @@
+
[start]
+
+
+
Основное
+
+
+
+
+
+ Основные настройки (SERVER.CFG )
+ Настроить
+
+
+ Администраторы на сервере (ADMINS_SIMPLE.INI )
+ Настроить
+
+
+ Забаненные на сервере (banned_user.cfg / banned_ip.cfg )
+ Настроить
+
+
+ Блокировка на оборудовании (FireWall )
+ Настроить
+
+
+ Планировщик задач (CronTab )
+ Настроить
+
+
+ Защита от DDoS атак Информация
+
+
+
+
+ [antiddos]
+
+
+ Сохранить
+
+
+ Сборка игрового сервера (После изменения сборки, сервер необходимо переустановить, чтобы все изменения вступили в силу)
+
+
+
+
+ [packs]
+
+
+ Сохранить
+
+
+
+ |!api|Для получения ключа, необходимо активировать API|_!api|
+ |api|Индивидуальный ключ: [api] API интерфейс
|_api|
+
+
+
+ |!api|Включить
|_!api|
+ |api|Отключить
|_api|
+
+
+
+
+
+
+
+
+
+
+
Логи
+
+
+
+
+
+ Снимки консоли (StartLogs )
+ Посмотреть
+
+
+ Краш сервера (debug )
+ Посмотреть
+
+
+ Логи игрового сервера (logs/*.log )
+ Посмотреть
+
+
+ Логи SourceMod (addons/sourcemod/logs/*.log )
+ Посмотреть
+
+
+
+
+
+|edits|
+
+
+
Редактируемые файлы
+
+
+|_edits|
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/settings/admins.html b/template/sections/servers/csgo/settings/admins.html
new file mode 100644
index 0000000..722aba8
--- /dev/null
+++ b/template/sections/servers/csgo/settings/admins.html
@@ -0,0 +1,39 @@
+
Управление администраторами на сервере
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/settings/admins/list.html b/template/sections/servers/csgo/settings/admins/list.html
new file mode 100644
index 0000000..41b6cfa
--- /dev/null
+++ b/template/sections/servers/csgo/settings/admins/list.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+ Удалить
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/settings/bans.html b/template/sections/servers/csgo/settings/bans.html
new file mode 100644
index 0000000..ed0cb40
--- /dev/null
+++ b/template/sections/servers/csgo/settings/bans.html
@@ -0,0 +1,58 @@
+
+
Файл: banned_user.cfg
+
+
+
+
+
+
Файл: banned_ip.cfg
+
+
+
+
+
+
+
Инструменты
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/settings/crontab.html b/template/sections/servers/csgo/settings/crontab.html
new file mode 100644
index 0000000..68e2920
--- /dev/null
+++ b/template/sections/servers/csgo/settings/crontab.html
@@ -0,0 +1,149 @@
+
Планировщик задач
+
+
+
+
+
+
Список добавленных задач
+
+
+
+
+
+ Задание
+ Время
+ Дни недели
+
+
+
+ [crontab]
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/settings/crontab/list.html b/template/sections/servers/csgo/settings/crontab/list.html
new file mode 100644
index 0000000..e69de29
diff --git a/template/sections/servers/csgo/settings/servercfg.html b/template/sections/servers/csgo/settings/servercfg.html
new file mode 100644
index 0000000..fcadf5e
--- /dev/null
+++ b/template/sections/servers/csgo/settings/servercfg.html
@@ -0,0 +1,23 @@
+
Параметры server.cfg
+
+
+
+
diff --git a/template/sections/servers/csgo/settings/start.html b/template/sections/servers/csgo/settings/start.html
new file mode 100644
index 0000000..714e81d
--- /dev/null
+++ b/template/sections/servers/csgo/settings/start.html
@@ -0,0 +1,92 @@
+
Параметры запуска
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/vmenu.html b/template/sections/servers/csgo/vmenu.html
new file mode 100644
index 0000000..2e81499
--- /dev/null
+++ b/template/sections/servers/csgo/vmenu.html
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/csgo/web.html b/template/sections/servers/csgo/web.html
new file mode 100644
index 0000000..f99d03e
--- /dev/null
+++ b/template/sections/servers/csgo/web.html
@@ -0,0 +1 @@
+[web]
\ No newline at end of file
diff --git a/template/sections/servers/css/console.html b/template/sections/servers/css/console.html
new file mode 100644
index 0000000..926a2e2
--- /dev/null
+++ b/template/sections/servers/css/console.html
@@ -0,0 +1,122 @@
+
Интерактивная консоль
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/copy.html b/template/sections/servers/css/copy.html
new file mode 100644
index 0000000..a8ae57d
--- /dev/null
+++ b/template/sections/servers/css/copy.html
@@ -0,0 +1,84 @@
+
Создание полной копии
+
+
+
+
+
+
+ cstrike
+ Все содержимое директории cstrike
+
+
+ Создать полную резервную копию
+
+
+
+
+
+
+
+
+
Создание копии
+
+
+
+
+
+ [list]
+
+
+ Перед созданием резервной копии, убедитесь что игровой север выключен.
+ Создать резервную копию
+
+
+
+
+
+
+
+
+
+
Созданные копии
+
+
+
+
+
Информация
+
+
+
+
+ Полное восстановление - при восстановлении все директории в копии будут полностью заменена на имеющиеся.
+
+
+
+ Частичное восстановление - при восстановлении происходит замена одинаковых файлов, не удаляются другие файлы и папки.
+
+
+
+ Удаление копии - после удаление резервной копии, восстановить её будет невозможно.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/css/gmenu.html b/template/sections/servers/css/gmenu.html
new file mode 100644
index 0000000..fec13aa
--- /dev/null
+++ b/template/sections/servers/css/gmenu.html
@@ -0,0 +1,21 @@
+[notice]
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/index.html b/template/sections/servers/css/index.html
new file mode 100644
index 0000000..72d64de
--- /dev/null
+++ b/template/sections/servers/css/index.html
@@ -0,0 +1,129 @@
+
Игровой сервер: #[id]
+
+
+
+
+
+
+
+ [name]
+ Игра: [game] (id: [id])
+ Создан: [date]
+
+
+ Адрес: [address]
+ Локация: [unit]
+ Арендован до: [time]
+
+
+ [status]
+ Сборка: [pack]
+ [time_end]
+
+
+
+ Онлайн: [online] / [slots]
+ x
+
+
+ Тариф: [tarif]
+
+
+ [btn]
+ Продлить аренду
+
+
+
+
+
+
+
+
+
+
+
Показатели нагрузки
+
+
+
+
+
+
+
Быстрый доступ
+
+
+
+
+
+ Админы на сервере (admins_simple.ini)
+ Перейти
+
+
+ Бан листы (banned_user.cfg / banned_ip.cfg)
+ Перейти
+
+
+ Основные настройки (server.cfg)
+ Перейти
+
+
+ Снимки консоли (StartLogs)
+ Перейти
+
+
+
+
+
+
+
+
+
RCON УПРАВЛЕНИЕ
+
+
+
+
Игроки онлайн
+
+
+
+
+
+ #
+ Ник
+ Фраги
+ Время
+
+
+
+ [players]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/maps/listing.html b/template/sections/servers/css/maps/listing.html
new file mode 100644
index 0000000..21b397f
--- /dev/null
+++ b/template/sections/servers/css/maps/listing.html
@@ -0,0 +1,65 @@
+
Настроить списки
+
+
Установить карты
+
+
+
+
+
+
Файл: maplist.txt
+
+
+
+
+
+
+
+
Файл: mapcycle.txt
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/maps/types.html b/template/sections/servers/css/maps/types.html
new file mode 100644
index 0000000..1114f2c
--- /dev/null
+++ b/template/sections/servers/css/maps/types.html
@@ -0,0 +1,63 @@
+
Тип карт
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/owners.html b/template/sections/servers/css/owners.html
new file mode 100644
index 0000000..cd1711b
--- /dev/null
+++ b/template/sections/servers/css/owners.html
@@ -0,0 +1,103 @@
+
+
Добавление совладельца
+
+
+
+
+
+
Список совладельцев
+
+
+
+
+
+
+
Выданные права
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/css/rcon.html b/template/sections/servers/css/rcon.html
new file mode 100644
index 0000000..b75a1fb
--- /dev/null
+++ b/template/sections/servers/css/rcon.html
@@ -0,0 +1,70 @@
+
Управление игроками
+
+
+
+
+
+
+ Выбранный игрок:
+ ____________
+
+
+ Обновить список
+
+
+ Убить
+
+
+ Кикнуть
+
+
+ Забанить
+
+
+ Заблокировать
+
+
+
+
+
+
+
+
+
+
+ #
+ Ник
+ SteamID
+ Время
+ Пинг
+ Адрес
+ Управление
+
+
+
+
+
+
+
+
+
+
+
Game Server Control Panel
+
+
+
Приложение для удобного управления игровыми серверами со своего смартфона.
+
+
С помощью него Вы всегда будете в курсе состояния вашего игрового сервера.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/rcon/player.html b/template/sections/servers/css/rcon/player.html
new file mode 100644
index 0000000..e5c796f
--- /dev/null
+++ b/template/sections/servers/css/rcon/player.html
@@ -0,0 +1,18 @@
+
+ [i]
+ [name]
+ [steamid]
+ [time]
+ [ping]мс
+
+
+ [ip]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/settings.html b/template/sections/servers/css/settings.html
new file mode 100644
index 0000000..38ff095
--- /dev/null
+++ b/template/sections/servers/css/settings.html
@@ -0,0 +1,109 @@
+
[start]
+
+
+
Основное
+
+
+
+
+
+ Основные настройки (SERVER.CFG )
+ Настроить
+
+
+ Администраторы на сервере (ADMINS_SIMPLE.INI )
+ Настроить
+
+
+ Забаненные на сервере (banned_user.cfg / banned_ip.cfg )
+ Настроить
+
+
+ Блокировка на оборудовании (FireWall )
+ Настроить
+
+
+ Планировщик задач (CronTab )
+ Настроить
+
+
+ Защита от DDoS атак Информация
+
+
+
+
+ [antiddos]
+
+
+ Сохранить
+
+
+ Сборка игрового сервера (После изменения сборки, сервер необходимо переустановить, чтобы все изменения вступили в силу)
+
+
+
+
+ [packs]
+
+
+ Сохранить
+
+
+
+ |!api|Для получения ключа, необходимо активировать API|_!api|
+ |api|Индивидуальный ключ: [api] API интерфейс
|_api|
+
+
+
+ |!api|Включить
|_!api|
+ |api|Отключить
|_api|
+
+
+
+
+
+
+
+
+
+
+
Логи
+
+
+
+
+
+ Снимки консоли (StartLogs )
+ Посмотреть
+
+
+ Краш сервера (debug )
+ Посмотреть
+
+
+ Логи игрового сервера (logs/*.log )
+ Посмотреть
+
+
+ Логи SourceMod (addons/sourcemod/logs/*.log )
+ Посмотреть
+
+
+
+
+
+|edits|
+
+
+
Редактируемые файлы
+
+
+|_edits|
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/settings/admins.html b/template/sections/servers/css/settings/admins.html
new file mode 100644
index 0000000..722aba8
--- /dev/null
+++ b/template/sections/servers/css/settings/admins.html
@@ -0,0 +1,39 @@
+
Управление администраторами на сервере
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/settings/admins/list.html b/template/sections/servers/css/settings/admins/list.html
new file mode 100644
index 0000000..41b6cfa
--- /dev/null
+++ b/template/sections/servers/css/settings/admins/list.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+ Удалить
+
\ No newline at end of file
diff --git a/template/sections/servers/css/settings/bans.html b/template/sections/servers/css/settings/bans.html
new file mode 100644
index 0000000..ed0cb40
--- /dev/null
+++ b/template/sections/servers/css/settings/bans.html
@@ -0,0 +1,58 @@
+
+
Файл: banned_user.cfg
+
+
+
+
+
+
Файл: banned_ip.cfg
+
+
+
+
+
+
+
Инструменты
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/settings/crontab.html b/template/sections/servers/css/settings/crontab.html
new file mode 100644
index 0000000..68e2920
--- /dev/null
+++ b/template/sections/servers/css/settings/crontab.html
@@ -0,0 +1,149 @@
+
Планировщик задач
+
+
+
+
+
+
Список добавленных задач
+
+
+
+
+
+ Задание
+ Время
+ Дни недели
+
+
+
+ [crontab]
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/settings/crontab/list.html b/template/sections/servers/css/settings/crontab/list.html
new file mode 100644
index 0000000..e69de29
diff --git a/template/sections/servers/css/settings/servercfg.html b/template/sections/servers/css/settings/servercfg.html
new file mode 100644
index 0000000..fcadf5e
--- /dev/null
+++ b/template/sections/servers/css/settings/servercfg.html
@@ -0,0 +1,23 @@
+
Параметры server.cfg
+
+
+
+
diff --git a/template/sections/servers/css/settings/start.html b/template/sections/servers/css/settings/start.html
new file mode 100644
index 0000000..714e81d
--- /dev/null
+++ b/template/sections/servers/css/settings/start.html
@@ -0,0 +1,92 @@
+
Параметры запуска
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/css/vmenu.html b/template/sections/servers/css/vmenu.html
new file mode 100644
index 0000000..2e81499
--- /dev/null
+++ b/template/sections/servers/css/vmenu.html
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/css/web.html b/template/sections/servers/css/web.html
new file mode 100644
index 0000000..f99d03e
--- /dev/null
+++ b/template/sections/servers/css/web.html
@@ -0,0 +1 @@
+[web]
\ No newline at end of file
diff --git a/template/sections/servers/cssold/console.html b/template/sections/servers/cssold/console.html
new file mode 100644
index 0000000..926a2e2
--- /dev/null
+++ b/template/sections/servers/cssold/console.html
@@ -0,0 +1,122 @@
+
Интерактивная консоль
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/copy.html b/template/sections/servers/cssold/copy.html
new file mode 100644
index 0000000..a8ae57d
--- /dev/null
+++ b/template/sections/servers/cssold/copy.html
@@ -0,0 +1,84 @@
+
Создание полной копии
+
+
+
+
+
+
+ cstrike
+ Все содержимое директории cstrike
+
+
+ Создать полную резервную копию
+
+
+
+
+
+
+
+
+
Создание копии
+
+
+
+
+
+ [list]
+
+
+ Перед созданием резервной копии, убедитесь что игровой север выключен.
+ Создать резервную копию
+
+
+
+
+
+
+
+
+
+
Созданные копии
+
+
+
+
+
Информация
+
+
+
+
+ Полное восстановление - при восстановлении все директории в копии будут полностью заменена на имеющиеся.
+
+
+
+ Частичное восстановление - при восстановлении происходит замена одинаковых файлов, не удаляются другие файлы и папки.
+
+
+
+ Удаление копии - после удаление резервной копии, восстановить её будет невозможно.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/cssold/gmenu.html b/template/sections/servers/cssold/gmenu.html
new file mode 100644
index 0000000..fec13aa
--- /dev/null
+++ b/template/sections/servers/cssold/gmenu.html
@@ -0,0 +1,21 @@
+[notice]
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/index.html b/template/sections/servers/cssold/index.html
new file mode 100644
index 0000000..72d64de
--- /dev/null
+++ b/template/sections/servers/cssold/index.html
@@ -0,0 +1,129 @@
+
Игровой сервер: #[id]
+
+
+
+
+
+
+
+ [name]
+ Игра: [game] (id: [id])
+ Создан: [date]
+
+
+ Адрес: [address]
+ Локация: [unit]
+ Арендован до: [time]
+
+
+ [status]
+ Сборка: [pack]
+ [time_end]
+
+
+
+ Онлайн: [online] / [slots]
+ x
+
+
+ Тариф: [tarif]
+
+
+ [btn]
+ Продлить аренду
+
+
+
+
+
+
+
+
+
+
+
Показатели нагрузки
+
+
+
+
+
+
+
Быстрый доступ
+
+
+
+
+
+ Админы на сервере (admins_simple.ini)
+ Перейти
+
+
+ Бан листы (banned_user.cfg / banned_ip.cfg)
+ Перейти
+
+
+ Основные настройки (server.cfg)
+ Перейти
+
+
+ Снимки консоли (StartLogs)
+ Перейти
+
+
+
+
+
+
+
+
+
RCON УПРАВЛЕНИЕ
+
+
+
+
Игроки онлайн
+
+
+
+
+
+ #
+ Ник
+ Фраги
+ Время
+
+
+
+ [players]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/maps/listing.html b/template/sections/servers/cssold/maps/listing.html
new file mode 100644
index 0000000..21b397f
--- /dev/null
+++ b/template/sections/servers/cssold/maps/listing.html
@@ -0,0 +1,65 @@
+
Настроить списки
+
+
Установить карты
+
+
+
+
+
+
Файл: maplist.txt
+
+
+
+
+
+
+
+
Файл: mapcycle.txt
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/maps/types.html b/template/sections/servers/cssold/maps/types.html
new file mode 100644
index 0000000..1114f2c
--- /dev/null
+++ b/template/sections/servers/cssold/maps/types.html
@@ -0,0 +1,63 @@
+
Тип карт
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/owners.html b/template/sections/servers/cssold/owners.html
new file mode 100644
index 0000000..cd1711b
--- /dev/null
+++ b/template/sections/servers/cssold/owners.html
@@ -0,0 +1,103 @@
+
+
Добавление совладельца
+
+
+
+
+
+
Список совладельцев
+
+
+
+
+
+
+
Выданные права
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/cssold/rcon.html b/template/sections/servers/cssold/rcon.html
new file mode 100644
index 0000000..b75a1fb
--- /dev/null
+++ b/template/sections/servers/cssold/rcon.html
@@ -0,0 +1,70 @@
+
Управление игроками
+
+
+
+
+
+
+ Выбранный игрок:
+ ____________
+
+
+ Обновить список
+
+
+ Убить
+
+
+ Кикнуть
+
+
+ Забанить
+
+
+ Заблокировать
+
+
+
+
+
+
+
+
+
+
+ #
+ Ник
+ SteamID
+ Время
+ Пинг
+ Адрес
+ Управление
+
+
+
+
+
+
+
+
+
+
+
Game Server Control Panel
+
+
+
Приложение для удобного управления игровыми серверами со своего смартфона.
+
+
С помощью него Вы всегда будете в курсе состояния вашего игрового сервера.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/rcon/player.html b/template/sections/servers/cssold/rcon/player.html
new file mode 100644
index 0000000..e5c796f
--- /dev/null
+++ b/template/sections/servers/cssold/rcon/player.html
@@ -0,0 +1,18 @@
+
+ [i]
+ [name]
+ [steamid]
+ [time]
+ [ping]мс
+
+
+ [ip]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/settings.html b/template/sections/servers/cssold/settings.html
new file mode 100644
index 0000000..38ff095
--- /dev/null
+++ b/template/sections/servers/cssold/settings.html
@@ -0,0 +1,109 @@
+
[start]
+
+
+
Основное
+
+
+
+
+
+ Основные настройки (SERVER.CFG )
+ Настроить
+
+
+ Администраторы на сервере (ADMINS_SIMPLE.INI )
+ Настроить
+
+
+ Забаненные на сервере (banned_user.cfg / banned_ip.cfg )
+ Настроить
+
+
+ Блокировка на оборудовании (FireWall )
+ Настроить
+
+
+ Планировщик задач (CronTab )
+ Настроить
+
+
+ Защита от DDoS атак Информация
+
+
+
+
+ [antiddos]
+
+
+ Сохранить
+
+
+ Сборка игрового сервера (После изменения сборки, сервер необходимо переустановить, чтобы все изменения вступили в силу)
+
+
+
+
+ [packs]
+
+
+ Сохранить
+
+
+
+ |!api|Для получения ключа, необходимо активировать API|_!api|
+ |api|Индивидуальный ключ: [api] API интерфейс
|_api|
+
+
+
+ |!api|Включить
|_!api|
+ |api|Отключить
|_api|
+
+
+
+
+
+
+
+
+
+
+
Логи
+
+
+
+
+
+ Снимки консоли (StartLogs )
+ Посмотреть
+
+
+ Краш сервера (debug )
+ Посмотреть
+
+
+ Логи игрового сервера (logs/*.log )
+ Посмотреть
+
+
+ Логи SourceMod (addons/sourcemod/logs/*.log )
+ Посмотреть
+
+
+
+
+
+|edits|
+
+
+
Редактируемые файлы
+
+
+|_edits|
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/settings/admins.html b/template/sections/servers/cssold/settings/admins.html
new file mode 100644
index 0000000..722aba8
--- /dev/null
+++ b/template/sections/servers/cssold/settings/admins.html
@@ -0,0 +1,39 @@
+
Управление администраторами на сервере
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/settings/admins/list.html b/template/sections/servers/cssold/settings/admins/list.html
new file mode 100644
index 0000000..41b6cfa
--- /dev/null
+++ b/template/sections/servers/cssold/settings/admins/list.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+ Удалить
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/settings/bans.html b/template/sections/servers/cssold/settings/bans.html
new file mode 100644
index 0000000..ed0cb40
--- /dev/null
+++ b/template/sections/servers/cssold/settings/bans.html
@@ -0,0 +1,58 @@
+
+
Файл: banned_user.cfg
+
+
+
+
+
+
Файл: banned_ip.cfg
+
+
+
+
+
+
+
Инструменты
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/settings/crontab.html b/template/sections/servers/cssold/settings/crontab.html
new file mode 100644
index 0000000..16bb9f3
--- /dev/null
+++ b/template/sections/servers/cssold/settings/crontab.html
@@ -0,0 +1,148 @@
+
Планировщик задач
+
+
+
+
+
+
Список добавленных задач
+
+
+
+
+
+ Задание
+ Время
+ Дни недели
+
+
+
+ [crontab]
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/settings/crontab/list.html b/template/sections/servers/cssold/settings/crontab/list.html
new file mode 100644
index 0000000..e69de29
diff --git a/template/sections/servers/cssold/settings/servercfg.html b/template/sections/servers/cssold/settings/servercfg.html
new file mode 100644
index 0000000..fcadf5e
--- /dev/null
+++ b/template/sections/servers/cssold/settings/servercfg.html
@@ -0,0 +1,23 @@
+
Параметры server.cfg
+
+
+
+
diff --git a/template/sections/servers/cssold/settings/start.html b/template/sections/servers/cssold/settings/start.html
new file mode 100644
index 0000000..8294540
--- /dev/null
+++ b/template/sections/servers/cssold/settings/start.html
@@ -0,0 +1,106 @@
+
Параметры запуска
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/vmenu.html b/template/sections/servers/cssold/vmenu.html
new file mode 100644
index 0000000..2e81499
--- /dev/null
+++ b/template/sections/servers/cssold/vmenu.html
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/cssold/web.html b/template/sections/servers/cssold/web.html
new file mode 100644
index 0000000..f99d03e
--- /dev/null
+++ b/template/sections/servers/cssold/web.html
@@ -0,0 +1 @@
+[web]
\ No newline at end of file
diff --git a/template/sections/servers/games/change_list.html b/template/sections/servers/games/change_list.html
new file mode 100644
index 0000000..a064d55
--- /dev/null
+++ b/template/sections/servers/games/change_list.html
@@ -0,0 +1 @@
+
[name]
\ No newline at end of file
diff --git a/template/sections/servers/games/copy/copy.html b/template/sections/servers/games/copy/copy.html
new file mode 100644
index 0000000..e2f5cd1
--- /dev/null
+++ b/template/sections/servers/games/copy/copy.html
@@ -0,0 +1,23 @@
+
+
+ Игровой cервер: #[server]
+ Содержимое: [info]
+
+ [date]
+
+ |created|
+
+
+
+
+
+
+
+
+
+ |_created|
+ |!created|
+ Создается...
+ |_!created|
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/copy/list.html b/template/sections/servers/games/copy/list.html
new file mode 100644
index 0000000..558c7a0
--- /dev/null
+++ b/template/sections/servers/games/copy/list.html
@@ -0,0 +1,9 @@
+
+
+ [name]
+ [info]
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/filetp/filetp_back.html b/template/sections/servers/games/filetp/filetp_back.html
new file mode 100644
index 0000000..bc6b709
--- /dev/null
+++ b/template/sections/servers/games/filetp/filetp_back.html
@@ -0,0 +1,3 @@
+
+ ..
+
\ No newline at end of file
diff --git a/template/sections/servers/games/filetp/filetp_find.html b/template/sections/servers/games/filetp/filetp_find.html
new file mode 100644
index 0000000..60ae5f8
--- /dev/null
+++ b/template/sections/servers/games/filetp/filetp_find.html
@@ -0,0 +1,21 @@
+
+
+ |file||edit||_edit||_file|
+ |folder| |_folder|
+
+
+ [find]
+
+ |folder| |_folder|
+ |file||edit||_edit||_file|
+
+ [size]
+ [chmod]
+ [owner] / [group]
+ [time] - [day] [month]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/filetp/filetp_list.html b/template/sections/servers/games/filetp/filetp_list.html
new file mode 100644
index 0000000..9766391
--- /dev/null
+++ b/template/sections/servers/games/filetp/filetp_list.html
@@ -0,0 +1,21 @@
+
+
+ |file||edit||_edit||_file|
+ |folder| |_folder|
+
+
+ [name]
+
+ |folder| |_folder|
+ |file||edit||_edit||_file|
+
+ [size]
+ [chmod]
+ [owner] / [group]
+ [time] - [day] [month]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/filetp/filetp_logs.html b/template/sections/servers/games/filetp/filetp_logs.html
new file mode 100644
index 0000000..98f8e89
--- /dev/null
+++ b/template/sections/servers/games/filetp/filetp_logs.html
@@ -0,0 +1,8 @@
+
+ [file]
+ [size]
+ [action]
+ [acticon]
+ [who]
+ [day].[month].[year] - [time]
+
\ No newline at end of file
diff --git a/template/sections/servers/games/filetp/filetp_off.html b/template/sections/servers/games/filetp/filetp_off.html
new file mode 100644
index 0000000..5c41667
--- /dev/null
+++ b/template/sections/servers/games/filetp/filetp_off.html
@@ -0,0 +1,16 @@
+
Информация
+
+
+
+
+
+
+ При включенном FTP, вы получаете данные для подключения к директории вашего игрового сервера через FTP-клиент, например FileZilla .
+ При обнаружении каких-либо проблем тех.поддержка вправе отказать в помощи, все проблемы с настройками сервера вам придется решать самостоятельно.
+ Если у вас недостаточно знаний в настройке игрового сервера, рекомендуем не включать FTP.
+
+ Включить
+
+
+
+
diff --git a/template/sections/servers/games/filetp/filetp_on.html b/template/sections/servers/games/filetp/filetp_on.html
new file mode 100644
index 0000000..afb20ca
--- /dev/null
+++ b/template/sections/servers/games/filetp/filetp_on.html
@@ -0,0 +1,140 @@
+
Информация
+
+
+
+
+
+ Сервер:
+ [server]:21
+ Выключить
+
+
+ Логин:
+ [login]
+
+
+ Пароль:
+
+ [passwd]
+ сменить
+
+
+
+ Адрес:
+
+ [address]:21
+
+ показать логи операций
+
+
+
+
+
+
+
+
+
+
Последние операции
+
+
+
+
+
+ Файл
+ Размер
+ Действие
+
+ Инициатор
+ Дата
+
+
+
+
+
+
+
+
+
+
Файловый менеджер
+
+
+
Обновить
+
+
Создать папку
+
Создать файл
+
+
+
+
+
+
+
+
+
+ Название
+ Размер
+ Права
+ Владелец / Группа
+ Последнее изменение
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/graph.html b/template/sections/servers/games/graph.html
new file mode 100644
index 0000000..d649a1b
--- /dev/null
+++ b/template/sections/servers/games/graph.html
@@ -0,0 +1,135 @@
+
График за последние 24 часа
+
+
+
+
+
+
+
+
График за последние 7 дней
+
+
+
+
+
+
+
+
График за последние 30 дней
+
+
+
+
+
+
+
+
Баннеры 160x248
+
+
+
+
+
+
Баннеры 560x95
+
+
+
+
diff --git a/template/sections/servers/games/maps.html b/template/sections/servers/games/maps.html
new file mode 100644
index 0000000..1d05c03
--- /dev/null
+++ b/template/sections/servers/games/maps.html
@@ -0,0 +1,29 @@
+
Настроить списки
+
+
Установить карты
+
+
+
+
+
+ Выделить все
+
+ Отменить выделение
+
+ Удалить выбранные карты
+
+
+
+ Список установленных карт
+
+ [maps]
+
+
+
+
+
+
diff --git a/template/sections/servers/games/maps/install.html b/template/sections/servers/games/maps/install.html
new file mode 100644
index 0000000..8173648
--- /dev/null
+++ b/template/sections/servers/games/maps/install.html
@@ -0,0 +1,76 @@
+
Настроить списки
+
+
Карты на сервере
+
+
+
+[types]
+
+
Поиск по картам
+
+
+
+
+
+
+
+
+ Выделить все
+
+ Отменить выделение
+
+ Установить выбранные карты
+
+
+
+ Результаты поиска
+
+
+
+
+
+
+
+
+
+
+
+
+Выделить все
+
+Отменить выделение
+
+Установить выбранные карты
+
+[pages]
+
+
+
+Список карт
+
+[maps]
+
+
+
+[pages]
+
+
+
+
diff --git a/template/sections/servers/games/maps/map_install.html b/template/sections/servers/games/maps/map_install.html
new file mode 100644
index 0000000..c92559b
--- /dev/null
+++ b/template/sections/servers/games/maps/map_install.html
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/games/maps/map_search.html b/template/sections/servers/games/maps/map_search.html
new file mode 100644
index 0000000..c92559b
--- /dev/null
+++ b/template/sections/servers/games/maps/map_search.html
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/games/maps/map_server.html b/template/sections/servers/games/maps/map_server.html
new file mode 100644
index 0000000..72fd5e5
--- /dev/null
+++ b/template/sections/servers/games/maps/map_server.html
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/games/owners/access.html b/template/sections/servers/games/owners/access.html
new file mode 100644
index 0000000..3baadd8
--- /dev/null
+++ b/template/sections/servers/games/owners/access.html
@@ -0,0 +1,4 @@
+
+ [info]
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/owners/index.html b/template/sections/servers/games/owners/index.html
new file mode 100644
index 0000000..8b8b17b
--- /dev/null
+++ b/template/sections/servers/games/owners/index.html
@@ -0,0 +1,64 @@
+
+
Добавление совладельца
+
+
+
+
+
+
Список совладельцев
+
+
+
+
+
+
+
Выданные права
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/games/owners/owners.html b/template/sections/servers/games/owners/owners.html
new file mode 100644
index 0000000..c4889aa
--- /dev/null
+++ b/template/sections/servers/games/owners/owners.html
@@ -0,0 +1,6 @@
+
+ [user]
+ [rights]
+ [time]
+ Удалить
+
\ No newline at end of file
diff --git a/template/sections/servers/games/plugins.html b/template/sections/servers/games/plugins.html
new file mode 100644
index 0000000..71861e7
--- /dev/null
+++ b/template/sections/servers/games/plugins.html
@@ -0,0 +1,81 @@
+
Поиск по плагинам
+
+
+
+
+
+
+
Результаты поиска
+
+
+
+
+
Доступные плагины
+
+
+
+
+
+
Установленные плагины
+
+
[install]
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/plugins/category.html b/template/sections/servers/games/plugins/category.html
new file mode 100644
index 0000000..7b486f0
--- /dev/null
+++ b/template/sections/servers/games/plugins/category.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/games/plugins/config.html b/template/sections/servers/games/plugins/config.html
new file mode 100644
index 0000000..924a7a3
--- /dev/null
+++ b/template/sections/servers/games/plugins/config.html
@@ -0,0 +1,15 @@
+
+ [name]
+
+
+ [data]
+
+
+
+
+ Вернуться к списку
+
+ Сохранить
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/plugins/config_list.html b/template/sections/servers/games/plugins/config_list.html
new file mode 100644
index 0000000..5ebf03d
--- /dev/null
+++ b/template/sections/servers/games/plugins/config_list.html
@@ -0,0 +1,9 @@
+
+
+ [name]
+ [file]
+
+
+ Редактировать
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/plugins/configs.html b/template/sections/servers/games/plugins/configs.html
new file mode 100644
index 0000000..5e24d02
--- /dev/null
+++ b/template/sections/servers/games/plugins/configs.html
@@ -0,0 +1,48 @@
+
Информация
+
+
+ Плагин:
[name]
+
[info]
+
+ |images|
+
+
+
[images]
+ |_images|
+
+
+|configs|
+
+
+
Редактируемые файлы
+
+
+|_configs|
+
+
+
+
Вернуться к плагинам
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/plugins/plugin.html b/template/sections/servers/games/plugins/plugin.html
new file mode 100644
index 0000000..bdd9e68
--- /dev/null
+++ b/template/sections/servers/games/plugins/plugin.html
@@ -0,0 +1,13 @@
+
+
+
[name]
+
[desc]
+ |stable|
Стабильный|_stable|
+ |unstable|
Нестабильный|_unstable|
+ |testing|
Тестируемый|_testing|
+
+
+ |images|
[images]
|_images|
+
\ No newline at end of file
diff --git a/template/sections/servers/games/plugins/plugin_images.html b/template/sections/servers/games/plugins/plugin_images.html
new file mode 100644
index 0000000..87fbed0
--- /dev/null
+++ b/template/sections/servers/games/plugins/plugin_images.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/games/plugins/plugin_install.html b/template/sections/servers/games/plugins/plugin_install.html
new file mode 100644
index 0000000..fb75d8c
--- /dev/null
+++ b/template/sections/servers/games/plugins/plugin_install.html
@@ -0,0 +1,14 @@
+
+
+
[name] (Установлен: [time])
+
[desc]
+ |stable|
Стабильный|_stable|
+ |unstable|
Нестабильный|_unstable|
+ |testing|
Тестируемый|_testing|
+ |update|
Есть обновление |_update|
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/plugins/search.html b/template/sections/servers/games/plugins/search.html
new file mode 100644
index 0000000..ad06926
--- /dev/null
+++ b/template/sections/servers/games/plugins/search.html
@@ -0,0 +1,19 @@
+
+
+
[name] |install|
(Установлен: [time]) |_install|
+
[desc]
+ |stable|
Стабильный|_stable|
+ |unstable|
Нестабильный|_unstable|
+ |testing|
Тестируемый|_testing|
+ |install|
+ |update|
Есть обновление |_update|
+ |_install|
+
+
+ |!install|
Установить
|_!install|
+ |install|
+ |config|
Настроить
|_config|
+
Удалить
+ |_install|
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/bans_list.html b/template/sections/servers/games/settings/bans_list.html
new file mode 100644
index 0000000..f8bb717
--- /dev/null
+++ b/template/sections/servers/games/settings/bans_list.html
@@ -0,0 +1,4 @@
+
+ [value]
+ Разбанить
+
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/crontab_list.html b/template/sections/servers/games/settings/crontab_list.html
new file mode 100644
index 0000000..60cae46
--- /dev/null
+++ b/template/sections/servers/games/settings/crontab_list.html
@@ -0,0 +1,8 @@
+
+ [task]
+ [time]
+
+ [week]
+ Удалить
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/debug.html b/template/sections/servers/games/settings/debug.html
new file mode 100644
index 0000000..6b54f89
--- /dev/null
+++ b/template/sections/servers/games/settings/debug.html
@@ -0,0 +1,9 @@
+
Отладочный лог (debug)
+
+
+ [log]
+
+
+
+
+
Если лог пустой, то ваш игровой сервер не падал.
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/edits_list.html b/template/sections/servers/games/settings/edits_list.html
new file mode 100644
index 0000000..2f65da0
--- /dev/null
+++ b/template/sections/servers/games/settings/edits_list.html
@@ -0,0 +1,7 @@
+
+
+ [name]
+ [desc]
+
+ Редактировать
+
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/file.html b/template/sections/servers/games/settings/file.html
new file mode 100644
index 0000000..6209bbc
--- /dev/null
+++ b/template/sections/servers/games/settings/file.html
@@ -0,0 +1,13 @@
+
+ [file]
+
+
+ [data]
+
+
+
+
+ Сохранить
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/firewall.html b/template/sections/servers/games/settings/firewall.html
new file mode 100644
index 0000000..dd67839
--- /dev/null
+++ b/template/sections/servers/games/settings/firewall.html
@@ -0,0 +1,65 @@
+
Блокировка злоумышленников на оборудовании
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/firewall/list.html b/template/sections/servers/games/settings/firewall/list.html
new file mode 100644
index 0000000..e717a4f
--- /dev/null
+++ b/template/sections/servers/games/settings/firewall/list.html
@@ -0,0 +1,4 @@
+
+ [address]
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/logs.html b/template/sections/servers/games/settings/logs.html
new file mode 100644
index 0000000..0e11817
--- /dev/null
+++ b/template/sections/servers/games/settings/logs.html
@@ -0,0 +1,25 @@
+
Удалить все логи
+
+
+
+
Список лог файлов игрового сервера
+
+
+
+
+
+ Лог файл
+ Размер
+ Дата
+
+
+
+
+ [logs]
+
+
+
+
+
+
+
Чтобы появились логи, в настройках должны быть установлены параметры логирования.
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/logs/list.html b/template/sections/servers/games/settings/logs/list.html
new file mode 100644
index 0000000..2e31a59
--- /dev/null
+++ b/template/sections/servers/games/settings/logs/list.html
@@ -0,0 +1,10 @@
+
+ [name]
+ [size]
+ [date]
+
+
+ Подробнее
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/logs/view.html b/template/sections/servers/games/settings/logs/view.html
new file mode 100644
index 0000000..b1c0180
--- /dev/null
+++ b/template/sections/servers/games/settings/logs/view.html
@@ -0,0 +1,9 @@
+
Файл: [name]
+
+
+ [log]
+
+
+
+
+
Вернуться к списку
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/servercfg_list.html b/template/sections/servers/games/settings/servercfg_list.html
new file mode 100644
index 0000000..9daeb62
--- /dev/null
+++ b/template/sections/servers/games/settings/servercfg_list.html
@@ -0,0 +1,7 @@
+
+
+ [name]
+ [desc]
+
+ [form]
+
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/startlogs.html b/template/sections/servers/games/settings/startlogs.html
new file mode 100644
index 0000000..296ff29
--- /dev/null
+++ b/template/sections/servers/games/settings/startlogs.html
@@ -0,0 +1,21 @@
+
Удалить все логи
+
+
+
+
Снимки предудыщих запусков игрового сервера
+
+
+
+
+
+ Лог файл
+ Размер
+ Дата
+
+
+
+
+ [logs]
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/settings/startlogs/list.html b/template/sections/servers/games/settings/startlogs/list.html
new file mode 100644
index 0000000..0cb564a
--- /dev/null
+++ b/template/sections/servers/games/settings/startlogs/list.html
@@ -0,0 +1,10 @@
+
+ [name]
+ [size]
+ [date]
+
+ |download|Посмотреть
|_download|
+ |!download|Включите_FTP
|_!download|
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/tarif.html b/template/sections/servers/games/tarif.html
new file mode 100644
index 0000000..54c31a4
--- /dev/null
+++ b/template/sections/servers/games/tarif.html
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/tarif/addextend.html b/template/sections/servers/games/tarif/addextend.html
new file mode 100644
index 0000000..4fc4e9b
--- /dev/null
+++ b/template/sections/servers/games/tarif/addextend.html
@@ -0,0 +1,27 @@
+
Продление выделенного адреса
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/tarif/address.html b/template/sections/servers/games/tarif/address.html
new file mode 100644
index 0000000..1ecd87a
--- /dev/null
+++ b/template/sections/servers/games/tarif/address.html
@@ -0,0 +1,30 @@
+
Аренда выделенного адреса
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/tarif/address_extend.html b/template/sections/servers/games/tarif/address_extend.html
new file mode 100644
index 0000000..4fc4e9b
--- /dev/null
+++ b/template/sections/servers/games/tarif/address_extend.html
@@ -0,0 +1,27 @@
+
Продление выделенного адреса
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/tarif/extend.html b/template/sections/servers/games/tarif/extend.html
new file mode 100644
index 0000000..1fb60b2
--- /dev/null
+++ b/template/sections/servers/games/tarif/extend.html
@@ -0,0 +1,51 @@
+
Продление аренды сервера
+
+
+
+
+
+|extend_address|
+
+
+
+
+|_extend_address|
\ No newline at end of file
diff --git a/template/sections/servers/games/tarif/extend_address.html b/template/sections/servers/games/tarif/extend_address.html
new file mode 100644
index 0000000..1a1c8f4
--- /dev/null
+++ b/template/sections/servers/games/tarif/extend_address.html
@@ -0,0 +1,9 @@
+
+
+ + Выделенный адрес:
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/tarif/extend_sp.html b/template/sections/servers/games/tarif/extend_sp.html
new file mode 100644
index 0000000..7651741
--- /dev/null
+++ b/template/sections/servers/games/tarif/extend_sp.html
@@ -0,0 +1,58 @@
+
Продление аренды сервера
+
+
+
+
+
+|extend_address|
+
+
+
+
+|_extend_address|
+
diff --git a/template/sections/servers/games/tarif/plan.html b/template/sections/servers/games/tarif/plan.html
new file mode 100644
index 0000000..3f8171f
--- /dev/null
+++ b/template/sections/servers/games/tarif/plan.html
@@ -0,0 +1,30 @@
+
Смена тарифного плана
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/tarif/slots.html b/template/sections/servers/games/tarif/slots.html
new file mode 100644
index 0000000..b6f27c2
--- /dev/null
+++ b/template/sections/servers/games/tarif/slots.html
@@ -0,0 +1,30 @@
+
Смена количества слот
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/tarif/slots_buy.html b/template/sections/servers/games/tarif/slots_buy.html
new file mode 100644
index 0000000..23c67ae
--- /dev/null
+++ b/template/sections/servers/games/tarif/slots_buy.html
@@ -0,0 +1,29 @@
+
Добавление слот
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/tarif/unit.html b/template/sections/servers/games/tarif/unit.html
new file mode 100644
index 0000000..4eb8eeb
--- /dev/null
+++ b/template/sections/servers/games/tarif/unit.html
@@ -0,0 +1,36 @@
+
Смена физического расположения
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/web/block.html b/template/sections/servers/games/web/block.html
new file mode 100644
index 0000000..82b3225
--- /dev/null
+++ b/template/sections/servers/games/web/block.html
@@ -0,0 +1,11 @@
+
[name]
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/games/web/list.html b/template/sections/servers/games/web/list.html
new file mode 100644
index 0000000..7d8f0be
--- /dev/null
+++ b/template/sections/servers/games/web/list.html
@@ -0,0 +1,4 @@
+
+ [name] - [desc]
+ Установить
+
\ No newline at end of file
diff --git a/template/sections/servers/games/web/list_install.html b/template/sections/servers/games/web/list_install.html
new file mode 100644
index 0000000..ef6c13b
--- /dev/null
+++ b/template/sections/servers/games/web/list_install.html
@@ -0,0 +1,4 @@
+
+ [name] - [desc]
+ Управление
+
\ No newline at end of file
diff --git a/template/sections/servers/list.html b/template/sections/servers/list.html
new file mode 100644
index 0000000..9b1c5d4
--- /dev/null
+++ b/template/sections/servers/list.html
@@ -0,0 +1,40 @@
+
Игровой сервер: #[id]
+
+
+
+
+
+
+
+ [name]
+ Игра: [game] (id: [id])
+ Создан: [date]
+
+
+ Адрес: [address]
+ Локация: [unit]
+ Арендован до: [time]
+
+
+ [status]
+ Сборка: [pack]
+ [time_end]
+
+
+
+ Онлайн: [online] / [slots]
+ x
+
+
+ Тариф: [tarif]
+
+
+ [btn]
+ Управление сервером
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mc/change_list.html b/template/sections/servers/mc/change_list.html
new file mode 100644
index 0000000..b4b36e2
--- /dev/null
+++ b/template/sections/servers/mc/change_list.html
@@ -0,0 +1 @@
+
|workshop|
|_workshop|
[name]
\ No newline at end of file
diff --git a/template/sections/servers/mc/console.html b/template/sections/servers/mc/console.html
new file mode 100644
index 0000000..2152793
--- /dev/null
+++ b/template/sections/servers/mc/console.html
@@ -0,0 +1,110 @@
+
Интерактивная консоль
+
+
+
+
+
...
+
+
+
+
+ Цвет консоли:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mc/copy.html b/template/sections/servers/mc/copy.html
new file mode 100644
index 0000000..7281ede
--- /dev/null
+++ b/template/sections/servers/mc/copy.html
@@ -0,0 +1,84 @@
+
Создание полной копии
+
+
+
+
+
+
+ csgo
+ Все содержимое директории csgo
+
+
+ Создать полную резервную копию
+
+
+
+
+
+
+
+
+
Создание копии
+
+
+
+
+
+ [list]
+
+
+ Перед созданием резервной копии, убедитесь что игровой север выключен.
+ Создать резервную копию
+
+
+
+
+
+
+
+
+
+
Созданные копии
+
+
+
+
+
Информация
+
+
+
+
+ Полное восстановление - при восстановлении все директории в копии будут полностью заменена на имеющиеся.
+
+
+
+ Частичное восстановление - при восстановлении происходит замена одинаковых файлов, не удаляются другие файлы и папки.
+
+
+
+ Удаление копии - после удаление резервной копии, восстановить её будет невозможно.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/mc/gmenu.html b/template/sections/servers/mc/gmenu.html
new file mode 100644
index 0000000..bc7135e
--- /dev/null
+++ b/template/sections/servers/mc/gmenu.html
@@ -0,0 +1,20 @@
+[notice]
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mc/index.html b/template/sections/servers/mc/index.html
new file mode 100644
index 0000000..b7b1439
--- /dev/null
+++ b/template/sections/servers/mc/index.html
@@ -0,0 +1,123 @@
+
Игровой сервер: #[id]
+
+
+
+
+
+
+
+ [name]
+ Игра: [game] (id: [id])
+ Создан: [date]
+
+
+ Адрес: [address]
+ Локация: [unit]
+ Арендован до: [time]
+
+
+ [status]
+ Сборка: [pack]
+ [time_end]
+
+
+
+ Онлайн: [online] / [slots]
+ x
+
+
+ Тариф: [tarif]
+
+
+ [btn]
+ Продлить аренду
+
+
+
+
+
+
+
+
+
+
+
Показатели нагрузки
+
+
+
+
+
+
+
Быстрый доступ
+
+
+
+
+
+ Основные настройки (server.properties)
+ Перейти
+
+
+ Снимки консоли (StartLogs)
+ Перейти
+
+
+
+
+
+
+
+
+
+
Игроки онлайн
+
+
+
+
+
+ #
+ Ник
+
+
+
+ [players]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mc/owners.html b/template/sections/servers/mc/owners.html
new file mode 100644
index 0000000..3c9edd0
--- /dev/null
+++ b/template/sections/servers/mc/owners.html
@@ -0,0 +1,95 @@
+
+
Добавление совладельца
+
+
+
+
+
+
Список совладельцев
+
+
+
+
+
+
+
Выданные права
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/mc/settings.html b/template/sections/servers/mc/settings.html
new file mode 100644
index 0000000..4f83f32
--- /dev/null
+++ b/template/sections/servers/mc/settings.html
@@ -0,0 +1,78 @@
+
[start]
+
+
+
Основное
+
+
+
+
+
+ Основные настройки (server.properties )
+ Настроить
+
+
+ Блокировка на оборудовании (FireWall )
+ Настроить
+
+
+ Планировщик задач (CronTab )
+ Настроить
+
+
+ Сборка игрового сервера (После изменения сборки, сервер необходимо переустановить, чтобы все изменения вступили в силу)
+
+
+
+
+ [packs]
+
+
+ Сохранить
+
+
+
+ |!api|Для получения ключа, необходимо активировать API|_!api|
+ |api|Индивидуальный ключ: [api] |_api|
+
+
+
+ |!api|Включить
|_!api|
+ |api|Отключить
|_api|
+
+
+
+
+
+
+
+
+
+
+
Логи
+
+
+
+
+
+ Снимки консоли (StartLogs )
+ Посмотреть
+
+
+
+
+
+|edits|
+
+
+
Редактируемые файлы
+
+
+|_edits|
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mc/settings/crontab.html b/template/sections/servers/mc/settings/crontab.html
new file mode 100644
index 0000000..68e2920
--- /dev/null
+++ b/template/sections/servers/mc/settings/crontab.html
@@ -0,0 +1,149 @@
+
Планировщик задач
+
+
+
+
+
+
Список добавленных задач
+
+
+
+
+
+ Задание
+ Время
+ Дни недели
+
+
+
+ [crontab]
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mc/settings/crontab/list.html b/template/sections/servers/mc/settings/crontab/list.html
new file mode 100644
index 0000000..e69de29
diff --git a/template/sections/servers/mc/settings/oldstart.html b/template/sections/servers/mc/settings/oldstart.html
new file mode 100644
index 0000000..de601e4
--- /dev/null
+++ b/template/sections/servers/mc/settings/oldstart.html
@@ -0,0 +1,5 @@
+
Снимок предудыщуего запуска игрового сервера
+
+
+ [log]
+
\ No newline at end of file
diff --git a/template/sections/servers/mc/settings/servercfg.html b/template/sections/servers/mc/settings/servercfg.html
new file mode 100644
index 0000000..e96181a
--- /dev/null
+++ b/template/sections/servers/mc/settings/servercfg.html
@@ -0,0 +1,18 @@
+
Параметры server.properties
+
+
+
+
diff --git a/template/sections/servers/mc/settings/start.html b/template/sections/servers/mc/settings/start.html
new file mode 100644
index 0000000..61dd421
--- /dev/null
+++ b/template/sections/servers/mc/settings/start.html
@@ -0,0 +1,25 @@
+
Параметры запуска
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mc/vmenu.html b/template/sections/servers/mc/vmenu.html
new file mode 100644
index 0000000..05a4292
--- /dev/null
+++ b/template/sections/servers/mc/vmenu.html
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/mc/web.html b/template/sections/servers/mc/web.html
new file mode 100644
index 0000000..f99d03e
--- /dev/null
+++ b/template/sections/servers/mc/web.html
@@ -0,0 +1 @@
+[web]
\ No newline at end of file
diff --git a/template/sections/servers/mta/console.html b/template/sections/servers/mta/console.html
new file mode 100644
index 0000000..af8083e
--- /dev/null
+++ b/template/sections/servers/mta/console.html
@@ -0,0 +1,110 @@
+
Интерактивная консоль
+
+
+
+
+
...
+
+
+
+
+ Цвет консоли:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mta/copy.html b/template/sections/servers/mta/copy.html
new file mode 100644
index 0000000..aaa3b76
--- /dev/null
+++ b/template/sections/servers/mta/copy.html
@@ -0,0 +1,84 @@
+
Создание полной копии
+
+
+
+
+
+
+ *
+ Все содержимое игрового сервера
+
+
+ Создать полную резервную копию
+
+
+
+
+
+
+
+
+
Создание копии
+
+
+
+
+
+ [list]
+
+
+ Перед созданием резервной копии, убедитесь что игровой север выключен.
+ Создать резервную копию
+
+
+
+
+
+
+
+
+
+
Созданные копии
+
+
+
+
+
Информация
+
+
+
+
+ Полное восстановление - при восстановлении все директории в копии будут полностью заменена на имеющиеся.
+
+
+
+ Частичное восстановление - при восстановлении происходит замена одинаковых файлов, не удаляются другие файлы и папки.
+
+
+
+ Удаление копии - после удаление резервной копии, восстановить её будет невозможно.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/mta/gmenu.html b/template/sections/servers/mta/gmenu.html
new file mode 100644
index 0000000..bc7135e
--- /dev/null
+++ b/template/sections/servers/mta/gmenu.html
@@ -0,0 +1,20 @@
+[notice]
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mta/index.html b/template/sections/servers/mta/index.html
new file mode 100644
index 0000000..6ac288c
--- /dev/null
+++ b/template/sections/servers/mta/index.html
@@ -0,0 +1,125 @@
+
Игровой сервер: #[id]
+
+
+
+
+
+
+
+ [name]
+ Игра: [game] (id: [id])
+ Создан: [date]
+
+
+ Адрес: [address]
+ Локация: [unit]
+ Арендован до: [time]
+
+
+ [status]
+ Сборка: [pack]
+ [time_end]
+
+
+
+ Онлайн: [online] / [slots]
+ x
+
+
+ Тариф: [tarif]
+
+
+ [btn]
+ Продлить аренду
+
+
+
+
+
+
+
+
+
+
+
Показатели нагрузки
+
+
+
+
+
+
+
Быстрый доступ
+
+
+
+
+
+ Основные настройки (mtaserver.conf)
+ Перейти
+
+
+ Настройки прав (acl.xml)
+ Перейти
+
+
+ Настройки цветов (vehiclecolors.conf)
+ Перейти
+
+
+ Снимки консоли (StartLogs)
+ Перейти
+
+
+
+
+
+
+
+
+
Игроки онлайн
+
+
+
+
+
+ #
+ Ник
+ Фраги
+ Пинг
+
+
+
+ [players]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mta/owners.html b/template/sections/servers/mta/owners.html
new file mode 100644
index 0000000..3dddc1d
--- /dev/null
+++ b/template/sections/servers/mta/owners.html
@@ -0,0 +1,91 @@
+
+
Добавление совладельца
+
+
+
+
+
+
Список совладельцев
+
+
+
+
+
+
+
Выданные права
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/mta/settings.html b/template/sections/servers/mta/settings.html
new file mode 100644
index 0000000..da141ce
--- /dev/null
+++ b/template/sections/servers/mta/settings.html
@@ -0,0 +1,86 @@
+
[start]
+
+
+
Основное
+
+
+
+
+
+ Основной конфигурационный файл сервера (mtaserver.conf )
+ Настроить
+
+
+ Настройки цветов автомобилей на игровом сервере (acl.xml )
+ Настроить
+
+
+ Настройки прав на игровом сервере (vehiclecolors.conf )
+ Настроить
+
+
+ Блокировка на оборудовании (FireWall )
+ Настроить
+
+
+ Планировщик задач (CronTab )
+ Настроить
+
+
+ Сборка игрового сервера (После изменения сборки, сервер необходимо переустановить, чтобы все изменения вступили в силу)
+
+
+
+
+ [packs]
+
+
+ Сохранить
+
+
+
+ |!api|Для получения ключа, необходимо активировать API|_!api|
+ |api|Индивидуальный ключ: [api] |_api|
+
+
+
+ |!api|Включить
|_!api|
+ |api|Отключить
|_api|
+
+
+
+
+
+
+
+
+
+
+
Логи
+
+
+
+
+
+ Снимки консоли (StartLogs )
+ Посмотреть
+
+
+
+
+
+|edits|
+
+
+
Редактируемые файлы
+
+
+|_edits|
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mta/settings/crontab.html b/template/sections/servers/mta/settings/crontab.html
new file mode 100644
index 0000000..16bb9f3
--- /dev/null
+++ b/template/sections/servers/mta/settings/crontab.html
@@ -0,0 +1,148 @@
+
Планировщик задач
+
+
+
+
+
+
Список добавленных задач
+
+
+
+
+
+ Задание
+ Время
+ Дни недели
+
+
+
+ [crontab]
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mta/settings/crontab/list.html b/template/sections/servers/mta/settings/crontab/list.html
new file mode 100644
index 0000000..e69de29
diff --git a/template/sections/servers/mta/settings/oldstart.html b/template/sections/servers/mta/settings/oldstart.html
new file mode 100644
index 0000000..de601e4
--- /dev/null
+++ b/template/sections/servers/mta/settings/oldstart.html
@@ -0,0 +1,5 @@
+
Снимок предудыщуего запуска игрового сервера
+
+
+ [log]
+
\ No newline at end of file
diff --git a/template/sections/servers/mta/settings/servercfg.html b/template/sections/servers/mta/settings/servercfg.html
new file mode 100644
index 0000000..fcadf5e
--- /dev/null
+++ b/template/sections/servers/mta/settings/servercfg.html
@@ -0,0 +1,23 @@
+
Параметры server.cfg
+
+
+
+
diff --git a/template/sections/servers/mta/settings/start.html b/template/sections/servers/mta/settings/start.html
new file mode 100644
index 0000000..469bc77
--- /dev/null
+++ b/template/sections/servers/mta/settings/start.html
@@ -0,0 +1,37 @@
+
Параметры запуска
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/mta/vmenu.html b/template/sections/servers/mta/vmenu.html
new file mode 100644
index 0000000..05a4292
--- /dev/null
+++ b/template/sections/servers/mta/vmenu.html
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/mta/web.html b/template/sections/servers/mta/web.html
new file mode 100644
index 0000000..f99d03e
--- /dev/null
+++ b/template/sections/servers/mta/web.html
@@ -0,0 +1 @@
+[web]
\ No newline at end of file
diff --git a/template/sections/servers/players/cs.html b/template/sections/servers/players/cs.html
new file mode 100644
index 0000000..8633973
--- /dev/null
+++ b/template/sections/servers/players/cs.html
@@ -0,0 +1,6 @@
+
+ [i]
+ [name]
+ [score]
+ [time]
+
\ No newline at end of file
diff --git a/template/sections/servers/players/csgo.html b/template/sections/servers/players/csgo.html
new file mode 100644
index 0000000..8633973
--- /dev/null
+++ b/template/sections/servers/players/csgo.html
@@ -0,0 +1,6 @@
+
+ [i]
+ [name]
+ [score]
+ [time]
+
\ No newline at end of file
diff --git a/template/sections/servers/players/css.html b/template/sections/servers/players/css.html
new file mode 100644
index 0000000..8633973
--- /dev/null
+++ b/template/sections/servers/players/css.html
@@ -0,0 +1,6 @@
+
+ [i]
+ [name]
+ [score]
+ [time]
+
\ No newline at end of file
diff --git a/template/sections/servers/players/cssold.html b/template/sections/servers/players/cssold.html
new file mode 100644
index 0000000..8633973
--- /dev/null
+++ b/template/sections/servers/players/cssold.html
@@ -0,0 +1,6 @@
+
+ [i]
+ [name]
+ [score]
+ [time]
+
\ No newline at end of file
diff --git a/template/sections/servers/players/mc.html b/template/sections/servers/players/mc.html
new file mode 100644
index 0000000..2caa38e
--- /dev/null
+++ b/template/sections/servers/players/mc.html
@@ -0,0 +1,4 @@
+
+ [i]
+ [name]
+
\ No newline at end of file
diff --git a/template/sections/servers/players/mta.html b/template/sections/servers/players/mta.html
new file mode 100644
index 0000000..6d69cdd
--- /dev/null
+++ b/template/sections/servers/players/mta.html
@@ -0,0 +1,6 @@
+
+ [i]
+ [name]
+ [score]
+ [ping]
+
\ No newline at end of file
diff --git a/template/sections/servers/players/samp.html b/template/sections/servers/players/samp.html
new file mode 100644
index 0000000..6d69cdd
--- /dev/null
+++ b/template/sections/servers/players/samp.html
@@ -0,0 +1,6 @@
+
+ [i]
+ [name]
+ [score]
+ [ping]
+
\ No newline at end of file
diff --git a/template/sections/servers/samp/console.html b/template/sections/servers/samp/console.html
new file mode 100644
index 0000000..72af8b2
--- /dev/null
+++ b/template/sections/servers/samp/console.html
@@ -0,0 +1,109 @@
+
Консоль
+
+
+
+
+
...
+
+
+
+
+ Цвет консоли:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/samp/copy.html b/template/sections/servers/samp/copy.html
new file mode 100644
index 0000000..aaa3b76
--- /dev/null
+++ b/template/sections/servers/samp/copy.html
@@ -0,0 +1,84 @@
+
Создание полной копии
+
+
+
+
+
+
+ *
+ Все содержимое игрового сервера
+
+
+ Создать полную резервную копию
+
+
+
+
+
+
+
+
+
Создание копии
+
+
+
+
+
+ [list]
+
+
+ Перед созданием резервной копии, убедитесь что игровой север выключен.
+ Создать резервную копию
+
+
+
+
+
+
+
+
+
+
Созданные копии
+
+
+
+
+
Информация
+
+
+
+
+ Полное восстановление - при восстановлении все директории в копии будут полностью заменена на имеющиеся.
+
+
+
+ Частичное восстановление - при восстановлении происходит замена одинаковых файлов, не удаляются другие файлы и папки.
+
+
+
+ Удаление копии - после удаление резервной копии, восстановить её будет невозможно.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/samp/gmenu.html b/template/sections/servers/samp/gmenu.html
new file mode 100644
index 0000000..bc7135e
--- /dev/null
+++ b/template/sections/servers/samp/gmenu.html
@@ -0,0 +1,20 @@
+[notice]
+
+
\ No newline at end of file
diff --git a/template/sections/servers/samp/index.html b/template/sections/servers/samp/index.html
new file mode 100644
index 0000000..c25cba7
--- /dev/null
+++ b/template/sections/servers/samp/index.html
@@ -0,0 +1,125 @@
+
Игровой сервер: #[id]
+
+
+
+
+
+
+
+ [name]
+ Игра: [game] (id: [id])
+ Создан: [date]
+
+
+ Адрес: [address]
+ Локация: [unit]
+ Арендован до: [time]
+
+
+ [status]
+ Сборка: [pack]
+ [time_end]
+
+
+
+ Онлайн: [online] / [slots]
+ x
+
+
+ Тариф: [tarif]
+
+
+ [btn]
+ Продлить аренду
+
+
+
+
+
+
+
+
+
+
+
Показатели нагрузки
+
+
+
+
+
+
+
Быстрый доступ
+
+
+
+
+
+ Основные настройки (server.cfg)
+ Перейти
+
+
+ Снимки консоли (StartLogs)
+ Перейти
+
+
+
+
+
+
+
+
+
+
Игроки онлайн
+
+
+
+
+
+ #
+ Ник
+ Фраги
+ Пинг
+
+
+
+ [players]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/samp/owners.html b/template/sections/servers/samp/owners.html
new file mode 100644
index 0000000..3dddc1d
--- /dev/null
+++ b/template/sections/servers/samp/owners.html
@@ -0,0 +1,91 @@
+
+
Добавление совладельца
+
+
+
+
+
+
Список совладельцев
+
+
+
+
+
+
+
Выданные права
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/template/sections/servers/samp/settings.html b/template/sections/servers/samp/settings.html
new file mode 100644
index 0000000..5dc0b8c
--- /dev/null
+++ b/template/sections/servers/samp/settings.html
@@ -0,0 +1,78 @@
+
[start]
+
+
+
Основное
+
+
+
+
+
+ Основные настройки (SERVER.CFG )
+ Настроить
+
+
+ Блокировка на оборудовании (FireWall )
+ Настроить
+
+
+ Планировщик задач (CronTab )
+ Настроить
+
+
+ Сборка игрового сервера (После изменения сборки, сервер необходимо переустановить, чтобы все изменения вступили в силу)
+
+
+
+
+ [packs]
+
+
+ Сохранить
+
+
+
+ |!api|Для получения ключа, необходимо активировать API|_!api|
+ |api|Индивидуальный ключ: [api] |_api|
+
+
+
+ |!api|Включить
|_!api|
+ |api|Отключить
|_api|
+
+
+
+
+
+
+
+
+
+
+
Логи
+
+
+
+
+
+ Снимки консоли (StartLogs )
+ Посмотреть
+
+
+
+
+
+|edits|
+
+
+
Редактируемые файлы
+
+
+|_edits|
+
+
\ No newline at end of file
diff --git a/template/sections/servers/samp/settings/crontab.html b/template/sections/servers/samp/settings/crontab.html
new file mode 100644
index 0000000..a67977a
--- /dev/null
+++ b/template/sections/servers/samp/settings/crontab.html
@@ -0,0 +1,147 @@
+
Планировщик задач
+
+
+
+
+
+
Список добавленных задач
+
+
+
+
+
+ Задание
+ Время
+ Дни недели
+
+
+
+ [crontab]
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/samp/settings/crontab/list.html b/template/sections/servers/samp/settings/crontab/list.html
new file mode 100644
index 0000000..e69de29
diff --git a/template/sections/servers/samp/settings/oldstart.html b/template/sections/servers/samp/settings/oldstart.html
new file mode 100644
index 0000000..de601e4
--- /dev/null
+++ b/template/sections/servers/samp/settings/oldstart.html
@@ -0,0 +1,5 @@
+
Снимок предудыщуего запуска игрового сервера
+
+
+ [log]
+
\ No newline at end of file
diff --git a/template/sections/servers/samp/settings/servercfg.html b/template/sections/servers/samp/settings/servercfg.html
new file mode 100644
index 0000000..fcadf5e
--- /dev/null
+++ b/template/sections/servers/samp/settings/servercfg.html
@@ -0,0 +1,23 @@
+
Параметры server.cfg
+
+
+
+
diff --git a/template/sections/servers/samp/settings/start.html b/template/sections/servers/samp/settings/start.html
new file mode 100644
index 0000000..61dd421
--- /dev/null
+++ b/template/sections/servers/samp/settings/start.html
@@ -0,0 +1,25 @@
+
Параметры запуска
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/servers/samp/vmenu.html b/template/sections/servers/samp/vmenu.html
new file mode 100644
index 0000000..05a4292
--- /dev/null
+++ b/template/sections/servers/samp/vmenu.html
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/template/sections/servers/samp/web.html b/template/sections/servers/samp/web.html
new file mode 100644
index 0000000..f99d03e
--- /dev/null
+++ b/template/sections/servers/samp/web.html
@@ -0,0 +1 @@
+[web]
\ No newline at end of file
diff --git a/template/sections/servers/servers.html b/template/sections/servers/servers.html
new file mode 100644
index 0000000..48e25f5
--- /dev/null
+++ b/template/sections/servers/servers.html
@@ -0,0 +1,11 @@
+[list]
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/services/games/crmp.html b/template/sections/services/games/crmp.html
new file mode 100644
index 0000000..0888c4f
--- /dev/null
+++ b/template/sections/services/games/crmp.html
@@ -0,0 +1,105 @@
+
Аренда игрового сервера
+
+|!informer|
+
+
+
+
+
Внимание: заказывая сервер, вы соглашаетесь с условиями договора .
+|_!informer|
+|informer|
+
+
К сожалению, в данный момент нет свободного места на оборудовании для предоставления аренды нового игрового сервера.
+|_informer|
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/services/games/cs.html b/template/sections/services/games/cs.html
new file mode 100644
index 0000000..e2f9342
--- /dev/null
+++ b/template/sections/services/games/cs.html
@@ -0,0 +1,115 @@
+
Аренда игрового сервера
+
+|!informer|
+
+
+
+
+
Внимание: заказывая сервер, вы соглашаетесь с условиями договора .
+|_!informer|
+
+|informer|
+
+
К сожалению, в данный момент нет свободного места на оборудовании для предоставления аренды нового игрового сервера.
+|_informer|
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/services/games/csgo.html b/template/sections/services/games/csgo.html
new file mode 100644
index 0000000..8e7f19d
--- /dev/null
+++ b/template/sections/services/games/csgo.html
@@ -0,0 +1,113 @@
+
Аренда игрового сервера
+
+|!informer|
+
+
+
+
+
Внимание: заказывая сервер, вы соглашаетесь с условиями договора .
+|_!informer|
+|informer|
+
+
К сожалению, в данный момент нет свободного места на оборудовании для предоставления аренды нового игрового сервера.
+|_informer|
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/services/games/css.html b/template/sections/services/games/css.html
new file mode 100644
index 0000000..9b0c7e4
--- /dev/null
+++ b/template/sections/services/games/css.html
@@ -0,0 +1,113 @@
+
Аренда игрового сервера
+
+|!informer|
+
+
+
+
+
Внимание: заказывая сервер, вы соглашаетесь с условиями договора .
+|_!informer|
+|informer|
+
+
К сожалению, в данный момент нет свободного места на оборудовании для предоставления аренды нового игрового сервера.
+|_informer|
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/services/games/cssold.html b/template/sections/services/games/cssold.html
new file mode 100644
index 0000000..ca0bb43
--- /dev/null
+++ b/template/sections/services/games/cssold.html
@@ -0,0 +1,121 @@
+
Аренда игрового сервера
+
+|!informer|
+
+
+
+
+
Внимание: заказывая сервер, вы соглашаетесь с условиями договора .
+|_!informer|
+|informer|
+
+
К сожалению, в данный момент нет свободного места на оборудовании для предоставления аренды нового игрового сервера.
+|_informer|
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/services/games/mc.html b/template/sections/services/games/mc.html
new file mode 100644
index 0000000..87df020
--- /dev/null
+++ b/template/sections/services/games/mc.html
@@ -0,0 +1,113 @@
+
Аренда игрового сервера
+
+|!informer|
+
+
+
+
+
Внимание: заказывая сервер, вы соглашаетесь с условиями договора .
+|_!informer|
+|informer|
+
+
К сожалению, в данный момент нет свободного места на оборудовании для предоставления аренды нового игрового сервера.
+|_informer|
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/services/games/mta.html b/template/sections/services/games/mta.html
new file mode 100644
index 0000000..dc333c4
--- /dev/null
+++ b/template/sections/services/games/mta.html
@@ -0,0 +1,105 @@
+
Аренда игрового сервера
+
+|!informer|
+
+
+
+
+
Внимание: заказывая сервер, вы соглашаетесь с условиями договора .
+|_!informer|
+|informer|
+
+
К сожалению, в данный момент нет свободного места на оборудовании для предоставления аренды нового игрового сервера.
+|_informer|
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/services/games/samp.html b/template/sections/services/games/samp.html
new file mode 100644
index 0000000..4a571e2
--- /dev/null
+++ b/template/sections/services/games/samp.html
@@ -0,0 +1,105 @@
+
Аренда игрового сервера
+
+|!informer|
+
+
+
+
+
Внимание: заказывая сервер, вы соглашаетесь с условиями договора .
+|_!informer|
+|informer|
+
+
К сожалению, в данный момент нет свободного места на оборудовании для предоставления аренды нового игрового сервера.
+|_informer|
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/services/index.html b/template/sections/services/index.html
new file mode 100644
index 0000000..eac5a68
--- /dev/null
+++ b/template/sections/services/index.html
@@ -0,0 +1,59 @@
+
Аренда игровых серверов
+
+
+
+
+
+
Counter-Strike: Source v34
+
+
Заказать
+
+
+
+
+
Counter-Strike: Source
+
+
Заказать
+
+
+
+
+
+
+
+
+
+
+
+
+
Дополнительные услуги
diff --git a/template/sections/user/auth.html b/template/sections/user/auth.html
new file mode 100644
index 0000000..7bbabf3
--- /dev/null
+++ b/template/sections/user/auth.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/user/gmenu.html b/template/sections/user/gmenu.html
new file mode 100644
index 0000000..8345ea3
--- /dev/null
+++ b/template/sections/user/gmenu.html
@@ -0,0 +1,13 @@
+
diff --git a/template/sections/user/inputs/signup.html b/template/sections/user/inputs/signup.html
new file mode 100644
index 0000000..163f460
--- /dev/null
+++ b/template/sections/user/inputs/signup.html
@@ -0,0 +1,6 @@
+
+
+ [info]:
+
+
+
\ No newline at end of file
diff --git a/template/sections/user/lk/auth.html b/template/sections/user/lk/auth.html
new file mode 100644
index 0000000..d3a7151
--- /dev/null
+++ b/template/sections/user/lk/auth.html
@@ -0,0 +1,16 @@
+
Информация об авторизациях
+
+
+
+
+
+ Браузер
+ Адрес
+ Дата
+
+ [auth]
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/user/lk/auth/list.html b/template/sections/user/lk/auth/list.html
new file mode 100644
index 0000000..358cdbf
--- /dev/null
+++ b/template/sections/user/lk/auth/list.html
@@ -0,0 +1,5 @@
+
+ Авторизация через браузер: [browser] (подробнее )
+ [ip]
+ [date]
+
\ No newline at end of file
diff --git a/template/sections/user/lk/index.html b/template/sections/user/lk/index.html
new file mode 100644
index 0000000..c02cf85
--- /dev/null
+++ b/template/sections/user/lk/index.html
@@ -0,0 +1,120 @@
+
Персональная информация №[id]
+
+
+
+
+
+
Дополнительная информация
+
+
+
+
+
+
Приглашенные пользователи
+
+
+
+
+ #
+ Логин
+ Дата регистрации
+ Заказанных серверов
+
+
+
+ [part_user]
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/user/lk/logs.html b/template/sections/user/lk/logs.html
new file mode 100644
index 0000000..615413e
--- /dev/null
+++ b/template/sections/user/lk/logs.html
@@ -0,0 +1,15 @@
+
Логи последних операций
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/user/lk/logs/list.html b/template/sections/user/lk/logs/list.html
new file mode 100644
index 0000000..f7a60f8
--- /dev/null
+++ b/template/sections/user/lk/logs/list.html
@@ -0,0 +1,6 @@
+
+
+ Операция совершена: [date]
+ [text]
+
+
\ No newline at end of file
diff --git a/template/sections/user/lk/logs/replenish.html b/template/sections/user/lk/logs/replenish.html
new file mode 100644
index 0000000..f7a60f8
--- /dev/null
+++ b/template/sections/user/lk/logs/replenish.html
@@ -0,0 +1,6 @@
+
+
+ Операция совершена: [date]
+ [text]
+
+
\ No newline at end of file
diff --git a/template/sections/user/lk/security.html b/template/sections/user/lk/security.html
new file mode 100644
index 0000000..f2aa70d
--- /dev/null
+++ b/template/sections/user/lk/security.html
@@ -0,0 +1,118 @@
+
Защита по IP
+
+
+
+
+
+
Защита по коду
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/user/lk/security/list.html b/template/sections/user/lk/security/list.html
new file mode 100644
index 0000000..78d46c3
--- /dev/null
+++ b/template/sections/user/lk/security/list.html
@@ -0,0 +1,4 @@
+
+ [address]
+
+
\ No newline at end of file
diff --git a/template/sections/user/lk/settings.html b/template/sections/user/lk/settings.html
new file mode 100644
index 0000000..27d2ab5
--- /dev/null
+++ b/template/sections/user/lk/settings.html
@@ -0,0 +1,66 @@
+
Аватарка
+
+
+
+
+
+
Уведомления
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/user/recovery.html b/template/sections/user/recovery.html
new file mode 100644
index 0000000..20024a5
--- /dev/null
+++ b/template/sections/user/recovery.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/user/replenish.html b/template/sections/user/replenish.html
new file mode 100644
index 0000000..297c170
--- /dev/null
+++ b/template/sections/user/replenish.html
@@ -0,0 +1,94 @@
+
Форма пополнения баланса
+
+
+
+
+
+
Логи последних пополнений
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/user/signup.html b/template/sections/user/signup.html
new file mode 100644
index 0000000..d674b32
--- /dev/null
+++ b/template/sections/user/signup.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/user/vmenu.html b/template/sections/user/vmenu.html
new file mode 100644
index 0000000..6f8d9e6
--- /dev/null
+++ b/template/sections/user/vmenu.html
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/template/sections/web/astats/free/install.html b/template/sections/web/astats/free/install.html
new file mode 100644
index 0000000..962d76a
--- /dev/null
+++ b/template/sections/web/astats/free/install.html
@@ -0,0 +1,38 @@
+
Параметры установки
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/astats/free/manage.html b/template/sections/web/astats/free/manage.html
new file mode 100644
index 0000000..57c96d1
--- /dev/null
+++ b/template/sections/web/astats/free/manage.html
@@ -0,0 +1,44 @@
+
Информация
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/csbans/free/install.html b/template/sections/web/csbans/free/install.html
new file mode 100644
index 0000000..d99797c
--- /dev/null
+++ b/template/sections/web/csbans/free/install.html
@@ -0,0 +1,47 @@
+
Параметры установки
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/csbans/free/manage.html b/template/sections/web/csbans/free/manage.html
new file mode 100644
index 0000000..0033e17
--- /dev/null
+++ b/template/sections/web/csbans/free/manage.html
@@ -0,0 +1,59 @@
+
Информация
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/csstats/free/install.html b/template/sections/web/csstats/free/install.html
new file mode 100644
index 0000000..0abda13
--- /dev/null
+++ b/template/sections/web/csstats/free/install.html
@@ -0,0 +1,38 @@
+
Параметры установки
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/csstats/free/manage.html b/template/sections/web/csstats/free/manage.html
new file mode 100644
index 0000000..961cde8
--- /dev/null
+++ b/template/sections/web/csstats/free/manage.html
@@ -0,0 +1,48 @@
+
Информация
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/hosting/free/install.html b/template/sections/web/hosting/free/install.html
new file mode 100644
index 0000000..91f06be
--- /dev/null
+++ b/template/sections/web/hosting/free/install.html
@@ -0,0 +1,39 @@
+
Параметры установки
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/hosting/free/manage.html b/template/sections/web/hosting/free/manage.html
new file mode 100644
index 0000000..1b60254
--- /dev/null
+++ b/template/sections/web/hosting/free/manage.html
@@ -0,0 +1,42 @@
+
Информация
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/mysql/free/install.html b/template/sections/web/mysql/free/install.html
new file mode 100644
index 0000000..fc6be08
--- /dev/null
+++ b/template/sections/web/mysql/free/install.html
@@ -0,0 +1,29 @@
+
Параметры установки
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/mysql/free/manage.html b/template/sections/web/mysql/free/manage.html
new file mode 100644
index 0000000..93c7c29
--- /dev/null
+++ b/template/sections/web/mysql/free/manage.html
@@ -0,0 +1,36 @@
+
Информация
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/sourcebans/free/install.html b/template/sections/web/sourcebans/free/install.html
new file mode 100644
index 0000000..4a061b4
--- /dev/null
+++ b/template/sections/web/sourcebans/free/install.html
@@ -0,0 +1,47 @@
+
Параметры установки
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/web/sourcebans/free/manage.html b/template/sections/web/sourcebans/free/manage.html
new file mode 100644
index 0000000..7325075
--- /dev/null
+++ b/template/sections/web/sourcebans/free/manage.html
@@ -0,0 +1,59 @@
+
Информация
+
+
+
+
+
+
\ No newline at end of file
diff --git a/template/sections/wiki/answer.html b/template/sections/wiki/answer.html
new file mode 100644
index 0000000..1ded203
--- /dev/null
+++ b/template/sections/wiki/answer.html
@@ -0,0 +1,7 @@
+
[question]
+
\ No newline at end of file
diff --git a/template/sections/wiki/category.html b/template/sections/wiki/category.html
new file mode 100644
index 0000000..6ed9565
--- /dev/null
+++ b/template/sections/wiki/category.html
@@ -0,0 +1,29 @@
+
Поиск
+
+
+
+
+
Категории часто задаваемых вопросов
+
[list]
+
+
+
+
diff --git a/template/sections/wiki/category/list.html b/template/sections/wiki/category/list.html
new file mode 100644
index 0000000..08bc31d
--- /dev/null
+++ b/template/sections/wiki/category/list.html
@@ -0,0 +1 @@
+
[name]
\ No newline at end of file
diff --git a/template/sections/wiki/question.html b/template/sections/wiki/question.html
new file mode 100644
index 0000000..d54609b
--- /dev/null
+++ b/template/sections/wiki/question.html
@@ -0,0 +1,2 @@
+
[category]
+
[list]
\ No newline at end of file
diff --git a/template/sections/wiki/question/list.html b/template/sections/wiki/question/list.html
new file mode 100644
index 0000000..1cf729c
--- /dev/null
+++ b/template/sections/wiki/question/list.html
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/template/sections/wiki/search.html b/template/sections/wiki/search.html
new file mode 100644
index 0000000..b2129d5
--- /dev/null
+++ b/template/sections/wiki/search.html
@@ -0,0 +1,26 @@
+
Поиск
+
+
+
+
+
Поиск по тегам
+
+
+
+
+
diff --git a/upload/avatars/.htaccess b/upload/avatars/.htaccess
new file mode 100644
index 0000000..e69de29