f7cloud_client/themes/forbion/js/scripts.js
root 8b6a0139db f7cloud_client
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 22:59:26 +00:00

1228 lines
41 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('click', function (event) {
if (event.target.classList.contains('native-datetime-picker--input')) {
const inputElement = event.target;
inputElement.classList.add('is-set-value');
}
});
});
// Выпадающее меню
/*
document.addEventListener('DOMContentLoaded', function () {
const userMenuLink = document.getElementById('user-menu-f7mail');
const dropdownMenu = document.getElementById('header-menu-user-menu-f7mail');
if (userMenuLink && dropdownMenu) {
userMenuLink.addEventListener('click', function (e) {
e.preventDefault();
dropdownMenu.style.display = dropdownMenu.style.display === 'none' ? 'block' : 'none';
});
document.addEventListener('click', function (e) {
if (!dropdownMenu.contains(e.target) && e.target !== userMenuLink) {
dropdownMenu.style.display = 'none';
}
});
}
//уведомления иконка
setTimeout(function () {
const newNotifications = document.getElementsByClassName('notification__dot');
if (newNotifications.length > 0) {
const spanAlert = document.createElement('span');
spanAlert.className = 'alert-icon';
newNotifications[0].parentNode.parentNode.appendChild(spanAlert);
}
}, 2000);
});*/
document.addEventListener('DOMContentLoaded', () => {
const moveElements = () => {
console.log('moveElements вызван');
const content_wrap = document.getElementById('content-vue');
const searchBox = document.querySelector('.app-files .app-navigation__search .app-navigation-search');
const targetContainer = document.querySelector('.app-files .files-list__header');
const targetContainerFirstElement = document.querySelector('.app-files .files-list__breadcrumbs');
const searchBoxActual = document.querySelector('.app-files .app-navigation__search');
if (content_wrap) {
let mask_button = document.querySelector('.mask-button-load');
if (!mask_button) {
mask_button = document.createElement('div');
mask_button.classList.add('mask-button-load');
content_wrap.append(mask_button);
} else {
console.log('⚠️ mask-button-load уже существует');
}
}
let magnifier = document.querySelector('.app-files .magnifier-icon');
if (!magnifier && searchBox) {
magnifier = document.createElement('div');
magnifier.className = 'magnifier-icon';
magnifier.innerHTML = `
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3504_3055)">
<circle cx="5.5" cy="5.5" r="4.75" stroke="#808080" stroke-width="1.5"/>
<path d="M8.73541 9L14.7354 15" stroke="#808080" stroke-width="1.5" stroke-linecap="round"/>
</g>
<defs>
<clipPath id="clip0_3504_3055">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>
`;
magnifier.style.cssText = 'cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 4px;';
magnifier.style.cursor = 'pointer';
targetContainerFirstElement.after(magnifier);
magnifier.addEventListener('click', () => {
console.log('click', searchBox);
if (searchBox) {
searchBox.style.display = 'flex';
magnifier.style.display = 'none';
searchBox.querySelector('input')?.focus();
}
});
if (searchBox && targetContainer) {
targetContainer.insertBefore(searchBox, magnifier);
searchBox.style.display = 'none';
}
if (searchBox) {
const input = searchBox.querySelector('input');
if (input) {
input.addEventListener('focus', () => {
if (magnifier) {
magnifier.style.display = 'none';
}
});
input.addEventListener('blur', () => {
if (magnifier) {
magnifier.style.display = 'flex';
searchBox.style.display = 'none';
}
});
}
}
}
};
const observer = new MutationObserver((mutations) => {
const appFiles = document.querySelector('.app-files');
if (appFiles) {
moveElements();
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
document.addEventListener('DOMContentLoaded', function () {
const toggleButton = document.querySelector('[href="#profile-visibility"]');
const profileWrapper = document.querySelector('.settings-visible-profile');
if (toggleButton && profileWrapper) {
toggleButton.addEventListener('click', function () {
if (profileWrapper.style.display === 'none' || profileWrapper.style.display === '') {
profileWrapper.style.display = 'block';
} else {
profileWrapper.style.display = 'none';
}
});
}
});
function initSnappyMailPasswordToggle() {
if (!window.location.pathname.includes('/apps/snappymail/')) {
return;
}
const setupPasswordToggle = () => {
const passwordControl = document.querySelector('#password-control');
if (passwordControl) {
const passwordToggleIcon = passwordControl.querySelector('.password-toggle-icon');
const inputField = passwordControl.querySelector('input[name="Password"]');
const eyeOpenIcon = passwordControl.querySelector('.eye-icon.eye-open');
const eyeClosedIcon = passwordControl.querySelector('.eye-icon.eye-closed');
if (passwordToggleIcon && inputField && eyeOpenIcon && eyeClosedIcon) {
passwordToggleIcon.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
passwordControl.classList.toggle('show-password');
if (inputField.type === 'password') {
inputField.type = 'text';
} else {
inputField.type = 'password';
}
eyeOpenIcon.classList.toggle('active');
eyeClosedIcon.classList.toggle('active');
});
return true;
}
}
return false;
};
if (setupPasswordToggle()) {
return;
}
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
if (setupPasswordToggle()) {
observer.disconnect();
return;
}
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
if (document.readyState === 'complete') {
initSnappyMailPasswordToggle();
} else {
document.addEventListener('DOMContentLoaded', initSnappyMailPasswordToggle);
}
//hamburger
document.addEventListener('DOMContentLoaded', function () {
const burgerButton = document.querySelector('#header .is-header__burger');
const appMenu = document.querySelector('#header .app-menu');
const html = document.querySelector('html');
if (burgerButton && appMenu) {
burgerButton.addEventListener('click', function () {
this.classList.toggle('is-show-mobile-menu');
appMenu.classList.toggle('show');
html.classList.toggle('no-scroll');
});
}
});
// logic for correct print dots icon (close-open right sidebar)
document.addEventListener('DOMContentLoaded', function () {
let currentObserver = null;
let headerObserver = null;
function cleanupObservers() {
if (currentObserver) {
currentObserver.disconnect();
currentObserver = null;
}
if (headerObserver) {
headerObserver.disconnect();
headerObserver = null;
}
}
function initializeFeature() {
const divMain = document.querySelector('#content.app-spreed');
const buttonDotsCheck = document.querySelector('.action-item.action-item--default-popover.action-item--tertiary');
if (!divMain || !buttonDotsCheck) {
cleanupObservers();
return;
}
try {
const appContentDiv = document.getElementById('app-content-vue');
if (!appContentDiv) {
cleanupObservers();
return;
}
const topHeaderWrapper = appContentDiv.querySelector('.top-bar.top-bar--authorised');
if (!topHeaderWrapper) {
cleanupObservers();
return;
}
const buttonDots = topHeaderWrapper.querySelector('.action-item.action-item--default-popover.action-item--tertiary');
if (!buttonDots) {
cleanupObservers();
return;
}
const buttonDotsSpan = buttonDots.querySelector('.material-design-icon.dots-horizontal-icon');
if (!buttonDotsSpan) {
cleanupObservers();
return;
}
const appRightSidebar = document.getElementById('app-sidebar-vue');
if (!appRightSidebar) {
cleanupObservers();
return;
}
const headerConf = appContentDiv.querySelector('.conversation-header');
if (!headerConf) {
cleanupObservers();
return;
}
const headerConfText = headerConf.querySelector('.title');
if (!headerConfText) {
cleanupObservers();
return;
}
function updateIconVisibility() {
const headerConfTextContent = headerConfText.textContent.trim();
const shouldHideIcon = headerConfTextContent === "Личные заметки" || headerConfTextContent === "Обновления приложения «Конференции» ✅";
if (shouldHideIcon) {
if (getComputedStyle(appRightSidebar).display === 'none') {
buttonDotsSpan.style.display = 'none';
} else {
buttonDotsSpan.style.display = 'flex';
}
} else {
buttonDotsSpan.style.display = 'flex';
}
}
cleanupObservers();
currentObserver = new MutationObserver(() => {
updateIconVisibility();
});
headerObserver = new MutationObserver(() => {
updateIconVisibility();
});
currentObserver.observe(appRightSidebar, { attributes: true, attributeFilter: ['style'] });
headerObserver.observe(headerConfText, {
characterData: true,
childList: true,
subtree: true
});
updateIconVisibility();
} catch (e) {
cleanupObservers();
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeFeature);
} else {
initializeFeature();
}
const handleNavigation = () => {
requestAnimationFrame(() => {
initializeFeature();
});
};
const documentObserver = new MutationObserver(handleNavigation);
documentObserver.observe(document.body, {
childList: true,
subtree: true
});
setInterval(handleNavigation, 1000);
});
// ------------------------------------------------------------
document.addEventListener('DOMContentLoaded', function () {
const divMain = document.querySelector('#content.app-spreed');
if (!divMain) return;
const HIDDEN_BODY_CLASS = 'my-in-call-hidden-header';
let currentInCall = null;
let createdPanels = null;
let placing = false;
const style = document.createElement('style');
style.textContent = `
body.${HIDDEN_BODY_CLASS} .top-bar.top-bar--in-call {
display: none !important;
}
`;
document.head.appendChild(style);
function waitFor(selector, scope = document, timeout = 2000) {
return new Promise(resolve => {
let el = scope.querySelector(selector);
if (el) return resolve(el);
let time = 0;
const iv = setInterval(() => {
el = scope.querySelector(selector);
if (el) {
clearInterval(iv);
resolve(el);
}
time += 50;
if (time >= timeout) {
clearInterval(iv);
resolve(null);
}
}, 50);
});
}
async function collectElements(inCallEl) {
const header = await waitFor('.main-view .top-bar.top-bar--in-call', inCallEl);
const callContainer = await waitFor('.main-view #call-container', inCallEl);
if (!header || !callContainer) return null;
let bottomMenu = null;
const bottomMenuSearch = callContainer.children[2];
if (bottomMenuSearch) {
if (bottomMenuSearch.classList.contains('bottom-bar')) {
bottomMenu = bottomMenuSearch;
}
} else if (callContainer.children[1].classList.contains('bottom-bar')) {
bottomMenu = callContainer.children[1];
}
console.log('bottomMenu', bottomMenu)
if (!bottomMenu) return null;
const icon = header.querySelector('a.top-bar__icon-wrapper');
const title = header.querySelector('.conversation-header');
const time = header.querySelector('.v-popper.call-time');
const participants = header.querySelector('.button-vue--tertiary.top-bar__participants-button');
const dots = header.querySelector('.top-bar-menu');
const sections = [...bottomMenu.children];
return { header, bottomMenu, icon, title, time, participants, dots, sections };
}
function move(el, to) {
if (!el || !to) return;
if (!to.contains(el)) {
try { to.appendChild(el); } catch (e) { console.warn(e); }
}
}
function buildPanels(parts) {
if (createdPanels) return;
const { bottomMenu, icon, title, time, participants, dots, sections } = parts;
const wrapper = document.createElement('div');
wrapper.className = 'my-call-toolbar-wrapper';
wrapper.dataset.ourPanel = 'true';
const left = document.createElement('div');
left.className = 'left-panel';
const middle = document.createElement('div');
middle.className = 'middle-panel';
const right = document.createElement('div');
right.className = 'right-panel';
wrapper.append(left, middle, right);
move(icon, left);
move(title, left);
const [sec1, sec2, sec3] = sections;
move(sec1, middle);
move(sec2, middle);
move(sec3, middle);
move(time, right);
move(participants, right);
move(dots, right);
bottomMenu.appendChild(wrapper);
createdPanels = { wrapper, parts };
document.body.classList.add(HIDDEN_BODY_CLASS);
console.log('✔ Created call panels');
}
function removePanels() {
if (!createdPanels) return;
const { wrapper, parts } = createdPanels;
wrapper.remove();
const { header, icon, title, time, participants, dots, sections } = parts;
if (icon) header.prepend(icon);
if (title) {
const after = header.querySelector('a.top-bar__icon-wrapper');
if (after?.nextSibling) header.insertBefore(title, after.nextSibling);
else header.appendChild(title);
}
move(time, header);
move(participants, header);
if (dots) {
const wrap = header.querySelector('.top-bar__wrapper');
if (wrap) {
const join = wrap.querySelector('.button-vue.join-call');
if (join) wrap.insertBefore(dots, join);
else wrap.appendChild(dots);
} else {
header.appendChild(dots);
}
}
const bottomMenu = parts.bottomMenu;
if (bottomMenu) {
sections.forEach(sec => {
if (sec) bottomMenu.appendChild(sec);
});
}
createdPanels = null;
document.body.classList.remove(HIDDEN_BODY_CLASS);
console.log('🧹 Panels restored');
}
async function onEnterInCall(inCallEl) {
if (placing) return;
placing = true;
if (currentInCall === inCallEl) {
placing = false;
return;
}
currentInCall = inCallEl;
const parts = await collectElements(inCallEl);
if (!parts) {
placing = false;
return;
}
buildPanels(parts);
placing = false;
}
function onExitCall() {
currentInCall = null;
removePanels();
}
function initialCheck() {
const inCall = divMain.querySelector('.content.app-talk.in-call');
if (inCall) onEnterInCall(inCall);
}
initialCheck();
const observer = new MutationObserver(() => {
clearTimeout(observer._t);
observer._t = setTimeout(() => {
const inCall = divMain.querySelector('.content.app-talk.in-call');
if (inCall) {
onEnterInCall(inCall);
} else {
onExitCall();
}
}, 120);
});
observer.observe(divMain, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class']
});
window.addEventListener('beforeunload', () => {
observer.disconnect();
removePanels();
});
});
//settings - sharing
document.addEventListener('DOMContentLoaded', function () {
const divMain = document.querySelector('#app-content');
if (divMain && divMain.dataset.activeSectionId === "sharing") {
const filesSettings = divMain.querySelector('#files-personal-settings');
const filesSharingSettings = divMain.querySelector('#files-sharing-personal-settings');
const transferSelectRow = filesSettings.querySelector('.transfer-select-row');
const buttonText = transferSelectRow.querySelector('.button-vue__text');
const titleSharingContent = document.createElement('h2');
titleSharingContent.textContent = "Настройки доступа";
titleSharingContent.classList.add('sharing-content-title');
const titleDisSharingContent = document.createElement('p');
titleDisSharingContent.textContent = "Настройки доступа к вашей информации в сервисах Forbion";
titleDisSharingContent.classList.add('sharing-content-title-discription');
const contentWrapperSharingContent = document.createElement('div');
contentWrapperSharingContent.classList.add('sharing-settings-box');
divMain.appendChild(titleSharingContent);
divMain.appendChild(titleDisSharingContent);
divMain.appendChild(contentWrapperSharingContent);
contentWrapperSharingContent.appendChild(filesSettings);
contentWrapperSharingContent.appendChild(filesSharingSettings);
buttonText.textContent = "Выбрать";
}
})
//settings - persons
document.addEventListener('DOMContentLoaded', function () {
const divMain = document.querySelector('#app-content');
if (divMain && divMain.dataset.activeSectionId === "personal-info") {
const divContent = document.getElementById('personal-settings');
const titlePersonContent = document.createElement('h2');
titlePersonContent.textContent = "Личная информация";
titlePersonContent.classList.add('person-content-title');
const titleDisPersonContent = document.createElement('p');
titleDisPersonContent.textContent = "Сведения о вас и ваших настройках в сервисах Forbion";
titleDisPersonContent.classList.add('person-content-title-discription');
const divAvatar = document.getElementById('vue-avatar-section');
const divProfile = divContent.querySelector('.personal-settings-setting-box.personal-settings-setting-box-profile');
const divDetails = divContent.querySelector('.personal-settings-setting-box.personal-settings-setting-box-detail');
const divName = divContent.querySelectorAll('.personal-settings-setting-box')[2];
const divEmail = divContent.querySelectorAll('.personal-settings-setting-box')[4];
const divNumber = divContent.querySelectorAll('.personal-settings-setting-box')[5];
const divPlace = divContent.querySelectorAll('.personal-settings-setting-box')[6];
const divDate = divContent.querySelectorAll('.personal-settings-setting-box')[7];
const divLang = divContent.querySelector('.personal-settings-language-box');
const divMestoim = divContent.querySelectorAll('.personal-settings-setting-box')[3];
const divLocate = divContent.querySelector('.personal-settings-locale-box');
const divWeek = divContent.querySelectorAll('.personal-settings-setting-box')[10];
const divTime = divContent.querySelectorAll('.personal-settings-setting-box')[11];
const divUrlSite = divContent.querySelectorAll('.personal-settings-setting-box')[12];
const divTW = divContent.querySelectorAll('.personal-settings-setting-box')[13];
const divBluesky = divContent.querySelectorAll('.personal-settings-setting-box')[14];
const divFederate = divContent.querySelectorAll('.personal-settings-setting-box')[15];
const divOrg = divContent.querySelectorAll('.personal-settings-setting-box')[16];
const divRole = divContent.querySelectorAll('.personal-settings-setting-box')[17];
const divTitle = divContent.querySelectorAll('.personal-settings-setting-box')[18];
const divAbout = divContent.querySelectorAll('.personal-settings-setting-box')[19];
divMestoim.style.display = "none";
divLocate.style.display = "none";
divWeek.style.display = "none";
divTime.style.display = "none";
divUrlSite.style.display = "none";
divTW.style.display = "none";
divBluesky.style.display = "none";
divFederate.style.display = "none";
divOrg.style.display = "none";
divRole.style.display = "none";
divTitle.style.display = "none";
divAbout.style.display = "none";
const divAboutF7cloud = divMain.querySelector('.development-notice');
divAboutF7cloud.style.display = "none";
const wrapperBoxAvatarInfo = document.createElement('div');
wrapperBoxAvatarInfo.classList.add('wrapper-content-box');
wrapperBoxAvatarInfo.id = 'wrapper-content-box-avater-and-info';
const wrapperBoxContent = document.createElement('div');
wrapperBoxContent.classList.add('content-box');
const titleBoxAvatarInfo = document.createElement('p');
titleBoxAvatarInfo.classList.add('title-box');
titleBoxAvatarInfo.textContent = 'Основная иформация профиля';
const divGridPersonInfo = document.createElement('div');
divGridPersonInfo.classList.add('person-grid');
const wrapperBoxProfile = document.createElement('div');
wrapperBoxProfile.classList.add('wrapper-content-box');
wrapperBoxProfile.id = 'wrapper-content-box-profile-info';
const wrapperBoxContent2 = document.createElement('div');
wrapperBoxContent2.classList.add('content-box');
const titleBoxProfile = document.createElement('p');
titleBoxProfile.classList.add('title-box');
titleBoxProfile.textContent = 'Подробные сведения';
divContent.append(titlePersonContent);
divContent.append(titleDisPersonContent);
divContent.append(wrapperBoxAvatarInfo);
divContent.append(wrapperBoxProfile);
// section 1
wrapperBoxAvatarInfo.append(titleBoxAvatarInfo);
wrapperBoxAvatarInfo.append(wrapperBoxContent);
wrapperBoxContent.append(divAvatar);
wrapperBoxContent.append(divGridPersonInfo);
divGridPersonInfo.append(divName);
divGridPersonInfo.append(divNumber);
divGridPersonInfo.append(divDate);
divGridPersonInfo.append(divPlace);
divGridPersonInfo.append(divEmail);
divGridPersonInfo.append(divLang);
//
divGridPersonInfo.append(divMestoim);
divGridPersonInfo.append(divLocate);
divGridPersonInfo.append(divWeek);
divGridPersonInfo.append(divTime);
divGridPersonInfo.append(divUrlSite);
divGridPersonInfo.append(divTW);
divGridPersonInfo.append(divBluesky);
divGridPersonInfo.append(divFederate);
divGridPersonInfo.append(divOrg);
divGridPersonInfo.append(divRole);
divGridPersonInfo.append(divTitle);
divGridPersonInfo.append(divAbout);
// section 2
wrapperBoxProfile.append(titleBoxProfile);
wrapperBoxProfile.append(wrapperBoxContent2);
wrapperBoxContent2.append(divProfile);
wrapperBoxContent2.append(divDetails);
}
})
// Анимация сайдбара-хеддера
document.addEventListener('DOMContentLoaded', function () {
const userMenuLink = document.getElementById('user-menu-f7mail');
const dropdownMenu = document.getElementById('header-menu-user-menu-f7mail');
if (userMenuLink && dropdownMenu) {
userMenuLink.addEventListener('click', function (e) {
e.preventDefault();
dropdownMenu.style.display = dropdownMenu.style.display === 'none' ? 'block' : 'none';
});
document.addEventListener('click', function (e) {
if (!dropdownMenu.contains(e.target) && e.target !== userMenuLink) {
dropdownMenu.style.display = 'none';
}
});
}
setTimeout(function () {
const newNotifications = document.getElementsByClassName('notification__dot');
if (newNotifications.length > 0) {
const spanAlert = document.createElement('span');
spanAlert.className = 'alert-icon';
newNotifications[0].parentNode.parentNode.appendChild(spanAlert);
}
}, 2000);
const moreBtn = document.getElementById('app-menu-toggle--more');
const hiddenMenu = document.getElementById('hidden-menu');
const appList = document.querySelector('.app-menu__list');
moreBtn.addEventListener('click', e => {
e.preventDefault();
e.stopPropagation();
if (hiddenMenu.classList.contains('is-active')) {
hiddenMenu.style.maxHeight = hiddenMenu.scrollHeight + 'px';
requestAnimationFrame(() => {
hiddenMenu.style.maxHeight = '0';
hiddenMenu.style.opacity = '0';
hiddenMenu.style.transform = 'translateY(20px)';
});
hiddenMenu.addEventListener('transitionend', function handler() {
hiddenMenu.classList.remove('is-active');
hiddenMenu.style.maxHeight = '';
hiddenMenu.removeEventListener('transitionend', handler);
});
appList.scrollTop = 0;
} else {
hiddenMenu.classList.add('is-active');
hiddenMenu.style.maxHeight = '0';
hiddenMenu.style.opacity = '0';
hiddenMenu.style.transform = 'translateY(20px)';
requestAnimationFrame(() => {
hiddenMenu.style.maxHeight = hiddenMenu.scrollHeight + 'px';
hiddenMenu.style.opacity = '1';
hiddenMenu.style.transform = 'translateY(0)';
});
}
});
document.addEventListener('click', e => {
if (!hiddenMenu.contains(e.target) && !e.target.closest('#app-menu-toggle--more')) {
if (hiddenMenu.classList.contains('is-active')) {
moreBtn.click();
}
}
});
document.addEventListener('keydown', e => {
if (e.key === 'Escape' && hiddenMenu.classList.contains('is-active')) {
moreBtn.click();
}
});
});
document.addEventListener('DOMContentLoaded', () => {
const tooltip = document.createElement('div');
tooltip.className = 'global-tooltip';
document.body.appendChild(tooltip);
const entries = document.querySelectorAll('.app-menu-entry');
function findScrollParent(el) {
let p = el.parentElement;
while (p) {
const overflowY = window.getComputedStyle(p).overflowY;
if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') return p;
p = p.parentElement;
}
return window;
}
let activeEntry = null;
let scrollParent = null;
function showTooltipFor(entry) {
const labelEl = entry.querySelector('.tooltip-header-hide');
if (!labelEl) return;
const text = labelEl.textContent.trim();
tooltip.textContent = text;
tooltip.classList.add('visible');
requestAnimationFrame(() => positionTooltip(entry));
activeEntry = entry;
scrollParent = findScrollParent(entry);
if (scrollParent && scrollParent !== window) {
scrollParent.addEventListener('scroll', onScrollOrResize, { passive: true });
}
window.addEventListener('scroll', onScrollOrResize, { passive: true });
window.addEventListener('resize', onScrollOrResize);
}
function hideTooltip() {
tooltip.classList.remove('visible');
if (scrollParent && scrollParent !== window) {
scrollParent.removeEventListener('scroll', onScrollOrResize);
}
window.removeEventListener('scroll', onScrollOrResize);
window.removeEventListener('resize', onScrollOrResize);
activeEntry = null;
scrollParent = null;
}
function onScrollOrResize() {
if (activeEntry) positionTooltip(activeEntry);
}
function positionTooltip(entry) {
const rect = entry.getBoundingClientRect();
const gap = 8;
const preferLeft = false;
const tooltipRect = tooltip.getBoundingClientRect();
let top = rect.top + rect.height / 2;
let left = rect.right + gap;
const viewportWidth = document.documentElement.clientWidth;
if (left + tooltipRect.width > viewportWidth - 8) {
left = rect.left - gap - tooltipRect.width;
}
left = Math.max(8, Math.min(left, viewportWidth - tooltipRect.width - 8));
const viewportHeight = document.documentElement.clientHeight;
const halfTooltipHeight = tooltipRect.height / 2;
top = Math.max(8 + halfTooltipHeight, Math.min(top, viewportHeight - 8 - halfTooltipHeight));
tooltip.style.left = Math.round(left) + 'px';
tooltip.style.top = Math.round(top) + 'px';
}
entries.forEach(entry => {
const target = entry.querySelector('.app-menu-entry__link') || entry;
// mouseenter / mouseleave
entry.addEventListener('mouseenter', () => showTooltipFor(entry));
entry.addEventListener('mouseleave', hideTooltip);
if (target) {
target.addEventListener('focus', () => showTooltipFor(entry));
target.addEventListener('blur', hideTooltip);
}
});
});
document.addEventListener('DOMContentLoaded', () => {
const divFile = document.querySelector('.content.app-files')
if (!divFile) return
function waitForElement(selector, timeout = 5000) {
return new Promise((resolve, reject) => {
const el = document.querySelector(selector)
if (el) return resolve(el)
const obs = new MutationObserver(() => {
const el2 = document.querySelector(selector)
if (el2) {
obs.disconnect()
resolve(el2)
}
})
obs.observe(document.body, { childList: true, subtree: true })
setTimeout(() => {
obs.disconnect()
reject(new Error(`Элемент ${selector} не найден за ${timeout}ms`))
}, timeout)
})
}
async function moveFiltersOnce() {
try {
const filtersRoot = await waitForElement('.files-list__filters')
const nameSortButton = await waitForElement('.files-list__column-sort-button[title="Имя"]')
if (filtersRoot.dataset.moved === '1') return
const thead = nameSortButton.closest('thead')
if (!thead) return
const nameTh = nameSortButton.closest('th')
if (!nameTh) return
// Оборачиваем в div для выравнивания
let wrapper = thead.querySelector('.files-list__filters-moved')
if (!wrapper) {
wrapper = document.createElement('div')
wrapper.className = 'files-list__filters-moved'
wrapper.style.display = 'flex'
wrapper.style.gap = '8px'
wrapper.style.marginLeft = '12px'
wrapper.style.alignItems = 'center'
nameTh.appendChild(wrapper)
}
wrapper.appendChild(filtersRoot)
filtersRoot.style.display = ''
filtersRoot.dataset.moved = '1'
} catch (e) {
console.error('moveFiltersOnce error', e)
}
}
moveFiltersOnce()
const observer = new MutationObserver(mutations => {
for (const m of mutations) {
if (m.type === 'childList' && m.addedNodes.length) {
if ([...m.addedNodes].some(
n => n.nodeType === 1 && (n.matches?.('.files-list__filters') || n.querySelector?.('.files-list__filters'))
)) {
setTimeout(moveFiltersOnce, 50)
break
}
}
}
})
observer.observe(divFile, { childList: true, subtree: true })
})
document.addEventListener('DOMContentLoaded', () => {
const divFile = document.getElementById('body-public')?.querySelector('.content.app-files');
if (!divFile) return;
const setupHeader = () => {
const header = document.getElementById('header');
if (!header || header.querySelector('#header-controls')) return;
const headerRight = document.querySelector('.header-right');
const talkTrigger = document.getElementById('talk-sidebar-trigger');
console.log(headerRight)
if (headerRight && talkTrigger) {
const controls = document.createElement('div');
controls.id = 'header-controls';
controls.className = 'header-controls';
console.log(controls)
if (headerRight.parentNode) headerRight.parentNode.removeChild(headerRight);
if (talkTrigger.parentNode) talkTrigger.parentNode.removeChild(talkTrigger);
controls.appendChild(headerRight);
controls.appendChild(talkTrigger);
const headerLeft = header.querySelector('.header-left');
if (headerLeft) {
headerLeft.insertAdjacentElement('afterend', controls);
}
}
};
const initWithRetry = () => {
setupHeader();
setTimeout(setupHeader, 200);
setTimeout(setupHeader, 800);
};
initWithRetry();
const observer = new MutationObserver(() => {
if (document.getElementById('talk-sidebar-trigger')) {
setTimeout(setupHeader, 50);
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class']
});
});
//add dop field for contact
document.addEventListener('DOMContentLoaded', () => {
const bodyUser = document.getElementById('body-user');
if (!bodyUser) return;
const contactsApp = bodyUser.querySelector('.app-contacts');
if (!contactsApp) return;
const processedButtons = new WeakSet();
const buttonObserver = new MutationObserver(() => {
const addButton = [...contactsApp.querySelectorAll(
'button.action-item__menutoggle'
)].find(btn =>
btn.querySelector('.button-vue__text')
?.textContent.includes('Добавить больше информации')
);
if (!addButton) return;
if (processedButtons.has(addButton)) return;
processedButtons.add(addButton);
handleAddButton(addButton);
});
buttonObserver.observe(contactsApp, {
childList: true,
subtree: true
});
async function handleAddButton(addButton) {
addButton.click();
let menu;
try {
menu = await waitForPopperMenu();
} catch (e) {
console.error(e);
return;
}
clickByIcon(menu, '.cake-variant-outline-icon', 'День рождения');
const moreBtn = menu.querySelector(
'span[aria-label="Дополнительные поля"]'
)?.closest('button');
if (moreBtn) {
moreBtn.click();
}
setTimeout(() => {
document.body.click();
}, 300);
}
function clickByIcon(menu, iconSelector, label) {
const btn = menu.querySelector(iconSelector)?.closest('button');
if (btn) {
btn.click();
} else {
console.warn(`⚠️ Поле не найдено: ${label}`);
}
}
function waitForPopperMenu(timeout = 3000) {
return new Promise((resolve, reject) => {
const start = performance.now();
let rafId;
function check() {
const poppers = [...document.querySelectorAll(
'.v-popper__popper[aria-hidden="false"]'
)];
if (poppers.length) {
const popper = poppers[poppers.length - 1];
const menu = popper.querySelector('ul[role="menu"]');
if (menu) {
cancelAnimationFrame(rafId);
observer.disconnect();
resolve(menu);
return;
}
}
if (performance.now() - start > timeout) {
cancelAnimationFrame(rafId);
observer.disconnect();
reject('❌ Popper-меню не найдено');
return;
}
rafId = requestAnimationFrame(check);
}
const observer = new MutationObserver(check);
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['aria-hidden', 'class']
});
check();
});
}
});
document.addEventListener('DOMContentLoaded', function () {
const observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
mutation.addedNodes.forEach(function (node) {
if (node.nodeType === 1) {
if (node.id === 'notifications' && node.hasAttribute('title')) {
node.removeAttribute('title');
observer.disconnect();
return;
}
const notificationsContainer = node.id === 'notifications' ||
node.closest?.('#notifications');
if (notificationsContainer && notificationsContainer.hasAttribute('title')) {
notificationsContainer.removeAttribute('title');
observer.disconnect();
}
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
const existingContainer = document.querySelector('#notifications[title]');
if (existingContainer) {
existingContainer.removeAttribute('title');
observer.disconnect();
}
});