Compare commits

...

16 commits

Author SHA1 Message Date
n1rwana
7782a231d3
Merge a16e15eaef into 48ccd7ef43 2024-12-09 17:46:29 +03:00
mrilyew
48ccd7ef43 fix(ajax): maybe last fixes 2024-12-08 16:45:35 +03:00
mrilyew
9d75f0b249 fix(feed): fix dementia 2024-12-08 12:03:32 +03:00
mrilyew
35bfe97360 fix(ajax): fix #1169, fix #1172 2024-12-08 11:45:19 +03:00
mrilyew
efa3e1213c fix(ajax player): i guess last fix 2024-12-07 21:58:53 +03:00
mrilyew
21f3ebea3d fix(ajax player): reset tracks on player close 2024-12-07 21:42:30 +03:00
mrilyew
e329d3dcd4 fix(audios): fix invalid avatar at group add msg 2024-12-07 20:48:08 +03:00
mrilyew
cf43a52713 fix(ajax): track set, photo commenting, reposts
fixes #1170
2024-12-07 19:36:56 +03:00
mrilyew
d504067722 fix(ajax): fix photo picker 2024-12-07 17:48:04 +03:00
mrilyew
9ceeee9bd5 fix(audios): do not show ajax player if no track 2024-12-07 17:27:28 +03:00
mrilyew
7d450c18fd
feat: ajax, audio refactor (#1164)
* Make audio player pizdatey

* Simple ajax routing (scripts are broken)

* Fix most common script problems pt1

* Add ajax player

Осталось пофиксить скрипты и создание плейлистов! Ну и ещё некоторые хуйни по аудиозаписям которые я задумал.

* Add context menu for audios

* Refactor audio upload page

* Repair playlists

* Fix main problems

* Midnight teme adaptation

* Stupid bug fix

* Save audios list in da localstorage and fix msgbox

* Fix time setting

* add beforeUnload event

* Stupid bugs fix

* update page footer on transition

* fix wall publihing

* fix 500 on non existent page
2024-12-07 17:18:29 +03:00
mrilyew
c44a4ce988 fix(api): remove usless rate limits and fix php8.1 2024-12-06 21:31:05 +03:00
veselcraft
2aedc3e861
fix(video): instead of scailing it down to 4:3 with black bars scale it by 480px 2024-12-04 16:26:03 +03:00
Малушік
7474d46743
fix(locale-ua): fix spelling errors and add new translations (#1159)
* resolve 5c19637715 (locale-uk)

* resolve 73a067a0c5 (locale-uk)

* resolve cf0b4be3fb (locale-uk)

* resolve aff19de2ea (locale-uk)

* fix(locale-uk): fix spelling errors

* fix(locale-uk): fix spelling errors

* fix(locale-uk): fix spelling errors

* fix(locale-uk): fix spelling errors

* fix(locale-uk): fix spelling errors

* resolve 2af8447a0f (locale-uk)

* resolve e4a79a8ffb (locale-uk)

* fix(locale-uk): fix spelling errors

* small typo fix
2024-11-27 13:49:44 +03:00
mrilyew
e4a79a8ffb feat(privacy): add ability to hide your like 2024-11-25 22:29:33 +03:00
n1rwana
a16e15eaef
Фикс проверки заблокированных ссылок 2023-08-05 14:49:41 +03:00
69 changed files with 3613 additions and 2363 deletions

View file

@ -287,7 +287,6 @@ final class Board extends VKAPIRequestHandler
{ {
# start_comment_id ne robit # start_comment_id ne robit
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction();
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id); $topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
@ -321,7 +320,6 @@ final class Board extends VKAPIRequestHandler
{ {
# order и extended ничё не делают # order и extended ничё не делают
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction();
$arr = []; $arr = [];
$club = (new ClubsRepo)->get($group_id); $club = (new ClubsRepo)->get($group_id);
@ -330,7 +328,7 @@ final class Board extends VKAPIRequestHandler
$arr["count"] = (new TopicsRepo)->getClubTopicsCount($club); $arr["count"] = (new TopicsRepo)->getClubTopicsCount($club);
$arr["items"] = []; $arr["items"] = [];
$arr["default_order"] = $order; $arr["default_order"] = $order;
$arr["can_add_topics"] = $club->canBeModifiedBy($this->getUser()) ? true : $club->isEveryoneCanCreateTopics() ? true : false; $arr["can_add_topics"] = $club->canBeModifiedBy($this->getUser()) ? true : ($club->isEveryoneCanCreateTopics() ? true : false);
$arr["profiles"] = []; $arr["profiles"] = [];
if(empty($topic_ids)) { if(empty($topic_ids)) {

View file

@ -288,7 +288,6 @@ final class Photos extends VKAPIRequestHandler
function getAlbums(int $owner_id, string $album_ids = "", int $offset = 0, int $count = 100, bool $need_system = true, bool $need_covers = true, bool $photo_sizes = false) function getAlbums(int $owner_id, string $album_ids = "", int $offset = 0, int $count = 100, bool $need_system = true, bool $need_covers = true, bool $photo_sizes = false)
{ {
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction();
$res = []; $res = [];
@ -360,7 +359,6 @@ final class Photos extends VKAPIRequestHandler
function getAlbumsCount(int $user_id = 0, int $group_id = 0) function getAlbumsCount(int $user_id = 0, int $group_id = 0)
{ {
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction();
if($user_id == 0 && $group_id == 0 || $user_id > 0 && $group_id > 0) if($user_id == 0 && $group_id == 0 || $user_id > 0 && $group_id > 0)
$this->fail(21, "Select user_id or group_id"); $this->fail(21, "Select user_id or group_id");
@ -389,7 +387,6 @@ final class Photos extends VKAPIRequestHandler
function getById(string $photos, bool $extended = false, bool $photo_sizes = false) function getById(string $photos, bool $extended = false, bool $photo_sizes = false)
{ {
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction();
$phts = explode(",", $photos); $phts = explode(",", $photos);
$res = []; $res = [];
@ -413,7 +410,6 @@ final class Photos extends VKAPIRequestHandler
function get(int $owner_id, int $album_id, string $photo_ids = "", bool $extended = false, bool $photo_sizes = false, int $offset = 0, int $count = 10) function get(int $owner_id, int $album_id, string $photo_ids = "", bool $extended = false, bool $photo_sizes = false, int $offset = 0, int $count = 10)
{ {
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction();
$res = []; $res = [];
@ -632,7 +628,6 @@ final class Photos extends VKAPIRequestHandler
function getAll(int $owner_id, bool $extended = false, int $offset = 0, int $count = 100, bool $photo_sizes = false) function getAll(int $owner_id, bool $extended = false, int $offset = 0, int $count = 100, bool $photo_sizes = false)
{ {
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction();
if($owner_id < 0) if($owner_id < 0)
$this->fail(4, "This method doesn't works with clubs"); $this->fail(4, "This method doesn't works with clubs");
@ -661,7 +656,6 @@ final class Photos extends VKAPIRequestHandler
function getComments(int $owner_id, int $photo_id, bool $need_likes = false, int $offset = 0, int $count = 100, bool $extended = false, string $fields = "") function getComments(int $owner_id, int $photo_id, bool $need_likes = false, int $offset = 0, int $count = 100, bool $extended = false, string $fields = "")
{ {
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction();
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id); $photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
$comms = array_slice(iterator_to_array($photo->getComments(1, $offset + $count)), $offset); $comms = array_slice(iterator_to_array($photo->getComments(1, $offset + $count)), $offset);

View file

@ -147,6 +147,11 @@ class Audio extends Media
return $this->getRecord()->performer; return $this->getRecord()->performer;
} }
function getPerformers(): array
{
return explode(", ", $this->getRecord()->performer);
}
function getName(): string function getName(): string
{ {
return $this->getPerformer() . "" . $this->getTitle(); return $this->getPerformer() . "" . $this->getTitle();

View file

@ -39,7 +39,7 @@ class BannedLink extends RowModel
function getRegexpRule(): string function getRegexpRule(): string
{ {
return addslashes("/" . $this->getDomain() . $this->getRawRegexp() . "/"); return "/^" . $this->getDomain() . "\/" . $this->getRawRegexp() . "$/i";
} }
function getRawRegexp(): string function getRawRegexp(): string

View file

@ -301,7 +301,7 @@ class Post extends Postable
{ {
$liked = parent::toggleLike($user); $liked = parent::toggleLike($user);
if($this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment) if(!$user->isPrivateLikes() && $this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment)
(new LikeNotification($this->getOwner(false), $this, $user))->emit(); (new LikeNotification($this->getOwner(false), $this, $user))->emit();
foreach($this->getChildren() as $attachment) foreach($this->getChildren() as $attachment)

View file

@ -97,8 +97,14 @@ abstract class Postable extends Attachable
"target" => $this->getRecord()->id, "target" => $this->getRecord()->id,
])->page($page, $perPage); ])->page($page, $perPage);
foreach($sel as $like) foreach($sel as $like) {
yield (new Users)->get($like->origin); $user = (new Users)->get($like->origin);
if($user->isPrivateLikes() && OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"]) {
$user = (new Users)->get((int) OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"]);
}
yield $user;
}
} }
function isAnonymous(): bool function isAnonymous(): bool

View file

@ -498,6 +498,7 @@ class User extends RowModel
"wall.write", "wall.write",
"messages.write", "messages.write",
"audios.read", "audios.read",
"likes.read",
], ],
])->get($id); ])->get($id);
} }
@ -1062,6 +1063,7 @@ class User extends RowModel
"wall.write", "wall.write",
"messages.write", "messages.write",
"audios.read", "audios.read",
"likes.read",
], ],
])->set($id, $status)->toInteger()); ])->set($id, $status)->toInteger());
} }
@ -1338,6 +1340,11 @@ class User extends RowModel
return $this->getId(); return $this->getId();
} }
function isPrivateLikes(): bool
{
return $this->getPrivacySetting("likes.read") == User::PRIVACY_NO_ONE;
}
function toVkApiStruct(?User $user = NULL, string $fields = ''): object function toVkApiStruct(?User $user = NULL, string $fields = ''): object
{ {
$res = (object) []; $res = (object) [];

View file

@ -250,13 +250,15 @@ class Audios
$result = $this->audios->where([ $result = $this->audios->where([
"unlisted" => 0, "unlisted" => 0,
"deleted" => 0, "deleted" => 0,
/*"withdrawn" => 0,
"processed" => 1,*/
]); ]);
$order_str = (in_array($order['type'], ['id', 'length', 'listens']) ? $order['type'] : 'id') . ' ' . ($order['invert'] ? 'ASC' : 'DESC');; $order_str = (in_array($order['type'], ['id', 'length', 'listens']) ? $order['type'] : 'id') . ' ' . ($order['invert'] ? 'ASC' : 'DESC');;
if($params["only_performers"] == "1") { if($params["only_performers"] == "1") {
$result->where("performer LIKE ?", $query); $result->where("performer LIKE ?", $query);
} else { } else {
$result->where("name LIKE ? OR performer LIKE ?", $query, $query); $result->where("CONCAT_WS(' ', performer, name) LIKE ?", $query);
} }
foreach($params as $paramName => $paramValue) { foreach($params as $paramName => $paramValue) {

View file

@ -3,6 +3,7 @@ namespace openvk\Web\Models\Repositories;
use Chandler\Database\DatabaseConnection as DB; use Chandler\Database\DatabaseConnection as DB;
use Nette\Database\Table\{ActiveRow, Selection}; use Nette\Database\Table\{ActiveRow, Selection};
use openvk\Web\Models\Entities\BannedLink; use openvk\Web\Models\Entities\BannedLink;
use function Symfony\Component\Translation\t;
class BannedLinks class BannedLinks
{ {
@ -43,7 +44,7 @@ class BannedLinks
function isDomainBanned(string $domain): bool function isDomainBanned(string $domain): bool
{ {
return sizeof($this->bannedLinks->where(["link" => $domain, "regexp_rule" => ""])) > 0; return sizeof($this->bannedLinks->where(["domain" => $domain, "regexp_rule" => ""])) > 0;
} }
function genLinks($rules): \Traversable function genLinks($rules): \Traversable
@ -57,12 +58,14 @@ class BannedLinks
foreach($links as $link) foreach($links as $link)
if (preg_match($link->getRegexpRule(), $uri)) if (preg_match($link->getRegexpRule(), $uri))
yield $link->getId(); yield $link->getId();
else if ($this->isDomainBanned($link->getDomain()))
yield $link->getId();
} }
function check(string $url): ?array function check(string $url): ?array
{ {
$uri = strstr(str_replace(["https://", "http://"], "", $url), "/", true); $uri = str_replace(["https://", "http://"], "", $url);
$domain = str_replace("www.", "", $uri); $domain = explode("/", str_replace("www.", "", $uri))[0];
$rules = $this->getByDomain($domain); $rules = $this->getByDomain($domain);
if (is_null($rules)) if (is_null($rules))

View file

@ -13,7 +13,7 @@ Move-Item $file $temp
# video stub logic was implicitly deprecated, so we start processing at once # video stub logic was implicitly deprecated, so we start processing at once
ffmpeg -i $temp -ss 00:00:01.000 -vframes 1 "$dir$hashT/$hash.gif" ffmpeg -i $temp -ss 00:00:01.000 -vframes 1 "$dir$hashT/$hash.gif"
ffmpeg -i $temp -c:v libx264 -q:v 7 -c:a libmp3lame -q:a 4 -tune zerolatency -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y $temp2 ffmpeg -i $temp -c:v libx264 -q:v 7 -c:a libmp3lame -q:a 4 -tune zerolatency -vf "scale=480:-1,setsar=1" -y $temp2
Move-Item $temp2 "$dir$hashT/$hash.mp4" Move-Item $temp2 "$dir$hashT/$hash.mp4"
Remove-Item $temp Remove-Item $temp

View file

@ -3,7 +3,7 @@ tmpfile="$RANDOM-$(date +%s%N)"
cp $2 "/tmp/vid_$tmpfile.bin" cp $2 "/tmp/vid_$tmpfile.bin"
nice ffmpeg -i "/tmp/vid_$tmpfile.bin" -ss 00:00:01.000 -vframes 1 $3${4:0:2}/$4.gif nice ffmpeg -i "/tmp/vid_$tmpfile.bin" -ss 00:00:01.000 -vframes 1 $3${4:0:2}/$4.gif
nice -n 20 ffmpeg -i "/tmp/vid_$tmpfile.bin" -c:v libx264 -q:v 7 -c:a libmp3lame -q:a 4 -tune zerolatency -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y "/tmp/ffmOi$tmpfile.mp4" nice -n 20 ffmpeg -i "/tmp/vid_$tmpfile.bin" -c:v libx264 -q:v 7 -c:a libmp3lame -q:a 4 -tune zerolatency -vf "scale=480:-1,setsar=1" -y "/tmp/ffmOi$tmpfile.mp4"
rm -rf $3${4:0:2}/$4.mp4 rm -rf $3${4:0:2}/$4.mp4
mv "/tmp/ffmOi$tmpfile.mp4" $3${4:0:2}/$4.mp4 mv "/tmp/ffmOi$tmpfile.mp4" $3${4:0:2}/$4.mp4

View file

@ -481,7 +481,7 @@ final class AdminPresenter extends OpenVKPresenter
if ($link) { if ($link) {
$link->setDomain($new_domain ?? $this->postParam("link")); $link->setDomain($new_domain ?? $this->postParam("link"));
$link->setReason($new_reason); $link->setReason($new_reason);
$link->setRegexp_rule($this->postParam("regexp")); $link->setRegexp_rule(mb_strlen(trim($this->postParam("regexp"))) > 0 ? $this->postParam("regexp") : "");
$link->save(); $link->save();
} else { } else {
if (!$new_domain) if (!$new_domain)
@ -490,7 +490,7 @@ final class AdminPresenter extends OpenVKPresenter
$link = new BannedLink; $link = new BannedLink;
$link->setDomain($new_domain); $link->setDomain($new_domain);
$link->setReason($new_reason); $link->setReason($new_reason);
$link->setRegexp_rule($this->postParam("regexp")); $link->setRegexp_rule(mb_strlen(trim($this->postParam("regexp"))) > 0 ? $this->postParam("regexp") : "");
$link->setInitiator($this->user->identity->getId()); $link->setInitiator($this->user->identity->getId());
$link->save(); $link->save();

View file

@ -97,9 +97,12 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->club = $owner < 0 ? $entity : NULL; $this->template->club = $owner < 0 ? $entity : NULL;
$this->template->isMy = ($owner > 0 && ($entity->getId() === $this->user->id)); $this->template->isMy = ($owner > 0 && ($entity->getId() === $this->user->id));
$this->template->isMyClub = ($owner < 0 && $entity->canBeModifiedBy($this->user->identity)); $this->template->isMyClub = ($owner < 0 && $entity->canBeModifiedBy($this->user->identity));
} else { } else if ($mode === 'alone_audio') {
$audios = $this->audios->getPopular(); $audios = [$this->template->alone_audio];
$audiosCount = $audios->size(); $audiosCount = 1;
$this->template->owner = $this->user->identity;
$this->template->ownerId = $this->user->id;
} }
// $this->renderApp("owner=$owner"); // $this->renderApp("owner=$owner");
@ -271,6 +274,19 @@ final class AudioPresenter extends OpenVKPresenter
} }
} }
function renderAloneAudio(int $owner_id, int $audio_id): void
{
$this->assertUserLoggedIn();
$found_audio = $this->audios->get($audio_id);
if(!$found_audio || $found_audio->isDeleted() || !$found_audio->canBeViewedBy($this->user->identity)) {
$this->notFound();
}
$this->template->alone_audio = $found_audio;
$this->renderList(NULL, 'alone_audio');
}
function renderListen(int $id): void function renderListen(int $id): void
{ {
if ($_SERVER["REQUEST_METHOD"] === "POST") { if ($_SERVER["REQUEST_METHOD"] === "POST") {
@ -327,17 +343,15 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->club = $club; $this->template->club = $club;
} }
$this->template->owner = $owner;
if ($_SERVER["REQUEST_METHOD"] === "POST") { if ($_SERVER["REQUEST_METHOD"] === "POST") {
$title = $this->postParam("title"); $title = $this->postParam("title");
$description = $this->postParam("description"); $description = $this->postParam("description");
$is_unlisted = (int)$this->postParam('is_unlisted'); $is_unlisted = (int)$this->postParam('is_unlisted');
$is_ajax = (int)$this->postParam('ajax') == 1;
$audios = !empty($this->postParam("audios")) ? array_slice(explode(",", $this->postParam("audios")), 0, 1000) : []; $audios = array_slice(explode(",", $this->postParam("audios")), 0, 1000);
if(empty($title) || iconv_strlen($title) < 1) if(empty($title) || iconv_strlen($title) < 1)
$this->flashFail("err", tr("error"), tr("set_playlist_name")); $this->flashFail("err", tr("error"), tr("set_playlist_name"), NULL, $is_ajax);
$playlist = new Playlist; $playlist = new Playlist;
$playlist->setOwner($owner); $playlist->setOwner($owner);
@ -348,12 +362,12 @@ final class AudioPresenter extends OpenVKPresenter
if($_FILES["cover"]["error"] === UPLOAD_ERR_OK) { if($_FILES["cover"]["error"] === UPLOAD_ERR_OK) {
if(!str_starts_with($_FILES["cover"]["type"], "image")) if(!str_starts_with($_FILES["cover"]["type"], "image"))
$this->flashFail("err", tr("error"), tr("not_a_photo")); $this->flashFail("err", tr("error"), tr("not_a_photo"), NULL, $is_ajax);
try { try {
$playlist->fastMakeCover($this->user->id, $_FILES["cover"]); $playlist->fastMakeCover($this->user->id, $_FILES["cover"]);
} catch(\Throwable $e) { } catch(\Throwable $e) {
$this->flashFail("err", tr("error"), tr("invalid_cover_photo")); $this->flashFail("err", tr("error"), tr("invalid_cover_photo"), NULL, $is_ajax);
} }
} }
@ -361,25 +375,22 @@ final class AudioPresenter extends OpenVKPresenter
foreach($audios as $audio) { foreach($audios as $audio) {
$audio = $this->audios->get((int)$audio); $audio = $this->audios->get((int)$audio);
if(!$audio || $audio->isDeleted())
if(!$audio || $audio->isDeleted() || !$audio->canBeViewedBy($this->user->identity))
continue; continue;
$playlist->add($audio); $playlist->add($audio);
} }
$playlist->bookmark(isset($club) ? $club : $this->user->identity); $playlist->bookmark(isset($club) ? $club : $this->user->identity);
if($is_ajax) {
$this->returnJson([
'success' => true,
'redirect' => '/playlist' . $owner . "_" . $playlist->getId()
]);
}
$this->redirect("/playlist" . $owner . "_" . $playlist->getId()); $this->redirect("/playlist" . $owner . "_" . $playlist->getId());
} else { } else {
if(isset($club)) { $this->template->owner = $owner;
$this->template->audios = iterator_to_array($this->audios->getByClub($club, 1, 10));
$count = (new Audios)->getClubCollectionSize($club);
} else {
$this->template->audios = iterator_to_array($this->audios->getByUser($this->user->identity, 1, 10));
$count = (new Audios)->getUserCollectionSize($this->user->identity);
}
$this->template->pagesCount = ceil($count / 10);
} }
} }
@ -435,28 +446,20 @@ final class AudioPresenter extends OpenVKPresenter
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();
$playlist = $this->audios->getPlaylistByOwnerAndVID($owner_id, $virtual_id); $playlist = $this->audios->getPlaylistByOwnerAndVID($owner_id, $virtual_id);
$page = (int)($this->queryParam("p") ?? 1);
if (!$playlist || $playlist->isDeleted() || !$playlist->canBeModifiedBy($this->user->identity)) if (!$playlist || $playlist->isDeleted() || !$playlist->canBeModifiedBy($this->user->identity))
$this->notFound(); $this->notFound();
$this->template->playlist = $playlist; $this->template->playlist = $playlist;
$this->template->page = $page;
$audios = iterator_to_array($playlist->fetch(1, $playlist->size())); $audios = iterator_to_array($playlist->fetch(1, $playlist->size()));
$this->template->audios = array_slice($audios, 0, 10); $this->template->audios = array_slice($audios, 0, 1000);
$audiosIds = [];
foreach($audios as $aud)
$audiosIds[] = $aud->getId();
$this->template->audiosIds = implode(",", array_unique($audiosIds)) . ",";
$this->template->ownerId = $owner_id; $this->template->ownerId = $owner_id;
$this->template->owner = $playlist->getOwner(); $this->template->owner = $playlist->getOwner();
$this->template->pagesCount = $pagesCount = ceil($playlist->size() / 10);
if($_SERVER["REQUEST_METHOD"] !== "POST") if($_SERVER["REQUEST_METHOD"] !== "POST")
return; return;
$is_ajax = (int)$this->postParam('ajax') == 1;
$title = $this->postParam("title"); $title = $this->postParam("title");
$description = $this->postParam("description"); $description = $this->postParam("description");
$is_unlisted = (int)$this->postParam('is_unlisted'); $is_unlisted = (int)$this->postParam('is_unlisted');
@ -471,12 +474,12 @@ final class AudioPresenter extends OpenVKPresenter
$playlist->resetLength(); $playlist->resetLength();
$playlist->setUnlisted((bool)$is_unlisted); $playlist->setUnlisted((bool)$is_unlisted);
if($_FILES["new_cover"]["error"] === UPLOAD_ERR_OK) { if($_FILES["cover"]["error"] === UPLOAD_ERR_OK) {
if(!str_starts_with($_FILES["new_cover"]["type"], "image")) if(!str_starts_with($_FILES["cover"]["type"], "image"))
$this->flashFail("err", tr("error"), tr("not_a_photo")); $this->flashFail("err", tr("error"), tr("not_a_photo"));
try { try {
$playlist->fastMakeCover($this->user->id, $_FILES["new_cover"]); $playlist->fastMakeCover($this->user->id, $_FILES["cover"]);
} catch(\Throwable $e) { } catch(\Throwable $e) {
$this->flashFail("err", tr("error"), tr("invalid_cover_photo")); $this->flashFail("err", tr("error"), tr("invalid_cover_photo"));
} }
@ -490,13 +493,18 @@ final class AudioPresenter extends OpenVKPresenter
foreach ($new_audios as $new_audio) { foreach ($new_audios as $new_audio) {
$audio = (new Audios)->get((int)$new_audio); $audio = (new Audios)->get((int)$new_audio);
if(!$audio || $audio->isDeleted()) if(!$audio || $audio->isDeleted())
continue; continue;
$playlist->add($audio); $playlist->add($audio);
} }
if($is_ajax) {
$this->returnJson([
'success' => true,
'redirect' => '/playlist' . $playlist->getPrettyId()
]);
}
$this->redirect("/playlist".$playlist->getPrettyId()); $this->redirect("/playlist".$playlist->getPrettyId());
} }
@ -762,6 +770,15 @@ final class AudioPresenter extends OpenVKPresenter
$audios = $stream->page($page, 10); $audios = $stream->page($page, 10);
$audiosCount = $stream->size(); $audiosCount = $stream->size();
break; break;
case 'alone_audio':
$found_audio = $this->audios->get($ctx_id);
if(!$found_audio || $found_audio->isDeleted() || !$found_audio->canBeViewedBy($this->user->identity)) {
$this->flashFail("err", "Error", "Not found", 89, true);
}
$audios = [$found_audio];
$audiosCount = 1;
break;
} }
$pagesCount = ceil($audiosCount / $perPage); $pagesCount = ceil($audiosCount / $perPage);
@ -779,16 +796,21 @@ final class AudioPresenter extends OpenVKPresenter
$audiosArr = []; $audiosArr = [];
foreach($audios as $audio) { foreach($audios as $audio) {
$audiosArr[] = [ $output_array = [];
"id" => $audio->getId(), $output_array['id'] = $audio->getId();
"name" => $audio->getTitle(), $output_array['name'] = $audio->getTitle();
"performer" => $audio->getPerformer(), $output_array['performer'] = $audio->getPerformer();
"keys" => $audio->getKeys(),
"url" => $audio->getUrl(), if(!$audio->isWithdrawn()) {
"length" => $audio->getLength(), $output_array['keys'] = $audio->getKeys();
"available" => $audio->isAvailable(), $output_array['url'] = $audio->getUrl();
"withdrawn" => $audio->isWithdrawn(), }
];
$output_array['length'] = $audio->getLength();
$output_array['available'] = $audio->isAvailable();
$output_array['withdrawn'] = $audio->isWithdrawn();
$audiosArr[] = $output_array;
} }
$resultArr = [ $resultArr = [

View file

@ -228,6 +228,7 @@ final class AuthPresenter extends OpenVKPresenter
return; return;
} }
$this->template->disable_ajax = 1;
$this->template->is2faEnabled = $request->getUser()->is2faEnabled(); $this->template->is2faEnabled = $request->getUser()->is2faEnabled();
if($_SERVER["REQUEST_METHOD"] === "POST") { if($_SERVER["REQUEST_METHOD"] === "POST") {

View file

@ -7,7 +7,7 @@ final class AwayPresenter extends OpenVKPresenter
{ {
function renderAway(): void function renderAway(): void
{ {
$checkBanEntries = (new BannedLinks)->check($this->queryParam("to") . "/"); $checkBanEntries = (new BannedLinks)->check($this->queryParam("to"));
if (OPENVK_ROOT_CONF["openvk"]["preferences"]["susLinks"]["warnings"]) if (OPENVK_ROOT_CONF["openvk"]["preferences"]["susLinks"]["warnings"])
if (sizeof($checkBanEntries) > 0) if (sizeof($checkBanEntries) > 0)
$this->pass("openvk!Away->view", $checkBanEntries[0]); $this->pass("openvk!Away->view", $checkBanEntries[0]);

View file

@ -63,6 +63,7 @@ final class MessengerPresenter extends OpenVKPresenter
$this->flash("err", tr("warning"), tr("user_may_not_reply")); $this->flash("err", tr("warning"), tr("user_may_not_reply"));
} }
$this->template->disable_ajax = 1;
$this->template->selId = $sel; $this->template->selId = $sel;
$this->template->correspondent = $correspondent; $this->template->correspondent = $correspondent;
} }

View file

@ -38,6 +38,7 @@ final class NoSpamPresenter extends OpenVKPresenter
if ($mode === "form") { if ($mode === "form") {
$this->template->_template = "NoSpam/Index"; $this->template->_template = "NoSpam/Index";
$this->template->disable_ajax = 1;
$foundClasses = []; $foundClasses = [];
foreach (Finder::findFiles('*.php')->from($targetDir) as $file) { foreach (Finder::findFiles('*.php')->from($targetDir) as $file) {
$content = file_get_contents($file->getPathname()); $content = file_get_contents($file->getPathname());
@ -67,6 +68,7 @@ final class NoSpamPresenter extends OpenVKPresenter
$this->template->models = $models; $this->template->models = $models;
} else if ($mode === "templates") { } else if ($mode === "templates") {
$this->template->_template = "NoSpam/Templates.xml"; $this->template->_template = "NoSpam/Templates.xml";
$this->template->disable_ajax = 1;
$filter = []; $filter = [];
if ($this->queryParam("id")) { if ($this->queryParam("id")) {
$filter["id"] = (int)$this->queryParam("id"); $filter["id"] = (int)$this->queryParam("id");

View file

@ -283,10 +283,10 @@ abstract class OpenVKPresenter extends SimplePresenter
} }
} }
/*if($this->queryParam('al') == '1') { if($_SERVER['HTTP_X_OPENVK_AJAX_QUERY'] == '1' && $this->user->identity) {
$this->assertNoCSRF(); error_reporting(0);
header('Content-Type: text/plain; charset=UTF-8'); header('Content-Type: text/plain; charset=UTF-8');
}*/ }
parent::onStartup(); parent::onStartup();
} }

View file

@ -43,6 +43,7 @@ final class ReportPresenter extends OpenVKPresenter
"perPage" => 15, "perPage" => 15,
]; ];
$this->template->mode = $act ?? "all"; $this->template->mode = $act ?? "all";
$this->template->disable_ajax = 1;
if ($_SERVER["REQUEST_METHOD"] === "POST") { if ($_SERVER["REQUEST_METHOD"] === "POST") {
$reports = []; $reports = [];
@ -78,6 +79,7 @@ final class ReportPresenter extends OpenVKPresenter
$this->notFound(); $this->notFound();
$this->template->report = $report; $this->template->report = $report;
$this->template->disable_ajax = 1;
} }
function renderCreate(int $id): void function renderCreate(int $id): void

View file

@ -34,7 +34,7 @@ final class UserPresenter extends OpenVKPresenter
$this->template->_template = "User/deactivated.xml"; $this->template->_template = "User/deactivated.xml";
$this->template->user = $user; $this->template->user = $user;
} else if(!$user->canBeViewedBy($this->user->identity)) { } else if(!is_null($user) && !$user->canBeViewedBy($this->user->identity)) {
$this->template->_template = "User/private.xml"; $this->template->_template = "User/private.xml";
$this->template->user = $user; $this->template->user = $user;
@ -503,6 +503,7 @@ final class UserPresenter extends OpenVKPresenter
"wall.write", "wall.write",
"messages.write", "messages.write",
"audios.read", "audios.read",
"likes.read",
]; ];
foreach($settings as $setting) { foreach($settings as $setting) {
$input = $this->postParam(str_replace(".", "_", $setting)); $input = $this->postParam(str_replace(".", "_", $setting));

View file

@ -20,7 +20,6 @@
{script "js/utils.js"} {script "js/utils.js"}
{script "js/node_modules/dashjs/dist/dash.all.min.js"} {script "js/node_modules/dashjs/dist/dash.all.min.js"}
<script src="/assets/packages/static/openvk/js/node_modules/cropperjs/dist/cropper.js" type="module"></script> <script src="/assets/packages/static/openvk/js/node_modules/cropperjs/dist/cropper.js" type="module"></script>
{script "js/al_music.js"}
{css "js/node_modules/tippy.js/dist/backdrop.css"} {css "js/node_modules/tippy.js/dist/backdrop.css"}
{css "js/node_modules/cropperjs/dist/cropper.css"} {css "js/node_modules/cropperjs/dist/cropper.css"}
@ -31,6 +30,10 @@
{script "js/node_modules/@popperjs/core/dist/umd/popper.min.js"} {script "js/node_modules/@popperjs/core/dist/umd/popper.min.js"}
{script "js/node_modules/tippy.js/dist/tippy-bundle.umd.min.js"} {script "js/node_modules/tippy.js/dist/tippy-bundle.umd.min.js"}
{script "js/node_modules/handlebars/dist/handlebars.min.js"} {script "js/node_modules/handlebars/dist/handlebars.min.js"}
{script "js/node_modules/react/dist/react-with-addons.min.js"}
{script "js/node_modules/react-dom/dist/react-dom.min.js"}
{script "js/vnd_literallycanvas.js"}
{css "js/node_modules/literallycanvas/lib/css/literallycanvas.css"}
{if $isTimezoned == NULL} {if $isTimezoned == NULL}
{script "js/timezone.js"} {script "js/timezone.js"}
@ -199,18 +202,18 @@
{var $canAccessHelpdesk = $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)} {var $canAccessHelpdesk = $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
{var $menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')} {var $menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')}
<div n:if="$canAccessAdminPanel || $canAccessHelpdesk || $menuLinksAvaiable" class="menu_divider"></div> <div n:if="$canAccessAdminPanel || $canAccessHelpdesk || $menuLinksAvaiable" class="menu_divider"></div>
<a href="/admin" class="link" n:if="$canAccessAdminPanel" title="{_admin} [Alt+Shift+A]" accesskey="a">{_admin}</a> <a href="/admin" class="link" n:if="$canAccessAdminPanel" title="{_admin} [Alt+Shift+A]" accesskey="a" rel="nofollow">{_admin}</a>
<a href="/support/tickets" class="link" n:if="$canAccessHelpdesk">{_helpdesk} <a href="/support/tickets" class="link" n:if="$canAccessHelpdesk" rel="nofollow">{_helpdesk}
{if $helpdeskTicketNotAnsweredCount > 0} {if $helpdeskTicketNotAnsweredCount > 0}
(<b>{$helpdeskTicketNotAnsweredCount}</b>) (<b>{$helpdeskTicketNotAnsweredCount}</b>)
{/if} {/if}
</a> </a>
<a n:if="$canAccessHelpdesk" href="/scumfeed" class="link">{tr("reports")} <a n:if="$canAccessHelpdesk" href="/scumfeed" class="link" rel="nofollow">{tr("reports")}
{if $reportNotAnsweredCount > 0} {if $reportNotAnsweredCount > 0}
(<b>{$reportNotAnsweredCount}</b>) (<b>{$reportNotAnsweredCount}</b>)
{/if} {/if}
</a> </a>
<a n:if="$canAccessHelpdesk" href="/noSpam" class="link"> <a n:if="$canAccessHelpdesk" href="/noSpam" class="link" rel="nofollow">
noSpam noSpam
</a> </a>
<a <a
@ -392,6 +395,9 @@
{script "js/al_suggestions.js"} {script "js/al_suggestions.js"}
{script "js/al_navigation.js"} {script "js/al_navigation.js"}
{script "js/al_comments.js"} {script "js/al_comments.js"}
{script "js/al_music.js"}
{script "js/al_despacito_wall.js"}
{script "js/al_photos.js"}
{ifset $thisUser} {ifset $thisUser}
{script "js/al_notifs.js"} {script "js/al_notifs.js"}
@ -433,19 +439,21 @@
//]]> //]]>
</script> </script>
<script> <script id='_js_ep_script'>
window.openvk = { window.openvk = {
"audio_genres": {\openvk\Web\Models\Entities\Audio::genres}, "audio_genres": {\openvk\Web\Models\Entities\Audio::genres},
"at_search": {$atSearch ?? false}, "at_search": {$atSearch ?? false},
"max_attachments": {\OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"] ?? 10}, "max_attachments": {\OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"] ?? 10},
"max_filesize_mb": 5, "max_filesize_mb": 5,
"current_id": {$thisUser ? $thisUser->getId() : 0}, "current_id": {$thisUser ? $thisUser->getId() : 0},
"disable_ajax": {$disable_ajax ? $disable_ajax : 0},
} }
</script> </script>
{ifset bodyScripts} {ifset bodyScripts}
{include bodyScripts} {include bodyScripts}
{/ifset} {/ifset}
{script "js/router.js"}
</body> </body>
</html> </html>
{/if} {/if}

View file

@ -101,12 +101,6 @@
</div> </div>
<script> <script>
async function withdraw(id) { window.coins = {$coins}
let coins = await API.Apps.withdrawFunds(id);
if(coins == 0)
MessageBox({_app_withdrawal}, {_app_withdrawal_empty}, ["OK"], [Function.noop]);
else
MessageBox({_app_withdrawal}, {tr("app_withdrawal_created", $coins)}, ["OK"], [Function.noop]);
}
</script> </script>
{/block} {/block}

View file

@ -7,7 +7,7 @@
{block header} {block header}
{$name} {$name}
<a style="float: right;" onClick="reportApp()" n:if="$canReport ?? false">{_report}</a> <a style="float: right;" onClick="reportApp({$id})" n:if="$canReport ?? false">{_report}</a>
{/block} {/block}
{block content} {block content}
@ -36,29 +36,5 @@
window.appOrigin = {$origin}; window.appOrigin = {$origin};
</script> </script>
<script n:if="$canReport ?? false">
function reportApp() {
uReportMsgTxt = {_going_to_report_app};
uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
(function() {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + {$id} + "?reason=" + res + "&type=app", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
else
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
</script>
{script "js/al_games.js"} {script "js/al_games.js"}
{/block} {/block}

View file

@ -13,90 +13,44 @@
{/block} {/block}
{block content} {block content}
<div class="playlistBlock" style="display: flex;margin-top: 0px;"> <div class='PE_wrapper'>
<div class='PE_playlistEditPage'>
<div class="playlistCover"> <div class="playlistCover">
<a> <a onclick="document.querySelector(`input[name='cover']`).click()">
<input type='file' name='cover' style='display:none;' accept=".jpg,.png">
<img src="{$playlist->getCoverURL('normal')}" alt="{_playlist_cover}"> <img src="{$playlist->getCoverURL('normal')}" alt="{_playlist_cover}">
</a> </a>
<div class="profile_links" style="width: 139px;"> <div class="profile_links" style="width: 139px;">
<a class="profile_link" style="width: 98%;" id="_deletePlaylist" data-id="{$playlist->getId()}">{_delete_playlist}</a> <a class="profile_link" style="width: 98%;" id="_deletePlaylist" data-id="{$playlist->getId()}">{_delete_playlist}</a>
</div> </div>
</div> </div>
<div style="padding-left: 13px;width:75%"> <div class="PE_playlistInfo">
<div class="playlistInfo"> <div>
<input value="{$playlist->getName()}" type="text" name="title" maxlength="125"> <input value='{$playlist->getName()}' type="text" name="title" placeholder="{_title}" maxlength="125" />
</div> </div>
<div class="moreInfo"> <div class="moreInfo">
<textarea placeholder="{_description}" name="description" maxlength="2045" style="margin-top: 11px;">{$playlist->getDescription()}</textarea> <textarea placeholder="{_description}" name="description" maxlength="2045">{$playlist->getDescription()}</textarea>
</div> </div>
<label> <label>
<input type='checkbox' name='is_unlisted' n:attr='checked => $playlist->isUnlisted()'> <input type='checkbox' name='is_unlisted' value='1' n:attr='checked => $playlist->isUnlisted()'>
{_playlist_hide_from_search} {_playlist_hide_from_search}
</label> </label>
<a id='_playlistAppendTracks'>{_add_audio_verb}</a>
</div> </div>
</div> </div>
<div class='PE_audios generic_audio_list'>
<div style="margin-top: 19px;"> <div n:foreach='$audios as $audio' class='vertical-attachment upload-item' data-id='{$audio->getId()}'>
<input id="playlist_query" type="text" style="height: 26px;" placeholder="{_header_search}"> <div class='vertical-attachment-content'>
<div class="playlistAudiosContainer editContainer"> {include 'player.xml', audio => $audio, hideButtons => true}
<div id="newPlaylistAudios" n:foreach="$audios as $audio">
<div class="playerContainer">
{include "player.xml", audio => $audio, hideButtons => true}
</div> </div>
<div class="attachAudio addToPlaylist" data-id="{$audio->getId()}"> <div class="vertical-attachment-remove">
<span>{_remove_from_playlist}</span> <div id="small_remove_button"></div>
</div> </div>
</div> </div>
</div> </div>
<div class='PE_end'>
<div class="showMoreAudiosPlaylist" data-page="2" data-playlist="{$playlist->getId()}" n:if="$pagesCount > 1"> <input class="button" type="button" id='playlist_edit' value="{_save}">
{_show_more_audios}
</div> </div>
</div> </div>
<form method="post" id="editPlaylistForm" data-id="{$playlist->getId()}" enctype="multipart/form-data">
<input type="hidden" name="title" maxlength="128" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="hidden" name="is_unlisted" value="0" />
<textarea style="display:none;" name="description" maxlength="2048" />
<input type="hidden" name="audios">
<input type="file" style="display:none;" name="new_cover" accept=".jpg,.png">
<div style="float:right;margin-top: 8px;">
<button class="button" type="submit">{_save}</button>
</div>
</form>
<script>
document.querySelector("input[name='audios']").value = {$audiosIds}
u("#editPlaylistForm").on("submit", (e) => {
document.querySelector("#editPlaylistForm input[name='title']").value = document.querySelector(".playlistInfo input[name='title']").value
document.querySelector("#editPlaylistForm textarea[name='description']").value = document.querySelector(".playlistBlock textarea[name='description']").value
document.querySelector("#editPlaylistForm input[name='is_unlisted']").value = document.querySelector("input[name='is_unlisted']").checked ? 1 : 0
})
u("#editPlaylistForm input[name='new_cover']").on("change", (e) => {
if(!e.currentTarget.files[0].type.startsWith("image/")) {
fastError(tr("not_a_photo"))
return
}
let image = URL.createObjectURL(e.currentTarget.files[0])
document.querySelector(".playlistCover img").src = image
})
u(".playlistCover img").on("click", (e) => {
document.querySelector("input[name='new_cover']").click()
})
document.querySelector("#editPlaylistForm input[name='new_cover']").value = ""
</script>
{script "js/al_playlists.js"}
{/block} {/block}

View file

@ -11,6 +11,8 @@
{_audio_new} {_audio_new}
{elseif $mode == 'popular'} {elseif $mode == 'popular'}
{_audio_popular} {_audio_popular}
{elseif $mode == 'alone_audio'}
{$alone_audio->getName()}
{else} {else}
{if $ownerId > 0} {if $ownerId > 0}
{_playlists} {$owner->getMorphedName("genitive", false)} {_playlists} {$owner->getMorphedName("genitive", false)}
@ -47,6 +49,10 @@
» »
{if $isMy}{_my_playlists}{else}{_playlists}{/if} {if $isMy}{_my_playlists}{else}{_playlists}{/if}
</div> </div>
<div n:if="$mode == 'alone_audio'">
{_my_audios_small}
</div>
{/block} {/block}
{block content} {block content}
@ -54,18 +60,30 @@
{include "bigplayer.xml"} {include "bigplayer.xml"}
<input n:if="$mode == 'list'" type="hidden" name="bigplayer_context" data-type="entity_audios" data-entity="{$ownerId}" data-page="{$page}"> <script>
<input n:if="$mode == 'new'" type="hidden" name="bigplayer_context" data-type="new_audios" data-entity="0" data-page="1"> window.__current_page_audio_context = null
<input n:if="$mode == 'popular'" type="hidden" name="bigplayer_context" data-type="popular_audios" data-entity="0" data-page="1"> {if $mode == 'list'}
<div class="bigPlayerDetector"></div> window.__current_page_audio_context = {
name: 'entity_audios',
entity_id: {$ownerId},
page: {$page}
}
{elseif $mode == 'alone_audio'}
window.__current_page_audio_context = {
name: 'alone_audio',
entity_id: {$alone_audio->getId()},
page: 1
}
{/if}
</script>
<div class="audiosDiv"> <div class="audiosDiv">
<div style="width: 74%;" class="audiosContainer audiosPaddingContainer" n:if="$mode != 'playlists'"> <div class="audiosContainer audiosSideContainer audiosPaddingContainer" n:if="$mode != 'playlists'">
<div n:if="$audiosCount <= 0" style='height: 50%;'> <div n:if="$audiosCount <= 0" style='height: 100%;'>
{include "../components/content_error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_audios_thisuser") : tr("no_audios_user")) : tr("no_audios_club")} {include "../components/content_error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_audios_thisuser") : tr("no_audios_user")) : tr("no_audios_club")}
</div> </div>
<div n:if="$audiosCount > 0" class="scroll_container infContainer"> <div n:if="$audiosCount > 0" class="scroll_container">
<div class="scroll_node infObj" n:foreach="$audios as $audio"> <div class="scroll_node" n:foreach="$audios as $audio">
{include "player.xml", audio => $audio, club => $club} {include "player.xml", audio => $audio, club => $club}
</div> </div>
</div> </div>
@ -81,12 +99,12 @@
</div> </div>
</div> </div>
<div style="width: 71.8%;" class="audiosPaddingContainer audiosPaddingContainer" n:if="$mode == 'playlists'"> <div class="audiosPaddingContainer audiosSideContainer audiosPaddingContainer" style="width: 72.2%;" n:if="$mode == 'playlists'">
<div n:if="$playlistsCount <= 0" style='height: 100%;'> <div n:if="$playlistsCount <= 0" style='height: 100%;'>
{include "../components/content_error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_playlists_thisuser") : tr("no_playlists_user")) : tr("no_playlists_club")} {include "../components/content_error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_playlists_thisuser") : tr("no_playlists_user")) : tr("no_playlists_club")}
</div> </div>
<div class="scroll_container infContainer playlistContainer" n:if="$playlistsCount > 0"> <div class="scroll_container playlistContainer" n:if="$playlistsCount > 0">
<div class='scroll_node' n:foreach='$playlists as $playlist'> <div class='scroll_node' n:foreach='$playlists as $playlist'>
{include 'playlistListView.xml', playlist => $playlist} {include 'playlistListView.xml', playlist => $playlist}
</div> </div>

View file

@ -19,97 +19,32 @@
{/block} {/block}
{block content} {block content}
<style> <div class='PE_wrapper'>
textarea[name='description'] { <div class='PE_playlistEditPage'>
padding: 4px; <div class="playlistCover" onclick="document.querySelector(`input[name='cover']`).click()">
}
.playlistInfo {
width: 76%;
margin-left: 8px;
}
</style>
<div style="display:flex;">
<div class="playlistCover" onclick="document.querySelector(`#newPlaylistForm input[name='cover']`).click()">
<a> <a>
<input type='file' name='cover' style='display:none;' accept=".jpg,.png">
<img src="/assets/packages/static/openvk/img/song.jpg" alt="{_playlist_cover}"> <img src="/assets/packages/static/openvk/img/song.jpg" alt="{_playlist_cover}">
</a> </a>
</div> </div>
<div style="padding-left: 17px;width: 75%;" class="plinfo"> <div class="PE_playlistInfo">
<div> <div>
<input type="text" name="title" placeholder="{_title}" maxlength="125" /> <input type="text" name="title" placeholder="{_title}" maxlength="125" />
</div> </div>
<div class="moreInfo" style="margin-top: 11px;"> <div class="moreInfo">
<textarea placeholder="{_description}" name="description" maxlength="2045" /> <textarea placeholder="{_description}" name="description" maxlength="2045" />
</div> </div>
<label> <label>
<input type='checkbox' name='is_unlisted'> <input type='checkbox' name='is_unlisted' value='1'>
{_playlist_hide_from_search} {_playlist_hide_from_search}
</label> </label>
<a id='_playlistAppendTracks'>{_add_audio_verb}</a>
</div> </div>
</div> </div>
<div class='PE_audios generic_audio_list'></div>
<div style="margin-top: 19px;"> <div class='PE_end'>
<input id="playlist_query" type="text" style="height: 26px;" placeholder="{_header_search}"> <input class="button" type="button" id='playlist_create' value="{_create}">
<div class="playlistAudiosContainer editContainer">
<div id="newPlaylistAudios" n:foreach="$audios as $audio">
<div style="width: 78%;float: left;">
{include "player.xml", audio => $audio, hideButtons => true}
</div>
<div class="attachAudio addToPlaylist" data-id="{$audio->getId()}">
<span>{_add_to_playlist}</span>
</div> </div>
</div> </div>
</div>
<div class="showMoreAudiosPlaylist" data-page="2" {if !is_null($_GET["gid"])}data-club="{abs($_GET['gid'])}"{/if} n:if="$pagesCount > 1">
{_show_more_audios}
</div>
</div>
<form method="post" id="newPlaylistForm" enctype="multipart/form-data">
<input type="hidden" name="title" maxlength="125" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="hidden" name="is_unlisted" value="0" />
<textarea style="display:none;" name="description" maxlength="2045" />
<input type="hidden" name="audios">
<input type="file" style="display:none;" name="cover" accept=".jpg,.png">
<div style="float: right;margin-top: 9px;">
<button class="button" type="submit">{_create}</button>
</div>
</form>
<script>
document.querySelector("input[name='audios']").value = ""
u("#newPlaylistForm").on("submit", (e) => {
document.querySelector("#newPlaylistForm input[name='title']").value = document.querySelector(".plinfo input[name='title']").value
document.querySelector("#newPlaylistForm textarea[name='description']").value = document.querySelector(".plinfo textarea[name='description']").value
document.querySelector("#newPlaylistForm input[name='is_unlisted']").value = document.querySelector(".plinfo input[name='is_unlisted']").checked ? 1 : 0
})
u("#newPlaylistForm input[name='cover']").on("change", (e) => {
if(!e.currentTarget.files[0].type.startsWith("image/")) {
fastError(tr("not_a_photo"))
return
}
let image = URL.createObjectURL(e.currentTarget.files[0])
document.querySelector(".playlistCover img").src = image
document.querySelector(".playlistCover img").style.display = "block"
})
u(".playlistCover img").on("click", (e) => {
document.querySelector("#newPlaylistForm input[name='cover']").value = ""
e.currentTarget.href = ""
})
document.querySelector("#newPlaylistForm input[name='cover']").value = ""
</script>
{script "js/al_playlists.js"}
{/block} {/block}

View file

@ -32,10 +32,16 @@
{block content} {block content}
{include "bigplayer.xml"} {include "bigplayer.xml"}
<script>
window.__current_page_audio_context = {
name: 'playlist_context',
entity_id: {$playlist->getId()},
page: {$page}
}
</script>
<input type="hidden" name="bigplayer_context" data-type="playlist_context" data-entity="{$playlist->getId()}" data-page="{$page}">
<div class="playlistBlock"> <div class="playlistBlock">
<div class="playlistCover" style="float: left;"> <div class="playlistCover">
{if $cover} {if $cover}
<a href="{$cover_url}" target="_blank"> <a href="{$cover_url}" target="_blank">
<img onclick="OpenMiniature(event, {$cover_url}, null, {$cover->getPrettyId()}, null)" src="{$cover_url}" alt="{_playlist_cover}"> <img onclick="OpenMiniature(event, {$cover_url}, null, {$cover->getPrettyId()}, null)" src="{$cover_url}" alt="{_playlist_cover}">
@ -56,7 +62,7 @@
<div class='playlistWrapper'> <div class='playlistWrapper'>
<div class="playlistInfo"> <div class="playlistInfo">
<h4 style="border-bottom:unset;">{$playlist->getName()}</h4> <h4>{$playlist->getName()}</h4>
<div class="moreInfo"> <div class="moreInfo">
{$playlist->getMetaDescription()|noescape} {$playlist->getMetaDescription()|noescape}
@ -64,7 +70,7 @@
<div style="margin-top: 11px;"> <div style="margin-top: 11px;">
<span>{nl2br($playlist->getDescriptionHTML())|noescape}</span> <span>{nl2br($playlist->getDescriptionHTML())|noescape}</span>
</div> </div>
<hr style="color: #f7f7f7;"> <hr>
</div> </div>
</div> </div>
<div class="audiosContainer scroll_container infContainer" style="margin-top: 14px;"> <div class="audiosContainer scroll_container infContainer" style="margin-top: 14px;">

View file

@ -38,217 +38,196 @@
<li>{tr("audio_requirements_2")}</li> <li>{tr("audio_requirements_2")}</li>
</ul> </ul>
<div id="audio_upload"> <div id="audio_upload">
<form enctype="multipart/form-data" method="POST"> <input id="audio_input" multiple type="file" name="blob" accept="audio/*" style="display:none" />
<input type="hidden" name="name" />
<input type="hidden" name="performer" />
<input type="hidden" name="lyrics" />
<input type="hidden" name="genre" />
<input type="hidden" name="explicit" />
<input type="hidden" name="unlisted" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<input id="audio_input" type="file" name="blob" accept="audio/*" style="display:none" />
<input value="{_upload_button}" class="button" type="button" onclick="document.querySelector('#audio_input').click()"> <input value="{_upload_button}" class="button" type="button" onclick="document.querySelector('#audio_input').click()">
</form>
</div><br/> </div><br/>
<span>{_you_can_also_add_audio_using} <b><a href="/search?section=audios">{_search_audio_inst}</a></b>.<span> <span>{_you_can_also_add_audio_using} <b><a href="/search?section=audios">{_search_audio_inst}</a></b>.<span>
</div> </div>
<div id="lastStep" style="display:none;"> <div id="lastStep" style="display:none;">
<table cellspacing="7" cellpadding="0" border="0" align="center"> <div id="lastStepContainers"></div>
<tbody> <div id="lastStepButtons" style="text-align: center;margin-top: 10px;">
<tr> <input class="button" type="button" id="uploadMusic" value="{_upload_button}">
<td width="120" valign="top"><span class="nobold">{_performer}:</span></td> <input class="button" type="button" id="backToUpload" onclick="document.querySelector('#audio_input').click()" value="{_select_another_file}">
<td><input name="performer" type="text" autocomplete="off" maxlength="80" /></td> </div>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">{_audio_name}:</span></td>
<td><input type="text" name="name" autocomplete="off" maxlength="80" /></td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">{_genre}:</span></td>
<td>
<select name="genre">
<option n:foreach='\openvk\Web\Models\Entities\Audio::genres as $genre' n:attr="selected: $genre == 'Other'" value="{$genre}">
{$genre}
</option>
</select>
</td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">{_lyrics}:</span></td>
<td><textarea name="lyrics" style="resize: vertical;max-height: 300px;"></textarea></td>
</tr>
<tr>
<td width="120" valign="top"></td>
<td>
<label style='display:block'><input type="checkbox" name="explicit">{_audios_explicit}</label>
<label><input type="checkbox" name="unlisted">{_audios_unlisted}</label>
</td>
</tr>
<tr>
<td width="120" valign="top"></td>
<td>
<input class="button" type="button" id="uploadMuziko" value="{_upload_button}">
<input class="button" type="button" id="backToUpload" value="{_select_another_file}">
</td>
</tr>
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
<script type="module"> <script type="module" n:syntax='off'>
import * as id3 from "/assets/packages/static/openvk/js/node_modules/id3js/lib/id3.js"; import * as id3 from "/assets/packages/static/openvk/js/node_modules/id3js/lib/id3.js";
u("#audio_input").on("change", async function(e) { window.__audio_upload_page = new class {
let files = e.currentTarget.files files_list = []
if(files.length <= 0)
return;
document.querySelector("#firstStep").style.display = "none" hideFirstPage() {
document.querySelector("#lastStep").style.display = "block" u('#firstStep').attr('style', 'display:none')
u('#lastStep').attr('style', 'display:block')
}
showFirstPage() {
u('#firstStep').attr('style', 'display:block')
u('#lastStep').attr('style', 'display:none')
}
async detectTags(blob) {
const return_params = {
performer: '',
name: '',
genre: '',
lyrics: '',
explicit: 0,
unlisted: 0,
}
function fallback() { function fallback() {
console.info('Tags not found, setting default values.') console.info('Tags not found, setting default values.')
return_params.name = remove_file_format(blob.name)
document.querySelector("#lastStep input[name=name]").value = files[0].name return_params.genre = 'Other'
document.querySelector("#lastStep select[name=genre]").value = "Other" return_params.performer = tr('track_unknown')
document.querySelector("#lastStep input[name=performer]").value = tr("track_unknown");
} }
let tags = null let tags = null
try { try {
tags = await id3.fromFile(files[0]); tags = await id3.fromFile(blob)
} catch(e) { } catch(e) {
console.error(e) console.error(e)
} }
console.log(tags) console.log(tags)
if(tags != null) { if(tags != null) {
console.log("ID" + tags.kind + " detected, setting values..."); console.log("ID" + tags.kind + " detected, setting values...")
if(tags.title) {
return_params.name = tags.title
} else {
return_params.name = remove_file_format(blob.name)
}
if(tags.title != null) if(tags.artist) {
document.querySelector("#lastStep input[name=name]").value = tags.title; return_params.performer = tags.artist
else } else {
document.querySelector("#lastStep input[name=name]").value = files[0].name return_params.performer = tr('track_unknown')
// todo: split performer and title from filename
if(tags.artist != null) }
document.querySelector("#lastStep input[name=performer]").value = tags.artist;
else
document.querySelector("#lastStep input[name=performer]").value = tr("track_unknown");
if(tags.genre != null) { if(tags.genre != null) {
// if there are more than one genre
if(tags.genre.split(', ').length > 1) { if(tags.genre.split(', ').length > 1) {
const genres = tags.genre.split(', ') const genres = tags.genre.split(', ')
genres.forEach(genre => { genres.forEach(genre => {
if(document.querySelector("#lastStep select[name=genre] > option[value='" + genre + "']") != null) { if(window.openvk.audio_genres[genre]) {
document.querySelector("#lastStep select[name=genre]").value = genre; return_params.genre = genre;
} }
}) })
} else { } else {
if(document.querySelector("#lastStep select[name=genre] > option[value='" + tags.genre + "']") != null) { if(window.openvk.audio_genres.indexOf(tags.genre) != -1) {
document.querySelector("#lastStep select[name=genre]").value = tags.genre; return_params.genre = tags.genre
} else { } else {
console.warn("Unknown genre: " + tags.genre); console.warn("Unknown genre: " + tags.genre)
document.querySelector("#lastStep select[name=genre]").value = "Other" return_params.genre = 'Other'
} }
} }
} else { } else {
document.querySelector("#lastStep select[name=genre]").value = "Other" return_params.genre = 'Other'
} }
if(tags.comments != null) if(tags.comments != null)
document.querySelector("#lastStep textarea[name=lyrics]").value = tags.comments return_params.lyrics = tags.comments
} else { } else {
fallback() fallback()
} }
});
u("#backToUpload").on("click", (e) => { return return_params
document.querySelector("#firstStep").style.display = "block" }
document.querySelector("#lastStep").style.display = "none"
document.querySelector("#lastStep input[name=name]").value = "" async appendFile(appender)
document.querySelector("#lastStep input[name=performer]").value = "" {
document.querySelector("#lastStep select[name=genre]").value = "" appender.info = await this.detectTags(appender.file)
document.querySelector("#lastStep textarea[name=lyrics]").value = "" const audio_index = this.files_list.push(appender) - 1
document.querySelector("#audio_input").value = "" this.appendAudioFrame(audio_index)
}
appendAudioFrame(audio_index) {
const audio_element = this.files_list[audio_index]
if(!audio_element) {
return
}
const template = u(`
<div class='upload_container_element' data-index="${audio_index}">
<div class='upload_container_name'>
<span>${ovk_proc_strtr(escapeHtml(audio_element.file.name), 63)}</span>
<div id="small_remove_button"></div>
</div>
<table cellspacing="7" cellpadding="0" border="0" align="center">
<tbody>
<tr>
<td width="120" valign="top"><span class="nobold">${tr('performer')}:</span></td>
<td><input value='${audio_element.info.performer}' name="performer" type="text" autocomplete="off" maxlength="80" /></td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">${tr('audio_name')}:</span></td>
<td><input type="text" value='${audio_element.info.name}' name="name" autocomplete="off" maxlength="80" /></td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">${tr('genre')}:</span></td>
<td>
<select name="genre"></select>
</td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">${tr('lyrics')}:</span></td>
<td><textarea name="lyrics" style="resize: vertical;max-height: 300px;">${audio_element.info.lyrics}</textarea></td>
</tr>
<tr>
<td width="120" valign="top"></td>
<td>
<label style='display:block'><input type="checkbox" name="explicit">${tr('audios_explicit')}</label>
<label><input type="checkbox" name="unlisted">${tr('audios_unlisted')}</label>
</td>
</tr>
</tbody>
</table>
</div>
`)
window.openvk.audio_genres.forEach(genre => {
template.find('select').append(`
<option ${genre == audio_element.info.genre ? 'selected': ''} value='${genre}'>${genre}</option>
`)
}) })
u('#lastStep #lastStepContainers').append(template)
}
}
u("#uploadMuziko").on("click", (e) => { u(`#audio_upload input`).on('change', (e) => {
var name_ = document.querySelector("#audio_upload input[name=name]"); const files = e.target.files
var perf_ = document.querySelector("#audio_upload input[name=performer]"); if(files.length <= 0) {
var genre_ = document.querySelector("#audio_upload input[name=genre]"); return
var lyrics_ = document.querySelector("#audio_upload input[name=lyrics]"); }
var explicit_ = document.querySelector("#audio_upload input[name=explicit]");
var unlisted_ = document.querySelector("#audio_upload input[name=unlisted]");
name_.value = document.querySelector("#lastStep input[name=name]").value Array.from(files).forEach(async file => {
perf_.value = document.querySelector("#lastStep input[name=performer]").value let has_duplicates = false
genre_.value = document.querySelector("#lastStep select[name=genre]").value const appender = {
lyrics_.value = document.querySelector("#lastStep textarea[name=lyrics]").value 'file': file
explicit_.value = document.querySelector("#lastStep input[name=explicit]").checked ? "on" : "off" }
unlisted_.value = document.querySelector("#lastStep input[name=unlisted]").checked ? "on" : "off"
$("#audio_upload > form").trigger("submit");
})
$(document).on("dragover drop", (e) => {
e.preventDefault()
return false;
})
$(".container_gray").on("drop", (e) => {
e.originalEvent.dataTransfer.dropEffect = 'move';
e.preventDefault()
let file = e.originalEvent.dataTransfer.files[0]
if(!file.type.startsWith('audio/')) { if(!file.type.startsWith('audio/')) {
MessageBox(tr("error"), tr("only_audios_accepted", escapeHtml(file.name)), [tr("ok")], [() => Function.noop]) makeError(tr('only_audios_accepted', escapeHtml(file.name)))
return; return
} }
document.getElementById("audio_input").files = e.originalEvent.dataTransfer.files window.__audio_upload_page.files_list.forEach(el => {
u("#audio_input").trigger("change") if(el && file.name == el.file.name) {
}) has_duplicates = true
$("#audio_upload").on("submit", "form", (e) => {
e.preventDefault()
let fd = new FormData(e.currentTarget)
fd.append("ajax", 1)
$.ajax({
type: "POST",
url: location.href,
contentType: false,
processData: false,
data: fd,
beforeSend: function() {
document.querySelector("#lastStep").classList.add("lagged")
document.querySelector("#upload_container").classList.add("uploading")
},
success: (response) => {
document.querySelector("#lastStep").classList.remove("lagged")
document.querySelector("#upload_container").classList.remove("uploading")
if(response.success) {
u("#backToUpload").trigger("click")
NewNotification(tr("success"), tr("audio_successfully_uploaded"), null, () => {
window.location.assign(response.redirect_link)
})
} else {
fastError(response.flash.message)
}
} }
}) })
if(!has_duplicates) {
window.__audio_upload_page.appendFile(appender)
}
})
window.__audio_upload_page.hideFirstPage()
}) })
</script> </script>
{/block} {/block}

View file

@ -1,56 +1,56 @@
<div n:class="bigPlayer, $tidy ? tidy"> <div n:class="bigPlayer, ctx_place, $tidy ? tidy">
<audio class="audio" /> <div class="bigPlayerWrapper">
<div class="paddingLayer">
<div class="playButtons"> <div class="playButtons">
<div class="playButton musicIcon" title="{_play_tip} [Space]"></div> <div class="playButton musicIcon" data-tip='simple' data-title="{_play_tip} [Space]"></div>
<div class="arrowsButtons"> <div class="arrowsButtons">
<div> <div class="nextButton musicIcon" data-tip='simple' data-title=""></div>
<div class="nextButton musicIcon"></div> <div class="backButton musicIcon" data-tip='simple' data-title=""></div>
</div>
<div>
<div class="backButton musicIcon"></div>
</div>
</div> </div>
</div> </div>
<div class="trackPanel"> <div class="trackPanel">
<div class="trackInfo"> <div class="trackInfo">
<div class="trackName"> <div class="trackName">
<a>{_track_unknown}</a> <span class="trackPerformers">
<a>{_track_unknown}</a>
</span>
<span>{_track_noname}</span> <span>{_track_noname}</span>
</div> </div>
<div class="timer" style="float:right"> <div class="timer">
<span class="time">00:00</span> <span class="time">00:00</span>
<span>/</span> <span>/</span>
<span class="elapsedTime">-00:00</span> <span class="elapsedTime">-00:00</span>
</div> </div>
</div> </div>
<div class="track" style="margin-top: -2px;"> <div class="track">
<div class="bigPlayerTip">00:00</div>
<div class="selectableTrack"> <div class="selectableTrack">
<div id='bigPlayerLengthSliderWrapper'>&nbsp; <div id='bigPlayerLengthSliderWrapper'>&nbsp;
<div class="slider"></div> <div class="slider"></div>
</div> </div>
<div class='selectableTrackLoadProgress'>
<div class="load_bar"></div>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="volumePanel"> <div class="volumePanel">
<div class="volumePanelTrack">
<div class="selectableTrack"> <div class="selectableTrack">
<div id='bigPlayerVolumeSliderWrapper'>&nbsp; <div id='bigPlayerVolumeSliderWrapper'>&nbsp;
<div class="slider"></div> <div class="slider"></div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="additionalButtons"> <div class="additionalButtons">
<div class="repeatButton musicIcon" title="{_repeat_tip} [R]" ></div> <div class="repeatButton musicIcon" data-tip='simple' data-title="{_repeat_tip} [R]" ></div>
<div class="shuffleButton musicIcon" title="{_shuffle_tip}"></div> <div class="shuffleButton musicIcon" data-tip='simple' data-title="{_shuffle_tip}"></div>
<div class="deviceButton musicIcon" title="{_mute_tip} [M]"></div> <div class="deviceButton musicIcon" data-tip='simple' data-title="{_mute_tip} [M]"></div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,24 +1,25 @@
{php $id = $audio->getId() . rand(0, 1000)} {php $id = $audio->getId() . rand(0, 1000)}
{php $isWithdrawn = $audio->isWithdrawn()} {php $isWithdrawn = $audio->isWithdrawn()}
{php $isAvailable = $audio->isAvailable()} {php $isAvailable = $audio->isAvailable()}
{php $performers = $audio->getPerformers()}
{php $editable = isset($thisUser) && $audio->canBeModifiedBy($thisUser)} {php $editable = isset($thisUser) && $audio->canBeModifiedBy($thisUser)}
<div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" {if $hideButtons}data-prettyid="{$audio->getPrettyId()}" data-name="{$audio->getName()}"{/if} data-genre="{$audio->getGenre()}" class="audioEmbed {if !$isAvailable}processed{/if} {if $isWithdrawn}withdrawn{/if}" data-length="{$audio->getLength()}" data-keys="{json_encode($audio->getKeys())}" data-url="{$audio->getURL()}"> <div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" {if $hideButtons}data-prettyid="{$audio->getPrettyId()}"{/if} data-name="{$audio->getName()}" data-genre="{$audio->getGenre()}" n:class="audioEmbed, ctx_place, !$isAvailable ? processed, $isWithdrawn ? withdrawn" data-length="{$audio->getLength()}" data-keys="{json_encode($audio->getKeys())}" data-url="{$audio->getURL()}">
<audio class="audio" /> <audio class="audio" />
<div id="miniplayer" class="audioEntry"> <div id="miniplayer" class="audioEntry">
<div class='audioEntryWrapper' style="display: flex;" draggable='true'> <div class='audioEntryWrapper' draggable='true'>
<div class="playerButton"> <div class="playerButton">
<div class="playIcon"></div> <div class="playIcon"></div>
</div> </div>
<div class="status" draggable='false'> <div class="status">
<div class="mediaInfo noOverflow"> <div class="mediaInfo noOverflow">
<div class="info"> <div class="info">
<strong class="performer"> <strong class="performer" n:foreach='$performers as $performer'>
<a href="/search?section=audios&order=listens&only_performers=on&q={$audio->getPerformer()}">{$audio->getPerformer()}</a> <a draggable='false' href="/search?section=audios&order=listens&only_performers=on&q={$performer}">{$performer}</a>{if $performer != end($performers)}, {/if}
</strong> </strong>
<span class="title {if !empty($audio->getLyrics())}withLyrics{/if}">{$audio->getTitle()}</span> <span draggable='false' class="title {if !empty($audio->getLyrics())}withLyrics{/if}">{$audio->getTitle()}</span>
</div> </div>
<svg n:if="$audio->isExplicit()" class="explicitMark" xmlns="http://www.w3.org/2000/svg" height="11" viewBox="0 0 11 11" width="11"> <svg n:if="$audio->isExplicit()" class="explicitMark" xmlns="http://www.w3.org/2000/svg" height="11" viewBox="0 0 11 11" width="11">
@ -27,7 +28,7 @@
</div> </div>
</div> </div>
<div class="volume"> <div class="mini_timer">
<span class="nobold {if !$hideButtons}hideOnHover{/if}" data-unformatted="{$audio->getLength()}">{$audio->getFormattedLength()}</span> <span class="nobold {if !$hideButtons}hideOnHover{/if}" data-unformatted="{$audio->getLength()}">{$audio->getFormattedLength()}</span>
<div class="buttons"> <div class="buttons">
{php $hasAudio = isset($thisUser) && $audio->isInLibraryOf($thisUser)} {php $hasAudio = isset($thisUser) && $audio->isInLibraryOf($thisUser)}
@ -44,10 +45,13 @@
</div> </div>
</div> </div>
</div> </div>
<div class="subTracks" draggable='false'> <div class="subTracks" draggable='false' n:if="!$isWithdrawn">
<div class="lengthTrackWrapper"> <div class="lengthTrackWrapper">
<div class="track lengthTrack"> <div class="track lengthTrack">
<div class="selectableTrack" n:attr="style => $isWithdrawn ? 'display: none;' : ''"> <div class="selectableTrack">
<div class='selectableTrackLoadProgress'>
<div class="load_bar"></div>
</div>
<div class="selectableTrackSlider"> <div class="selectableTrackSlider">
<div class="slider"></div> <div class="slider"></div>
</div> </div>

View file

@ -2,6 +2,7 @@
<div class="verticalGrayTabs"> <div class="verticalGrayTabs">
<div class='with_padding'> <div class='with_padding'>
<a n:if="isset($thisUser)" n:attr="id => $mode === 'list' && $isMy ? 'used' : 'ki'" href="/audios{$thisUser->getId()}">{_my_music}</a> <a n:if="isset($thisUser)" n:attr="id => $mode === 'list' && $isMy ? 'used' : 'ki'" href="/audios{$thisUser->getId()}">{_my_music}</a>
{* TODO: show upload link as and plusick (little plus) in button up*}
<a n:if="isset($thisUser)" href="/player/upload{if $isMyClub}?gid={abs($ownerId)}{/if}">{_upload_audio}</a> <a n:if="isset($thisUser)" href="/player/upload{if $isMyClub}?gid={abs($ownerId)}{/if}">{_upload_audio}</a>
<a n:if="isset($thisUser)" n:attr="id => $mode === 'new' ? 'used' : 'ki'" href="/search?section=audios">{_audio_new}</a> <a n:if="isset($thisUser)" n:attr="id => $mode === 'new' ? 'used' : 'ki'" href="/search?section=audios">{_audio_new}</a>
<a n:if="isset($thisUser)" n:attr="id => $mode === 'popular' ? 'used' : 'ki'" href="/search?section=audios&order=listens">{_audio_popular}</a> <a n:if="isset($thisUser)" n:attr="id => $mode === 'popular' ? 'used' : 'ki'" href="/search?section=audios&order=listens">{_audio_popular}</a>
@ -12,7 +13,7 @@
<a n:if="isset($thisUser)" href="/audios/newPlaylist">{_new_playlist}</a> <a n:if="isset($thisUser)" href="/audios/newPlaylist">{_new_playlist}</a>
{if !$isMy && $mode !== 'popular' && $mode !== 'new'} {if !$isMy && $mode !== 'popular' && $mode !== 'new' && $mode != 'alone_audio'}
<hr> <hr>
<a n:if="!$isMy" n:attr="id => $mode === 'list' ? 'used' : 'ki'" href="/audios{$ownerId}">{if $ownerId > 0}{_music_user}{else}{_music_club}{/if}</a> <a n:if="!$isMy" n:attr="id => $mode === 'list' ? 'used' : 'ki'" href="/audios{$ownerId}">{if $ownerId > 0}{_music_user}{else}{_music_club}{/if}</a>
@ -24,13 +25,13 @@
{if $friendsAudios} {if $friendsAudios}
<div class="friendsAudiosList"> <div class="friendsAudiosList">
<a href="/audios{$fr->getRealId()}" n:foreach="$friendsAudios as $fr"> <a href="/audios{$friend->getRealId()}" n:foreach="$friendsAudios as $friend">
<div class="elem"> <div class="elem">
<img src="{$fr->getAvatarURL()}" /> <img src="{$friend->getAvatarURL()}" />
<div class="additionalInfo"> <div class="additionalInfo">
<span class="name noOverflow">{$fr->getCanonicalName()}</span> <span class="name noOverflow">{$friend->getCanonicalName()}</span>
<span class="desc">{$audioStatus ? $audioStatus->getName() : tr("audios_count", $fr->getAudiosCollectionSize())}</span> <span class="desc">{$audioStatus ? $audioStatus->getName() : tr("audios_count", $friend->getAudiosCollectionSize())}</span>
</div> </div>
</div> </div>
</a> </a>

View file

@ -13,7 +13,7 @@
{block content} {block content}
<div class="gift_grid scroll_container"> <div class="gift_grid scroll_container">
<div n:foreach="$gifts as $gift" n:class="scroll_node, gift_sel, !$gift->canUse($thisUser) ? disabled" data-gift="{$gift->getId()}"> <a href='/gifts?act=confirm&user={$user->getId()}&pack={$cat->getId()}&elid={$gift->getId()}' n:foreach="$gifts as $gift" n:class="scroll_node, gift_sel, !$gift->canUse($thisUser) ? disabled" data-gift="{$gift->getId()}">
<img class="gift_pic" src="{$gift->getImage(2)}" alt="{_gift}" loading=lazy /> <img class="gift_pic" src="{$gift->getImage(2)}" alt="{_gift}" loading=lazy />
<strong class="gift_price"> <strong class="gift_price">
@ -29,7 +29,7 @@
{tr("gifts_left", $gift->getUsagesLeft($thisUser))} {tr("gifts_left", $gift->getUsagesLeft($thisUser))}
{/if}&nbsp; {/if}&nbsp;
</strong> </strong>
</div> </a>
</div> </div>
<div style="padding: 8px;"> <div style="padding: 8px;">
@ -41,18 +41,3 @@
]} ]}
</div> </div>
{/block} {/block}
{block bodyScripts}
<script>
$(".gift_sel").click(function() {
let el = $(this);
if(el.hasClass("disabled"))
return false;
let link = "/gifts?act=confirm&user={$user->getId()}&pack={$cat->getId()}&elid=";
let gift = el.data("gift");
window.location.assign(link + gift);
});
</script>
{/block}

View file

@ -80,27 +80,6 @@
</tbody> </tbody>
</table> </table>
<script n:if="$club->getOwner()->getId() != $user->getId() && $manager && $thisUser->getId() == $club->getOwner()->getId()">
function changeOwner(club, newOwner) {
const action = "/groups/" + club + "/setNewOwner/" + newOwner;
MessageBox({_group_changeowner_modal_title}, `
{tr("group_changeowner_modal_text", htmlentities($user->getFullName()))|noescape}
<br/><br/>
<form id="transfer-owner-permissions-form" method="post">
<label for="password">{_password|noescape}</label>
<input type="password" id="password" name="password" required />
<input type="hidden" name="hash" value={$csrfToken} />
</form>
`, [{_transfer}, {_cancel}], [
() => {
$("#transfer-owner-permissions-form").attr("action", action);
document.querySelector("#transfer-owner-permissions-form").submit();
}, Function.noop
]);
}
</script>
{/block} {/block}
{block actions} {block actions}
@ -115,7 +94,7 @@
{/if} {/if}
</a> </a>
{if $club->getOwner()->getId() != $user->getId() && $manager && $thisUser->getId() == $club->getOwner()->getId()} {if $club->getOwner()->getId() != $user->getId() && $manager && $thisUser->getId() == $club->getOwner()->getId()}
<a class="profile_link" href="javascript:changeOwner({$club->getId()}, {$user->getId()})"> <a class="profile_link" href="javascript:changeOwner({$club->getId()}, {$user->getId()}, '{$user->getCanonicalName()}')">
{_promote_to_owner} {_promote_to_owner}
</a> </a>
{/if} {/if}

View file

@ -145,7 +145,7 @@
<div n:ifset="$thisUser" id="profile_links"> <div n:ifset="$thisUser" id="profile_links">
{if $club->canBeModifiedBy($thisUser)} {if $club->canBeModifiedBy($thisUser)}
<a href="/club{$club->getId()}/edit" id="profile_link">{_edit_group}</a> <a href="/club{$club->getId()}/edit" id="profile_link">{_edit_group}</a>
<a href="/club{$club->getId()}/stats" id="profile_link">{_statistics}</a> <a href="/club{$club->getId()}/stats" rel="nofollow" id="profile_link">{_statistics}</a>
{/if} {/if}
{if $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)} {if $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
<a href="/admin/clubs/id{$club->getId()}" id="profile_link">{_manage_group_action}</a> <a href="/admin/clubs/id{$club->getId()}" id="profile_link">{_manage_group_action}</a>
@ -168,31 +168,7 @@
{/if} {/if}
{var $canReport = $thisUser->getId() != $club->getOwner()->getId()} {var $canReport = $thisUser->getId() != $club->getOwner()->getId()}
{if $canReport} {if $canReport}
<a class="profile_link" style="display:block;" href="javascript:reportClub()">{_report}</a> <a class="profile_link" style="display:block;" href="javascript:reportClub({$club->getId()})">{_report}</a>
<script>
function reportClub() {
uReportMsgTxt = tr("going_to_report_club");
uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
(function() {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + {$club->getId()} + "?reason=" + res + "&type=group", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
else
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
</script>
{/if} {/if}
<a n:if="!$club->isHideFromGlobalFeedEnabled()" class="profile_link" style="display:block;" id="__ignoreSomeone" data-val='{!$ignore_status ? 1 : 0}' data-id="{$club->getRealId()}"> <a n:if="!$club->isHideFromGlobalFeedEnabled()" class="profile_link" style="display:block;" id="__ignoreSomeone" data-val='{!$ignore_status ? 1 : 0}' data-id="{$club->getRealId()}">
{if !$ignore_status}{_ignore_club}{else}{_unignore_club}{/if} {if !$ignore_status}{_ignore_club}{else}{_unignore_club}{/if}
@ -318,7 +294,3 @@
</div> </div>
{/block} {/block}
{block bodyScripts}
{script "js/al_despacito_wall.js"}
{/block}

View file

@ -20,7 +20,7 @@
<div class="crp-list scroll_container"> <div class="crp-list scroll_container">
<div n:foreach="$corresps as $coresp" <div n:foreach="$corresps as $coresp"
class="scroll_node crp-entry" class="scroll_node crp-entry"
onmousedown="window.location.href = {$coresp->getURL()};" > onmousedown="window.location.assign({$coresp->getURL()});" >
{var $recipient = $coresp->getCorrespondents()[1]} {var $recipient = $coresp->getCorrespondents()[1]}
{var $lastMsg = $coresp->getPreviewMessage()} {var $lastMsg = $coresp->getPreviewMessage()}

View file

@ -20,7 +20,7 @@
{tr("notes_list", $count)} {tr("notes_list", $count)}
<span n:if="isset($thisUser) && $thisUser->getId() == $owner->getId()"> <span n:if="isset($thisUser) && $thisUser->getId() == $owner->getId()">
&nbsp;|&nbsp; &nbsp;|&nbsp;
<a href="/notes/create">{_create_note}</a> <a href="/notes/create" rel='nofollow'>{_create_note}</a>
</span> </span>
</div> </div>
</div> </div>
@ -99,7 +99,7 @@
<span n:if="isset($thisUser) && $thisUser->getId() === $dat->getOwner()->getId()">&nbsp;|&nbsp; <span n:if="isset($thisUser) && $thisUser->getId() === $dat->getOwner()->getId()">&nbsp;|&nbsp;
<a id="_noteDelete" href="/note{$dat->getOwner()->getId()}_{$dat->getId()}/delete">{_delete}</a> <a id="_noteDelete" href="/note{$dat->getOwner()->getId()}_{$dat->getId()}/delete">{_delete}</a>
&nbsp;|&nbsp; &nbsp;|&nbsp;
<a href="/note{$dat->getOwner()->getId()}_{$dat->getVirtualId()}/edit">{_edit}</a> <a href="/note{$dat->getOwner()->getId()}_{$dat->getVirtualId()}/edit" rel='nofollow'>{_edit}</a>
</span> </span>
</div> </div>
</div> </div>

View file

@ -66,7 +66,7 @@
<span n:if="isset($thisUser) && $thisUser->getId() === $note->getOwner()->getId()">&nbsp;|&nbsp; <span n:if="isset($thisUser) && $thisUser->getId() === $note->getOwner()->getId()">&nbsp;|&nbsp;
<a id="_noteDelete" href="/note{$note->getOwner()->getId()}_{$note->getId()}/delete">{_delete}</a> <a id="_noteDelete" href="/note{$note->getOwner()->getId()}_{$note->getId()}/delete">{_delete}</a>
&nbsp;|&nbsp; &nbsp;|&nbsp;
<a href="/note{$note->getOwner()->getId()}_{$note->getVirtualId()}/edit">{_edit}</a> <a href="/note{$note->getOwner()->getId()}_{$note->getVirtualId()}/edit" rel='nofollow'>{_edit}</a>
</span> </span>
</div> </div>
</div> </div>

View file

@ -25,9 +25,9 @@
</div> </div>
</div> </div>
<input type="file" accept=".jpg,.png,.gif" name="files[]" multiple class="button" id="uploadButton" style="display:none"> <input type="file" accept=".jpg,.png,.gif" name="files[]" multiple class="button photo_ajax_upload_button" id="uploadButton" style="display:none">
<div class="container_gray" style="min-height: 344px;"> <div class="container_gray photo_upload_container" style="min-height: 344px;">
<div class="insertThere"></div> <div class="insertThere"></div>
<div class="whiteBox" style="display: block;"> <div class="whiteBox" style="display: block;">
<div class="boxContent"> <div class="boxContent">
@ -62,7 +62,3 @@
uploadButton.value = '' uploadButton.value = ''
</script> </script>
{/block} {/block}
{block bodyScripts}
{script "js/al_photos.js"}
{/block}

View file

@ -17,8 +17,18 @@
<div class="page_wrap"> <div class="page_wrap">
{if $section == 'audios' && $count > 1} {if $section == 'audios' && $count > 1}
{include "../Audio/bigplayer.xml", tidy => true} {include "../Audio/bigplayer.xml", tidy => true}
<script>
<input type="hidden" name="bigplayer_context" data-type="classic_search_context" data-entity='{"order":"{$order}","query":"{$query}","invert":{$invert ? 1 : 0},"only_performers":{$_REQUEST['only_performers'] ? 1 : 0},"genre":"{$_REQUEST['genre']}","with_lyrics":"{$_REQUEST['with_lyrics'] ? 1 : 0}"}' data-page="{$page}"> window.__current_page_audio_context = {
'name': 'classic_search_context',
'order': {$order},
'query': {$query},
'genre': {$_REQUEST['genre']},
'invert': {$invert ? 1 : 0},
'only_performers': {$_REQUEST['only_performers'] ? 1 : 0},
'with_lyrics': {$_REQUEST['with_lyrics'] ? 1 : 0},
'page': {$page}
}
</script>
{/if} {/if}
<div class='summaryBar summaryBarFlex padding'> <div class='summaryBar summaryBarFlex padding'>
<div class='summary'> <div class='summary'>

View file

@ -86,11 +86,4 @@
setupWallPostInputHandlers(1); setupWallPostInputHandlers(1);
}); });
</script> </script>
{if $graffiti}
{script "js/node_modules/react/dist/react-with-addons.min.js"}
{script "js/node_modules/react-dom/dist/react-dom.min.js"}
{script "js/vnd_literallycanvas.js"}
{css "js/node_modules/literallycanvas/lib/css/literallycanvas.css"}
{/if}
{/block} {/block}

View file

@ -179,17 +179,6 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<script>
function toggleMaritalStatus(e) {
let elem = $("#maritalstatus-user");
$("#maritalstatus-user-select").empty();
if ([0, 1, 8].includes(Number(e.value))) {
elem.hide();
} else {
elem.show();
}
}
</script>
</form> </form>
{elseif $isContacts} {elseif $isContacts}

View file

@ -174,7 +174,7 @@
<input type="submit" class="profile_link" value="{_friends_reject}" /> <input type="submit" class="profile_link" value="{_friends_reject}" />
</form> </form>
{elseif $subStatus === 3} {elseif $subStatus === 3}
<a href="/im?sel={$x->getId()}" class="profile_link">{_send_message}</a> <a href="/im?sel={$x->getId()}" class="profile_link" rel="nofollow">{_send_message}</a>
<form action="/setSub/user" method="post" class="profile_link_form" id="_submitUserSubscriptionAction"> <form action="/setSub/user" method="post" class="profile_link_form" id="_submitUserSubscriptionAction">
<input type="hidden" name="act" value="rem" /> <input type="hidden" name="act" value="rem" />
<input type="hidden" name="id" value="{$x->getId()}" /> <input type="hidden" name="id" value="{$x->getId()}" />

View file

@ -395,6 +395,17 @@
</select> </select>
</td> </td>
</tr> </tr>
<tr>
<td width="120" valign="top">
<span class="nobold">{_privacy_setting_see_likes}</span>
</td>
<td>
<select name="likes.read", style="width: 164px;">
<option value="2" {if $user->getPrivacySetting('likes.read') == 2}selected{/if}>{_privacy_value_anybody}</option>
<option value="0" {if $user->getPrivacySetting('likes.read') == 0}selected{/if}>{_privacy_value_only_me_dative}</option>
</select>
</td>
</tr>
<tr> <tr>
<td width="120" valign="top"> <td width="120" valign="top">
<span class="nobold">{_profile_type}</span> <span class="nobold">{_profile_type}</span>
@ -481,30 +492,6 @@
<input type="hidden" name="hash" value="{$csrfToken}" /> <input type="hidden" name="hash" value="{$csrfToken}" />
<input type="submit" value="{_redeem}" class="button" /> <input type="submit" value="{_redeem}" class="button" />
</form> </form>
<script>
u(".vouncher_input").on("paste", function(event) {
const vouncher = event.clipboardData.getData("text");
let segments;
if(vouncher.length === 27) {
segments = vouncher.split("-");
if(segments.length !== 4)
segments = undefined;
} else if(vouncher.length === 24) {
segments = chunkSubstr(vouncher, 6);
}
if(segments !== undefined) {
document.vouncher_form.key0.value = segments[0];
document.vouncher_form.key1.value = segments[1];
document.vouncher_form.key2.value = segments[2];
document.vouncher_form.key3.value = segments[3];
document.vouncher_form.key3.focus();
}
event.preventDefault();
});
</script>
{elseif $isInterface} {elseif $isInterface}

View file

@ -113,10 +113,10 @@
<a href="javascript:warnUser()" class="profile_link" style="width: 194px;"> <a href="javascript:warnUser()" class="profile_link" style="width: 194px;">
{_warn_user_action} {_warn_user_action}
</a> </a>
<a href="/admin/user{$user->getId()}/bans" class="profile_link"> <a href="/admin/user{$user->getId()}/bans" class="profile_link" rel="nofollow">
{_blocks} {_blocks}
</a> </a>
<a href="/admin/logs?uid={$user->getId()}" class="profile_link" style="width: 194px;"> <a href="/admin/logs?uid={$user->getId()}" class="profile_link" style="width: 194px;" rel="nofollow">
{_last_actions} {_last_actions}
</a> </a>
{/if} {/if}
@ -132,7 +132,7 @@
{/if} {/if}
<a style="width: 194px;" n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $user->getGiftCount() == 0" href="/gifts?act=pick&user={$user->getId()}" class="profile_link">{_send_gift}</a> <a style="width: 194px;" n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $user->getGiftCount() == 0" href="/gifts?act=pick&user={$user->getId()}" class="profile_link">{_send_gift}</a>
<a style="width: 194px;" n:if="$user->getPrivacyPermission('messages.write', $thisUser)" href="/im?sel={$user->getId()}" class="profile_link">{_send_message}</a> <a style="width: 194px;" n:if="$user->getPrivacyPermission('messages.write', $thisUser)" href="/im?sel={$user->getId()}" class="profile_link" rel="nofollow">{_send_message}</a>
{var $subStatus = $user->getSubscriptionStatus($thisUser)} {var $subStatus = $user->getSubscriptionStatus($thisUser)}
{if $subStatus === 0} {if $subStatus === 0}
@ -165,33 +165,10 @@
</form> </form>
{/if} {/if}
<a class="profile_link" style="display:block;width:96%;" href="javascript:reportUser()">{_report}</a> <a class="profile_link" style="display:block;width:96%;" href="javascript:reportUser({$user->getId()})">{_report}</a>
<a n:if="!$user->isHideFromGlobalFeedEnabled()" class="profile_link" style="display:block;width:96%;" id="__ignoreSomeone" data-val='{!$ignore_status ? 1 : 0}' data-id="{$user->getId()}"> <a n:if="!$user->isHideFromGlobalFeedEnabled()" class="profile_link" style="display:block;width:96%;" id="__ignoreSomeone" data-val='{!$ignore_status ? 1 : 0}' data-id="{$user->getId()}">
{if !$ignore_status}{_ignore_user}{else}{_unignore_user}{/if} {if !$ignore_status}{_ignore_user}{else}{_unignore_user}{/if}
</a> </a>
<script>
function reportUser() {
uReportMsgTxt = tr("going_to_report_user");
uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
(function() {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + {$user->getId()} + "?reason=" + res + "&type=user", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
else
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
</script>
{/if} {/if}
<a style="width: 194px;" n:if="$user->getFollowersCount() > 0" href="/friends{$user->getId()}?act=incoming" class="profile_link">{tr("followers", $user->getFollowersCount())}</a> <a style="width: 194px;" n:if="$user->getFollowersCount() > 0" href="/friends{$user->getId()}?act=incoming" class="profile_link">{tr("followers", $user->getFollowersCount())}</a>
</div> </div>
@ -760,51 +737,6 @@
} }
{/if} {/if}
</script> </script>
<script n:if="isset($thisUser) && $user->getId() == $thisUser->getId()" n:syntax="off">
function setStatusEditorShown(shown) {
document.getElementById("status_editor").style.display = shown ? "block" : "none";
}
document.addEventListener("click", event => {
if(!event.target.closest("#status_editor") && !event.target.closest("#page_status_text"))
setStatusEditorShown(false);
});
document.getElementById("page_status_text").onclick = setStatusEditorShown.bind(this, true);
async function changeStatus() {
const status = document.status_popup_form.status.value;
const broadcast = document.status_popup_form.broadcast.checked;
document.status_popup_form.submit.innerHTML = "<div class=\"button-loading\"></div>";
document.status_popup_form.submit.disabled = true;
const formData = new FormData();
formData.append("status", status);
formData.append("broadcast", Number(broadcast));
formData.append("hash", document.status_popup_form.hash.value);
const response = await ky.post("/edit?act=status", {body: formData});
if(!parseAjaxResponse(await response.text())) {
document.status_popup_form.submit.innerHTML = tr("send");
document.status_popup_form.submit.disabled = false;
return;
}
if(document.status_popup_form.status.value === "") {
document.querySelector("#page_status_text").innerHTML = `[ ${tr("change_status")} ]`;
document.querySelector("#page_status_text").className = "edit_link page_status_edit_button";
} else {
document.querySelector("#page_status_text").innerHTML = status;
document.querySelector("#page_status_text").className = "page_status page_status_edit_button";
}
setStatusEditorShown(false);
document.status_popup_form.submit.innerHTML = tr("send");
document.status_popup_form.submit.disabled = false;
}
</script>
</div> </div>
{/if} {/if}
@ -813,7 +745,3 @@
{include "banned.xml"} {include "banned.xml"}
{/if} {/if}
{/block} {/block}
{block bodyScripts}
{script "js/al_despacito_wall.js"}
{/block}

View file

@ -17,7 +17,7 @@
style="width: 100%; image-rendering: -webkit-optimize-contrast;" /> style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
</div> </div>
<div id="profile_links" n:if="isset($thisUser)"> <div id="profile_links" n:if="isset($thisUser)">
<a style="width: 194px;" n:if="$user->getPrivacyPermission('messages.write', $thisUser)" href="/im?sel={$user->getId()}" class="profile_link">{_send_message}</a> <a style="width: 194px;" n:if="$user->getPrivacyPermission('messages.write', $thisUser)" href="/im?sel={$user->getId()}" class="profile_link" rel='nofollow'>{_send_message}</a>
{var $subStatus = $user->getSubscriptionStatus($thisUser)} {var $subStatus = $user->getSubscriptionStatus($thisUser)}
{if $subStatus === 0} {if $subStatus === 0}
<form action="/setSub/user" method="post" class="profile_link_form" id="addToFriends"> <form action="/setSub/user" method="post" class="profile_link_form" id="addToFriends">
@ -41,30 +41,7 @@
<input type="submit" class="profile_link" value="{_friends_reject}" style="width: 194px;" /> <input type="submit" class="profile_link" value="{_friends_reject}" style="width: 194px;" />
</form> </form>
{/if} {/if}
<a class="profile_link" style="display:block;width:96%;" href="javascript:reportUser()">{_report}</a> <a class="profile_link" style="display:block;width:96%;" href="javascript:reportUser({$user->getId()})">{_report}</a>
<script>
function reportUser() {
uReportMsgTxt = "Вы собираетесь пожаловаться на данного пользователя.";
uReportMsgTxt += "<br/>Что именно вам кажется недопустимым в этом материале?";
uReportMsgTxt += "<br/><br/><b>Причина жалобы</b>: <input type='text' id='uReportMsgInput' placeholder='Причина' />"
MessageBox("Пожаловаться?", uReportMsgTxt, ["Подтвердить", "Отмена"], [
(function() {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + {$user->getId()} + "?reason=" + res + "&type=user", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]);
else
MessageBox("Операция успешна", "Скоро её рассмотрят модераторы", ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
</script>
</div> </div>
</div> </div>

View file

@ -24,7 +24,7 @@
</div> </div>
<div class='scroll_container'> <div class='scroll_container'>
<div class='scroll_node' n:foreach='$posts as $post'> <div class='scroll_node' n:foreach='$posts as $post' data-uniqueid="{$post->getPrettyId()}">
<a name="postGarter={$post->getId()}"></a> <a name="postGarter={$post->getId()}"></a>
{include "../components/post.xml", post => $post, onWallOf => true, commentSection => true} {include "../components/post.xml", post => $post, onWallOf => true, commentSection => true}
</div> </div>

View file

@ -57,7 +57,7 @@
{if $thisUser->getId() != $author->getRealId()} {if $thisUser->getId() != $author->getRealId()}
| |
{var $canReport = true} {var $canReport = true}
<a href="javascript:reportComment()">{_report}</a> <a href="javascript:reportComment({$comment->getId()})">{_report}</a>
{/if} {/if}
<div style="float: right; font-size: .7rem;"> <div style="float: right; font-size: .7rem;">
<a class="post-like-button" href="/comment{$comment->getId()}/like?hash={rawurlencode($csrfToken)}" data-likes='{$likesCount}' data-id="1_{$comment->getPrettyId()}" data-type='comment'> <a class="post-like-button" href="/comment{$comment->getId()}/like?hash={rawurlencode($csrfToken)}" data-likes='{$likesCount}' data-id="1_{$comment->getPrettyId()}" data-type='comment'>
@ -82,26 +82,3 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<script n:if="$canReport ?? false">
function reportComment() {
uReportMsgTxt = tr("going_to_report_comment");
uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
(function() {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + {$comment->getId()} + "?reason=" + res + "&type=comment", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
else
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
</script>

View file

@ -54,11 +54,11 @@
<input type="checkbox" name="as_group" /> {_comment_as_group} <input type="checkbox" name="as_group" /> {_comment_as_group}
</label> </label>
</div> </div>
<input type="hidden" name="horizontal_attachments" value="" /> <input type="hidden" name="horizontal_attachments" value="" autocomplete="off" />
<input type="hidden" name="vertical_attachments" value="" /> <input type="hidden" name="vertical_attachments" value="" autocomplete="off" />
<input type="hidden" name="poll" value="none" /> <input type="hidden" name="poll" value="none" autocomplete="off" />
<input type="hidden" id="source" name="source" value="none" /> <input type="hidden" id="source" name="source" value="none" autocomplete="off" />
<input type="hidden" name="type" value="1" /> <input type="hidden" name="type" value="1" autocomplete="off" />
<input type="hidden" name="hash" value="{$csrfToken}" /> <input type="hidden" name="hash" value="{$csrfToken}" />
<br/> <br/>
<input type="submit" value="{_write}" class="button" /> <input type="submit" value="{_write}" class="button" />
@ -104,15 +104,3 @@
</div> </div>
</form> </form>
</div> </div>
<script>
u("#post-buttons{$textAreaId} input[name='horizontal_attachments']")["nodes"].at(0).value = ""
u("#post-buttons{$textAreaId} input[name='vertical_attachments']")["nodes"].at(0).value = ""
</script>
{if $graffiti}
{script "js/node_modules/react/dist/react-with-addons.min.js"}
{script "js/node_modules/react-dom/dist/react-dom.min.js"}
{script "js/vnd_literallycanvas.js"}
{css "js/node_modules/literallycanvas/lib/css/literallycanvas.css"}
{/if}

View file

@ -15,7 +15,7 @@
<div class="content scroll_container"> <div class="content scroll_container">
{if sizeof($posts) > 0} {if sizeof($posts) > 0}
<div class='scroll_node' n:foreach='$posts as $post'> <div class='scroll_node' n:foreach='$posts as $post' data-uniqueid="{$post->getPrettyId()}">
<a name="postGarter={$post->getId()}"></a> <a name="postGarter={$post->getId()}"></a>
{include "../components/post.xml", post => $post, commentSection => true} {include "../components/post.xml", post => $post, commentSection => true}

View file

@ -201,14 +201,10 @@ routes:
handler: "Audio->upload" handler: "Audio->upload"
- url: "/audios{num}" - url: "/audios{num}"
handler: "Audio->list" handler: "Audio->list"
- url: "/audios/popular"
handler: "Audio->popular"
- url: "/audios/new"
handler: "Audio->new"
- url: "/audio{num}_{num}/embed.xhtml"
handler: "Audio->embed"
- url: "/audio{num}/listen" - url: "/audio{num}/listen"
handler: "Audio->listen" handler: "Audio->listen"
- url: "/audio{num}_{num}"
handler: "Audio->aloneAudio"
- url: "/audios/search" - url: "/audios/search"
handler: "Audio->search" handler: "Audio->search"
- url: "/audios/newPlaylist" - url: "/audios/newPlaylist"

View file

@ -8,8 +8,12 @@
padding: 8px; padding: 8px;
} }
.audiosSideContainer {
width: 74%;
}
.musicIcon { .musicIcon {
background-image: url('/assets/packages/static/openvk/img/audios_controls.png?v=2'); background-image: url('/assets/packages/static/openvk/img/audios_controls.png?v=6');
background-repeat: no-repeat; background-repeat: no-repeat;
cursor: pointer; cursor: pointer;
} }
@ -18,6 +22,8 @@
filter: brightness(150%); filter: brightness(150%);
} }
/* Main music player */
.bigPlayer { .bigPlayer {
background-color: rgb(240, 241, 242); background-color: rgb(240, 241, 242);
margin-left: -10px; margin-left: -10px;
@ -28,68 +34,61 @@
box-shadow: 1px 0px 8px 0px rgba(34, 60, 80, 0.2); box-shadow: 1px 0px 8px 0px rgba(34, 60, 80, 0.2);
position: sticky; position: sticky;
top: 0px; top: 0px;
z-index: 1; z-index: 10;
} }
/* for search */
.bigPlayer.tidy { .bigPlayer.tidy {
width: 100%; width: 100%;
margin-left: unset; margin-left: unset;
margin-top: unset; margin-top: unset;
} }
.bigPlayer.floating { .bigPlayer .bigPlayerWrapper {
position: fixed; padding: 0px 14px 0px 14px;
z-index: 199; display: grid;
width: 627px; grid-template-columns: 0fr 3fr 1fr 0fr;
margin-top: -76px; align-items: center;
height: 46px;
} }
.bigPlayer .paddingLayer { /* Play button and arrows */
padding: 0px 0px 0px 14px; .bigPlayer .playButtons {
display: flex;
align-items: center;
gap: 7px;
width: 62px;
} }
.bigPlayer .paddingLayer .playButtons { .bigPlayer .playButtons .playButton {
padding: 12px 0px;
}
.bigPlayer .paddingLayer .playButtons .playButton {
width: 22px; width: 22px;
height: 22px; height: 22px;
float: left;
background-position-x: -72px; background-position-x: -72px;
} }
.bigPlayer .paddingLayer .playButtons .playButton.pause { .bigPlayer .playButtons .playButton.pause {
background-position-x: -168px; background-position-x: -168px;
} }
.bigPlayer .paddingLayer .playButtons .nextButton { .bigPlayer .playButtons .nextButton,
width: 16px; .bigPlayer .playButtons .backButton {
height: 16px; width: 12px;
background-position-y: -47px; height: 12px;
} }
.bigPlayer .paddingLayer .playButtons .backButton { .bigPlayer .playButtons .nextButton {
width: 16px; background-position: -3px -51px;
height: 16px;
background-position-y: -47px;
background-position-x: -16px;
margin-left: 6px;
} }
.bigPlayer .paddingLayer .additionalButtons { .bigPlayer .playButtons .backButton {
float: left; background-position: -18px -51px;
margin-top: -6px;
width: 11%;
} }
.bigPlayer .paddingLayer .additionalButtons .repeatButton { .bigPlayer .playButtons .arrowsButtons {
width: 14px; display: flex;
height: 16px; align-items: center;
background-position-y: -49px; gap: 9px;
background-position-x: -31px; height: 11px;
margin-left: 7px;
float: left;
} }
.broadcastButton { .broadcastButton {
@ -111,55 +110,47 @@
float: left; float: left;
} }
.bigPlayer .paddingLayer .additionalButtons .shuffleButton { /* Track panel and volume */
width: 14px; .bigPlayer .trackPanel {
height: 16px; position: relative;
background-position: -50px -50px; margin-left: 15px;
margin-left: 7px;
float: left;
}
.bigPlayer .paddingLayer .additionalButtons .deviceButton {
width: 12px;
height: 16px;
background-position: -202px -50px;
margin-left: 7px;
float: left;
}
.bigPlayer .paddingLayer .playButtons .arrowsButtons {
float: left;
display: flex; display: flex;
padding-left: 4px; flex-direction: column;
padding-top: 1.2px; width: 386px;
} }
.bigPlayer .paddingLayer .trackPanel { .bigPlayer .trackPanel .track {
float: left; margin-top: -3px;
margin-top: -13px; }
margin-left: 13px;
width: 63%; .tip_result {
width: max-content;
height: 11px;
padding: 4px;
top: -6px;
background: #f7f7f7;
border: 1px solid #d8d8d8;
position: absolute;
z-index: 10;
transition: all .1s ease-out;
user-select: none;
transform: translate(-20%, -15%);
}
.bigPlayer .volumePanel {
display: flex;
align-items: center;
padding-top: 12px;
width: 73px;
position: relative; position: relative;
} }
.bigPlayer .paddingLayer .bigPlayerTip { .bigPlayer .volumePanel .volumePanelTrack {
display: none; width: 100%;
z-index: 999;
background: #cecece;
padding: 3px;
top: -3px;
position: absolute;
transition: all .1s ease-out;
user-select: none;
} }
.bigPlayer .paddingLayer .volumePanel { .bigPlayer .slider, .audioEmbed .track .slider {
width: 73px; width: 15px;
float: left;
}
.bigPlayer .paddingLayer .slider, .audioEmbed .track .slider {
width: 18px;
height: 7px; height: 7px;
background: #606060; background: #606060;
position: absolute; position: absolute;
@ -168,59 +159,97 @@
pointer-events: none; pointer-events: none;
} }
.bigPlayer .paddingLayer .trackInfo .timer { .bigPlayer .trackInfo {
float: right; display: flex;
margin-right: 8px; flex-direction: row;
font-size: 10px; height: 15px;
justify-content: space-between;
} }
.bigPlayer .paddingLayer .trackInfo .timer .elapsedTime { .bigPlayer .trackPanel .trackInfo,
cursor: pointer; .bigPlayer .trackPanel .track,
.bigPlayer .volumePanel .volumePanelTrack {
padding-right: 8px;
} }
.bigPlayer .paddingLayer .trackInfo .trackName { .bigPlayer .trackInfo .trackName {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
width: 81%; max-width: 81%;
height: 13px; height: 16px;
display: inline-block; display: inline-block;
line-height: 14px; line-height: 14px;
} }
.bigPlayer .paddingLayer .trackInfo .timer span { .bigPlayer .trackInfo .timer span,
font-size: 10px; .bigPlayer .trackInfo .timer {
font-size: 9px;
} }
.bigPlayer .paddingLayer .trackInfo a { .bigPlayer .trackInfo a {
font-weight: bold; font-weight: bold;
color: black; color: black;
} }
.bigPlayer .paddingLayer .trackInfo a:hover { .bigPlayer .trackInfo .timer .elapsedTime {
cursor: pointer;
}
/* Additional buttons */
.bigPlayer .additionalButtons {
display: flex;
flex-direction: row;
align-items: center;
position: relative;
gap: 7px;
height: 43px;
}
.bigPlayer .additionalButtons .repeatButton,
.bigPlayer .additionalButtons .shuffleButton,
.bigPlayer .additionalButtons .deviceButton {
width: 13px;
height: 14px;
}
.bigPlayer .additionalButtons .repeatButton {
background-position: -32px -49px;
}
.bigPlayer .additionalButtons .shuffleButton {
background-position: -50px -50px;
}
.bigPlayer .additionalButtons .deviceButton {
background-position: -201px -50px;
}
.bigPlayer .trackInfo a:hover {
text-decoration: underline; text-decoration: underline;
cursor: pointer; cursor: pointer;
} }
.bigPlayer .paddingLayer .trackPanel .track .selectableTrack > div { .bigPlayer .trackPanel .track .selectableTrack > div {
width: 95%; width: 96%;
position: relative; position: relative;
} }
.bigPlayer .paddingLayer .volumePanel .selectableTrack > div { .bigPlayer .volumePanel .selectableTrack > div {
position: relative; position: relative;
width:72% width: 77%;
} }
.audioEmbed .track > .selectableTrack, .bigPlayer .selectableTrack { .audioEmbed .track > .selectableTrack, .bigPlayer .selectableTrack {
margin-top: 3px; margin-top: 3px;
width: calc(100% - 8px); width: 100%;
border-top: #606060 1px solid; border-top: #606060 1px solid;
height: 6px; height: 6px;
position: relative; position: relative;
user-select: none; user-select: none;
} }
/* Audio miniplayer */
#audioEmbed { #audioEmbed {
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
@ -236,6 +265,26 @@
border: 1px solid #8B8B8B; border: 1px solid #8B8B8B;
} }
/* Audio states */
.audioEntry {
width: 100%;
height: 100%;
}
.audioEntry .audioEntryWrapper {
padding: 9px 9px;
display: grid;
grid-template-columns: 0fr 10fr 1fr;
align-items: center;
gap: 9px;
height: 17px;
position: relative;
}
.audioEntry .audioEntryWrapper.compact {
padding: 10px 0px;
}
.audioEntry.nowPlaying { .audioEntry.nowPlaying {
background: #606060; background: #606060;
outline: 1px solid #4f4f4f; outline: 1px solid #4f4f4f;
@ -251,10 +300,35 @@
border-radius: inherit !important; border-radius: inherit !important;
} }
.audioEntry.nowPlaying .performer a { .audioEntry.nowPlaying .performer a,
.audioEntry.nowPlaying .performer {
color: #f4f4f4 !important; color: #f4f4f4 !important;
} }
.audioEmbed.withdrawn .status > *, .audioEmbed.processed .playerButton > *, .audioEmbed.withdrawn .playerButton > * {
pointer-events: none;
}
.audioEmbed.withdrawn {
opacity: 0.8;
}
.audioEmbed.processed {
filter: opacity(0.6);
}
/* Audio subparts */
.audioEntry .playerButton {
position: relative;
width: 16px;
height: 16px;
}
.audioEntry .nobold {
text-align: center;
min-width: 28px;
}
.audioEntry .performer a { .audioEntry .performer a {
color: #4C4C4C; color: #4C4C4C;
} }
@ -267,18 +341,12 @@
color: white; color: white;
} }
.audioEntry .volume { .audioEntry .mini_timer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 14%;
} }
.audioEntry .nobold { .audioEntry.nowPlaying .mini_timer .nobold {
text-align: center;
margin-top: 12px;
}
.audioEntry.nowPlaying .volume .nobold {
color: white !important; color: white !important;
} }
@ -286,6 +354,7 @@
fill: #ffffff; fill: #ffffff;
} }
/* Audio icons */
.audioEntry.nowPlaying .buttons .musicIcon.edit-icon { .audioEntry.nowPlaying .buttons .musicIcon.edit-icon {
background-position: -152px -51px; background-position: -152px -51px;
} }
@ -310,25 +379,12 @@
background-position: -108px -67px; background-position: -108px -67px;
} }
.audioEntry {
height: 100%;
position: relative;
width: 100%;
min-height: 39px;
}
.audioEntry .playerButton {
position: relative;
padding: 10px 9px 9px 9px;
width: 16px;
height: 16px;
}
.audioEntry .subTracks { .audioEntry .subTracks {
display: none; display: none;
padding-bottom: 5px; padding-bottom: 5px;
padding-left: 8px; padding-left: 8px;
padding-right: 12px; padding-right: 12px;
margin-top: -5px;
} }
.audioEntry .subTracks.shown { .audioEntry .subTracks.shown {
@ -348,25 +404,37 @@
} }
.audioEntry .status { .audioEntry .status {
/*position: relative;*/
height: 14px;
overflow: hidden; overflow: hidden;
display: grid;
grid-template-columns: 1fr;
width: 85%;
height: 23px;
margin-top: 12px;
} }
.audioEntry .status .mediaInfo { .audioEntry .status .mediaInfo {
cursor: pointer; cursor: pointer;
display: flex;
line-height: 14px; line-height: 14px;
width: 100%; width: 100%;
} }
.overflowedName { .audioEntry .status .mediaInfo .info {
display: inline;
}
@keyframes marquee {
from { left: -100%; }
to { left: 100%; }
}
.audioEntry .status:hover .mediaInfo {
position: absolute; position: absolute;
z-index: 99; z-index: 2;
width: 80% !important; overflow: visible;
white-space: wrap;
text-overflow: unset;
width: 83%;
}
.audioEntry .status:hover .mediaInfo .info {
width: 100%;
} }
.audioEntry .status strong { .audioEntry .status strong {
@ -381,13 +449,41 @@
width: 100%; width: 100%;
} }
.selectableTrack .selectableTrackLoadProgress {
top: -13px;
z-index: -1;
overflow: hidden;
height: 7px;
width: 100% !important;
}
.selectableTrack .selectableTrackLoadProgress .load_bar {
background: #e7e7e7;
border-bottom: 1px solid #dfdfdf;
box-sizing: border-box;
height: 7px;
position: absolute;
}
.audioEmbed .selectableTrack .selectableTrackLoadProgress {
position: absolute;
top: 0px;
}
.audioEmbed .track .selectableTrack .selectableTrackSlider { .audioEmbed .track .selectableTrack .selectableTrackSlider {
position: relative; position: relative;
width: calc(100% - 18px); width: calc(100% - 18px);
} }
.audioEmbed .subTracks .lengthTrackWrapper { .audioEmbed .subTracks .lengthTrackWrapper,
.audioEmbed .subTracks .volumeTrackWrapper {
width: 100%; width: 100%;
position: relative;
}
.audioEmbed .subTracks .lengthTrackWrapper .tip_result,
.audioEmbed .subTracks .volumeTrackWrapper .tip_result {
top: -20px;
} }
.audioEmbed .subTracks .volumeTrackWrapper { .audioEmbed .subTracks .volumeTrackWrapper {
@ -408,86 +504,71 @@
display: flex; display: flex;
} }
.audioEntry:hover .volume .hideOnHover { .audioEntry:hover .mini_timer .hideOnHover {
display: none; display: none;
} }
.audioEntry .buttons { .audioEntry .buttons {
display: none; display: none;
flex-direction: row-reverse; flex-direction: row-reverse;
gap: 5px;
align-items: center;
justify-content: flex-start; justify-content: flex-start;
width: 62px; align-items: center;
height: 20px; gap: 5px;
position: absolute; position: absolute;
right: 3%; z-index: 9;
top: 2px; right: 10px;
margin-top: 7px; top: 0;
/* чтоб избежать заедания во время ховера кнопки добавления */ /* чтоб избежать заедания во время ховера кнопки добавления */
clip-path: inset(0 0 0 0); clip-path: inset(0 0 0 0);
width: 62px;
height: 100%;
}
.audioEntry .buttons .edit-icon,
.audioEntry .buttons .download-icon,
.audioEntry .buttons .add-icon,
.audioEntry .buttons .add-icon-group,
.audioEntry .buttons .report-icon,
.add-icon-noaction,
.audioEntry .buttons .remove-icon,
.audioEntry .buttons .remove-icon-group {
width: 11px;
height: 11px;
} }
.audioEntry .buttons .edit-icon { .audioEntry .buttons .edit-icon {
width: 11px;
height: 11px;
float: right;
background-position: -137px -51px; background-position: -137px -51px;
} }
.audioEntry .buttons .download-icon { .audioEntry .buttons .download-icon {
width: 11px;
height: 11px;
float: right;
background-position: -136px -67px; background-position: -136px -67px;
} }
.audioEntry .buttons .add-icon { .audioEntry .buttons .add-icon {
width: 11px;
height: 11px;
float: right;
background-position: -80px -52px; background-position: -80px -52px;
} }
.audioEntry .buttons .add-icon-group { .audioEntry .buttons .add-icon-group {
width: 14px;
height: 11px;
float: right;
background-position: -94px -52px; background-position: -94px -52px;
transition: margin-right 0.1s ease-out, opacity 0.1s ease-out; transition: margin-right 0.1s ease-out, opacity 0.1s ease-out;
} }
.audioEntry .buttons .report-icon { .audioEntry .buttons .report-icon {
width: 12px; background-position: -51px -67px;
height: 11px;
float: right;
background-position: -50px -67px;
}
.add-icon-noaction {
background-image: url('/assets/packages/static/openvk/img/audios_controls.png');
width: 11px;
height: 11px;
float: right;
background-position: -94px -52px;
margin-top: 2px;
margin-right: 2px;
} }
.audioEntry .buttons .remove-icon { .audioEntry .buttons .remove-icon {
width: 11px;
height: 11px;
float: right;
background-position: -108px -52px; background-position: -108px -52px;
} }
.audioEntry .buttons .remove-icon-group { .audioEntry .buttons .remove-icon-group {
width: 13px; width: 13px;
height: 11px; height: 11px;
float: right;
background-position: -122px -52px; background-position: -122px -52px;
} }
/* Lyrics */
.audioEmbed .lyrics { .audioEmbed .lyrics {
display: none; display: none;
padding: 6px 33px 10px 33px; padding: 6px 33px 10px 33px;
@ -506,33 +587,59 @@
text-decoration: underline; text-decoration: underline;
} }
.audioEmbed.withdrawn .status > *, .audioEmbed.processed .playerButton > *, .audioEmbed.withdrawn .playerButton > * {
pointer-events: none;
}
.audioEmbed.withdrawn {
opacity: 0.8;
}
.playlistCover img { .playlistCover img {
max-width: 135px; max-width: 135px;
max-height: 135px; max-height: 135px;
} }
.playlistBlock { .playlistBlock {
display: flex;
gap: 13px;
margin-top: 14px; margin-top: 14px;
} }
.playlistBlock .playlistCover {
max-width: 135px;
}
.playlistBlock .playlistWrapper { .playlistBlock .playlistWrapper {
float: left;
padding-left: 13px;
width: 75% width: 75%
} }
.playlistBlock .playlistInfo h4 {
border-bottom: unset;
}
.playlistBlock .playlistInfo hr {
color: #f7f7f7;
}
.playlistCover .profile_links .profile_link { .playlistCover .profile_links .profile_link {
width: 100%; width: 100%;
} }
.PE_playlistEditPage {
display: flex;
gap: 10px;
}
.PE_playlistEditPage .PE_playlistInfo {
width: 76%;
display: flex;
flex-direction: column;
gap: 10px;
}
.PE_playlistEditPage textarea[name='description'] {
padding: 4px;
font-size: 11px;
}
.PE_end {
text-align: right;
margin-top: 10px;
}
/* playlist listview */ /* playlist listview */
.playlistListView { .playlistListView {
@ -617,10 +724,10 @@
} }
.explicitMark { .explicitMark {
margin-top: 2px; width: 13px;
margin-left: 3px;
width: 11px;
height: 11px; height: 11px;
margin-bottom: -2px;
display: inline-block;
} }
.explicitMark path { .explicitMark path {
@ -809,3 +916,146 @@
margin-top: 6px; margin-top: 6px;
padding: 1px; padding: 1px;
} }
/* AJAX player */
#ajax_audio_player {
transition: background .1s ease-out;
background: rgba(44, 44, 44, 0.7);
padding: 1px;
width: 500px;
height: 37px;
position: fixed;
z-index: 1025;
border-radius: 3px;
}
#ajax_audio_player.hidden {
display: none;
}
#ajax_audio_player #aj_player {
position: relative;
height: 100%;
}
#ajax_audio_player #aj_player #aj_player_internal_controls {
padding: 7px 8px;
display: flex;
gap: 7px;
}
#ajax_audio_player.ui-draggable-dragging {
background: rgba(20, 20, 20, 0.9);
}
#ajax_audio_player #aj_player_close_btn, #ajax_audio_player #aj_player_play {
padding: 3px 0px;
}
#ajax_audio_player #aj_player_close_btn,
#ajax_audio_player #aj_player_play #aj_player_play_btn,
#ajax_audio_player #aj_player_buttons > div {
background: url('/assets/packages/static/openvk/img/audios_controls.png?v=6');
background-repeat: no-repeat;
cursor: pointer;
}
#ajax_audio_player #aj_player_close_btn {
position: absolute;
top: 0;
right: 0;
width: 10px;
height: 9px;
background-position: 0px -77px;
opacity: 0.6;
}
#ajax_audio_player #aj_player_close_btn:hover {
opacity: 1;
}
#ajax_audio_player #aj_player_play #aj_player_play_btn {
width: 16px;
height: 16px;
background-position: -147px -28px;
}
#ajax_audio_player #aj_player_play #aj_player_play_btn.paused {
background-position: -165px -28px;
}
#ajax_audio_player #aj_player_track {
width: 100%;
position: relative;
}
#ajax_audio_player #aj_player_track #aj_player_track_name {
display: flex;
justify-content: space-between;
}
#ajax_audio_player #aj_player_track #aj_player_track_name #aj_player_track_title,
#ajax_audio_player #aj_player_track #aj_player_track_name #aj_player_track_title b,
#ajax_audio_player #aj_player_track #aj_player_track_name #aj_player_track_title span,
#ajax_audio_player #aj_player_track #aj_player_track_name span {
color: white;
}
#ajax_audio_player #aj_time {
cursor: pointer;
}
#ajax_audio_player #aj_time:hover {
text-decoration: underline;
}
#ajax_audio_player .selectableTrack {
width: 100%;
position: relative;
height: 6px;
border-top: #ffffff 1px solid;
user-select: none;
}
#ajax_audio_player .selectableTrack .slider {
width: 11px;
height: 6px;
background: #ffffff;
position: absolute;
}
#ajax_audio_player #aj_player_volume {
width: 60px;
padding-top: 15px;
position: relative;
}
#ajax_audio_player #aj_player_buttons {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
opacity: 0.9;
}
#ajax_audio_player #aj_player_buttons #aj_player_previous {
width: 14px;
height: 12px;
background-position: -194px -5px;
}
#ajax_audio_player #aj_player_buttons #aj_player_repeat {
width: 14px;
height: 13px;
background-position: -233px -5px;
}
#ajax_audio_player #aj_player_buttons #aj_player_repeat.pressed {
opacity: 0.6;
}
#ajax_audio_player #aj_player_buttons #aj_player_next {
width: 15px;
height: 12px;
background-position: -214px -5px;
}

View file

@ -139,7 +139,7 @@ body.dimmed > .dimmer #absolute_territory {
} }
.miniplayer { .miniplayer {
position: absolute; position: fixed;
background: rgba(54, 54, 54, 0.95); background: rgba(54, 54, 54, 0.95);
border-radius: 3px; border-radius: 3px;
min-width: 299px; min-width: 299px;

View file

@ -2261,6 +2261,25 @@ table td[width="120"] {
margin: 0; margin: 0;
} }
#upload_container #lastStepContainers {
display: flex;
flex-direction: column;
gap: 10px;
}
#upload_container .upload_container_element {
border: 1px solid #d6d6d6;
background: #f6f6f6;
}
#upload_container .upload_container_element .upload_container_name {
padding: 5px 5px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
#audio_upload { #audio_upload {
width: 350px; width: 350px;
margin: 20px auto; margin: 20px auto;
@ -2554,57 +2573,55 @@ a.poll-retract-vote {
border-radius: 3px; border-radius: 3px;
} }
.post-vertical .vertical-attachment { .vertical-attachment {
display: grid; display: grid;
grid-template-columns: 1fr 0fr; grid-template-columns: 1fr 0fr;
} }
.post-vertical .vertical-attachment:first-of-type { .vertical-attachment:first-of-type {
margin-top: 7px; margin-top: 7px;
} }
.post-vertical .vertical-attachment .audioEntry { .vertical-attachment .audioEntry {
max-height: 28px; max-height: 28px;
min-height: 18px; min-height: 18px;
} }
.post-vertical .vertical-attachment .audioEntry:hover { .vertical-attachment .audioEntry:hover {
background: unset !important; background: unset !important;
} }
.post-vertical .vertical-attachment .audioEntry .playerButton { .vertical-attachment .audioEntry .buttons {
padding: 0px 3px 0px 0px;
}
.post-vertical .vertical-attachment .audioEntry .buttons {
display: none; display: none;
} }
.post-vertical .vertical-attachment .audioEntry .status { .vertical-attachment .audioEntry .status {
margin-top: 1px; margin-top: 1px;
margin-left: 2px; margin-left: 2px;
height: 15px; height: 15px;
} }
.post-vertical .vertical-attachment .audioEntry .nobold { .vertical-attachment .audioEntry .nobold {
margin-top: 1px; margin-top: 1px;
} }
.post-vertical .vertical-attachment .audioEntry .subTracks { .vertical-attachment .audioEntry .subTracks {
padding-bottom: 2px; padding-left: 0px;
padding-left: 3px;
padding-right: 0px; padding-right: 0px;
margin-top: 2px;
} }
.post-vertical .vertical-attachment .audioEntry .audioEntryWrapper { .vertical-attachment .audioEntry .audioEntryWrapper {
height: 18px; height: 14px;
padding: 0px 4px 0px 0px;
gap: 2px;
} }
.post-vertical .vertical-attachment .vertical-attachment-content { .post-buttons .vertical-attachment .vertical-attachment-content {
max-height: 27px; max-height: 27px;
} }
.post-vertical .vertical-attachment .vertical-attachment-content .overflowedName { .vertical-attachment .vertical-attachment-content .overflowedName {
position: initial; position: initial;
width: 100% !important; width: 100% !important;
} }
@ -2833,6 +2850,10 @@ a.poll-retract-vote {
z-index: 2; z-index: 2;
} }
.page_header.search_expanded_at_all .header_navigation #search_box #searchBoxFastTips.shown {
display: none;
}
.page_header.search_expanded .link { .page_header.search_expanded .link {
display: none; display: none;
} }
@ -3559,7 +3580,7 @@ hr {
cursor: pointer; cursor: pointer;
} }
#upload_container.uploading { #upload_container .upload_container_element .upload_container_name.uploading {
background: white url('/assets/packages/static/openvk/img/progressbar.gif') !important; background: white url('/assets/packages/static/openvk/img/progressbar.gif') !important;
background-position-x: 0% !important; background-position-x: 0% !important;
background-position-y: 0% !important; background-position-y: 0% !important;
@ -3857,3 +3878,36 @@ hr {
.tippy-box[data-animation='up_down'][data-state='visible'] { .tippy-box[data-animation='up_down'][data-state='visible'] {
inset: auto auto 0px 0px; inset: auto auto 0px 0px;
} }
.ctx_place {
position: relative;
}
#ctx_menu {
position: absolute;
z-index: 100;
background: #f7f7f7;
border: 1px solid #d8d8d8;
display: flex;
flex-direction: column;
min-width: 185px;
}
#ctx_menu a {
padding: 6px 6px 6px 26px;
color: black;
position: relative;
}
#ctx_menu a:hover {
background: #e9e9e9;
}
#ctx_menu a.pressed::before {
content: '✓';
display: inline-block;
position: absolute;
left: 9px;
top: 3px;
font-size: 15px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -1,4 +1,3 @@
const contentPage = document.querySelector(".page_content");
const rootElement = document.documentElement; const rootElement = document.documentElement;
// охуенное название файла, КТО ЭТО ПРИДУМАЛ КРАСАВА Я ИЗ КОМНАТЫ С ЭТОГО УЛЕТЕЛ НАХУЙ // охуенное название файла, КТО ЭТО ПРИДУМАЛ КРАСАВА Я ИЗ КОМНАТЫ С ЭТОГО УЛЕТЕЛ НАХУЙ
@ -8,11 +7,11 @@ let scrolledAndHidden = false;
let smallBlockObserver = new IntersectionObserver(entries => { let smallBlockObserver = new IntersectionObserver(entries => {
entries.forEach(x => { entries.forEach(x => {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
let pastHeight = contentPage.getBoundingClientRect().height; let pastHeight = u('.page_content').nodes[0].getBoundingClientRect().height;
if(x.isIntersecting) if(x.isIntersecting)
contentPage.classList.remove("overscrolled"); u('.page_content').nodes[0].classList.remove("overscrolled");
else else
contentPage.classList.add("overscrolled"); u('.page_content').nodes[0].classList.add("overscrolled");
// let currentHeight = contentPage.getBoundingClientRect().height; // let currentHeight = contentPage.getBoundingClientRect().height;
// let ratio = currentHeight / pastHeight; // let ratio = currentHeight / pastHeight;
@ -22,7 +21,7 @@ let smallBlockObserver = new IntersectionObserver(entries => {
// То что я задокументировал - работает мегакриво. // То что я задокументировал - работает мегакриво.
// Пусть юзер и проскролливает какую-то часть контента, зато не получит // Пусть юзер и проскролливает какую-то часть контента, зато не получит
// эпилепсии при использовании :) // эпилепсии при использовании :)
}, contentPage); }, u('.page_content').nodes[0]);
}); });
}, { }, {
root: null, // screen root: null, // screen

View file

@ -157,7 +157,7 @@ u(document).on('click', '#__feed_settings_link', (e) => {
FINAL_URL.searchParams.delete('return_banned') FINAL_URL.searchParams.delete('return_banned')
} }
window.location.assign(FINAL_URL.href) window.router.route(FINAL_URL.href)
}) })
COUNT.forEach(item => { COUNT.forEach(item => {

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
$(document).on("change", "#uploadButton", (e) => { $(document).on("change", ".photo_ajax_upload_button", (e) => {
let iterator = 0 let iterator = 0
if(e.currentTarget.files.length > 10) { if(e.currentTarget.files.length > 10) {
@ -56,7 +56,7 @@ $(document).on("change", "#uploadButton", (e) => {
<textarea style="margin-left: 62px; resize: none;" maxlength="255"></textarea> <textarea style="margin-left: 62px; resize: none;" maxlength="255"></textarea>
</div> </div>
<div class="uploadedImage"> <div class="uploadedImage">
<a href="${photo.link}" target="_blank"><img width="125" src="${photo.url}"></a> <a onclick="OpenMiniature(event, '${photo.link}', null, '${photo.pretty_id}', null)"><img width="125" src="${photo.url}"></a>
<a class="profile_link" style="width: 125px;" id="deletePhoto" data-id="${photo.vid}" data-owner="${photo.owner}">${tr("delete")}</a> <a class="profile_link" style="width: 125px;" id="deletePhoto" data-id="${photo.vid}" data-owner="${photo.owner}">${tr("delete")}</a>
<!--<div class="smallFrame" style="margin-top: 6px;"> <!--<div class="smallFrame" style="margin-top: 6px;">
<div class="smallBtn">${tr("album_poster")}</div> <div class="smallBtn">${tr("album_poster")}</div>
@ -76,7 +76,7 @@ $(document).on("change", "#uploadButton", (e) => {
xhr.send(photos) xhr.send(photos)
}) })
$(document).on("click", "#endUploading", (e) => { $(document).on("click", ".photo_upload_container #endUploading", (e) => {
let table = document.querySelector("#photos") let table = document.querySelector("#photos")
let data = new FormData() let data = new FormData()
let arr = new Map(); let arr = new Map();
@ -108,7 +108,7 @@ $(document).on("click", "#endUploading", (e) => {
document.querySelector(".page_content .insertPhotos").innerHTML = "" document.querySelector(".page_content .insertPhotos").innerHTML = ""
document.getElementById("endUploading").style.display = "none" document.getElementById("endUploading").style.display = "none"
NewNotification(tr("photos_successfully_uploaded"), tr("click_to_go_to_album"), null, () => {window.location.assign(`/album${result.owner}_${result.album}`)}) NewNotification(tr("photos_successfully_uploaded"), tr("click_to_go_to_album"), null, () => {window.router.route({url:`/album${result.owner}_${result.album}`})})
document.querySelector(".whiteBox").style.display = "block" document.querySelector(".whiteBox").style.display = "block"
document.querySelector(".insertAgain").append(document.getElementById("fakeButton")) document.querySelector(".insertAgain").append(document.getElementById("fakeButton"))
@ -120,7 +120,7 @@ $(document).on("click", "#endUploading", (e) => {
xhr.send(data) xhr.send(data)
}) })
$(document).on("click", "#deletePhoto", (e) => { $(document).on("click", ".photo_upload_container #deletePhoto", (e) => {
let data = new FormData() let data = new FormData()
data.append("hash", u("meta[name=csrf]").attr("value")) data.append("hash", u("meta[name=csrf]").attr("value"))
@ -154,19 +154,19 @@ $(document).on("dragover drop", (e) => {
return false; return false;
}) })
$(".container_gray").on("dragover", (e) => { $(document).on("dragover", ".photo_upload_container", (e) => {
e.preventDefault() e.preventDefault()
document.querySelector("#fakeButton").classList.add("dragged") document.querySelector("#fakeButton").classList.add("dragged")
document.querySelector("#fakeButton").value = tr("drag_files_here") document.querySelector("#fakeButton").value = tr("drag_files_here")
}) })
$(".container_gray").on("dragleave", (e) => { $(document).on("dragleave", ".photo_upload_container", (e) => {
e.preventDefault() e.preventDefault()
document.querySelector("#fakeButton").classList.remove("dragged") document.querySelector("#fakeButton").classList.remove("dragged")
document.querySelector("#fakeButton").value = tr("upload_picts") document.querySelector("#fakeButton").value = tr("upload_picts")
}) })
$(".container_gray").on("drop", (e) => { $(document).on("drop", ".photo_upload_container", (e) => {
e.originalEvent.dataTransfer.dropEffect = 'move'; e.originalEvent.dataTransfer.dropEffect = 'move';
e.preventDefault() e.preventDefault()
@ -190,7 +190,7 @@ $(".container_gray").on("drop", (e) => {
u("#uploadButton").trigger("change") u("#uploadButton").trigger("change")
}) })
u(".container_gray").on("paste", (e) => { u(document).on("paste", ".photo_upload_container", (e) => {
if(e.clipboardData.files.length > 0 && e.clipboardData.files.length < 10) { if(e.clipboardData.files.length > 0 && e.clipboardData.files.length < 10) {
document.getElementById("uploadButton").files = e.clipboardData.files; document.getElementById("uploadButton").files = e.clipboardData.files;
u("#uploadButton").trigger("change") u("#uploadButton").trigger("change")

View file

@ -1,113 +0,0 @@
let context_type = "entity_audios"
let context_id = 0
if(document.querySelector("#editPlaylistForm")) {
context_type = "playlist_context"
context_id = document.querySelector("#editPlaylistForm").dataset.id
}
if(document.querySelector(".showMoreAudiosPlaylist") && document.querySelector(".showMoreAudiosPlaylist").dataset.club != null) {
context_type = "entity_audios"
context_id = Number(document.querySelector(".showMoreAudiosPlaylist").dataset.club) * -1
}
let searcher = new playersSearcher(context_type, context_id)
searcher.successCallback = (response, thisc) => {
let domparser = new DOMParser()
let result = domparser.parseFromString(response, "text/html")
let pagesCount = Number(result.querySelector("input[name='pagesCount']").value)
let count = Number(result.querySelector("input[name='count']").value)
result.querySelectorAll(".audioEmbed").forEach(el => {
let id = Number(el.dataset.realid)
let isAttached = (document.querySelector("input[name='audios']").value.includes(`${id},`))
document.querySelector(".playlistAudiosContainer").insertAdjacentHTML("beforeend", `
<div id="newPlaylistAudios">
<div class="playerContainer">
${el.outerHTML}
</div>
<div class="attachAudio addToPlaylist" data-id="${id}">
<span>${isAttached ? tr("remove_from_playlist") : tr("add_to_playlist")}</span>
</div>
</div>
`)
})
if(count < 1)
document.querySelector(".playlistAudiosContainer").insertAdjacentHTML("beforeend", `
${tr("no_results")}
`)
if(Number(thisc.page) >= pagesCount)
u(".showMoreAudiosPlaylist").remove()
else {
if(document.querySelector(".showMoreAudiosPlaylist") != null) {
document.querySelector(".showMoreAudiosPlaylist").setAttribute("data-page", thisc.page + 1)
if(thisc.query != "") {
document.querySelector(".showMoreAudiosPlaylist").setAttribute("data-query", thisc.query)
}
document.querySelector(".showMoreAudiosPlaylist").style.display = "block"
} else {
document.querySelector(".playlistAudiosContainer").parentNode.insertAdjacentHTML("beforeend", `
<div class="showMoreAudiosPlaylist" data-page="2"
${thisc.query != "" ? `"data-query="${thisc.query}"` : ""}
${thisc.context_type == "entity_audios" ? `"data-playlist="${thisc.context_id}"` : ""}
${thisc.context_id < 0 ? `"data-club="${thisc.context_id}"` : ""}>
${tr("show_more_audios")}
</div>
`)
}
}
u("#loader").remove()
}
searcher.beforesendCallback = () => {
document.querySelector(".playlistAudiosContainer").parentNode.insertAdjacentHTML("beforeend", `<img id="loader" src="/assets/packages/static/openvk/img/loading_mini.gif">`)
if(document.querySelector(".showMoreAudiosPlaylist") != null)
document.querySelector(".showMoreAudiosPlaylist").style.display = "none"
}
searcher.errorCallback = () => {
fastError("Error when loading players")
}
searcher.clearContainer = () => {
document.querySelector(".playlistAudiosContainer").innerHTML = ""
}
$(document).on("click", ".showMoreAudiosPlaylist", (e) => {
searcher.movePage(Number(e.currentTarget.dataset.page))
})
$(document).on("change", "input#playlist_query", async (e) => {
e.preventDefault()
await new Promise(r => setTimeout(r, 500));
if(e.currentTarget.value === document.querySelector("input#playlist_query").value) {
searcher.clearContainer()
if(e.currentTarget.value == "") {
searcher.context_type = "entity_audios"
searcher.context_id = 0
searcher.query = ""
searcher.movePage(1)
return
}
searcher.context_type = "search_context"
searcher.context_id = 0
searcher.query = e.currentTarget.value
searcher.movePage(1)
return;
}
})

View file

@ -161,6 +161,7 @@ function loadMoreSuggestedPosts() {
// нажатие на "x предложенных записей" // нажатие на "x предложенных записей"
$(document).on("click", ".sugglist a", (e) => { $(document).on("click", ".sugglist a", (e) => {
e.preventDefault() e.preventDefault()
e.stopPropagation()
if(e.currentTarget.getAttribute("data-toogled") == null || e.currentTarget.getAttribute("data-toogled") == "false") { if(e.currentTarget.getAttribute("data-toogled") == null || e.currentTarget.getAttribute("data-toogled") == "false") {
e.currentTarget.setAttribute("data-toogled", "true") e.currentTarget.setAttribute("data-toogled", "true")

View file

@ -53,8 +53,9 @@ u(document).on('click', '.menu_toggler', (e) => {
} }
}) })
$(document).on("click", ".post-like-button", function(e) { u(document).on("click", ".post-like-button", function(e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation()
var thisBtn = u(this).first(); var thisBtn = u(this).first();
var link = u(this).attr("href"); var link = u(this).attr("href");
@ -90,6 +91,7 @@ async function OpenMiniature(e, photo, post, photo_id, type = "post") {
костыли но смешные однако костыли но смешные однако
*/ */
e.preventDefault(); e.preventDefault();
e.stopPropagation()
// Значения для переключения фоток // Значения для переключения фоток
@ -422,7 +424,7 @@ async function OpenVideo(video_arr = [], init_player = true)
u('body').append(miniplayer) u('body').append(miniplayer)
miniplayer.find('.miniplayer-body').nodes[0].append(msgbox.getNode().find('.center-part > *').nodes[0]) miniplayer.find('.miniplayer-body').nodes[0].append(msgbox.getNode().find('.center-part > *').nodes[0])
miniplayer.attr('style', `left:100px;top:${scrollY}px;`) miniplayer.attr('style', `left:100px;top:0px;`)
miniplayer.find('#__miniplayer_return').on('click', (e) => { miniplayer.find('#__miniplayer_return').on('click', (e) => {
msgbox.reveal() msgbox.reveal()
msgbox.getNode().find('.center-part').nodes[0].append(miniplayer.find('.miniplayer-body > *').nodes[0]) msgbox.getNode().find('.center-part').nodes[0].append(miniplayer.find('.miniplayer-body > *').nodes[0])
@ -434,7 +436,7 @@ async function OpenVideo(video_arr = [], init_player = true)
u('.miniplayer').remove() u('.miniplayer').remove()
}) })
$('.miniplayer').draggable({cursor: 'grabbing', containment: 'body', cancel: '.miniplayer-body'}) $('.miniplayer').draggable({cursor: 'grabbing', containment: 'window', cancel: '.miniplayer-body'})
$('.miniplayer').resizable({ $('.miniplayer').resizable({
maxHeight: 2000, maxHeight: 2000,
maxWidth: 3000, maxWidth: 3000,
@ -448,6 +450,7 @@ async function OpenVideo(video_arr = [], init_player = true)
u(document).on('click', '#videoOpen', (e) => { u(document).on('click', '#videoOpen', (e) => {
e.preventDefault() e.preventDefault()
e.stopPropagation()
try { try {
const target = e.target.closest('#videoOpen') const target = e.target.closest('#videoOpen')
@ -470,6 +473,7 @@ u(document).on('keydown', '.edit_menu #write', (e) => {
e.target.closest('.edit_menu').querySelector('#__edit_save').click() e.target.closest('.edit_menu').querySelector('#__edit_save').click()
}) })
// Migrated from inline start
function reportPhoto(photo_id) { function reportPhoto(photo_id) {
uReportMsgTxt = tr("going_to_report_photo"); uReportMsgTxt = tr("going_to_report_photo");
uReportMsgTxt += "<br/>"+tr("report_question_text"); uReportMsgTxt += "<br/>"+tr("report_question_text");
@ -514,6 +518,280 @@ function reportVideo(video_id) {
]); ]);
} }
function reportUser(user_id) {
uReportMsgTxt = tr("going_to_report_user");
uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
(function() {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + user_id + "?reason=" + res + "&type=user", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
else
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
function reportComment(comment_id) {
uReportMsgTxt = tr("going_to_report_comment");
uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
(function() {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + comment_id + "?reason=" + res + "&type=comment", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
else
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
function reportApp(id) {
uReportMsgTxt = tr('going_to_report_app');
uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
(function() {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + id + "?reason=" + res + "&type=app", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
else
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
function reportClub(club_id) {
uReportMsgTxt = tr("going_to_report_club");
uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
(function() {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + club_id + "?reason=" + res + "&type=group", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
else
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
$(document).on("click", "#_photoDelete, #_videoDelete", function(e) {
var formHtml = "<form id='tmpPhDelF' action='" + u(this).attr("href") + "' >";
formHtml += "<input type='hidden' name='hash' value='" + u("meta[name=csrf]").attr("value") + "' />";
formHtml += "</form>";
u("body").append(formHtml);
MessageBox(tr('warning'), tr('question_confirm'), [
tr('yes'),
tr('no')
], [
(function() {
u("#tmpPhDelF").nodes[0].submit();
}),
(function() {
u("#tmpPhDelF").remove();
}),
]);
e.stopPropagation()
return e.preventDefault();
});
/* @rem-pai why this func wasn't named as "#_deleteDialog"? It looks universal IMO */
u(document).on("click", "#_noteDelete", function(e) {
var formHtml = "<form id='tmpPhDelF' action='" + u(this).attr("href") + "' >";
formHtml += "<input type='hidden' name='hash' value='" + u("meta[name=csrf]").attr("value") + "' />";
formHtml += "</form>";
u("body").append(formHtml);
MessageBox(tr('warning'), tr('question_confirm'), [
tr('yes'),
tr('no')
], [
(function() {
u("#tmpPhDelF").nodes[0].submit();
}),
(function() {
u("#tmpPhDelF").remove();
}),
]);
e.stopPropagation()
return e.preventDefault();
});
// TODO REWRITE cuz its a little broken
u(document).on("click", "#_pinGroup", async function(e) {
e.preventDefault();
e.stopPropagation()
let link = u(this).attr("href");
let thisButton = u(this);
let groupName = u(this).attr("data-group-name");
let groupUrl = u(this).attr("data-group-url");
let list = u('#_groupListPinnedGroups');
thisButton.nodes[0].classList.add('loading');
thisButton.nodes[0].classList.add('disable');
let req = await ky(link);
if(req.ok == false) {
NewNotification(tr('error'), tr('error_1'), null);
thisButton.nodes[0].classList.remove('loading');
thisButton.nodes[0].classList.remove('disable');
return;
}
if(!parseAjaxResponse(await req.text())) {
thisButton.nodes[0].classList.remove('loading');
thisButton.nodes[0].classList.remove('disable');
return;
}
// Adding a divider if not already there
if(list.nodes[0].children.length == 0) {
list.nodes[0].append(u('<div class="menu_divider"></div>').first());
}
// Changing the button name
if(thisButton.html().trim() == tr('remove_from_left_menu')) {
thisButton.html(tr('add_to_left_menu'));
for(let i = 0; i < list.nodes[0].children.length; i++) {
let element = list.nodes[0].children[i];
if(element.pathname == groupUrl) {
element.remove();
}
}
}else{
thisButton.html(tr('remove_from_left_menu'));
list.nodes[0].append(u('<a href="' + groupUrl + '" class="link group_link">' + groupName + '</a>').first());
}
// Adding the group to the left group list
if(list.nodes[0].children[0].className != "menu_divider" || list.nodes[0].children.length == 1) {
list.nodes[0].children[0].remove();
}
thisButton.nodes[0].classList.remove('loading');
thisButton.nodes[0].classList.remove('disable');
return false;
});
u(document).handle("submit", "#_submitUserSubscriptionAction", async function(e) {
e.preventDefault()
e.stopPropagation()
u(this).nodes[0].parentElement.classList.add('loading');
u(this).nodes[0].parentElement.classList.add('disable');
console.log(e.target);
const data = await fetch(u(this).attr('action'), { method: 'POST', body: new FormData(e.target) });
if (data.ok) {
u(this).nodes[0].parentElement.classList.remove('loading');
u(this).nodes[0].parentElement.classList.remove('disable');
if (e.target[0].value == "add") {
u(this).nodes[0].parentElement.innerHTML = tr("friends_add_msg");
} else if (e.target[0].value == "rej") {
u(this).nodes[0].parentElement.innerHTML = tr("friends_rej_msg");
} else if (e.target[0].value == "rem") {
u(this).nodes[0].parentElement.innerHTML = tr("friends_rem_msg");
}
}
})
function changeOwner(club, newOwner, newOwnerName) {
const action = "/groups/" + club + "/setNewOwner/" + newOwner;
MessageBox(tr('group_changeowner_modal_title'), `
${tr("group_changeowner_modal_text", escapeHtml(newOwnerName))}
<br/><br/>
<form id="transfer-owner-permissions-form" method="post">
<label for="password">${tr('password')}</label>
<input type="password" id="password" name="password" required />
<input type="hidden" name="hash" value='${window.router.csrf}' />
</form>
`, [tr('transfer'), tr('cancel')], [
() => {
$("#transfer-owner-permissions-form").attr("action", action);
document.querySelector("#transfer-owner-permissions-form").submit();
}, Function.noop
]);
}
async function withdraw(id) {
let coins = await API.Apps.withdrawFunds(id);
if(coins == 0)
MessageBox(tr('app_withdrawal'), tr('app_withdrawal_empty'), ["OK"], [Function.noop]);
else
MessageBox(tr('app_withdrawal'), tr("app_withdrawal_created", window.coins), ["OK"], [Function.noop]);
}
function toggleMaritalStatus(e) {
let elem = $("#maritalstatus-user");
$("#maritalstatus-user-select").empty();
if ([0, 1, 8].includes(Number(e.value))) {
elem.hide();
} else {
elem.show();
}
}
u(document).on("paste", ".vouncher_input", function(event) {
const vouncher = event.clipboardData.getData("text");
let segments;
if(vouncher.length === 27) {
segments = vouncher.split("-");
if(segments.length !== 4)
segments = undefined;
} else if(vouncher.length === 24) {
segments = chunkSubstr(vouncher, 6);
}
if(segments !== undefined) {
document.vouncher_form.key0.value = segments[0];
document.vouncher_form.key1.value = segments[1];
document.vouncher_form.key2.value = segments[2];
document.vouncher_form.key3.value = segments[3];
document.vouncher_form.key3.focus();
}
event.preventDefault();
});
// Migrated from inline end
var tooltipClientTemplate = Handlebars.compile(` var tooltipClientTemplate = Handlebars.compile(`
<table> <table>
<tr> <tr>
@ -886,7 +1164,7 @@ async function __uploadToTextarea(file, textareaNode) {
if(filetype == 'photo') { if(filetype == 'photo') {
const temp_url = URL.createObjectURL(file) const temp_url = URL.createObjectURL(file)
const rand = random_int(0, 1000) const rand = random_int(0, 1000)
textareaNode.find('.post-horizontal').append(`<a id='temp_filler${rand}' class="upload-item"><img src='${temp_url}'></a>`) textareaNode.find('.post-horizontal').append(`<a id='temp_filler${rand}' class="upload-item lagged"><img src='${temp_url}'></a>`)
const res = await fetch(`/photos/upload`, { const res = await fetch(`/photos/upload`, {
method: 'POST', method: 'POST',
@ -949,14 +1227,14 @@ u(document).on('paste', '#write .small-textarea', (e) => {
} }
}) })
u(document).on('dragstart', '#write .post-horizontal .upload-item, .post-vertical .upload-item', (e) => { u(document).on('dragstart', '#write .post-horizontal .upload-item, .post-vertical .upload-item, .PE_audios .vertical-attachment', (e) => {
//e.preventDefault() //e.preventDefault()
//console.log(e) //console.log(e)
u(e.target).closest('.upload-item').addClass('currently_dragging') u(e.target).closest('.upload-item').addClass('currently_dragging')
return return
}) })
u(document).on('dragover', '#write .post-horizontal .upload-item, .post-vertical .upload-item', (e) => { u(document).on('dragover', '#write .post-horizontal .upload-item, .post-vertical .upload-item, .PE_audios .vertical-attachment', (e) => {
e.preventDefault() e.preventDefault()
const target = u(e.target).closest('.upload-item') const target = u(e.target).closest('.upload-item')
@ -973,7 +1251,12 @@ u(document).on('dragover', '#write .post-horizontal .upload-item, .post-vertical
return return
}) })
u(document).on('#write dragleave dragend', '.post-horizontal .upload-item, .post-vertical .upload-item', (e) => { u(document).on("dragover drop", async (e) => {
e.preventDefault()
return false;
})
u(document).on('dragleave dragend', '#write .post-horizontal .upload-item, .post-vertical .upload-item, .PE_audios .vertical-attachment', (e) => {
//console.log(e) //console.log(e)
u(e.target).closest('.upload-item').removeClass('dragged') u(e.target).closest('.upload-item').removeClass('dragged')
return return
@ -1007,25 +1290,6 @@ u(document).on("drop", '#write', function(e) {
} }
}) })
u(document).on('submit', '#write > form', (e) => {
const target = u(e.target)
const horizontal_array = []
const horizontal_input = target.find(`input[name='horizontal_attachments']`)
const horizontal_attachments = target.find(`.post-horizontal > a`)
horizontal_attachments.nodes.forEach(_node => {
horizontal_array.push(`${_node.dataset.type}${_node.dataset.id}`)
})
horizontal_input.nodes[0].value = horizontal_array.join(',')
const vertical_array = []
const vertical_input = target.find(`input[name='vertical_attachments']`)
const vertical_attachments = target.find(`.post-vertical > .vertical-attachment`)
vertical_attachments.nodes.forEach(_node => {
vertical_array.push(`${_node.dataset.type}${_node.dataset.id}`)
})
vertical_input.nodes[0].value = vertical_array.join(',')
})
// !!! PHOTO PICKER !!! // !!! PHOTO PICKER !!!
u(document).on("click", "#__photoAttachment", async (e) => { u(document).on("click", "#__photoAttachment", async (e) => {
const photos_per_page = 23 const photos_per_page = 23
@ -1117,6 +1381,7 @@ u(document).on("click", "#__photoAttachment", async (e) => {
// add photo // add photo
u(".ovk-diag-body .attachment_selector").on("click", ".album-photo", async (ev) => { u(".ovk-diag-body .attachment_selector").on("click", ".album-photo", async (ev) => {
ev.preventDefault() ev.preventDefault()
ev.stopPropagation()
const target = u(ev.target).closest('.album-photo') const target = u(ev.target).closest('.album-photo')
const dataset = target.nodes[0].dataset const dataset = target.nodes[0].dataset
@ -1635,13 +1900,14 @@ u(document).on('click', `.post-horizontal .upload-item .upload-delete`, (e) => {
u(e.target).closest('.upload-item').remove() u(e.target).closest('.upload-item').remove()
}) })
u(document).on('click', `.post-vertical .vertical-attachment #small_remove_button`, (e) => { u(document).on('click', `.vertical-attachment #small_remove_button`, (e) => {
e.preventDefault() e.preventDefault()
u(e.target).closest('.vertical-attachment').remove() u(e.target).closest('.vertical-attachment').remove()
}) })
u(document).on('click', '.post-buttons .upload-item', (e) => { u(document).on('click', '.post-buttons .upload-item', (e) => {
e.preventDefault() e.preventDefault()
e.stopPropagation()
}) })
u(document).on('click', '.post.post-nsfw .post-content', (e) => { u(document).on('click', '.post.post-nsfw .post-content', (e) => {
@ -1752,7 +2018,7 @@ async function repost(id, repost_type = 'post') {
} }
} }
NewNotification(tr('information_-1'), tr('shared_succ'), null, () => {window.location.assign(`/wall${res.pretty_id}`)}); NewNotification(tr('information_-1'), tr('shared_succ'), null, () => {window.router.route(`/wall${res.pretty_id}`)});
} catch(e) { } catch(e) {
console.error(e) console.error(e)
fastError(e.message) fastError(e.message)
@ -1935,7 +2201,6 @@ $(document).on("click", "#add_image", (e) => {
let video = document.querySelector("#_takeSelfieFrame video") let video = document.querySelector("#_takeSelfieFrame video")
if(!navigator.mediaDevices) { if(!navigator.mediaDevices) {
// ех вот бы месседжбоксы были бы классами
u("body").removeClass("dimmed"); u("body").removeClass("dimmed");
document.querySelector("html").style.overflowY = "scroll" document.querySelector("html").style.overflowY = "scroll"
u(".ovk-diag-cont").remove(); u(".ovk-diag-cont").remove();
@ -2070,6 +2335,15 @@ async function __processPaginatorNextPage(page)
const nodes = parsed_content.querySelectorAll(container_node) const nodes = parsed_content.querySelectorAll(container_node)
nodes.forEach(node => { nodes.forEach(node => {
const unique_id = node.dataset.uniqueid
if(unique_id) {
const elements_unique = u(`.scroll_node[data-uniqueid='${unique_id}']`).length
if(elements_unique > 0) {
console.info('AJAX | Found duplicates')
return
}
}
container.append(node) container.append(node)
}) })
@ -2078,10 +2352,19 @@ async function __processPaginatorNextPage(page)
container.nodes[0].append(u(`.paginator:not(.paginator-at-top)`).nodes[0].parentNode) container.nodes[0].append(u(`.paginator:not(.paginator-at-top)`).nodes[0].parentNode)
} }
if(window.player) { if(window.player && window.player.isAtAudiosPage() && window.player.isAtCurrentContextPage()) {
window.player.loadContextPage(page) window.player.loadContext(page)
window.player.__highlightActiveTrack()
} }
/*if(window.router) {
window.router.savePreviousPage()
}*/
const new_url = new URL(location.href)
new_url.hash = page
history.replaceState(null, null, new_url)
if(typeof __scrollHook != 'undefined') { if(typeof __scrollHook != 'undefined') {
__scrollHook(page) __scrollHook(page)
} }
@ -2098,6 +2381,10 @@ const showMoreObserver = new IntersectionObserver(entries => {
return return
} }
/*if(window.player && window.player.isAtAudiosPage() && !window.player.isAtCurrentContextPage()) {
return
}*/
const target = u(x.target) const target = u(x.target)
if(target.length < 1 || target.hasClass('paginator-at-top')) { if(target.length < 1 || target.hasClass('paginator-at-top')) {
return return
@ -2117,7 +2404,13 @@ const showMoreObserver = new IntersectionObserver(entries => {
} }
const page_number = Number(next_page.html()) const page_number = Number(next_page.html())
try {
await __processPaginatorNextPage(page_number) await __processPaginatorNextPage(page_number)
} catch(e) {
console.error(e)
}
bsdnHydrate() bsdnHydrate()
u('.paginator:not(.paginator-at-top)').removeClass('lagged') u('.paginator:not(.paginator-at-top)').removeClass('lagged')
} }
@ -2221,3 +2514,76 @@ u(document).on('keyup', async (e) => {
} }
} }
}) })
u(document).on('mouseover mousemove mouseout', `div[data-tip='simple']`, (e) => {
if(e.target.dataset.allow_mousemove != '1' && e.type == 'mousemove') {
return
}
if(e.type == 'mouseout') {
u(`.tip_result`).remove()
return
}
const target = u(e.target).closest(`div[data-tip='simple']`)
const title = target.attr('data-title')
if(title == '') {
return
}
target.nodes[0].parentNode.insertAdjacentHTML('afterbegin', `
<div class='tip_result' style='left:${e.layerX}px;'>
${escapeHtml(title)}
</div>
`)
})
function setStatusEditorShown(shown) {
document.getElementById("status_editor").style.display = shown ? "block" : "none";
}
u(document).on('click', (event) => {
u('#ctx_menu').remove()
if(u('#status_editor').length < 1) {
return
}
if(!event.target.closest("#status_editor") && !event.target.closest("#page_status_text"))
setStatusEditorShown(false);
})
u(document).on('click', '#page_status_text', (e) => {
setStatusEditorShown(true)
})
async function changeStatus() {
const status = document.status_popup_form.status.value;
const broadcast = document.status_popup_form.broadcast.checked;
document.status_popup_form.submit.innerHTML = "<div class=\"button-loading\"></div>";
document.status_popup_form.submit.disabled = true;
const formData = new FormData();
formData.append("status", status);
formData.append("broadcast", Number(broadcast));
formData.append("hash", document.status_popup_form.hash.value);
const response = await ky.post("/edit?act=status", {body: formData});
if(!parseAjaxResponse(await response.text())) {
document.status_popup_form.submit.innerHTML = tr("send");
document.status_popup_form.submit.disabled = false;
return;
}
if(document.status_popup_form.status.value === "") {
document.querySelector("#page_status_text").innerHTML = `[ ${tr("change_status")} ]`;
document.querySelector("#page_status_text").className = "edit_link page_status_edit_button";
} else {
document.querySelector("#page_status_text").innerHTML = status;
document.querySelector("#page_status_text").className = "page_status page_status_edit_button";
}
setStatusEditorShown(false);
document.status_popup_form.submit.innerHTML = tr("send");
document.status_popup_form.submit.disabled = false;
}

View file

@ -82,7 +82,7 @@ class CMessageBox {
__exitDialog() { __exitDialog() {
this.getNode().remove() this.getNode().remove()
if(u('.ovk-msg-all').length < 1) { if(u('.ovk-msg-all:not(.msgbox-hidden)').length < 1) {
u('body').removeClass('dimmed') u('body').removeClass('dimmed')
u('html').attr('style', 'overflow-y:scroll') u('html').attr('style', 'overflow-y:scroll')
} }
@ -101,13 +101,15 @@ class CMessageBox {
hide() { hide() {
u('body').removeClass('dimmed') u('body').removeClass('dimmed')
u('html').attr('style', 'overflow-y:scroll') u('html').attr('style', 'overflow-y:scroll')
this.getNode().attr('style', 'display: none;') this.getNode().attr('style', 'display: none;').addClass('msgbox-hidden')
this.hidden = true
} }
reveal() { reveal() {
u('body').addClass('dimmed') u('body').addClass('dimmed')
u('html').attr('style', 'overflow-y:hidden') u('html').attr('style', 'overflow-y:hidden')
this.getNode().attr('style', 'display: block;') this.getNode().attr('style', 'display: block;')
this.hidden = false
} }
static toggleLoader() { static toggleLoader() {

View file

@ -39,126 +39,6 @@ function parseAjaxResponse(responseString) {
} }
} }
document.addEventListener("DOMContentLoaded", function() { //BEGIN
$(document).on("click", "#_photoDelete, #_videoDelete", function(e) {
var formHtml = "<form id='tmpPhDelF' action='" + u(this).attr("href") + "' >";
formHtml += "<input type='hidden' name='hash' value='" + u("meta[name=csrf]").attr("value") + "' />";
formHtml += "</form>";
u("body").append(formHtml);
MessageBox(tr('warning'), tr('question_confirm'), [
tr('yes'),
tr('no')
], [
(function() {
u("#tmpPhDelF").nodes[0].submit();
}),
(function() {
u("#tmpPhDelF").remove();
}),
]);
return e.preventDefault();
});
/* @rem-pai why this func wasn't named as "#_deleteDialog"? It looks universal IMO */
u(document).on("click", "#_noteDelete", function(e) {
var formHtml = "<form id='tmpPhDelF' action='" + u(this).attr("href") + "' >";
formHtml += "<input type='hidden' name='hash' value='" + u("meta[name=csrf]").attr("value") + "' />";
formHtml += "</form>";
u("body").append(formHtml);
MessageBox(tr('warning'), tr('question_confirm'), [
tr('yes'),
tr('no')
], [
(function() {
u("#tmpPhDelF").nodes[0].submit();
}),
(function() {
u("#tmpPhDelF").remove();
}),
]);
return e.preventDefault();
});
u("#_pinGroup").on("click", async function(e) {
e.preventDefault();
let link = u(this).attr("href");
let thisButton = u(this);
let groupName = u(this).attr("data-group-name");
let groupUrl = u(this).attr("data-group-url");
let list = u('#_groupListPinnedGroups');
thisButton.nodes[0].classList.add('loading');
thisButton.nodes[0].classList.add('disable');
let req = await ky(link);
if(req.ok == false) {
NewNotification(tr('error'), tr('error_1'), null);
thisButton.nodes[0].classList.remove('loading');
thisButton.nodes[0].classList.remove('disable');
return;
}
if(!parseAjaxResponse(await req.text())) {
thisButton.nodes[0].classList.remove('loading');
thisButton.nodes[0].classList.remove('disable');
return;
}
// Adding a divider if not already there
if(list.nodes[0].children.length == 0) {
list.nodes[0].append(u('<div class="menu_divider"></div>').first());
}
// Changing the button name
if(thisButton.html().trim() == tr('remove_from_left_menu')) {
thisButton.html(tr('add_to_left_menu'));
for(let i = 0; i < list.nodes[0].children.length; i++) {
let element = list.nodes[0].children[i];
if(element.pathname == groupUrl) {
element.remove();
}
}
}else{
thisButton.html(tr('remove_from_left_menu'));
list.nodes[0].append(u('<a href="' + groupUrl + '" class="link group_link">' + groupName + '</a>').first());
}
// Adding the group to the left group list
if(list.nodes[0].children[0].className != "menu_divider" || list.nodes[0].children.length == 1) {
list.nodes[0].children[0].remove();
}
thisButton.nodes[0].classList.remove('loading');
thisButton.nodes[0].classList.remove('disable');
return false;
});
u(document).handle("submit", "#_submitUserSubscriptionAction", async function(e) {
u(this).nodes[0].parentElement.classList.add('loading');
u(this).nodes[0].parentElement.classList.add('disable');
console.log(e.target);
const data = await fetch(u(this).attr('action'), { method: 'POST', body: new FormData(e.target) });
if (data.ok) {
u(this).nodes[0].parentElement.classList.remove('loading');
u(this).nodes[0].parentElement.classList.remove('disable');
if (e.target[0].value == "add") {
u(this).nodes[0].parentElement.innerHTML = tr("friends_add_msg");
} else if (e.target[0].value == "rej") {
u(this).nodes[0].parentElement.innerHTML = tr("friends_rej_msg");
} else if (e.target[0].value == "rem") {
u(this).nodes[0].parentElement.innerHTML = tr("friends_rem_msg");
}
}
})
}); //END ONREADY DECLS
function setClubAdminComment(clubId, adminId, hash) { function setClubAdminComment(clubId, adminId, hash) {
MessageBox("Изменить комментарий к администратору", ` MessageBox("Изменить комментарий к администратору", `
<form action="/club${clubId}/setAdmin" method="post" id="uClubAdminCommentForm_${clubId}_${adminId}"> <form action="/club${clubId}/setAdmin" method="post" id="uClubAdminCommentForm_${clubId}_${adminId}">

389
Web/static/js/router.js Normal file
View file

@ -0,0 +1,389 @@
window.router = new class {
get csrf() {
return u("meta[name=csrf]").attr("value")
}
__isScriptAlreadyLoaded(script) {
if(script.src) {
const script_url = new URL(script.src)
const script_main_part = script_url.pathname
return u(`script[src^='${script_main_part}']`).length > 0
}
return false
}
__appendScript(script) {
const _t_scr = document.createElement('script')
_t_scr.crossorigin = 'anonymous'
if(script.getAttribute('integrity')) {
_t_scr.setAttribute('integrity', script.getAttribute('integrity'))
}
if(script.getAttribute('id')) {
_t_scr.id = script.id
}
if(script.getAttribute('type')) {
_t_scr.type = script.type
}
//const parent = script.parentNode
//const idx = Array.from(parent.children).indexOf(script)
if(script.src) {
_t_scr.src = script.src
} else {
_t_scr.async = false
_t_scr.textContent = script.textContent
}
//parent.children[idx].before(script)
document.body.appendChild(_t_scr)
}
__clearScripts() {
u(`script:not([src])`).remove()
}
__closeMsgs() {
window.messagebox_stack.forEach(msg => {
if(msg.hidden) {
return
}
msg.close()
})
}
__appendPage(parsed_content) {
const scripts_to_append = []
const page_body = u(parsed_content.querySelector('.page_body'))
const sidebar = u(parsed_content.querySelector('.sidebar'))
const page_header = u(parsed_content.querySelector('.page_header'))
const page_footer = u(parsed_content.querySelector('.page_footer'))
const backdrop = u(parsed_content.querySelector('#backdrop'))
if(page_body.length < 1) {
throw new Error('Invalid page has been loaded')
return
}
window.__current_page_audio_context = null
this.__clearScripts()
parsed_content.querySelectorAll('.page_body script, #_js_ep_script').forEach(script => {
if(!this.__isScriptAlreadyLoaded(script)) {
scripts_to_append.push(script)
script.parentNode.removeChild(script)
}
})
u('.page_body').html(page_body.html())
u('.sidebar').html(sidebar.html())
u('.page_footer').html(page_footer.html())
if(backdrop.length > 0) {
if(u('#backdrop').length == 0) {
u('body').append(`<div id="backdrop"></div>`)
}
u('#backdrop').nodes[0].outerHTML = (backdrop.nodes[0].outerHTML)
} else {
u('#backdrop').remove()
}
if(u('.page_header #search_box select').length > 0 && page_header.find('#search_box select').length > 0) {
u('.page_header #search_box select').nodes[0].value = page_header.find('#search_box select').nodes[0].value
}
if(u('.page_header #search_box input').length > 0 && page_header.find('#search_box input').length > 0) {
u('.page_header #search_box input').nodes[0].value = page_header.find('#search_box input').nodes[0].value
}
if(page_header.hasClass('search_expanded_at_all')) {
u('.page_header').addClass('search_expanded_at_all').addClass('search_expanded')
} else {
if(u('.page_header').hasClass('search_expanded_at_all')) {
u('.page_header').removeClass('search_expanded_at_all').removeClass('search_expanded')
}
}
u("meta[name=csrf]").attr("value", u(parsed_content.querySelector('meta[name=csrf]')).attr('value'))
document.title = parsed_content.title
scripts_to_append.forEach(append_me => {
this.__appendScript(append_me)
})
}
async __integratePage(scrolling = null) {
window.temp_y_scroll = null
u('.toTop').removeClass('has_down')
window.scrollTo(0, scrolling ?? 0)
bsdnHydrate()
if(u('.paginator:not(.paginator-at-top)').length > 0) {
showMoreObserver.observe(u('.paginator:not(.paginator-at-top)').nodes[0])
}
if(u(`div[class$="_small_block"]`).length > 0 && typeof smallBlockObserver != 'undefined') {
smallBlockObserver.observe(u(`div[class$="_small_block"]`).nodes[0])
}
if(window.player) {
window.player.dump()
await window.player._handlePageTransition()
}
}
__unlinkObservers() {
if(u('.paginator:not(.paginator-at-top)').length > 0) {
showMoreObserver.unobserve(u('.paginator:not(.paginator-at-top)').nodes[0])
}
if(u(`div[class$="_small_block"]`).length > 0 && typeof smallBlockObserver != 'undefined') {
smallBlockObserver.unobserve(u(`div[class$="_small_block"]`).nodes[0])
}
}
checkUrl(url) {
if(window.openvk.disable_ajax == 1) {
return false
}
if((localStorage.getItem('ux.disable_ajax_routing') ?? 0) == 1 || window.openvk.current_id == 0) {
return false
}
if(!url || url == '') {
return false
}
if(url.indexOf(location.origin) == -1) {
return false
}
if(url.indexOf('hash=') != -1) {
return false
}
return true
}
savePreviousPage() {
this.prev_page_html = {
url: location.href,
pathname: location.pathname,
html: u('.page_body').html(),
}
}
async route(params = {}) {
if(typeof params == 'string') {
params = {
url: params
}
}
const old_url = location.href
let url = params.url
if(url.indexOf(location.origin)) {
url = location.origin + url
}
if((localStorage.getItem('ux.disable_ajax_routing') ?? 0) == 1 || window.openvk.current_id == 0) {
window.location.assign(url)
return
}
if(this.prev_page_html && this.prev_page_html.pathname != location.pathname) {
this.prev_page_html = null
}
const push_url = params.push_state ?? true
const next_page_url = new URL(url)
if(push_url) {
history.pushState({'from_router': 1}, '', url)
} else {
history.replaceState({'from_router': 1}, '', url)
}
const parser = new DOMParser
const next_page_request = await fetch(next_page_url, {
method: 'AJAX',
referrer: old_url,
headers: {
'X-OpenVK-Ajax-Query': '1',
}
})
const next_page_text = await next_page_request.text()
const parsed_content = parser.parseFromString(next_page_text, 'text/html')
if(next_page_request.redirected) {
history.replaceState({'from_router': 1}, '', next_page_request.url)
}
this.__closeMsgs()
this.__unlinkObservers()
try {
this.__appendPage(parsed_content)
await this.__integratePage()
} catch(e) {
console.error(e)
next_page_url.searchParams.delete('al', 1)
location.assign(next_page_url)
}
}
}
u(document).on('click', 'a', async (e) => {
const target = u(e.target).closest('a')
const dom_url = target.attr('href')
const id = target.attr('id')
let url = target.nodes[0].href
if(id) {
if(['act_tab_a', 'ki', 'used', '_pinGroup', 'profile_link', 'minilink-friends', 'minilink-albums', 'minilink-messenger', 'minilink-groups', 'minilink-notifications'].indexOf(id) == -1) {
console.log('AJAX | Skipping cuz maybe its function call link.')
return
}
}
/*if(url.indexOf('hash=') != -1) {
e.preventDefault()
return false
}*/
if(target.attr('rel') == 'nofollow') {
console.log('AJAX | Skipped because its nofollow')
return
}
if(target.nodes[0].hasAttribute('download')) {
console.log('AJAX | Skipped because its download')
return
}
if(!dom_url || dom_url == '#' || dom_url.indexOf('javascript:') != -1) {
console.log('AJAX | Skipped because its anchor or function call')
return
}
if(target.attr('target') == '_blank') {
console.log('AJAX | Skipping because its _blank.')
return
}
if(!window.router.checkUrl(url)) {
return
}
// temporary fix
if(dom_url == '/') {
url = url + 'id0'
}
e.preventDefault()
console.log(`AJAX | Going to URL ${url}`)
await window.router.route({
url: url,
})
})
u(document).on('submit', 'form', async (e) => {
if(u('#ajloader').hasClass('shown')) {
e.preventDefault()
return
}
if((localStorage.getItem('ux.disable_ajax_routing') ?? 0) == 1 || window.openvk.current_id == 0) {
return false
}
if(window.openvk.disable_ajax == 1) {
return false
}
if(e.target.closest('#write')) {
const target = u(e.target)
collect_attachments_node(target)
}
u('#ajloader').addClass('shown')
const form = e.target
const method = form.method ?? 'get'
const url = form.action
if(form.onsubmit || url.indexOf('/settings?act=interface') != -1) {
u('#ajloader').removeClass('shown')
return false
}
e.preventDefault()
const url_object = new URL(url)
if(method == 'get' || method == 'GET') {
//url_object.searchParams.append('hash', window.router.csrf)
$(form).serializeArray().forEach(param => {
url_object.searchParams.append(param.name, param.value)
})
}
if(!url) {
u('#ajloader').removeClass('shown')
return
}
const form_data = serializeForm(form, e.submitter)
const request_object = {
method: method,
headers: {
'X-OpenVK-Ajax-Query': '1',
}
}
if(method != 'GET' && method != 'get') {
request_object.body = form_data
}
const form_res = await fetch(url_object, request_object)
const form_result = await form_res.text()
switch(form_res.status) {
case 500:
case 502:
makeError(form_res.statusText)
break
}
const parser = new DOMParser
const parsed_content = parser.parseFromString(form_result, 'text/html')
if(form_res.redirected) {
history.replaceState({'from_router': 1}, '', form_res.url)
} else {
const __new_url = new URL(form_res.url)
__new_url.searchParams.delete('al')
__new_url.searchParams.delete('hash')
history.pushState({'from_router': 1}, '', __new_url)
}
window.router.__appendPage(parsed_content)
window.router.__closeMsgs()
await window.router.__integratePage()
u('#ajloader').removeClass('shown')
})
window.addEventListener('popstate', (e) => {
e.preventDefault();
/*if(window.router.prev_page_html) {
u('.page_body').html(window.router.prev_page_html.html)
history.replaceState({'from_router': 1}, '', window.router.prev_page_html.url)
window.router.prev_page_html = null
window.router.__integratePage()
return
}*/
window.router.route({
url: location.href,
push_state: false,
})
})

View file

@ -173,3 +173,114 @@ function collect_attachments(target) {
return horizontal_array.concat(vertical_array) return horizontal_array.concat(vertical_array)
} }
function getRemainingTime(fullTime, time) {
let timer = fullTime - time
if(timer < 0) return "-00:00"
return "-" + fmtTime(timer)
}
function serializeForm(form, submitter = null)
{
const u_ = u(form)
const inputs = u_.find('input, textarea, button, select')
let fd = new FormData()
inputs.nodes.forEach(inp => {
if(!inp || !inp.name) {
return
}
if(inp.type == 'submit') {
if(inp !== submitter) {
return
}
}
switch(inp.type) {
case 'hidden':
case 'text':
case 'textarea':
case 'select':
case 'select-one':
case 'submit':
case 'email':
case 'phone':
case 'search':
case 'password':
case 'date':
case 'datetime-local':
fd.append(inp.name, inp.value)
break
case 'checkbox':
if(inp.checked) {
fd.append(inp.name, inp.value)
}
break
case 'file':
if(!inp.multiple) {
if(inp.files[0]) {
fd.append(inp.name, inp.files[0])
} else {
const emptyFile = new Blob([], { type: 'application/octet-stream' })
fd.append(inp.name, emptyFile, '')
}
}
break
case 'radio':
if(inp.checked) {
fd.append(inp.name, inp.value)
}
break
}
})
return fd
}
async function copyToClipboard(text) {
let fallback = () => {
prompt(text);
}
if(typeof navigator.clipboard == "undefined") {
fallback()
} else {
try {
await navigator.clipboard.writeText(text)
} catch(e) {
fallback()
}
}
}
function remove_file_format(text)
{
return text.replace(/\.[^.]*$/, '')
}
function sleep(time)
{
return new Promise((resolve) => setTimeout(resolve, time));
}
function collect_attachments_node(target)
{
const horizontal_array = []
const horizontal_input = target.find(`input[name='horizontal_attachments']`)
const horizontal_attachments = target.find(`.post-horizontal > a`)
horizontal_attachments.nodes.forEach(_node => {
horizontal_array.push(`${_node.dataset.type}${_node.dataset.id}`)
})
horizontal_input.nodes[0].value = horizontal_array.join(',')
const vertical_array = []
const vertical_input = target.find(`input[name='vertical_attachments']`)
const vertical_attachments = target.find(`.post-vertical > .vertical-attachment`)
vertical_attachments.nodes.forEach(_node => {
vertical_array.push(`${_node.dataset.type}${_node.dataset.id}`)
})
vertical_input.nodes[0].value = vertical_array.join(',')
}

View file

@ -698,6 +698,7 @@
"privacy_setting_write_wall" = "Who can publish posts on my wall"; "privacy_setting_write_wall" = "Who can publish posts on my wall";
"privacy_setting_write_messages" = "Who can write messages to me"; "privacy_setting_write_messages" = "Who can write messages to me";
"privacy_setting_view_audio" = "Who can see my audios"; "privacy_setting_view_audio" = "Who can see my audios";
"privacy_setting_see_likes" = "Who can see my likes";
"privacy_value_anybody" = "Anybody"; "privacy_value_anybody" = "Anybody";
"privacy_value_anybody_dative" = "Anybody"; "privacy_value_anybody_dative" = "Anybody";
"privacy_value_users" = "OpenVK users"; "privacy_value_users" = "OpenVK users";
@ -881,7 +882,7 @@
"genre" = "Genre"; "genre" = "Genre";
"lyrics" = "Lyrics"; "lyrics" = "Lyrics";
"select_another_file" = "Select another file"; "select_another_file" = "Upload another file";
"limits" = "Limits"; "limits" = "Limits";
"select_audio" = "Select audio from your computer"; "select_audio" = "Select audio from your computer";
@ -948,12 +949,11 @@
"add_to_playlist" = "Add to playlist"; "add_to_playlist" = "Add to playlist";
"remove_from_playlist" = "Remove from playlist"; "remove_from_playlist" = "Remove from playlist";
"delete_playlist" = "Delete playlist"; "delete_playlist" = "Delete playlist";
"playlist_cover" = "Playlist cover";
"playlists_user" = "User's playlists"; "playlists_user" = "User's playlists";
"playlists_club" = "Group's playlists"; "playlists_club" = "Group's playlists";
"change_cover" = "Change cover"; "change_cover" = "Change cover";
"playlist_cover" = "Playlist's cover"; "playlist_cover" = "Playlist cover";
"minutes_count_zero" = "Lasts no minutes"; "minutes_count_zero" = "Lasts no minutes";
"minutes_count_one" = "Lasts one minute"; "minutes_count_one" = "Lasts one minute";
@ -994,9 +994,17 @@
"repeat_tip" = "Repeat"; "repeat_tip" = "Repeat";
"shuffle_tip" = "Shuffle"; "shuffle_tip" = "Shuffle";
"mute_tip" = "Mute"; "mute_tip" = "Mute";
"mute_tip_noun" = "Muted";
"playlist_hide_from_search" = "Unlisted"; "playlist_hide_from_search" = "Unlisted";
"confirm_deleting_audio" = "Do you sure want to delete this audio?"; "confirm_deleting_audio" = "Do you sure want to delete this audio?";
"copy_link_to_audio" = "Copy link to clipboard";
"copy_link_to_audio_error_not_selected_track" = "Track was not selected";
"audio_ctx_add_to_group" = "Add to group";
"audio_ctx_add_to_playlist" = "Add to playlist";
"audio_ctx_play_next" = "Play next";
"audio_ctx_clear_context" = "Clear tracks list";
/* Notifications */ /* Notifications */
"feedback" = "Feedback"; "feedback" = "Feedback";
@ -1595,6 +1603,7 @@
"error_adding_source_regex" = "Error adding source: incorrect link."; "error_adding_source_regex" = "Error adding source: incorrect link.";
"error_adding_source_long" = "Error adding source: link is too long."; "error_adding_source_long" = "Error adding source: link is too long.";
"error_adding_source_sus" = "Error adding source: suspicious link."; "error_adding_source_sus" = "Error adding source: suspicious link.";
"error_playlist_creating_too_small" = "Add at least one audio";
/* Admin actions */ /* Admin actions */

View file

@ -671,6 +671,7 @@
"privacy_setting_write_wall" = "Кто может писать у меня на стене"; "privacy_setting_write_wall" = "Кто может писать у меня на стене";
"privacy_setting_write_messages" = "Кто может писать мне сообщения"; "privacy_setting_write_messages" = "Кто может писать мне сообщения";
"privacy_setting_view_audio" = "Кому видно мои аудиозаписи"; "privacy_setting_view_audio" = "Кому видно мои аудиозаписи";
"privacy_setting_see_likes" = "Кому видны мои лайки";
"privacy_value_anybody" = "Все желающие"; "privacy_value_anybody" = "Все желающие";
"privacy_value_anybody_dative" = "Всем желающим"; "privacy_value_anybody_dative" = "Всем желающим";
"privacy_value_users" = "Пользователям OpenVK"; "privacy_value_users" = "Пользователям OpenVK";
@ -836,7 +837,7 @@
"genre" = "Жанр"; "genre" = "Жанр";
"lyrics" = "Текст"; "lyrics" = "Текст";
"select_another_file" = "Выбрать другой файл"; "select_another_file" = "Загрузить ещё";
"limits" = "Ограничения"; "limits" = "Ограничения";
"select_audio" = "Выберите аудиозапись на Вашем компьютере"; "select_audio" = "Выберите аудиозапись на Вашем компьютере";
@ -907,7 +908,7 @@
"playlists_user" = "Плейлисты пользователя"; "playlists_user" = "Плейлисты пользователя";
"playlists_club" = "Плейлисты группы"; "playlists_club" = "Плейлисты группы";
"change_cover" = "Сменить обложку"; "change_cover" = "Сменить обложку";
"playlist_cover" = "Обложка плейлиста"; "add_audio_verb" = "Добавить аудиозаписи";
"minutes_count_zero" = "длится ноль минут"; "minutes_count_zero" = "длится ноль минут";
"minutes_count_one" = "длится одну минуту"; "minutes_count_one" = "длится одну минуту";
@ -949,9 +950,17 @@
"repeat_tip" = "Повторение"; "repeat_tip" = "Повторение";
"shuffle_tip" = "Перемешать"; "shuffle_tip" = "Перемешать";
"mute_tip" = "Заглушить"; "mute_tip" = "Заглушить";
"mute_tip_noun" = "Заглушено";
"playlist_hide_from_search" = "Не показывать в поиске"; "playlist_hide_from_search" = "Не показывать в поиске";
"confirm_deleting_audio" = "Вы действительно хотите полностью удалить аудиозапись?"; "confirm_deleting_audio" = "Вы действительно хотите полностью удалить аудиозапись?";
"copy_link_to_audio" = "Копировать ссылку на аудио";
"copy_link_to_audio_error_not_selected_track" = "Трек не выбран";
"audio_ctx_add_to_group" = "Добавить в группу";
"audio_ctx_add_to_playlist" = "Добавить в плейлист";
"audio_ctx_play_next" = "Воспроизвести следующим";
"audio_ctx_clear_context" = "Очистить список треков";
/* Notifications */ /* Notifications */
"feedback" = "Ответы"; "feedback" = "Ответы";
@ -1497,6 +1506,7 @@
"error_adding_source_regex" = "Ошибка добавления источника: некорректная ссылка."; "error_adding_source_regex" = "Ошибка добавления источника: некорректная ссылка.";
"error_adding_source_long" = "Ошибка добавления источника: слишком длинная ссылка."; "error_adding_source_long" = "Ошибка добавления источника: слишком длинная ссылка.";
"error_adding_source_sus" = "Ошибка добавления источника: гиперссылка заблокирована."; "error_adding_source_sus" = "Ошибка добавления источника: гиперссылка заблокирована.";
"error_playlist_creating_too_small" = "Добавь хотя бы одну аудиозапись.";
/* Admin actions */ /* Admin actions */

File diff suppressed because it is too large Load diff

View file

@ -490,7 +490,7 @@ input[type="radio"] {
border-top: #b9b9b9 1px solid !important; border-top: #b9b9b9 1px solid !important;
} }
.bigPlayer .paddingLayer .slider, .bigPlayer .slider,
.audioEmbed .track .slider { .audioEmbed .track .slider {
background: #b9b9b9 !important; background: #b9b9b9 !important;
} }
@ -500,11 +500,11 @@ input[type="radio"] {
outline: 1px solid #645a86 !important; outline: 1px solid #645a86 !important;
} }
.preformer { .preformer, .trackPerformers a {
color: #b7b7b7 !important; color: #b7b7b7 !important;
} }
.bigPlayer .paddingLayer .trackPanel .track .timeTip { .tip_result {
background: #b9b9b9 !important; background: #b9b9b9 !important;
color: black !important; color: black !important;
} }
@ -518,7 +518,7 @@ input[type="radio"] {
} }
.audioEntry .performer a, .audioEntry .performer a,
.bigPlayer .paddingLayer .trackInfo a { .bigPlayer .trackInfo a {
color: #a2a1a1 !important; color: #a2a1a1 !important;
} }
@ -526,10 +526,6 @@ input[type="radio"] {
opacity: 49%; opacity: 49%;
} }
.bigPlayer .paddingLayer .bigPlayerTip {
color: black !important;
}
.verticalGrayTabs a { .verticalGrayTabs a {
color: #bbb !important; color: #bbb !important;
} }
@ -547,6 +543,15 @@ input[type="radio"] {
filter: invert(81%); filter: invert(81%);
} }
.load_bar {
background: #2c2839 !important;
border-bottom-color: #151418 !important;
}
#ajax_audio_player, #ajloader {
box-shadow: rgb(58 53 73) 0px 0px 2px 3px;
}
img[src$='/assets/packages/static/openvk/img/camera_200.png'], img[src$='/assets/packages/static/openvk/img/camera_200.png'],
img[src$='/assets/packages/static/openvk/img/song.jpg'] { img[src$='/assets/packages/static/openvk/img/song.jpg'] {
filter: invert(100%); filter: invert(100%);
@ -623,3 +628,8 @@ ul {
#backdropFilePicker { #backdropFilePicker {
margin: 15px !important; margin: 15px !important;
} }
#upload_container .upload_container_element {
border: 1px solid #383052;
background: #1e1b2a;
}