SlimSome CMS







Description
For over a year, I've been managing a Counter-Strike 1.6 game project, and I contemplated the idea of establishing a dedicated website. This platform would serve as a central hub, encompassing essential information such as online status, news, an online shop, and statistics for players, administrators, and banned individuals.
Despite the availability of numerous ready-made options, driven by a desire for hands-on experience, I opted to develop my own system entirely from scratch. Thus, SlimSome CMS was created—an entirely free system designed to facilitate the easy construction of a website tailored for Counter-Strike 1.6 game projects.
Details
| Technologies | PHP, SQL, SQLite, CRON, NodeJS, Gulp, jQuery, Ajax, SASS, PostCSS, SVG, API, ChartJS, Wysiwyg, CyrToLat, Pawn |
|---|---|
| Features | Own CMS: pages, news, services and user management, Online shop with real-time game server integration, Personal Account, Server status, Online chat from game, Group chat on site, Admins list, Bans list, Detailed player statistics, Email notifications, SEO optimized, Search, Pagination, Hot/Top news/comments, Payments history, Charts for analytics, Accordion, Slider, Gallery, Form files download status and many more... |
| Date | May 2021 |
| Link | http://web.archive.org/web/20230613082011/https://awesomecs.com/ |
| Github | https://github.com/zakandaiev/slimsome-cms |
Code snippets
clike User Expiration Date - amxx plugin for CS 1.6 server written in Pawn language
#include <amxmodx>
new const USERS_FILE[] = "addons/amxmodx/configs/users.ini";
new Trie: g_trie;
new g_iAdminExpired[MAX_PLAYERS + 1];
public plugin_init() {
register_plugin("SlimSome CMS: User Expiration Date", "1.0", "szawesome");
g_trie = TrieCreate();
CheckDays();
GetDays();
}
public plugin_end() {
TrieDestroy(g_trie);
}
public client_authorized(id) {
g_iAdminExpired[id] = GetTrieParsedTime(id);
}
public plugin_natives() {
register_native("admin_expired", "admin_expired_callback", true);
}
public admin_expired_callback(id) {
return g_iAdminExpired[id];
}
CheckDays() {
new file = fopen(USERS_FILE, "r+");
if(!file) return 0;
new buffer[256], curr_line;
new nickname[64], password[64], flags[32], access[32], exp_date[32];
while(!feof(file)) {
fgets(file, buffer, charsmax(buffer));
trim(buffer);
curr_line++;
if(!buffer[0]) {
continue;
}
if(buffer[0] != '^"') {
continue;
}
parse(buffer,
nickname, charsmax(nickname),
password, charsmax(password),
flags, charsmax(flags),
access, charsmax(access),
exp_date, charsmax(exp_date));
if(str_to_num(exp_date) == 0 || str_to_num(exp_date) > get_systime()) {
formatex(buffer, charsmax(buffer), "^"%s^" ^"%s^" ^"%s^" ^"%s^" ^"%s^"", nickname, password, flags, access, exp_date);
} else {
formatex(buffer, charsmax(buffer), ";^"%s^" ^"%s^" ^"%s^" ^"%s^" ^"%s^"", nickname, password, flags, access, exp_date);
}
write_file(USERS_FILE, buffer, curr_line-1);
}
// server_cmd("amx_reloadadmins"); - если плагин поставить выше admin.amxx тогда можно и не посылать эту команду
return fclose(file);
}
GetDays() {
new file = fopen(USERS_FILE, "r");
if(!file) return 0;
new buffer[256], admin_id[32], exp_date[32];
while(!feof(file)) {
fgets(file, buffer, charsmax(buffer));
trim(buffer);
if(!buffer[0] || buffer[0] != '^"')
continue;
for(new i; i<5;i++) {
if(strlen(buffer) <= 0)
break;
if(!i) {
argbreak(buffer, admin_id, charsmax(admin_id), buffer, charsmax(buffer));
} else {
argbreak(buffer, exp_date, charsmax(exp_date), buffer, charsmax(buffer));
}
}
TrieSetCell(g_trie, admin_id, str_to_num(exp_date) == 0 ? 0 : str_to_num(exp_date));
}
return fclose(file);
}
stock GetTrieParsedTime(id) {
static _id[32], cell;
get_user_name(id, _id, charsmax(_id));
if(TrieGetCell(g_trie, _id, cell)) {
return cell;
}
get_user_authid(id, _id, charsmax(_id));
if(TrieGetCell(g_trie, _id, cell)) {
return cell;
}
get_user_ip(id, _id, charsmax(_id), 1);
if(TrieGetCell(g_trie, _id, cell)) {
return cell;
}
return -1;
}
sql DB structure install query
SET NAMES UTF8;
CREATE TABLE IF NOT EXISTS `%prefix%_settings` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(64) NOT NULL,
`value` TEXT DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE `name` (name)
) ENGINE = InnoDB;
INSERT INTO `%prefix%_settings` (`name`, `value`) VALUES
('db_host', '%db_host%'),
('db_user', '%db_user%'),
('db_pass', '%db_pass%'),
('db_name', '%db_name%'),
('db_prefix', '%prefix%'),
('t_zone', '%t_zone%'),
('cron_pass', '%cron_pass%'),
('site_name', '%site_name%'),
('site_url', '%site_url%'),
('site_email', '%site_email%'),
('site_logo', '%site_logo%'),
('site_background', null),
('site_background_styles', null),
('site_color_accent', '#313946'),
('site_color_accent_2', '#fe3f99'),
('site_color_body', '#faf9fa'),
('site_color_text', '#000'),
('site_description', 'Сайт игрового сервера по игре CS 1.6'),
('site_keywords', 'сайт сервера, сервер кс, кс 1.6, cs 1.6, counter-strike 1.6'),
('site_analytics_gtag', null),
('site_chat_enabled', 'true'),
('site_chat_enabled_for_unregistereds', 'true'),
('server_ip', '%server_ip%'),
('ftp_host', '%ftp_host%'),
('ftp_login', '%ftp_login%'),
('ftp_pass', '%ftp_pass%'),
('ftp_users_path', '%ftp_users_path%'),
('ftp_bans_path', '%ftp_bans_path%'),
('ftp_stats_path', '%ftp_stats_path%'),
('payments', '%payments%'),
('socials', '[{"icon": "vk.svg", "url": "https://vk.com/szawesome", "blank": true},{"icon": "dev-cs.svg", "url": "https://dev-cs.ru/members/7458/", "blank": true},{"icon": "email.svg", "url": "mailto:szawesome95@gmail.com", "blank": true}]'),
('server_online_info', null);
CREATE TABLE IF NOT EXISTS `%prefix%_users` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`login` VARCHAR(32) NOT NULL,
`password` VARCHAR(32) NOT NULL,
`email` VARCHAR(128) DEFAULT NULL,
`nick` VARCHAR(64) DEFAULT NULL,
`steam_id` VARCHAR(32) DEFAULT NULL,
`name` VARCHAR(32) DEFAULT NULL,
`service_id` INT DEFAULT NULL,
`service_start` TIMESTAMP NULL DEFAULT NULL,
`service_end` TIMESTAMP NULL DEFAULT NULL,
`service_nolimit` BOOLEAN NOT NULL DEFAULT FALSE,
`service_bind_type` INT DEFAULT NULL,
`isadmin` BOOLEAN NOT NULL DEFAULT FALSE,
`ismoder` BOOLEAN NOT NULL DEFAULT FALSE,
`ip` VARCHAR(32) DEFAULT NULL,
`last_sign` TIMESTAMP NULL DEFAULT NULL,
`cdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE `login` (`login`),
UNIQUE `email` (`email`),
UNIQUE `nick` (`nick`),
UNIQUE `steam_id` (`steam_id`)
) ENGINE = InnoDB;
INSERT INTO `%prefix%_users` (`login`, `password`, `email`, `name`, `isadmin`, `ismoder`) VALUES
('%adm_login%', '%adm_pass%', '%adm_email%', '%adm_name%', true, true);
CREATE TABLE IF NOT EXISTS `%prefix%_services` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(64) NOT NULL,
`flags` VARCHAR(32) NOT NULL,
`days` JSON NOT NULL COMMENT 'days,price_ua,price_rub',
`images` JSON DEFAULT NULL,
`user_avatar` TEXT DEFAULT NULL,
`description` TEXT DEFAULT NULL,
`buyable` BOOLEAN NOT NULL DEFAULT TRUE,
`enabled` BOOLEAN NOT NULL DEFAULT TRUE,
PRIMARY KEY (`id`),
UNIQUE `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `%prefix%_chat` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT NOT NULL,
`message` TEXT NOT NULL,
`refference` INT DEFAULT NULL,
`cdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `%prefix%_payments` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(64) NOT NULL,
`user_id` INT DEFAULT NULL,
`user_data` JSON DEFAULT NULL,
`service_id` INT NOT NULL,
`service_name` VARCHAR(128) NOT NULL,
`days` INT NOT NULL,
`price` INT NOT NULL,
`currency` INT NOT NULL,
`prolong` BOOLEAN NOT NULL DEFAULT FALSE,
`status` INT NOT NULL,
`cdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `%prefix%_pages` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(64) NOT NULL,
`url` VARCHAR(64) NOT NULL,
`content` TEXT DEFAULT NULL,
`template` VARCHAR(64) DEFAULT NULL,
`enabled` BOOLEAN NOT NULL DEFAULT TRUE,
`page_order` INT UNSIGNED NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE `name` (`name`),
UNIQUE `url` (`url`),
UNIQUE `page_order` (`page_order`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
INSERT INTO `%prefix%_pages` (`name`, `url`, `template`, `page_order`) VALUES
('Информация', 'info', 'info', 1),
('Правила', 'rules', 'rules', 2);
CREATE TABLE IF NOT EXISTS `%prefix%_bans` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`nick` VARCHAR(128) NOT NULL,
`ip` VARCHAR(32) NOT NULL,
`steam_id` VARCHAR(32) NOT NULL,
`reason` VARCHAR(128) NOT NULL,
`created` INT DEFAULT NULL,
`length` INT DEFAULT NULL,
`admin_nick` VARCHAR(128) NOT NULL,
`unbanned` BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `%prefix%_news` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`author` INT NOT NULL,
`title` VARCHAR(64) NOT NULL,
`url` VARCHAR(128) NOT NULL,
`body` TEXT DEFAULT NULL,
`image` TEXT DEFAULT NULL,
`meta_description` TEXT DEFAULT NULL,
`meta_keywords` TEXT DEFAULT NULL,
`enabled` BOOLEAN NOT NULL DEFAULT TRUE,
`cdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE `url` (`url`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `%prefix%_comments` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`news_id` INT NOT NULL,
`author` INT NOT NULL,
`comment` TEXT NOT NULL,
`cdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `%prefix%_stats` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`nick` VARCHAR(128) NOT NULL,
`uniq` VARCHAR(32) NOT NULL,
`teamkill` INT DEFAULT NULL,
`damage` INT DEFAULT NULL,
`deaths` INT DEFAULT NULL,
`kills` INT DEFAULT NULL,
`shots` INT DEFAULT NULL,
`hits` INT DEFAULT NULL,
`headshots` INT DEFAULT NULL,
`defusions` INT DEFAULT NULL,
`defused` INT DEFAULT NULL,
`plants` INT DEFAULT NULL,
`explosions` INT DEFAULT NULL,
`head` INT DEFAULT NULL,
`chest` INT DEFAULT NULL,
`stomach` INT DEFAULT NULL,
`leftarm` INT DEFAULT NULL,
`rightarm` INT DEFAULT NULL,
`leftleg` INT DEFAULT NULL,
`rightleg` INT DEFAULT NULL,
`rank` INT DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
php Queries for charts on Bans page
<?php
$bans_top_nicks = $pdo->query("
SELECT
COUNT(id) as bans_count, nick
FROM ".$prefix."_bans
GROUP BY nick
ORDER BY bans_count DESC
LIMIT 10
")->fetchAll(PDO::FETCH_ASSOC);
$bans_top_reasons = $pdo->query("
SELECT
COUNT(id) as bans_count, reason
FROM ".$prefix."_bans
GROUP BY reason
ORDER BY bans_count DESC
LIMIT 5
")->fetchAll(PDO::FETCH_ASSOC);
$bans_per_days = $pdo->query("
SELECT
(
SELECT COUNT(id)
FROM ".$prefix."_bans
WHERE FROM_UNIXTIME(created) >= date_sub(NOW(), INTERVAL 1 DAY)
) as today,
(
SELECT COUNT(id)
FROM ".$prefix."_bans
WHERE FROM_UNIXTIME(created) >= date_sub(NOW(), INTERVAL 1 MONTH)
) as month
")->fetch(PDO::FETCH_ASSOC);
/*$bans_per_months = $pdo->query("
SELECT * FROM (
SELECT
COUNT(id) as bans_count, EXTRACT(month FROM FROM_UNIXTIME(created)) as month, EXTRACT(year FROM FROM_UNIXTIME(created)) as year
FROM ".$prefix."_bans
GROUP BY month, year
ORDER BY year DESC, month DESC
LIMIT 5
) t1 ORDER BY t1.year, t1.month
")->fetchAll(PDO::FETCH_ASSOC);*/
$bans_per_months = $pdo->query("
SELECT coalesce(bans_count, 0) as bans_count, t2.year, t2.month FROM (
SELECT
EXTRACT(year FROM FROM_UNIXTIME(created)) as year, EXTRACT(month FROM FROM_UNIXTIME(created)) as month, count(*) as bans_count
FROM ".$prefix."_bans
GROUP BY year, month
) t1
RIGHT JOIN (
SELECT EXTRACT(year FROM tt.Date) as year, EXTRACT(month FROM tt.Date) as month
FROM (
SELECT curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a) ) DAY as Date
FROM (SELECT 0 as a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as a
CROSS JOIN (SELECT 0 as a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as b
CROSS JOIN (SELECT 0 as a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as c
CROSS JOIN (SELECT 0 as a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as d
) tt
WHERE tt.Date BETWEEN date_sub(NOW(), INTERVAL 4 month) AND NOW()
GROUP BY year, month
ORDER BY year, month
) t2 ON t2.month=t1.month AND t1.year=t2.year
WHERE t2.month IS NOT NULL
ORDER BY t2.year, t2.month
LIMIT 5
")->fetchAll(PDO::FETCH_ASSOC);
?>
<div>
... ... ...
skip html layout
... ... ...
</div>
<script>
const bans_reasons_labels = [
<? foreach($bans_top_reasons as $row): ?>
'<?=$row["reason"]?>: <?=$row["bans_count"]?>',
<? endforeach; ?>
];
const bans_reasons_data = [
<? foreach($bans_top_reasons as $row): ?>
<?=$row["bans_count"]?>,
<? endforeach; ?>
];
let bans_reasons = new Chart(
$("#chart-bans-reasons"),
{
type: 'pie',
data: {
labels: bans_reasons_labels,
datasets: [{
label: 'Топ бан-причиин',
data: bans_reasons_data,
fill: false,
borderColor: '#fff',
backgroundColor: [
'#ff6384',
'#36a2eb',
'#ffcd56',
'#fe3f99',
'#00b96c'
],
hoverOffset: 3
}]
},
options: {
plugins: {
legend: {
position: 'bottom',
align: 'start',
onHover: pieHandleHover,
onLeave: pieHandleLeave
},
tooltip: {
callbacks: {
label: function(context) {
var label = context.label;
var dataset = context.dataset;
var total = dataset.data.reduce(function(previousValue, currentValue, currentIndex, array) {
return previousValue + currentValue;
});
var currentValue = dataset.data[context.dataIndex];
var percentage = Math.floor(((currentValue/total) * 100)+0.5);
return label + " (" + percentage + "%)";
}
}
}
}
}
}
);
function pieHandleHover(evt, item, legend) {
legend.chart.data.datasets[0].backgroundColor.forEach((color, index, colors) => {
colors[index] = index === item.index || color.length === 9 ? color : color + '4D';
});
legend.chart.update();
}
function pieHandleLeave(evt, item, legend) {
legend.chart.data.datasets[0].backgroundColor.forEach((color, index, colors) => {
colors[index] = color.length === 9 ? color.slice(0, -2) : color;
});
legend.chart.update();
}
</script>
<script>
const bans_per_month_labels = [
<? foreach($bans_per_months as $row): ?>
'<?=getMonthName($row["month"])?>',
<? endforeach; ?>
];
const bans_per_month_data = [
<? foreach($bans_per_months as $row): ?>
<?=$row["bans_count"]?>,
<? endforeach; ?>
];
var bans_per_month = new Chart(
$("#chart-bans-per-month"),
{
type: 'line',
data: {
labels: bans_per_month_labels,
datasets: [{
label: 'Количество забаненых',
data: bans_per_month_data,
fill: false,
borderColor: '#fe3f99',
backgroundColor: '#313946',
tension: 0.3
}]
},
options: {
elements: {
point: {
radius: 5
}
},
plugins: {
legend: {
display: false
}
}
}
}
);
</script>
php Service description with carousel & gallery snippet on Buy page
<div class="buy-content__right">
<section class="block">
<h2 class="block__title">Описание</h2>
<?php foreach($db_services_buyable as $rows): ?>
<div id="service_desc_<?=$rows["id"]?>">
<? if(!empty($rows["images"])): ?>
<div class="buy-content__img appear-right">
<div class="carousel">
<? foreach(json_decode($rows["images"], true) as $image): ?>
<div class="carousel__img"><img src="<?=urlEncodeSpaces($image)?>" alt="Привилегия <?=$rows["name"]?>" class="buy-content" data-zoomable></div>
<? endforeach; ?>
</div>
</div>
<? endif; ?>
<div class="buy-content__description appear-right anim-delay-1">
<div class="well well_primary">
<h3 class="text-center"><?=$rows["name"]?></h3>
<?php foreach(json_decode($rows["days"], true) as $days): ?>
<?php
if ($days["days"] == 0) {
$days_count = 'навсегда';
} else {
$days_count = 'за '.$days["days"].' дней';
}
?>
<p><span class="label label-info"><?=$days["price_rub"]?> руб.</span> или <span class="label label-info"><?=$days["price_uah"]?> грн.</span> <?=$days_count?>.</p>
<? endforeach; ?>
<? if(isset($rows["description"])): ?>
<?=$rows["description"]?>
<? else: ?>
<p>Описание не заполнено.</p>
<? if($is_user_admin): ?>
<a href="profile?section=services&edit=<?=$rows["id"]?>" class="btn">Заполнить</a>
<? endif; ?>
<? endif; ?>
</div>
</div>
</div>
<? endforeach; ?>
</section>
</div>
php Hitbox model layout
<div class="hitbox">
<div class="hitbox__item head"><span class="hitbox__tooltip">Попаданий в голову: <?= $player_arr["head"] ?> (<?= round(100 * $player_arr["head"] / $hitbox_sum, 2) ?>%)</span></div>
<div class="hitbox__item chest"><span class="hitbox__tooltip">Попаданий в грудь: <?= $player_arr["chest"] ?> (<?= round(100 * $player_arr["chest"] / $hitbox_sum, 2) ?>%)</span></div>
<div class="hitbox__item stomach"><span class="hitbox__tooltip">Попаданий в живот: <?= $player_arr["stomach"] ?> (<?= round(100 * $player_arr["stomach"] / $hitbox_sum, 2) ?>%)</span></div>
<div class="hitbox__item larm"><span class="hitbox__tooltip">Попаданий в левую руку: <?= $player_arr["leftarm"] ?> (<?= round(100 * $player_arr["leftarm"] / $hitbox_sum, 2) ?>%)</span></div>
<div class="hitbox__item rarm"><span class="hitbox__tooltip">Попаданий в правую руку: <?= $player_arr["rightarm"] ?> (<?= round(100 * $player_arr["rightarm"] / $hitbox_sum, 2) ?>%)</span></div>
<div class="hitbox__item lleg"><span class="hitbox__tooltip">Попаданий в левую ногу: <?= $player_arr["leftleg"] ?> (<?= round(100 * $player_arr["leftleg"] / $hitbox_sum, 2) ?>%)</span></div>
<div class="hitbox__item rleg"><span class="hitbox__tooltip">Попаданий в правую ногу: <?= $player_arr["rightleg"] ?> (<?= round(100 * $player_arr["rightleg"] / $hitbox_sum, 2) ?>%)</span></div>
<img class="hitbox__image" src="img/stats/player_model.png" alt="hitbox">
</div>
scss Hitbox model styles
.hitbox {
display: block;
width: 100%;
position: relative;
&__image {
display: block;
width: 100%;
height: 100%;
filter: drop-shadow(0 3px 4px rgba(10, 31, 68, 0.1));
}
&__item {
position: absolute;
z-index: 1;
cursor: pointer;
&.head {
width: 14%;
height: 17%;
top: 0;
left: 50%;
transform: translateX(-50%);
}
&.chest {
width: 24%;
height: 18%;
top: 17%;
left: 50%;
transform: translateX(-50%);
}
&.stomach {
width: 24%;
height: 20%;
top: 35%;
left: 50%;
transform: translateX(-50%);
}
&.larm {
width: 38%;
height: 14%;
top: 17%;
right: 0;
}
&.rarm {
width: 38%;
height: 14%;
top: 17%;
left: 0;
}
&.lleg {
width: 14%;
height: 45%;
top: 55%;
right: 36%;
}
&.rleg {
width: 14%;
height: 45%;
top: 55%;
left: 36%;
}
}
&__tooltip {
// positioning
position: absolute;
z-index: 2;
top: 0;
left: 50%;
transform: translate(-50%, -110%);
position: absolute;
white-space: nowrap;
text-overflow: ellipsis;
// stylings
background: rgba(0, 0, 0, 0.9);
color: #fff;
text-align: center;
padding: 5px;
border-radius: $brs;
box-shadow: 0 3px 4px rgba(10, 31, 68, 0.1), 0 0 1px rgba(10, 31, 68, 0.08);
cursor: default;
&::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: rgba(0, 0, 0, 0.9) transparent transparent transparent;
}
// appearance
visibility: hidden;
opacity: 0;
transition: all .3s;
}
.hitbox__item:hover .hitbox__tooltip {
visibility: visible;
opacity: 1;
}
}
js Accordion handler
$(document).ready(function() {
// ACCODRION
const accondionsContent = $('.accordion__content').hide();
$('.accordion__title').click(function() {
if ($(this).hasClass("active")) {
$(this).next().slideUp();
} else {
$(this).next().slideDown();
}
$(this).toggleClass("active");
});
});
scss Accordion styles
.accordion {
display: block;
width: 100%;
&__title {
display: block;
width: 100%;
cursor: pointer;
position: relative;
&::before, &::after {
content: "";
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: #000;
transition: all .3s;
}
&::before {
right: 9px;
width: 2px;
height: 20px;
}
&::after {
right: 0;
width: 20px;
height: 2px;
}
&.active::before {
transform: translateY(-50%) rotate(90deg);
}
&.active::after {
transform: translateY(-50%) rotate(90deg);
opacity: 0;
visibility: hidden;
}
}
&__content {
display: block;
width: 100%;
}
}
php News prev&next buttons query
<?php
$news_extra_query = $pdo->prepare("
SELECT
JSON_OBJECT(
'title', prev_title,
'url', prev_url,
'image', prev_image
) as extra_prev,
JSON_OBJECT(
'title', next_title,
'url', next_url,
'image', next_image
) as extra_next
FROM (
SELECT id,
LAG(title) OVER (ORDER BY cdate) as prev_title,
LAG(url) OVER (ORDER BY cdate) as prev_url,
LAG(image) OVER (ORDER BY cdate) as prev_image,
LEAD(title) OVER (ORDER BY cdate) as next_title,
LEAD(url) OVER (ORDER BY cdate) as next_url,
LEAD(image) OVER (ORDER BY cdate) as next_image
FROM ".$prefix."_news
WHERE enabled IS TRUE
) t
WHERE id=:current_id;
");
$news_extra_query->bindParam(":current_id", $current_post["id"]);
$news_extra_query->execute();
$news_extra = $news_extra_query->fetch(PDO::FETCH_LAZY);
$extra_prev = json_decode($news_extra->extra_prev, true);
$extra_next = json_decode($news_extra->extra_next, true);
php Player skill formula
function getPlayerSkill($kills, $deaths) {
$sum = $kills + $deaths;
if($sum == 0) {
return "?";
}
return round(100 * $kills / $sum, 2);
}
php Replace smile codes with images
function replaceSmiles($text) {
$smiles = array(
':ban:' => '<img src="/img/smiles/ban.gif" alt="ban">',
':beer:' => '<img src="/img/smiles/beer.gif" alt="beer">',
':bomb:' => '<img src="/img/smiles/bomb.gif" alt="bomb">',
':cool:' => '<img src="/img/smiles/cool.gif" alt="cool">',
'8)' => '<img src="/img/smiles/cool.gif" alt="cool">',
':crazy:' => '<img src="/img/smiles/crazy.gif" alt="crazy">',
'oO' => '<img src="/img/smiles/crazy.gif" alt="crazy">',
'o0' => '<img src="/img/smiles/crazy.gif" alt="crazy">',
'Oo' => '<img src="/img/smiles/crazy.gif" alt="crazy">',
'0o' => '<img src="/img/smiles/crazy.gif" alt="crazy">',
':devil:' => '<img src="/img/smiles/devil.gif" alt="devil">',
'>:)' => '<img src="/img/smiles/devil.gif" alt="devil">',
':eek:' => '<img src="/img/smiles/eek.gif" alt="eek">',
'OO' => '<img src="/img/smiles/eek.gif" alt="eek">',
':fu:' => '<img src="/img/smiles/fu.gif" alt="fu">',
':lol:' => '<img src="/img/smiles/lol.gif" alt="lol">',
'xD' => '<img src="/img/smiles/lol.gif" alt="lol">',
':love:' => '<img src="/img/smiles/love.gif" alt="love">',
'<3' => '<img src="/img/smiles/love.gif" alt="love">',
':ms:' => '<img src="/img/smiles/ms.gif" alt="ms">',
':nice:' => '<img src="/img/smiles/nice.gif" alt="nice">',
':razz:' => '<img src="/img/smiles/razz.gif" alt="razz">',
':p' => '<img src="/img/smiles/razz.gif" alt="razz">',
':red:' => '<img src="/img/smiles/red.gif" alt="red">',
':.' => '<img src="/img/smiles/red.gif" alt="red">',
':sad:' => '<img src="/img/smiles/sad.gif" alt="sad">',
':(' => '<img src="/img/smiles/sad.gif" alt="sad">',
':shrug:' => '<img src="/img/smiles/shrug.gif" alt="shrug">',
':smile:' => '<img src="/img/smiles/smile.gif" alt="smile">',
':)' => '<img src="/img/smiles/smile.gif" alt="smile">',
':sos:' => '<img src="/img/smiles/sos.gif" alt="sos">',
':sps:' => '<img src="/img/smiles/sps.gif" alt="sps">',
':stop:' => '<img src="/img/smiles/stop.gif" alt="stop">',
':uzi:' => '<img src="/img/smiles/uzi.gif" alt="uzi">',
':wink:' => '<img src="/img/smiles/wink.gif" alt="wink">',
';)' => '<img src="/img/smiles/wink.gif" alt="wink">',
':woohoo:' => '<img src="/img/smiles/woohoo.gif" alt="woohoo">',
':xd:' => '<img src="/img/smiles/xd.gif" alt="xd">',
':D' => '<img src="/img/smiles/xd.gif" alt="xd">'
);
$output = $text;
foreach($smiles as $smile => $image) {
/*$smile = preg_quote($smile);
$output = preg_replace("~\b$smile\b~", $image, $output);*/
$output = str_replace($smile, $image, $output);
}
return $output;
}
php Get numerical noun form
function get_numerical_noun_form($number) {
// Nominative - комментарий
// Singular - комментария
// Plural - комментариев
if ($number > 10 && (($number % 100) / 10) == 1) {
return "Plural";
}
$number = $number % 10;
if ($number == 1) {
return "Nominative";
} else if ($number == 2 || $number == 3 || $number == 4) {
return "Singular";
} else {
return "Plural";
}
}
php Is number in range
function isNumInRange($number, $min, $max) {
if ($number >= $min && $number < $max) {
return true;
}
return false;
}
php Generate a password
function generatePassword($length) {
$string = "";
$chars = "abcdefghijklmanopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$size = strlen($chars);
for ($i = 0; $i < $length; $i++) {
$string .= $chars[rand(0, $size - 1)];
}
return $string;
}
js Chat load more on scroll
$(document).ready(function() {
// chat auto scroll to bottom on page load
if ($(".chat__messages").length) {
$(".chat__messages").scrollTop($(".chat__messages")[0].scrollHeight);
}
// chat load more
let is_chat_load_more_enabled = true;
$(".chat__messages").on("scroll", function() {
const $this = $(this);
let messages_count = $(this).find(".chat__message").length;
if ($(this).scrollTop() < 300 && is_chat_load_more_enabled) {
is_chat_load_more_enabled = false;
$.ajax({
method: "POST",
url: "../core/db_chat_load_more.php",
data: { from: messages_count }
}).done(function(response) {
const jsonData = JSON.parse(response);
if (jsonData.success == "1") {
is_chat_load_more_enabled = true;
$this.prepend(jsonData.body);
}
});
}
});
});
js Inputs with live color changer
$(document).ready(function() {
// COLORS LIVE CHANGE
$("input[name='site_color_accent']").on("input", function() {
$(":root").css("--accent-color", $(this).val());
});
$("input[name='site_color_accent_2']").on("input", function() {
$(":root").css("--accent-color-2", $(this).val());
});
$("input[name='site_color_body']").on("input", function() {
$(":root").css("--body-color", $(this).val());
});
$("input[name='site_color_text']").on("input", function() {
$(":root").css("--text-color", $(this).val());
});
});
js Copy to clipboard data-attribute handler
$(document).ready(function() {
// COPY TO CLIPBOARD
// usage: <span data-copy="text to copy">text to show</span>
$("[data-copy]").on("click", function() {
const $temp = $("<input>");
$("body").append($temp);
$temp.val($(this).data("copy")).select();
document.execCommand("copy");
$temp.remove();
if ($(this).data("copy-toast")) {
toastr["info"]($(this).data("copy-toast"), null, {"positionClass": "toast-bottom-right"});
} else {
toastr["info"]("Скопировано", null, {"positionClass": "toast-bottom-right"});
}
});
});