refactor messagebox and repost logic

This commit is contained in:
mrilyew 2024-11-03 21:28:32 +03:00
parent 48c7751d6a
commit 4cb88f94dc
14 changed files with 506 additions and 261 deletions

View file

@ -661,25 +661,13 @@ final class Wall extends VKAPIRequestHandler
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
if($owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2) {
$suggsCount = (new PostsRepo)->getSuggestedPostsCount($wallOwner->getId());
if($suggsCount % 10 == 0) {
$managers = $wallOwner->getManagers();
$owner = $wallOwner->getOwner();
(new NewSuggestedPostsNotification($owner, $wallOwner))->emit();
foreach($managers as $manager) {
(new NewSuggestedPostsNotification($manager->getUser(), $wallOwner))->emit();
}
}
return (object)["post_id" => "on_view"];
}
return (object)["post_id" => $post->getVirtualId()];
}
function repost(string $object, string $message = "", int $group_id = 0) {
function repost(string $object, string $message = "", string $attachments = "", int $group_id = 0, int $as_group = 0, int $signed = 0) {
$this->requireUser();
$this->willExecuteWriteAction();
@ -697,14 +685,22 @@ final class Wall extends VKAPIRequestHandler
$nPost->setOwner($this->user->getId());
if($group_id > 0) {
$club = (new ClubsRepo)->get($group_id);
$club = (new ClubsRepo)->get($group_id);
if(!$club)
$this->fail(42, "Invalid group");
if(!$club->canBeModifiedBy($this->user))
$this->fail(16, "Access to group denied");
$nPost->setWall($group_id * -1);
$nPost->setWall($club->getRealId());
$flags = 0;
if($as_group === 1 || $signed === 1)
$flags |= 0b10000000;
if($signed === 1)
$flags |= 0b01000000;
$nPost->setFlags($flags);
} else {
$nPost->setWall($this->user->getId());
}
@ -712,14 +708,24 @@ final class Wall extends VKAPIRequestHandler
$nPost->setContent($message);
$nPost->setApi_Source_Name($this->getPlatform());
$nPost->save();
$nPost->attach($post);
$attachments_arr = parseAttachments($attachments, ['photo', 'video', 'audio']);
foreach($attachments_arr as $attachment) {
if(!$attachment || $attachment->isDeleted() || !$attachment->canBeViewedBy($this->getUser())) {
continue;
}
$nPost->attach($attachment);
}
if($post->getOwner(false)->getId() !== $this->user->getId() && !($post->getOwner() instanceof Club))
(new RepostNotification($post->getOwner(false), $post, $this->user))->emit();
return (object) [
"success" => 1, // 👍
"post_id" => $nPost->getVirtualId(),
"pretty_id" => $nPost->getPrettyId(),
"reposts_count" => $post->getRepostCount(),
"likes_count" => $post->getLikesCount()
];

View file

@ -17,6 +17,7 @@
{script "js/node_modules/umbrellajs/umbrella.min.js"}
{script "js/l10n.js"}
{script "js/openvk.cls.js"}
{script "js/utils.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 "js/al_music.js"}
@ -51,7 +52,10 @@
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['bellsAndWhistles']['testLabel']" id="test-label">FOR TESTING PURPOSES ONLY</div>
<div class="notifications_global_wrap"></div>
<div class="dimmer"></div>
<div class="dimmer">
<div id='absolute_territory'></div>
</div>
<div class="upLeftErrors"></div>
<div class="articleView">
<a id="articleCloseButton" class="button" href="javascript:void(u('body').removeClass('article'));">{_close}</a>

View file

@ -126,7 +126,7 @@
<a n:if="!($forceNoCommentsLink ?? false) && $commentsCount == 0" href="javascript:expand_comment_textarea({$commentTextAreaId})">{_comment}</a>
<div class="like_wrap">
<a n:if="!($forceNoShareLink ?? false)" id="reposts{$post->getPrettyId()}" class="post-share-button" href="javascript:repostPost('{$post->getPrettyId()}', '{rawurlencode($csrfToken)}')">
<a n:if="!($forceNoShareLink ?? false)" id="reposts{$post->getPrettyId()}" class="post-share-button" href="javascript:repost('{$post->getPrettyId()}', 'post')">
<div class="repost-icon" style="opacity: 0.4;"></div>
<span class="likeCnt" id="repostsCount{$post->getPrettyId()}">{if $repostsCount > 0}{$repostsCount}{/if}</span>
</a>

View file

@ -114,9 +114,8 @@
setupWallPostInputHandlers({$textAreaId});
});
u("#post-buttons{$textAreaId} input[name='videos']")["nodes"].at(0).value = ""
u("#post-buttons{$textAreaId} input[name='photos']")["nodes"].at(0).value = ""
u("#post-buttons{$textAreaId} input[name='audios']")["nodes"].at(0).value = ""
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}

View file

@ -7,6 +7,11 @@ body.dimmed > .dimmer {
opacity: .5;
}
body.dimmed > .dimmer #absolute_territory {
margin: 100px;
height: 100%;
}
.ovk-diag-cont {
z-index: 1024;
position: fixed;

View file

@ -13,6 +13,10 @@ body {
word-wrap: break-word;
}
body, .ovk-fullscreen-dimmer {
scrollbar-gutter: stable both-edges;
}
.nobold,
nobold {
font-weight: normal;
@ -41,6 +45,11 @@ h1 {
margin-top: 0;
}
.display_flex_column {
display: flex;
flex-direction: column;
}
.layout {
width: 791px;
margin: 0 auto;
@ -254,6 +263,28 @@ h1 {
width: calc(550px - 16px - 38px - 171px);
}
.upLeftErrors {
position: absolute;
display: flex;
flex-direction: column;
z-index: 1025;
}
.upLeftErrors .upLeftError {
padding: 2px 2px 2px 2px;
display: inline-block;
border-radius: 0px 0px 5px 0px;
font-size: 15px;
}
.upLeftErrors .upLeftError.upLeftErrorRed {
background: #ff7474;
}
.upLeftErrors .upLeftError.upLeftErrorGreen {
background: #74ff92;
}
.page_body {
width: 632px;
float: right;

View file

@ -34,4 +34,22 @@ window.API = new Proxy(Object.create(null), {
window.API.Types = {};
window.API.Types.Message = (class Message {
});
});
window.OVKAPI = new class {
async call(method, params) {
if(!method) {
return
}
const url = `/method/${method}?auth_mechanism=roaming&${new URLSearchParams(params).toString()}`
const res = await fetch(url)
const json_response = await res.json()
if(json_response.response) {
return json_response.response
} else {
throw new Exception(json_response.error_msg)
}
}
}

View file

@ -1,13 +1,3 @@
function fmtTime(time) {
const mins = String(Math.floor(time / 60)).padStart(2, '0');
const secs = String(Math.floor(time % 60)).padStart(2, '0');
return `${ mins}:${ secs}`;
}
function fastError(message) {
MessageBox(tr("error"), message, [tr("ok")], [Function.noop])
}
// elapsed это вроде прошедшие, а оставшееся это remaining но ладно уже
function getElapsedTime(fullTime, time) {
let timer = fullTime - time

View file

@ -1,35 +1,3 @@
function humanFileSize(bytes, si) {
var thresh = si ? 1000 : 1024;
if(Math.abs(bytes) < thresh) {
return bytes + ' B';
}
var units = si
? ['kB','MB','GB','TB','PB','EB','ZB','YB']
: ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
var u = -1;
do {
bytes /= thresh;
++u;
} while(Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes.toFixed(1)+' '+units[u];
}
function trim(string) {
var newStr = string.substring(0, 10);
if(newStr.length !== string.length)
newStr += "…";
return newStr;
}
function trimNum(string, num) {
var newStr = string.substring(0, num);
if(newStr.length !== string.length)
newStr += "…";
return newStr;
}
function handleUpload(id) {
console.warn("блять...");
@ -49,23 +17,34 @@ function handleUpload(id) {
function initGraffiti(id) {
let canvas = null;
let msgbox = MessageBox(tr("draw_graffiti"), "<div id='ovkDraw'></div>", [tr("save"), tr("cancel")], [function() {
canvas.getImage({includeWatermark: false}).toBlob(blob => {
let fName = "Graffiti-" + Math.ceil(performance.now()).toString() + ".jpeg";
let image = new File([blob], fName, {type: "image/jpeg", lastModified: new Date().getTime()});
const msgbox = new CMessageBox({
title: tr("draw_graffiti"),
body: "<div id='ovkDraw'></div>",
close_on_buttons: false,
buttons: [tr("save"), tr("cancel")],
callbacks: [function() {
canvas.getImage({includeWatermark: false}).toBlob(blob => {
let fName = "Graffiti-" + Math.ceil(performance.now()).toString() + ".jpeg";
let image = new File([blob], fName, {type: "image/jpeg", lastModified: new Date().getTime()});
fastUploadImage(id, image)
}, "image/jpeg", 0.92);
fastUploadImage(id, image)
}, "image/jpeg", 0.92);
canvas.teardown();
}, function() {
canvas.teardown();
}]);
canvas.teardown();
msgbox.close()
}, async function() {
const res = await msgbox.__showCloseConfirmationDialog()
if(res === true) {
canvas.teardown()
msgbox.close()
}
}]
})
let watermarkImage = new Image();
watermarkImage.src = "/assets/packages/static/openvk/img/logo_watermark.gif";
msgbox.attr("style", "width: 750px;");
msgbox.getNode().attr("style", "width: 750px;");
canvas = LC.init(document.querySelector("#ovkDraw"), {
backgroundColor: "#fff",
imageURLPrefix: "/assets/packages/static/openvk/js/node_modules/literallycanvas/lib/img",
@ -1382,6 +1361,124 @@ $(document).on("click", "#photosAttachments", async (e) => {
})
})
async function repost(id, repost_type = 'post') {
const repostsCount = u(`#repostsCount${id}`)
const previousVal = repostsCount.length > 0 ? Number(repostsCount.html()) : 0;
MessageBox(tr('share'), `
<div class='display_flex_column' style='gap: 1px;'>
<b>${tr('auditory')}</b>
<div class='display_flex_column'>
<label>
<input type="radio" name="repost_type" value="wall" checked>
${tr("in_wall")}
</label>
<label>
<input type="radio" name="repost_type" value="group">
${tr("in_group")}
</label>
<select name="selected_repost_club" style='display:none;'></select>
</div>
<b>${tr('your_comment')}</b>
<input type='hidden' id='repost_attachments'>
<textarea id='repostMsgInput' placeholder='...'></textarea>
<div id="repost_signs" class='display_flex_column' style='display:none;'>
<label><input type='checkbox' name="asGroup">${tr('post_as_group')}</label>
<label><input type='checkbox' name="signed">${tr('add_signature')}</label>
</div>
</div>
`, [tr('send'), tr('cancel')], [
async () => {
const message = u('#repostMsgInput').nodes[0].value
const type = u(`input[name='repost_type']:checked`).nodes[0].value
const club_id = parseInt(u(`select[name='selected_repost_club']`).nodes[0].selectedOptions[0].value)
const as_group = u(`input[name='asGroup']`).nodes[0].checked
const signed = u(`input[name='signed']`).nodes[0].checked
const attachments = u(`#repost_attachments`).nodes[0].value
const params = {}
switch(repost_type) {
case 'post':
params.object = `wall${id}`
break
case 'video':
params.object = `video${id}`
break
}
params.message = message
if(type == 'group' && club_id != 0) {
params.group_id = club_id
}
if(as_group) {
params.as_group = Number(as_group)
}
if(signed) {
params.signed = Number(signed)
}
if(attachments != '') {
params.attachments = attachments
}
try {
res = await window.OVKAPI.call('wall.repost', params)
if(repostsCount.length > 0) {
repostsCount.html(previousVal + 1)
} else {
u('#reposts' + id).append(`(<b id='repostsCount${id}'>1</b>)`)
}
NewNotification(tr('information_-1'), tr('shared_succ'), null, () => {window.location.assign(`/wall${res.pretty_id}`)});
} catch(e) {
console.error(e)
fastError(e.message)
}
xhr = new XMLHttpRequest();
xhr.onload = (function() {
if(xhr.responseText.indexOf("wall_owner") === -1) {
let jsonR = JSON.parse(xhr.responseText);
repostsCount != null ?
repostsCount.innerHTML = prevVal+1 :
document.getElementById("reposts"+id).insertAdjacentHTML("beforeend", "(<b id='repostsCount"+id+"'>1</b>)") //для старого вида постов
}
});
},
Function.noop
]);
u('.ovk-diag-body').attr('style', 'padding: 14px;')
u('.ovk-diag-body').on('change', `input[name='repost_type']`, (e) => {
const value = e.target.value
switch(value) {
case 'wall':
u('#repost_signs').attr('style', 'display:none')
u(`select[name='selected_repost_club']`).attr('style', 'display:none')
break
case 'group':
u('#repost_signs').attr('style', 'display:flex')
u(`select[name='selected_repost_club']`).attr('style', 'display:block')
break
}
})
const clubs = await window.OVKAPI.call('groups.get', {'filter': 'admin', 'count': 100})
clubs.items.forEach(club => {
u(`select[name='selected_repost_club']`).append(`<option value='${club.id}'>${ovk_proc_strtr(escapeHtml(club.name), 100)}</option>`)
})
}
$(document).on("click", "#add_image", (e) => {
let isGroup = e.currentTarget.closest(".avatar_block").dataset.club != null
let group = isGroup ? e.currentTarget.closest(".avatar_block").dataset.club : 0

View file

@ -1,42 +1,148 @@
Function.noop = () => {};
function MessageBox(title, body, buttons, callbacks) {
if(u(".ovk-diag-cont").length > 0) return false;
document.querySelector("html").style.overflowY = "hidden"
let dialog = u(
`<div class="ovk-diag-cont">
<div class="ovk-diag">
<div class="ovk-diag-head">${title}</div>
<div class="ovk-diag-body">${body}</div>
<div class="ovk-diag-action"></div>
</div>
</div>`);
u("body").addClass("dimmed").append(dialog);
buttons.forEach((text, callback) => {
u(".ovk-diag-action").append(u(`<button class="button">${text}</button>`));
let button = u(u(".ovk-diag-action > button.button").last());
button.on("click", function(e) {
let __closeDialog = () => {
if(document.querySelector(".ovk-photo-view-dimmer") == null && document.querySelector(".ovk-fullscreen-player") == null) {
u("body").removeClass("dimmed");
document.querySelector("html").style.overflowY = "scroll"
class CMessageBox {
constructor(options = {}) {
const title = options.title ?? 'Untitled'
const body = options.body ?? '<hr>'
const buttons = options.buttons ?? []
const callbacks = options.callbacks ?? []
const close_on_buttons = options.close_on_buttons ?? true
const unique_name = options.unique_name ?? null
this.title = title
this.body = body
this.id = random_int(0, 10000)
this.close_on_buttons = close_on_buttons
this.unique_name = unique_name
u('body').addClass('dimmed').append(this.__getTemplate())
u('html').attr('style', 'overflow-y:hidden')
buttons.forEach((text, callback) => {
this.getNode().find('.ovk-diag-action').append(u(`<button class="button">${text}</button>`))
let button = u(this.getNode().find('.ovk-diag-action > button.button').last())
button.on("click", (e) => {
callbacks[callback]()
if(close_on_buttons) {
this.close()
}
u(".ovk-diag-cont").remove();
};
Reflect.apply(callbacks[callback], {
closeDialog: () => __closeDialog(),
$dialog: () => u(".ovk-diag-cont")
}, [e]);
})
})
window.messagebox_stack.push(this)
}
__getTemplate() {
return u(
`<div class="ovk-diag-cont" data-id="${this.id}">
<div class="ovk-diag">
<div class="ovk-diag-head">${this.title}</div>
<div class="ovk-diag-body">${this.body}</div>
<div class="ovk-diag-action"></div>
</div>
</div>`)
}
getNode() {
return u(`.ovk-diag-cont[data-id='${this.id}']`)
}
async __showCloseConfirmationDialog() {
if(window.messagebox_stack.find(item => item.unique_name == 'close_confirmation') != null) {
return
}
return new Promise((resolve, reject) => {
const msg = new CMessageBox({
title: tr('exit_noun'),
body: tr('exit_confirmation'),
warn_on_exit: false,
unique_name: 'close_confirmation',
buttons: [tr('no'), tr('yes')],
callbacks: [() => {
msg.close()
resolve(false)
}, () => {
this.__exitDialog()
resolve(true)
}]
})
})
}
__exitDialog() {
u(`.ovk-diag-cont[data-id='${this.id}']`).remove()
if(u('.ovk-diag-cont').length < 1) {
u('body').removeClass('dimmed')
u('html').attr('style', 'overflow-y:scroll')
}
const current_item = window.messagebox_stack.find(item => item.id == this.id)
const index_of_item = window.messagebox_stack.indexOf(current_item)
window.messagebox_stack = array_splice(window.messagebox_stack, index_of_item)
__closeDialog();
});
});
return u(".ovk-diag-cont");
delete this
}
close() {
this.__exitDialog()
}
}
window.messagebox_stack = []
function MessageBox(title, body, buttons, callbacks, return_msg = false) {
const msg = new CMessageBox({
title: title,
body: body,
buttons: buttons,
callbacks: callbacks,
})
if(return_msg) {
return msg
}
return msg.getNode()
}
// Close on 'Escape' key
u(document).on('keyup', async (e) => {
if(e.keyCode == 27 && window.messagebox_stack.length > 0) {
const msg = window.messagebox_stack[window.messagebox_stack.length - 1]
if(!msg) {
return
}
if(msg.close_on_buttons) {
msg.close()
return
}
const res = await msg.__showCloseConfirmationDialog()
if(res === true) {
msg.close()
}
}
})
// Close when clicking on shadow
u(document).on('click', 'body.dimmed .dimmer', async (e) => {
if(u(e.target).hasClass('dimmer')) {
const msg = window.messagebox_stack[window.messagebox_stack.length - 1]
if(!msg) {
return
}
if(msg.close_on_buttons) {
msg.close()
return
}
const res = await msg.__showCloseConfirmationDialog()
if(res === true) {
msg.close()
}
}
})

View file

@ -16,19 +16,6 @@ function expand_comment_textarea(id) {
wi.focus();
}
function edit_post(id, wid) {
var el = document.getElementById('text'+wid+'_'+id);
var ed = document.getElementById('text_edit'+wid+'_'+id);
if (el.style.display == "none") {
el.style.display = "block";
ed.style.display = "none";
} else {
el.style.display = "none";
ed.style.display = "block";
}
}
function hidePanel(panel, count = 0)
{
$(panel).toggleClass("content_title_expanded content_title_unexpanded");
@ -189,74 +176,6 @@ document.addEventListener("DOMContentLoaded", function() { //BEGIN
})
}); //END ONREADY DECLS
async function repostPost(id, hash) {
uRepostMsgTxt = `
<b>${tr('auditory')}:</b> <br/>
<input type="radio" name="type" onchange="signs.setAttribute('hidden', 'hidden');document.getElementById('groupId').setAttribute('hidden', 'hidden')" value="wall" checked>${tr("in_wall")}<br/>
<input type="radio" name="type" onchange="signs.removeAttribute('hidden');document.getElementById('groupId').removeAttribute('hidden')" value="group" id="group">${tr("in_group")}<br/>
<select style="width:50%;" id="groupId" name="groupId" hidden>
</select><br/>
<b>${tr('your_comment')}:</b>
<textarea id='uRepostMsgInput_${id}'></textarea>
<div id="signs" hidden>
<label><input onchange="signed.checked ? signed.checked = false : null" type="checkbox" id="asgroup" name="asGroup">${tr('post_as_group')}</label><br>
<label><input onchange="asgroup.checked = true" type="checkbox" id="signed" name="signed">${tr('add_signature')}</label>
</div>
<br/><br/>`;
let clubs = [];
repostsCount = document.getElementById("repostsCount"+id)
prevVal = repostsCount != null ? Number(repostsCount.innerHTML) : 0;
MessageBox(tr('share'), uRepostMsgTxt, [tr('send'), tr('cancel')], [
(function() {
text = document.querySelector("#uRepostMsgInput_"+id).value;
type = "user";
radios = document.querySelectorAll('input[name="type"]')
for(const r of radios)
{
if(r.checked)
{
type = r.value;
break;
}
}
groupId = document.querySelector("#groupId").value;
asGroup = asgroup.checked == true ? 1 : 0;
signed = signed.checked == true ? 1 : 0;
hash = encodeURIComponent(hash);
xhr = new XMLHttpRequest();
xhr.open("POST", "/wall"+id+"/repost?hash="+hash, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.onload = (function() {
if(xhr.responseText.indexOf("wall_owner") === -1)
MessageBox(tr('error'), tr('error_repost_fail'), [tr('ok')], [Function.noop]);
else {
let jsonR = JSON.parse(xhr.responseText);
NewNotification(tr('information_-1'), tr('shared_succ'), null, () => {window.location.href = "/wall" + jsonR.wall_owner});
repostsCount != null ?
repostsCount.innerHTML = prevVal+1 :
document.getElementById("reposts"+id).insertAdjacentHTML("beforeend", "(<b id='repostsCount"+id+"'>1</b>)") //для старого вида постов
}
});
xhr.send('text='+encodeURI(text) + '&type='+type + '&groupId='+groupId + "&asGroup="+asGroup + "&signed="+signed);
}),
Function.noop
]);
try
{
clubs = await API.Groups.getWriteableClubs();
for(const el of clubs) {
document.getElementById("groupId").insertAdjacentHTML("beforeend", `<option value="${el.id}">${escapeHtml(el.name)}</option>`)
}
} catch(rejection) {
console.error(rejection)
document.getElementById("group").setAttribute("disabled", "disabled")
}
}
function setClubAdminComment(clubId, adminId, hash) {
MessageBox("Изменить комментарий к администратору", `
<form action="/club${clubId}/setAdmin" method="post" id="uClubAdminCommentForm_${clubId}_${adminId}">
@ -322,17 +241,6 @@ function showCoinsTransferDialog(coinsCount, hash) {
]);
}
function chunkSubstr(string, size) {
const numChunks = Math.ceil(string.length / size);
const chunks = new Array(numChunks);
for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
chunks[i] = string.substr(o, size);
}
return chunks;
}
function autoTab(original, next, previous) {
if(original.getAttribute && original.value.length == original.getAttribute("maxlength") && next !== undefined)
next.focus();
@ -364,11 +272,6 @@ function supportFastAnswerDialogOnClick(answer) {
answerInput.focus();
}
function ovk_proc_strtr(string, length = 0) {
const newString = string.substring(0, length);
return newString + (string !== newString ? "…" : "");
}
function showProfileDeactivateDialog(hash) {
MessageBox(tr("profile_deactivate"), `
<div class="messagebox-content-header">
@ -491,57 +394,6 @@ function showIncreaseRatingDialog(coinsCount, userUrl, hash) {
};
}
function escapeHtml(text) {
var map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
function highlightText(searchText, container_selector, selectors = []) {
const container = u(container_selector)
const regexp = new RegExp(`(${searchText})`, 'gi')
function highlightNode(node) {
if(node.nodeType == 3) {
let newNode = escapeHtml(node.nodeValue)
newNode = newNode.replace(regexp, (match, ...args) => {
return `<span class='highlight'>${escapeHtml(match)}</span>`
})
const tempDiv = document.createElement('div')
tempDiv.innerHTML = newNode
while(tempDiv.firstChild) {
node.parentNode.insertBefore(tempDiv.firstChild, node)
}
node.parentNode.removeChild(node)
} else if(node.nodeType === 1 && node.tagName !== 'SCRIPT' && node.tagName !== 'BR' && node.tagName !== 'STYLE') {
Array.from(node.childNodes).forEach(highlightNode);
}
}
selectors.forEach(selector => {
elements = container.find(selector)
if(!elements || elements.length < 1) return;
elements.nodes.forEach(highlightNode)
})
}
String.prototype.escapeHtml = function() {
try {
return escapeHtml(this)
} catch(e) {
return ''
}
}
$(document).on("scroll", () => {
if($(document).scrollTop() > $(".sidebar").height() + 50) {
$(".floating_sidebar")[0].classList.add("show");

133
Web/static/js/utils.js Normal file
View file

@ -0,0 +1,133 @@
function escapeHtml(text) {
var map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
function highlightText(searchText, container_selector, selectors = []) {
const container = u(container_selector)
const regexp = new RegExp(`(${searchText})`, 'gi')
function highlightNode(node) {
if(node.nodeType == 3) {
let newNode = escapeHtml(node.nodeValue)
newNode = newNode.replace(regexp, (match, ...args) => {
return `<span class='highlight'>${escapeHtml(match)}</span>`
})
const tempDiv = document.createElement('div')
tempDiv.innerHTML = newNode
while(tempDiv.firstChild) {
node.parentNode.insertBefore(tempDiv.firstChild, node)
}
node.parentNode.removeChild(node)
} else if(node.nodeType === 1 && node.tagName !== 'SCRIPT' && node.tagName !== 'BR' && node.tagName !== 'STYLE') {
Array.from(node.childNodes).forEach(highlightNode);
}
}
selectors.forEach(selector => {
elements = container.find(selector)
if(!elements || elements.length < 1) return;
elements.nodes.forEach(highlightNode)
})
}
String.prototype.escapeHtml = function() {
try {
return escapeHtml(this)
} catch(e) {
return ''
}
}
function fmtTime(time) {
const mins = String(Math.floor(time / 60)).padStart(2, '0');
const secs = String(Math.floor(time % 60)).padStart(2, '0');
return `${ mins}:${ secs}`;
}
function fastError(message) {
MessageBox(tr("error"), message, [tr("ok")], [Function.noop])
}
function humanFileSize(bytes, si) {
var thresh = si ? 1000 : 1024;
if(Math.abs(bytes) < thresh) {
return bytes + ' B';
}
var units = si
? ['kB','MB','GB','TB','PB','EB','ZB','YB']
: ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
var u = -1;
do {
bytes /= thresh;
++u;
} while(Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes.toFixed(1)+' '+units[u];
}
function trim(string) {
var newStr = string.substring(0, 10);
if(newStr.length !== string.length)
newStr += "…";
return newStr;
}
function trimNum(string, num) {
return ovk_proc_strtr(string, num);
}
function ovk_proc_strtr(string, length = 0) {
const newString = string.substring(0, length);
return newString + (string !== newString ? "…" : "");
}
function chunkSubstr(string, size) {
const numChunks = Math.ceil(string.length / size);
const chunks = new Array(numChunks);
for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
chunks[i] = string.substr(o, size);
}
return chunks;
}
function random_int(min, max) {
return Math.round(Math.random() * (max - min) + min)
}
function makeError(text, color = 'Red', timeout = 10000) {
const rand_id = random_int(0, 10000)
u('.upLeftErrors').append(`
<div class='upLeftError upLeftError${color}' data-id='${rand_id}'>${escapeHtml(text)}</div>
`)
setTimeout(() => {
u(`.upLeftError[data-id='${rand_id}']`).remove()
}, timeout)
}
function array_splice(array, key)
{
let resultArray = [];
for(let i = 0; i < array.length; i++){
if(i != key){
resultArray.push(array[i]);
}
}
return resultArray;
}

View file

@ -1762,6 +1762,8 @@
"question_confirm" = "This action can't be undone. Do you really wanna do it?";
"confirm_m" = "Confirm";
"action_successfully" = "Success";
"exit_noun" = "Exit";
"exit_confirmation" = "Are you sure want to exit?";
/* User Alerts */

View file

@ -1652,6 +1652,8 @@
"question_confirm" = "Это действие нельзя отменить. Вы действительно уверены в том что хотите сделать?";
"confirm_m" = "Подтвердить";
"action_successfully" = "Операция успешна";
"exit_noun" = "Выход";
"exit_confirmation" = "Уверены, что хотите выйти?";
/* User alerts */