Български | Català | Deutsche | Hrvatski | Čeština | Dansk | Nederlandse | English | Eesti keel | Français | Ελληνικά | Magyar | Italiano | Latviski | Norsk | Polski | Português | Română | Русский | Српски | Slovenský | Slovenščina | Español | Svenska | Türkçe | 汉语 | 日本語 |
P

www html

Активна фраза
Дата на актуализиране на информацията: 2026/03/29

📊 Резюме: www html

📋
Честота на търсене: 72 481
език: en
Няма налични морфологични данни или дефиниция за тази фраза.

Позиции в Google

Фрази за търсене - Google

🔍
Позиция Домейн Страница Действия
1 www.w3schools.com /html/
Пълен URL адрес
Заглавие
HTML Tutorial
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
HTML Tutorial
HTML is the standard markup language for Web pages. With HTML you can create your own Website. HTML is easy to learn - You will enjoy it!
2 html.com /
Пълен URL адрес
Заглавие
HTML For Beginners The Easy Way
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
HTML For Beginners The Easy Way
Our step-by-step guide teaches you the basics of HTML and how to build your first website. That means how to layout an HTML page, how to add text and images , ...
3 qna.habr.com /q/695506
Пълен URL адрес
Заглавие
Как сменить document_root apache с /var/www/html на /var ...
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
Как сменить document_root apache с /var/www/html на /var ...
23 дек. 2019 г. — Как сменить document_root apache с /var/ www/html на /var/www/test/ html /public? Есть vps, на нем apache и php. из различных статей пробовал ...
4 developer.mozilla.org /ru/docs/web/html;19...
Заглавие
HTML - MDN Web Docs - Mozilla
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
HTML - MDN Web Docs - Mozilla
15 апр. 2025 г. — HTML (HyperText Markup Language — «язык гипертекстовой разметки») — самый базовый строительный блок Веба. Он определяет содержание и ...
5 stackoverflow.com /questions/16197663/...
Заглавие
What is /var/www/html? [closed] - php
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
What is /var/www/html? [closed] - php
/ var/www/html is just the default root folder of the web server. You can change that to be whatever folder you want by editing your apache.conf file.
6 htmlacademy.ru /
Пълен URL адрес
Заглавие
HTML Academy
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
HTML Academy
Готовим разработчиков напрямую для работодателей . Получите базовые навыки на тренажёрах, а затем пройдите серьёзные профессиональные программы для ...
7 ru.wikipedia.org /wiki/html;4107130
Заглавие
HTML
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
HTML
HTML (от англ. HyperText Markup Language — «язык гипертекстовой разметки») — стандартизированный язык гипертекстовой разметки документов для просмотра веб- ...
8 www.linux.org.ru /forum/general/88885...
Заглавие
Помогите разобраться с apache и /var/www/index.html
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
Помогите разобраться с apache и /var/www/index.html
24 февр. 2013 г. — вообщем главная страница в любом случае должна быть /var/www/index. html ? а остальные страницы где угодно, только чтоб в папке /var/www?
9 htmlbook.ru /
Пълен URL адрес
Заглавие
Home | htmlbook.ru
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
Home | htmlbook.ru
Аудио и видео. Семантический HTML . Каждый урок сопровождается задачами, которые надо решать прямиком в редакторе. Курс бесплатный, для изучения требуется только ...
10 html.spec.whatwg.org /
Пълен URL адрес
Заглавие
HTML Standard;5302519
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
HTML Standard;5302519
HTML is the World Wide Web's core markup language. Originally, HTML was primarily designed as a language for semantically describing scientific documents. Its ...;60549698

Позиции в Yandex

Фрази за търсене - Yandex

🔍
Позиция Домейн Страница Действия
1 inf.e-alekseev.ru /text/www_html.html
Заглавие
13.5. WWW и HTML
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
13.5. WWW и HTML
HTML – это формат гипертекстовых документов, использующихся в WWW для предоставления информации.
2 schoolsw3.com /html/html_intro.php
Заглавие
Введение в HTML за 5 минут — Как работает HTML ?
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
Введение в HTML за 5 минут — Как работает HTML ?
Простое объяснение HTML для новичков. Основные понятия, структура документа и примеры кода.
3 oslogic.ru /knowledge/75/osnovn...
Заглавие
Основные понятия World Wide Web, или WWW
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
Основные понятия World Wide Web, или WWW
WWW — (World Wide Web) — это часть Internet, представляющая собой гигантский набор документов, хранящихся на компьютерах по всему миру.
4 frolov-lib.ru /books/bsp/v23/ch4_1...
Заглавие
4 создание страниц www
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
4 создание страниц www
Практическое введение в Internet, E-Mail, FTP, WWW и HTML , программирование для Windows Sockets.
5 dzen.ru /a/zgzma5bzlrj7ofll
Пълен URL адрес
Заглавие
Основы html - база для всех тех, кто мечтает стать... | Дзен
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
Основы html - база для всех тех, кто мечтает стать... | Дзен
Подписчиков
6 htmlacademy.ru /blog/html/introduct...
Заглавие
Знакомство с HTML — журнал «Доктайп» | HTML Academy
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
Знакомство с HTML — журнал «Доктайп» | HTML Academy
Знакомство с HTML для тех, кто никогда не писал сайты.
7 help.sweb.ru /html-osnovy-yazyka-...
Заглавие
HTML
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
HTML
Основы HTML - для чего используется этот язык разметки и зачем он нужен; как выглядит код и синтаксис HyperText Markup Language.
8 academy.kgtk.ru /it3/web-programming...
Заглавие
Лекция 1. Основы World Wide Web ( WWW )
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
Лекция 1. Основы World Wide Web ( WWW )
В лекции рассмотрены общие принципы организации и функционирования сервиса WWW . Описаны структура и компоненты веб, включая протокол HTTP...
9 sky.pro /wiki/html/osnovy-ht...
Заглавие
Основы HTML
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
Основы HTML
Изучите основы HTML и создайте свою первую веб-страницу! В этой статье вы узнаете о ключевых тегах и структуре HTML -документа, научитесь...
10 developer.mozilla.org /ru/docs/learn_web_d...
Заглавие
Начало работы с HTML - Изучение веб-разработки | MDN
Последна актуализация
N/A
Право на страницата
N/A
Трафик: N/A
Обратни връзки: N/A
Социални споделяния: N/A
Време за зареждане: N/A
Визуализация на фрагмента:
Начало работы с HTML - Изучение веб-разработки | MDN
В этой статье мы охватим азы HTML , необходимые для начала работы. Дадим определение «элементам», «атрибутам»...

статия

📝

Разбиране на основите на www HTML: Изчерпателно ръководство

Добре дошли в дълбокото гмуркане в света на уеб разработката, където всеки ред код може да трансформира обикновен текст в динамични, интерактивни уебсайтове. Днес се фокусираме върху гръбнака на мрежата — www HTML. Независимо дали сте напълно начинаещ или освежавате уменията си, това ръководство ще ви предостави солидна основа в HTML и как той се вписва в по-голямата картина на уеб дизайна.

Какво е HTML?

HTML, което означава HyperText Markup Language, е основният език, използван за създаване на уеб страници. Той предоставя основна структура, която браузърите интерпретират, за да показват съдържание в интернет. Всеки уебсайт, който посещавате, започва като HTML код, което го прави основно умение за всеки, който се интересува от уеб разработка.

Ролята на www HTML

Може би се чудите защо специално споменаваме www HTML. Въпреки че частта „www“ обикновено не е необходима в съвременните уеб адреси, някога тя беше от решаващо значение. Световната мрежа (WWW) по същество е система от взаимосвързани хипертекстови документи, достъпни чрез интернет. HTML е езикът, използван за създаване на тези документи.

В днешния контекст споменаването на www HTML подчертава историческото значение и основополагащата роля на HTML в създаването на мрежата. Това ни напомня, че HTML е нещо повече от код – това е езикът на мрежата.

Основна HTML структура

Нека да разгледаме основната структура на HTML документ:

<преди> <глава> Заглавие на страница <тяло>

Моето първо заглавие

Моят първи параграф.

  • : Декларира типа на документа и версията на HTML.
  • : Основният елемент на HTML страница.
  • : Съдържа мета-информация за HTML документа, като неговото заглавие и връзки към таблици със стилове.
  • </strong>: Задава заглавието на уеб страницата, което се показва в раздела на браузъра.</li> <li><strong><body></strong>: Съдържа съдържанието на HTML документа, което е видимо за потребителите.</li> <li><strong><h1></strong>: Дефинира заглавие от най-високо ниво.</li> <li><strong><p></strong>: Представлява параграф от текст.</li> </ul> <h2>Създаване на текстови елементи с www HTML</h2> <p>HTML предоставя различни тагове за създаване на различни типове текстови елементи. Ето няколко примера:</p> <ul> <li><strong><h1>-<h6></strong>: Дефинирайте заглавия от най-голямото към най-малкото.</li> <li><strong><p></strong>: Създава параграфи от текст.</li> <li><strong><a></strong>: Вмъква хипервръзки към други уеб страници или ресурси.</li> <li><strong><em></strong> и <strong><strong></strong>: Подчертайте текст, като <strong><em></strong> обикновено се изобразява в курсив и <strong><strong></strong> в удебелен шрифт.</li> <li><strong><br></strong>: Вмъква нов ред.</li> </ul> <h3>Пример: Създаване на проста връзка</h3> <преди> <a href="https://www.serpulse.com">Посетете Serpulse</a> </pre> <p>Този код създава връзка, върху която може да се кликне, към уебсайта Serpulse. Атрибутът <strong>href</strong> указва URL адреса, към който сочи връзката.</p> <h2>Добавяне на изображения с www HTML</h2> <p>Включването на изображения във вашите уеб страници подобрява тяхната визуална привлекателност и потребителско изживяване. За да добавите изображение с помощта на HTML, използвайте етикета <strong><img></strong>:</p> <преди> <img src="path/to/image.jpg" alt="Описание на изображението"> </pre> <ul> <li><strong>src</strong>: Указва пътя до файла с изображението.</li> <li><strong>alt</strong>: Предоставя алтернативен текст за екранни четци и когато изображението не може да се покаже.</li> </ul> <h2>Създаване на списъци с www HTML</h2> <p>Списъците са от съществено значение за организиране на информацията на вашия уебсайт. HTML поддържа както подредени, така и неподредени списъци:</p> <h3>Неподреден списък</h3> <преди> <ul> <li>Първи елемент</li> <li>Втори елемент</li> <li>Трети елемент</li> </ul> </pre> <h3>Подреден списък</h3> <преди> <ol> <li>Първа стъпка</li> <li>Втора стъпка</li> <li>Трета стъпка</li> </ol> </pre> <h2>Таблици в www HTML</h2> <p>HTML също ви позволява да създавате таблици за показване на таблични данни:</p> <преди> <таблица> <tr> <th>Заглавие 1</th> <th>Заглавие 2</th> </tr> <tr> <td>Ред 1, клетка 1</td> <td>Ред 1, клетка 2</td> </tr> <tr><td>Ред 2, клетка 1</td> <td>Ред 2, клетка 2</td> </tr> </table> </pre> <ul> <li><strong><table></strong>: Дефинира таблицата.</li> <li><strong><tr></strong>: Дефинира ред на таблица.</li> <li><strong><th></strong>: Дефинира заглавна клетка в таблица.</li> <li><strong><td></strong>: Дефинира стандартна клетка с данни в таблица.</li> </ul> <h2>Формуляри в www HTML</h2> <p>Формулярите са от съществено значение за събиране на информация от потребителите. Ето основен пример за HTML формуляр:</p> <преди> <form action="/submit-form" method="post"> <label for="name">Име:</label> <input type="text" id="name" name="name"><br><br> <label for="email">Имейл:</label> <input type="email" id="email" name="email"><br><br> <input type="submit" value="Submit"> </form> </pre> <ul> <li><strong><form></strong>: Създава HTML формуляр за въвеждане от потребителя.</li> <li><strong>действие</strong>: Указва къде да се изпратят данните от формуляра, когато бъдат изпратени.</li> <li><strong>метод</strong>: Дефинира използвания HTTP метод (GET или POST).</li> <li><strong><label></strong>: Осигурява етикети за полета за въвеждане.</li> <li><strong><input></strong>: Създава различни типове елементи за въвеждане, като текстови полета и бутони.</li> </ul> <h2>Стил с CSS</h2> <p>Докато HTML осигурява структурата на вашите уеб страници, CSS (Cascading Style Sheets) е отговорен за външния им вид. Можете да свържете CSS файл към вашия HTML документ, като използвате маркера <strong><link></strong> в секцията <strong><head></strong>:</p> <преди> <link rel="stylesheet" href="styles.css"> </pre> <p>Алтернативно, можете да включите CSS директно във вашия HTML чрез маркера <strong><style></strong>:</p> <преди> <стил> тяло { цвят на фона: светлосин; } h1 { цвят: морско; margin-left: 20px; } </style> </pre> <h2>JavaScript за интерактивност</h2> <p>За да добавите интерактивност към вашите уеб страници, можете да използвате JavaScript. Въпреки че не е част от самия HTML, JavaScript работи заедно с HTML и CSS, за да създава динамични потребителски изживявания. Ето прост пример за вграждане на JavaScript в HTML документ:</p> <преди> <скрипт> функция showMessage() { предупреждение ("Здравей, свят!"); } </script> <button onclick="showMessage()">Щракнете върху мен!</button> </pre> <h2>Заключение</h2> <p>В това изчерпателно ръководство проучихме основите на <strong>www HTML</strong>, от ролята му в световната мрежа до създаването на сложни елементи като таблици и формуляри. Разбирайки HTML, вие ще бъдете на път да създадете професионално изглеждащи уебсайтове, които ангажират и информират вашата аудитория.</p> <p>Не забравяйте, че изучаването на HTML е само началото на вашето пътуване в уеб разработката. Докато овладявате HTML, не забравяйте да се потопите в CSS за стилизиране и JavaScript за интерактивност. Приятно кодиране!</p> <p>За повече ресурси и уроци посетете <a href="https://www.serpulse.com">Serpulse</a>.</p> <p><em>Автор: Serpulse.com</em></p> <p><em>Ключови думи: www html, основи на HTML, уеб разработка, уеб дизайн, HTML структура, HTML тагове, HTML текстови елементи, HTML изображения, HTML списъци, HTML таблици, HTML формуляри, CSS, JavaScript</em></p> ```уценка Тази статия предоставя подробен преглед на HTML, неговото значение и как се вписва в по-широкия контекст на уеб разработката. Той обхваща основни концепции и практически примери, което го прави отличен ресурс както за начинаещи, така и за опитни разработчици, които искат да опреснят знанията си. Включването на специфични HTML тагове и кодови фрагменти гарантира яснота и възможности за практическо обучение. ``` <p>Автор: serpuls.com</p> </div> </div> <!-- Words Definitions --> <div class="card"> <div class="card-header"> <h2 class="card-title">www html Думи</h2> <div class="card-icon">📚</div> </div> <div class="card-content"> <div class="word-section" style="margin-bottom: 2rem;"> <h3 style="color: var(--primary); margin-bottom: 1rem; border-bottom: 1px solid #e9ecef; padding-bottom: 0.5rem;"> html </h3> <div class="info-grid"> <div class="info-item"> <strong>Perl:</strong> <div class="text-muted" style="font-size:0.85em; margin:4px 0;"> 📖 Естественные науки </div> <span>Perl - язык программирования для разработки CGI-скриптов. Программы, написанные на Perl, запускаются на стороне сервера. Perl не является коммерческим продуктом.</span> </div> </div> </div> </div> </div> <!-- Additional Services --> <div class="card" id="additional-services"> <div class="card-header"> <h2 class="card-title">Допълнителни услуги</h2> <div class="card-icon">💎</div> </div> <div class="card-content"> <div class="info-grid"> <div class="info-item"> <span class="popup-trigger" alt="Пълен SEO одит с подробни препоръки"> <button class="cta-button" data-modal="paymentModal" data-title="Пълен SEO одит" data-text="Пълен SEO одит с подробни препоръки за подобряване на позициите в търсачките." data-price="99.99"> Пълен SEO одит </button> <span class="popup-text"></span> </span> </div> <div class="info-item"> <span class="popup-trigger" alt="Месечен мониторинг на позицията за 100 ключови фрази"> <button class="cta-button" data-modal="paymentModal" data-title="Мониторинг на позицията" data-text="Месечен мониторинг на позицията за 100 ключови фрази в Google и Yandex." data-price="49.99"> Мониторинг на позицията </button> <span class="popup-text"></span> </span> </div> <div class="info-item"> <span class="popup-trigger" alt="Анализ на конкурентите и идентифициране на техните стратегии за промоция"> <button class="cta-button" data-modal="paymentModal" data-title="Анализ на конкурентите" data-text="Анализ на конкурентите и идентифициране на техните стратегии за промоция." data-price="79.99"> Анализ на конкурентите </button> <span class="popup-text"></span> </span> </div> </div> </div> </div> <!-- JavaScript for AJAX Pagination --> <script> // Apply filters function function applyPhraseFilters(engine) { loadSearchDomains(engine, 1); } // Load search domains for phrase function loadSearchDomains(engine, page) { const domainFilter = document.getElementById(engine + 'DomainFilter').value; const pageFilter = document.getElementById(engine + 'PageFilter').value; const phrase_id = document.getElementById('phrase_id').value; const data = { type: 'search_domains', engine: engine, page: page, phrase_id: phrase_id, // Send the phrase domain_filter: domainFilter, page_filter: pageFilter }; sendRequest('/ajax/', data, function(response) { if (response.status === 'ok') { renderSearchDomains(engine, response.data); renderPagination(engine, response.pagination); } else { showToast('Грешка' + ':'+response.error); } }); } // Render domain pagination specifically for phrase pages function renderDomainPagination(engine, pagination) { const paginationContainer = document.getElementById(engine + 'Pagination'); paginationContainer.innerHTML = ''; const { current_page, total_pages, has_previous, has_next } = pagination; // Previous button const prevButton = document.createElement('button'); prevButton.innerHTML = '«'; prevButton.disabled = !has_previous; prevButton.addEventListener('click', () => { if (has_previous) loadSearchDomains(engine, current_page - 1); }); paginationContainer.appendChild(prevButton); // Page buttons for (let i = 1; i <= total_pages; i++) { const pageButton = document.createElement('button'); pageButton.textContent = i; pageButton.classList.toggle('active', i === current_page); pageButton.addEventListener('click', () => { if (i !== current_page) loadSearchDomains(engine, i); }); paginationContainer.appendChild(pageButton); } // Next button const nextButton = document.createElement('button'); nextButton.innerHTML = '»'; nextButton.disabled = !has_next; nextButton.addEventListener('click', () => { if (has_next) loadSearchDomains(engine, current_page + 1); }); paginationContainer.appendChild(nextButton); } // Get badge class based on position function getPositionBadgeClass(position) { if (position <= 3) return 'badge-success'; if (position <= 10) return 'badge-warning'; return 'badge-danger'; } // Toggle snippet visibility function toggleSnippet(button) { const snippet = button.nextElementSibling; const isVisible = snippet.style.display === 'block'; snippet.style.display = isVisible ? 'none' : 'block'; button.textContent = isVisible ? 'Показване на фрагмента' : 'Скриване на фрагмента'; } </script> <script> // Toggle page details visibility function togglePageDetails(button) { const row = button.closest('tr'); const detailsRow = row.nextElementSibling; const pageDetails = detailsRow.querySelector('.page-details'); // Close all other open page details document.querySelectorAll('.page-details').forEach(detail => { if (detail !== pageDetails) { detail.classList.remove('active'); } }); // Update all button texts document.querySelectorAll('.show-page-btn').forEach(btn => { if (btn !== button) { btn.textContent = 'Показване на подробности за страницата'; } }); // Toggle current page details if (pageDetails.classList.contains('active')) { pageDetails.classList.remove('active'); button.textContent = 'Показване на подробности за страницата'; } else { pageDetails.classList.add('active'); button.textContent = 'Скриване на подробностите за страницата'; } } // Close all page details when clicking outside document.addEventListener('click', function(e) { if (!e.target.closest('.show-page-btn') && !e.target.closest('.page-details')) { document.querySelectorAll('.page-details').forEach(detail => { detail.classList.remove('active'); }); document.querySelectorAll('.show-page-btn').forEach(btn => { btn.textContent = 'Показване на подробности за страницата'; }); } }); // Update the renderSearchDomains function to include show page buttons function renderSearchDomains(engine, data) { const tbody = document.getElementById(engine + 'PhrasesBody'); tbody.innerHTML = ''; data.forEach((item, index) => { const row = document.createElement('tr'); // Format position with change let positionHtml = `<span class="badge ${getPositionBadgeClass(item.position)}">${item.position}</span>`; if (item.change !== null && item.change !== 0) { const changeClass = item.change > 0 ? 'positive' : 'negative'; const changeSymbol = item.change > 0 ? '+' : ''; positionHtml += `<span class="position-change ${changeClass}">(${changeSymbol}${item.change})</span>`; } row.innerHTML = ` <td>${positionHtml}</td> <td><noindex><a href="/bg/domain/${item.domain}/" rel="nofollow">${item.domain}</a></noindex></td> <td>${item.page}</td> <td> <button class="show-page-btn" onclick="togglePageDetails(this)">Показване на подробности за страницата</button> </td> `; tbody.appendChild(row); // Add page details row const detailsRow = document.createElement('tr'); detailsRow.className = 'page-details-row'; detailsRow.innerHTML = ` <td colspan="4"> <div class="page-details" id="page-details-${engine}-${index}"> <div class="page-details-grid"> <div class="page-detail-item"> <div class="page-detail-label">Full URL</div> <div class="page-detail-value"> <a href="${item.full_url || '#'}" class="page-url" target="_blank" rel="nofollow"> ${item.full_url || 'N/A'} </a> </div> </div> <div class="page-detail-item"> <div class="page-detail-label">Title</div> <div class="page-detail-value">${item.page_title || 'N/A'}</div> </div> <div class="page-detail-item"> <div class="page-detail-label">Last Updated</div> <div class="page-detail-value">${item.last_updated || 'N/A'}</div> </div> <div class="page-detail-item"> <div class="page-detail-label">Page Authority</div> <div class="page-detail-value">${item.page_authority || 'N/A'}</div> </div> </div> <div class="page-metrics"> <div class="page-metric"> <span class="page-detail-label">Traffic:</span> <span class="page-metric-value">${item.traffic || 'N/A'}</span> </div> <div class="page-metric"> <span class="page-detail-label">Backlinks:</span> <span class="page-metric-value">${item.backlinks || 'N/A'}</span> </div> <div class="page-metric"> <span class="page-detail-label">Social Shares:</span> <span class="page-metric-value">${item.social_shares || 'N/A'}</span> </div> <div class="page-metric"> <span class="page-detail-label">Load Time:</span> <span class="page-metric-value">${item.load_time || 'N/A'}</span> </div> </div> <div class="snippet" style="margin-top: 1rem;"> <strong>Snippet Preview:</strong><br> ${item.snippet || 'No snippet available'} </div> </div> </td> `; tbody.appendChild(detailsRow); }); } </script> <script> (function() { 'use strict'; // --- Configuration --- const STORAGE_KEY = 'serpuls_phrase_sections_state'; const SECTIONS_TO_COLLAPSE = [ { id: 'morphological-lemmas', title: 'Морфологични леми', btnText: 'Леми' }, { id: 'google-search-section', title: 'Фрази за търсене - Google', btnText: 'Google' }, { id: 'yandex-search-section', title: 'Фрази за търсене - Yandex', btnText: 'Yandex' }, { id: 'book-samples-section', title: 'Примери за книги с фраза', btnText: 'Книги' }, { id: 'additional-services', title: 'Допълнителни услуги', btnText: 'Услуги' } ]; // --- Helper Functions --- function getSectionIdFromTitle(title) { if (title.includes('Морфологични леми')) return 'morphological-lemmas'; if (title.includes('Фрази за търсене - Google')) return 'google-search-section'; if (title.includes('Фрази за търсене - Yandex')) return 'yandex-search-section'; if (title.includes('Допълнителни услуги')) return 'additional-services'; return null; } function saveSectionState(sectionId, isCollapsed) { const savedState = localStorage.getItem(STORAGE_KEY); const state = savedState ? JSON.parse(savedState) : {}; state[sectionId] = isCollapsed; localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } function loadSectionState(sectionId) { const savedState = localStorage.getItem(STORAGE_KEY); if (!savedState) return false; const state = JSON.parse(savedState); return state[sectionId] || false; } // --- Create Floating TOC --- function createFloatingTOC() { // Find all major sections (cards with a card-header) const sections = Array.from(document.querySelectorAll('.card')).map(card => { const headerEl = card.querySelector('.card-header'); if (!headerEl) return null; const titleEl = headerEl.querySelector('.card-title'); if (!titleEl) return null; const title = titleEl.innerText.trim(); // Skip sections that are too small or not needed const skipTitles = [ ]; let shouldSkip = false; for (const skipTitle of skipTitles) { if (title.includes(skipTitle) || title === skipTitle) { shouldSkip = true; break; } } if (shouldSkip) return null; // Create a unique ID for the section if it doesn't have one let id = card.id; if (!id) { id = 'section-' + Math.random().toString(36).substr(2, 8); card.id = id; } return { id, title, element: card }; }).filter(s => s !== null); if (sections.length < 2) return; // Create TOC container const tocContainer = document.createElement('div'); tocContainer.id = 'floating-toc'; tocContainer.className = 'floating-toc'; tocContainer.innerHTML = ` <div class="floating-toc-header"> <span>📑 Съдържание</span> <button id="toc-toggle-btn" class="toc-toggle-btn" aria-label="Свиване на съдържанието">−</button> </div> <div id="toc-links-container" class="toc-links-container"> ${sections.map(section => `<a href="#${section.id}" class="toc-link" data-id="${section.id}">${escapeHtml(section.title)}</a>`).join('')} </div> `; document.body.appendChild(tocContainer); // Toggle TOC visibility const toggleBtn = document.getElementById('toc-toggle-btn'); const linksContainer = document.getElementById('toc-links-container'); let isTocExpanded = true; toggleBtn.addEventListener('click', () => { if (isTocExpanded) { linksContainer.style.display = 'none'; toggleBtn.textContent = '+'; tocContainer.classList.add('collapsed'); } else { linksContainer.style.display = 'block'; toggleBtn.textContent = '−'; tocContainer.classList.remove('collapsed'); } isTocExpanded = !isTocExpanded; }); // Highlight active section on scroll const tocLinks = document.querySelectorAll('.toc-link'); const sectionElements = sections.map(s => s.element); function updateActiveLink() { let current = ''; const scrollPosition = window.scrollY + 100; for (const section of sectionElements) { const sectionTop = section.offsetTop; const sectionBottom = sectionTop + section.offsetHeight; if (scrollPosition >= sectionTop && scrollPosition < sectionBottom) { current = section.id; break; } } tocLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('data-id') === current) { link.classList.add('active'); } }); } window.addEventListener('scroll', updateActiveLink); updateActiveLink(); // Smooth scroll tocLinks.forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const targetId = link.getAttribute('data-id'); const targetElement = document.getElementById(targetId); if (targetElement) { targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); } // --- Add Collapsible Buttons to Sections --- function addCollapsibleButtons() { SECTIONS_TO_COLLAPSE.forEach(section => { // Find the card by its title let targetCard = null; const cards = document.querySelectorAll('.card'); for (const card of cards) { const titleEl = card.querySelector('.card-title'); if (titleEl && titleEl.innerText.trim() === section.title) { targetCard = card; break; } } if (!targetCard) return; // Set ID if not exists if (!targetCard.id) targetCard.id = section.id; // Create button const button = document.createElement('button'); button.className = 'collapse-section-btn'; button.setAttribute('data-section-id', section.id); button.setAttribute('aria-label', 'Свиване/разгъване на раздел'); // Get initial state const isCollapsed = loadSectionState(section.id); if (isCollapsed) { button.innerHTML = '➕'; targetCard.classList.add('section-collapsed'); const content = targetCard.querySelector('.card-content'); if (content) content.style.display = 'none'; } else { button.innerHTML = '➖'; targetCard.classList.remove('section-collapsed'); const content = targetCard.querySelector('.card-content'); if (content) content.style.display = ''; } // Insert button into card-header const cardHeader = targetCard.querySelector('.card-header'); if (cardHeader) { const iconDiv = cardHeader.querySelector('.card-icon'); if (iconDiv) { cardHeader.insertBefore(button, iconDiv); } else { cardHeader.appendChild(button); } // Add click handler button.addEventListener('click', (e) => { e.stopPropagation(); const sectionId = button.getAttribute('data-section-id'); const card = document.getElementById(sectionId); if (!card) return; const content = card.querySelector('.card-content'); if (!content) return; if (card.classList.contains('section-collapsed')) { // Expand content.style.display = ''; card.classList.remove('section-collapsed'); button.innerHTML = '➖'; saveSectionState(sectionId, false); } else { // Collapse content.style.display = 'none'; card.classList.add('section-collapsed'); button.innerHTML = '➕'; saveSectionState(sectionId, true); } }); } }); } // --- Create Back to Top Button --- function createBackToTopButton() { const btn = document.createElement('button'); btn.id = 'back-to-top'; btn.innerHTML = '↑'; btn.setAttribute('aria-label', 'Обратно в началото'); btn.className = 'back-to-top-btn'; document.body.appendChild(btn); window.addEventListener('scroll', () => { if (window.scrollY > 500) { btn.classList.add('visible'); } else { btn.classList.remove('visible'); } }); btn.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); } // --- Improve Table Responsiveness (add scroll hints) --- function improveTables() { const tables = document.querySelectorAll('.data-table, .tasks-table'); tables.forEach(table => { const container = table.closest('.table-responsive') || table.parentElement; if (container && !container.querySelector('.scroll-hint')) { const hint = document.createElement('div'); hint.className = 'scroll-hint'; hint.innerHTML = '← → Плъзнете, за да видите повече'; hint.style.cssText = ` display: none; text-align: center; font-size: 0.7rem; color: var(--gray); margin-top: 0.5rem; padding: 0.25rem; background: rgba(67, 97, 238, 0.05); border-radius: 12px; `; container.style.position = 'relative'; container.appendChild(hint); // Show hint if table is wider than container function checkScroll() { if (container.scrollWidth > container.clientWidth) { hint.style.display = 'block'; } else { hint.style.display = 'none'; } } checkScroll(); window.addEventListener('resize', checkScroll); container.addEventListener('scroll', () => { hint.style.opacity = '0.5'; setTimeout(() => { hint.style.opacity = '1'; }, 300); }); } }); } // --- Escape HTML helper --- function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // --- Add custom styles for new elements --- function addCustomStyles() { const style = document.createElement('style'); style.textContent = ` /* Floating TOC */ .floating-toc { position: fixed; top: 80px; right: 20px; z-index: 1000; background: white; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); border: 1px solid #e9ecef; width: 260px; max-width: calc(100% - 40px); transition: all 0.3s ease; backdrop-filter: blur(8px); background: rgba(255,255,255,0.95); } .floating-toc-header { display: flex; justify-content: space-between; align-items: center; padding: 10px 15px; background: linear-gradient(135deg, var(--primary), var(--secondary)); color: white; border-radius: 12px 12px 0 0; font-weight: 600; font-size: 0.9rem; cursor: pointer; } .toc-toggle-btn { background: none; border: none; color: white; font-size: 1.2rem; cursor: pointer; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: background 0.2s; } .toc-toggle-btn:hover { background: rgba(255,255,255,0.2); } .floating-toc.collapsed { width: auto; } .floating-toc.collapsed .floating-toc-header { border-radius: 12px; } .toc-links-container { max-height: 400px; overflow-y: auto; padding: 8px 0; } .toc-link { display: block; padding: 8px 15px; color: var(--dark); text-decoration: none; font-size: 0.85rem; transition: all 0.2s; border-left: 3px solid transparent; } .toc-link:hover { background: rgba(67, 97, 238, 0.05); color: var(--primary); padding-left: 20px; } .toc-link.active { background: rgba(67, 97, 238, 0.1); border-left-color: var(--primary); color: var(--primary); font-weight: 500; } /* Collapse button in cards */ .collapse-section-btn { background: none; border: none; font-size: 1.1rem; cursor: pointer; width: 28px; height: 28px; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center; transition: all 0.2s; color: var(--gray); margin-right: 8px; } .collapse-section-btn:hover { background: rgba(67, 97, 238, 0.1); color: var(--primary); } .card.section-collapsed .card-header { border-bottom: none; } /* Back to Top Button */ .back-to-top-btn { position: fixed; bottom: 30px; right: 30px; width: 48px; height: 48px; border-radius: 50%; background: linear-gradient(135deg, var(--primary), var(--secondary)); color: white; border: none; cursor: pointer; font-size: 1.5rem; display: flex; align-items: center; justify-content: center; opacity: 0; visibility: hidden; transition: all 0.3s ease; z-index: 999; box-shadow: 0 4px 12px rgba(67, 97, 238, 0.4); } .back-to-top-btn.visible { opacity: 1; visibility: visible; } .back-to-top-btn:hover { transform: translateY(-3px); box-shadow: 0 6px 16px rgba(67, 97, 238, 0.5); } /* Mobile adjustments */ @media (max-width: 768px) { .floating-toc { top: auto; bottom: 20px; right: 20px; left: auto; width: 48px; overflow: hidden; } .floating-toc .floating-toc-header span { display: none; } .floating-toc .floating-toc-header { justify-content: center; padding: 12px; } .floating-toc .toc-toggle-btn { margin: 0; } .floating-toc:not(.collapsed) { width: 260px; left: auto; right: 20px; bottom: 20px; top: auto; } .floating-toc:not(.collapsed) .floating-toc-header span { display: inline; } .floating-toc:not(.collapsed) .floating-toc-header { justify-content: space-between; } .back-to-top-btn { bottom: 80px; right: 20px; width: 40px; height: 40px; font-size: 1.2rem; } .scroll-hint { display: block !important; font-size: 0.65rem; } } /* Smooth scroll behavior */ html { scroll-behavior: smooth; } /* Better card transitions */ .card .card-content { transition: display 0.2s ease; } /* RTL support for languages like Arabic/Hebrew */ [dir="rtl"] .floating-toc { right: auto; left: 20px; } [dir="rtl"] .back-to-top-btn { right: auto; left: 30px; } [dir="rtl"] .collapse-section-btn { margin-right: 0; margin-left: 8px; } `; document.head.appendChild(style); } // --- Initialize everything when DOM is ready --- function init() { addCustomStyles(); createFloatingTOC(); addCollapsibleButtons(); createBackToTopButton(); improveTables(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })(); </script></div> <!-- Footer --> <footer> <div class="footer-container"> <div class="footer-content"> <div class="footer-section"> <h3>About serpuls.com</h3> <div class="footer-info"> <p>Професионален инструмент за анализ на търсачки, мониторинг на позиции и SEO оптимизация.</p> <p style="margin-top: 1rem;"><strong>Skynet360 LP.</strong></p> <p><strong>Фирмен номер:</strong> SL027564</p> <p><strong>Адрес:</strong> Suite 1 4 Queen Street, Edinburgh, Scotland, EH2 1JE</p> </div> </div> <div class="footer-section"> <h3>Бързи връзки</h3> <ul class="footer-links"> <li><a href="/bg/">Начало</a></li> <li><a href="/bg/profile/">Профил</a></li> <li><a href="/bg/tasks/">Задачи за наблюдение</a></li> <li><a href="/bg/api_docs/">API документация</a></li> <li><a href="/bg/team/">Нашият екип</a></li> </ul> </div> <div class="footer-section"> <h3>Ресурси</h3> <ul class="footer-links"> <li><a href="/bg/blogs/">Блог</a></li> <li><a href="/bg/faq/">ЧЗВ</a></li> <li><a href="/bg/support/">поддръжка</a></li> </ul> </div> <div class="footer-section"> <h3>Контакти</h3> <div class="footer-info"> <p>Имейл: <a href="mailto:info@serpuls.com">info@serpuls.com</a></p> <p>Телеграма: <a href="https://t.me/serpulse_support">@serpulse_support</a></p> <div class="social-links"> <a href="#" class="social-link" title="Телеграма">📱</a> <a href="https://github.com/SkyNetJose/serpuls" class="social-link" title="GitHub">💻</a> <a href="#" class="social-link" title="Twitter">🐦</a> </div> </div> </div> </div> <div class="footer-bottom"> <p class="copyright">© 2026 serpuls.com. Всички права запазени. Skynet360 LP.</p> <div class="footer-bottom-links"> <a href="/bg/privacy/">Политика за поверителност</a> <a href="/bg/terms/">Условия за ползване</a> <a href="/bg/agreement/">Споразумение с потребителя</a> <a href="/bg/cookie/">Политика за бисквитки</a> </div> </div> </div> </footer> <!-- Updated Payment Modal HTML --> <div class="modal" id="paymentModal"> <div class="modal-content"> <span class="close-btn">×</span> <div class="modal-success" id="paymentSuccess"> <div class="success-icon">✓</div> <h3>Успешно плащане!</h3> <p>Благодарим ви за вашата поръчка. Ние ще се свържем с вас скоро.</p> <button class="cta-button" onclick="closeModal('paymentModal')">затвори</button> </div> <form id="paymentForm" onsubmit="return processPayment(event)"> <h2 class="modal-title" id="modalTitle">Плащане за услуга</h2> <p class="modal-text" id="modalText">Описание на услугата</p> <!-- Hidden fields for payment type and ID --> <input type="hidden" id="paymentType" name="payment_type" value=""> <input type="hidden" id="paymentItemId" name="payment_item_id" value=""> <!-- Payment Options --> <div class="payment-options" id="paymentOptions"> <!-- Options will be dynamically generated here --> </div> <!-- Manual Price Input (Optional) - Now dynamically shown based on config --> <div class="form-group" id="manualPriceGroup" style="display: none;"> <label for="manualPrice">Въведете сума (USD):</label> <input type="number" id="manualPrice" class="form-control" placeholder="Въведете сума" min="1" max="1000" step="0.01" oninput="updateManualPrice(this.value)"> <div class="error-message" id="manualPriceError"></div> </div> <!-- Custom Price Option Toggle - Now dynamically shown --> <div class="form-group" id="customPriceToggle" style="display: none; margin-bottom: 1rem;"> <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;"> <input type="radio" id="customPriceRadio" name="paymentOption" value="custom" onchange="toggleCustomPriceSelection(this.checked)"> Въведете персонализирана сума </label> </div> <div class="form-group"> <label for="modalName">Име:</label> <input type="text" id="modalName" class="form-control" placeholder="Въведете вашето име" required> <div class="error-message" id="modalNameError"></div> </div> <div class="form-group"> <label for="modalEmail">Имейл:</label> <input type="email" id="modalEmail" class="form-control" placeholder="Въведете своя имейл" required> <div class="error-message" id="modalEmailError"></div> </div> <p class="modal-price" id="modalPrice">Цена: $0.00</p> <button type="submit" class="cta-button" id="payButton">Плащане $0.00</button> </form> </div> </div> <!-- Redirect Modal (for iframe) --> <div class="modal" id="redirectModal"> <div class="modal-content modal-large"> <span class="close-btn">×</span> <h2 class="modal-title">Плащане</h2> <iframe id="redirectIframe" class="modal-iframe" src=""></iframe> </div> </div> <!-- Auth Modal --> <div class="modal" id="authModal"> <div class="modal-content"> <span class="close-btn">×</span> <div class="auth-tabs"> <div class="auth-tab active" data-tab="login">Вход</div> <div class="auth-tab" data-tab="register">Регистрирайте се</div> </div> <div class="auth-form active" id="loginForm"> <h2 class="modal-title">Влезте в акаунта си</h2> <div class="form-group"> <label for="loginEmail">Имейл:</label> <input type="email" id="loginEmail" class="form-control" autocomplete="email" placeholder="Въведете своя имейл"> <div class="error-message" id="loginEmailError"></div> </div> <div class="form-group"> <label for="loginPassword">Парола:</label> <input type="password" id="loginPassword" class="form-control" autocomplete="current-password" placeholder="Въведете вашата парола"> <div class="error-message" id="loginPasswordError"></div> </div> <button class="cta-button" style="width:100%" onclick="processLogin()">влезте</button> <div style="text-align:center;margin:1rem 0;color:var(--gray);border-top:1px solid #e9ecef;padding-top:1rem;">Или продължете с</div> <button class="cta-button secondary" style="width:100%;margin-bottom:.6rem" onclick="startOAuth('google')">Google</button> <button class="cta-button secondary" style="width:100%;margin-bottom:.6rem" onclick="startOAuth('github')">GitHub</button> <button class="cta-button" style="background:#0088cc;width:100%" onclick="loginWithTelegram()">📱 Влезте с Telegram</button> <p style="margin-top:.5rem;color:var(--gray);font-size:.9rem">За влизане в Telegram първо въведете имейла на акаунта в Serpulse, свързан с Telegram по-горе.</p> </div> <div class="auth-form" id="registerForm"> <h2 class="modal-title">Създаване на акаунт</h2> <div class="form-group"><label for="registerName">Име:</label><input type="text" id="registerName" class="form-control" autocomplete="name" required><div class="error-message" id="registerNameError"></div></div> <div class="form-group"><label for="registerEmail">Имейл:</label><input type="email" id="registerEmail" class="form-control" autocomplete="email" required><div class="error-message" id="registerEmailError"></div></div> <div class="form-group"><label for="registerPassword">Парола:</label><input type="password" id="registerPassword" class="form-control" autocomplete="new-password" minlength="8" required><div class="error-message" id="registerPasswordError"></div></div> <div class="form-group"><label for="registerConfirmPassword">Потвърдете паролата:</label><input type="password" id="registerConfirmPassword" class="form-control" autocomplete="new-password" minlength="8" required><div class="error-message" id="registerConfirmPasswordError"></div></div> <button class="cta-button" style="width:100%" onclick="processRegistration()">Създаване на акаунт</button> <div style="text-align:center;margin:1rem 0;color:var(--gray);border-top:1px solid #e9ecef;padding-top:1rem;">Или създайте акаунт с</div> <button class="cta-button secondary" style="width:100%;margin-bottom:.6rem" onclick="startOAuth('google')">Google</button> <button class="cta-button secondary" style="width:100%;margin-bottom:.6rem" onclick="startOAuth('github')">GitHub</button> <button class="cta-button" style="background:#0088cc;width:100%" onclick="startTelegramRegistration()">📱 Регистрирайте се в Telegram</button> <p style="margin-top:1rem;color:var(--gray);font-size:.9rem">Изисква се потвърждение на имейл за парола и регистрация в Telegram. Регистрацията в Google и GitHub използва потвърдения имейл, върнат от доставчика.</p> </div> </div> </div> <!-- Telegram Code Input Modal --> <div class="modal" id="telegramCodeModal"> <div class="modal-content"> <span class="close-btn" onclick="closeModal('telegramCodeModal')">×</span> <div class="centered-content"> <div class="success-icon">🔐</div> <h2 class="modal-title">Код за потвърждение</h2> <p class="modal-text">Кодът беше изпратен на Telegram. Въведете го по-долу:</p> <div class="form-group"> <input type="text" id="telegramLoginCode" class="form-control verify-code-input" placeholder="0000000" maxlength="7" style="font-size: 1.5rem; letter-spacing: 0.5rem;"> <div class="error-message" id="telegramLoginCodeError"></div> </div> <button class="cta-button" style="width: 100%; margin-top: 1rem;" onclick="verifyTelegramLoginCode()"> Потвърдете влизането </button> <div class="timer-container" id="loginCodeTimer"> Кодът е валиден за: <span id="loginTimer">05:00</span> </div> </div> </div> </div> <!-- Updated Telegram Verification Modal --> <div class="modal" id="telegramVerifyModal"> <div class="modal-content"> <span class="close-btn" onclick="closeTelegramVerifyModal()">×</span> <div class="centered-content"> <div class="success-icon">📱</div> <h2 class="modal-title">Пълна регистрация в Telegram</h2> <p class="modal-text" id="telegramModalText"> Telegram беше отворен в нов раздел.<br> Ако ботът не се отвори автоматично, използвайте бутона по-долу или сканирайте QR кода. </p> <!-- QR Code Container --> <div id="qrCodeContainer" style="margin: 1.5rem 0; text-align: center;"> <div id="qrcode" style="display: inline-block;"></div> <p style="font-size: 0.9rem; color: var(--gray); margin-top: 0.5rem;"> Сканирайте QR кода, за да отворите бота </p> </div> <!-- Timer and Status --> <div id="telegramTimer" class="timer-container"> Очаква се потвърждение... <span id="timer">05:00</span> </div> <!-- Action Buttons --> <div style="display: flex; gap: 1rem; margin-top: 1.5rem; flex-wrap: wrap; justify-content: center;"> <button class="cta-button" style="background: #0088cc;" onclick="openTelegramBot()" id="openTelegramBtn"> 📱 Отворете отново Telegram </button> <button class="cta-button secondary" onclick="refreshTelegramQR()" id="refreshQRBtn"> 🔄 Опресняване на QR кода </button> </div> <!-- Status Info --> <div class="info-box" style="margin-top: 1.5rem;"> <p style="margin: 0; font-size: 0.9rem;"> <strong>Как работи:</strong><br> 1. Щракнете върху Open Telegram или сканирайте QR кода<br> 2. В бота щракнете върху СТАРТ и споделете телефонния си номер<br> 3. Статусът на потвърждение ще се актуализира автоматично </p> </div> </div> </div> </div> <!-- Email Verification Modal --> <div class="modal" id="emailVerifyModal"> <div class="modal-content"> <span class="close-btn" onclick="closeEmailVerifyModal()">×</span> <div class="centered-content"> <div class="success-icon">📧</div> <h2 class="modal-title">Потвърдете своя имейл</h2> <p class="modal-text" id="emailVerifyMessage">Проверете входящата си поща и щракнете върху връзката за потвърждение, която ви изпратихме. Не можете да влезете с парола, докато имейлът ви не бъде потвърден.</p> <p id="emailVerifyAddress" style="font-weight:600;word-break:break-all"></p> <button id="resendEmailBtn" class="cta-button secondary" style="width:100%;margin-top:1rem" onclick="resendEmailVerification()">Повторно изпращане на имейл за потвърждение</button> <button class="cta-button" style="width:100%;margin-top:.75rem" onclick="closeEmailVerifyModal()">затвори</button> </div> </div> </div> <!-- Image reusable Modal --> <div class="modal" id="imgModal"> <div class="modal-content"> <span class="close-btn">×</span> <img id="modalImg" src="" style="max-width:90%; max-height:90%; margin-top:40px; border-radius:8px;"> </div> </div> <!--Loading overlay --> <div class="loading-overlay" id="loadingOverlay"> <div class="loading-spinner"></div> </div> <div class="toast" id="toast"></div> <script> // Get language from URL path like /en/about or /ru/products function getLangFromUrl() { const path = window.location.pathname; const match = path.match(/^\/([a-z]{2})(\/|$)/i); return match ? match[1] : 'en'; // default to 'en' if no language code found } // Store in global variable window.currentLang = getLangFromUrl(); // Show loading overlay function showLoading() { document.getElementById('loadingOverlay').classList.add('active'); } // Hide loading overlay function hideLoading() { document.getElementById('loadingOverlay').classList.remove('active'); } // Show toast notification function showToast(message, type = 'info') { const toast = document.getElementById('toast'); toast.textContent = message; toast.className = 'toast ' + type; toast.classList.add('show'); setTimeout(() => { toast.classList.remove('show'); }, 3000); } // Check user login status on page load document.addEventListener('DOMContentLoaded', function() { checkUserLoginStatus(); }); // Function to check if user is logged in function checkUserLoginStatus() { const userInfoElement = document.getElementById('userInfo'); const guestButtonsElement = document.getElementById('guestButtons'); const userNameElement = document.getElementById('userName'); // Show loading state or keep defaults while checking if (guestButtonsElement) { guestButtonsElement.style.display = 'flex'; } if (userInfoElement) { userInfoElement.style.display = 'none'; } const data = { type: 'check_user_logined' }; sendRequest('/ajax/', data, function(response) { if (response.status === 'ok') { if (response.user_logined === 1) { // User is logged in if (userInfoElement) { userInfoElement.style.display = 'block'; } if (guestButtonsElement) { guestButtonsElement.style.display = 'none'; } if (userNameElement) { // Use logined_user_name if available, otherwise show default userNameElement.textContent = response.logined_user_name || 'Профил'; } // Optionally store minimal info in localStorage for quick access // but don't rely on it for auth state if (response.logined_user_id) { localStorage.setItem('user_id', response.logined_user_id); } if (response.logined_user_name) { localStorage.setItem('user_name', response.logined_user_name); } } else { // User is not logged in if (userInfoElement) { userInfoElement.style.display = 'none'; } if (guestButtonsElement) { guestButtonsElement.style.display = 'flex'; } // Clear any previously stored user data localStorage.removeItem('user_id'); localStorage.removeItem('user_name'); localStorage.removeItem('user_info'); } } else { console.error('Грешка при проверка на състоянието на влизане:', response.error); // On error, default to guest view if (userInfoElement) { userInfoElement.style.display = 'none'; } if (guestButtonsElement) { guestButtonsElement.style.display = 'flex'; } } }, false); // Don't show loading overlay for this quick check } // Logout function function logout() { // Send logout request to server const data = { type: 'logout' }; sendRequest('/ajax/', data, function(response) { // Clear all user data from localStorage regardless of response localStorage.removeItem('user_info'); localStorage.removeItem('user_id'); localStorage.removeItem('user_name'); sessionStorage.removeItem('current_user_token'); // Update UI checkUserLoginStatus(); // Redirect to main page window.location.href = '/'; }); } </script> <script> // Mobile menu toggle - Combined approach document.getElementById('hamburger').addEventListener('click', function() { const mobileMenu = document.getElementById('mobileMenu'); mobileMenu.classList.toggle('active'); }); // Close mobile menu when clicking on links or buttons document.querySelectorAll('.nav-links a, .auth-buttons button').forEach(element => { element.addEventListener('click', function() { document.getElementById('mobileMenu').classList.remove('active'); }); }); // Close mobile menu when clicking outside document.addEventListener('click', function(event) { const mobileMenu = document.getElementById('mobileMenu'); const hamburger = document.getElementById('hamburger'); const navContainer = document.querySelector('.nav-container'); if (!navContainer.contains(event.target) && mobileMenu.classList.contains('active')) { mobileMenu.classList.remove('active'); } }); // Simple animation on scroll document.addEventListener('DOMContentLoaded', function() { const cards = document.querySelectorAll('.card, .domain-header, .chart-container, .lemma-card'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, { threshold: 0.0001, // Optional: keep small percentage rootMargin: '350px 0px 350px 0px' // Trigger 100px before element enters }); cards.forEach(card => { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; card.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; observer.observe(card); }); }); // Toggle links visibility function toggleLinks(className, btn, max_count = 5) { const list = document.getElementById(className); const items = list.querySelectorAll('li'); const hiddenItems = list.querySelectorAll('li.hidden'); if (hiddenItems.length > 0) { // Show all list.querySelectorAll('li.hidden').forEach(el => el.classList.remove('hidden')); btn.textContent = 'Скрий се'; } else { // Hide all beyond the first 5 items.forEach((el, index) => { if (index >= max_count) el.classList.add('hidden'); }); btn.textContent = 'Покажи повече'; } } // Toggle raw data visibility function toggleRawData(elementId, btn) { const element = document.getElementById(elementId); if (element.style.maxHeight === '200px') { element.style.maxHeight = 'none'; btn.textContent = 'Скриване на пълните данни'; } else { element.style.maxHeight = '200px'; btn.textContent = 'Показване на пълните данни'; } } </script> <script> // Payment Modal // Global variable to store payment config let paymentConfig = { allowCustomAmount: false, minAmount: 1, maxAmount: 1000 }; // Payment options functionality document.addEventListener('DOMContentLoaded', function() { // Replace the entire payment options event listener with this: document.addEventListener('change', function(e) { if (e.target && e.target.name === 'paymentOption') { // Update selected state for regular options document.querySelectorAll('.payment-option').forEach(opt => { opt.classList.remove('selected'); }); // Handle custom price radio separately if (e.target.id === 'customPriceRadio') { // For custom price, we don't have a .payment-option wrapper // Just update the custom price toggle UI document.querySelectorAll('.payment-option').forEach(opt => { opt.classList.remove('selected'); }); // You might want to add visual feedback for custom price selection const customPriceToggle = document.getElementById('customPriceToggle'); if (customPriceToggle) { customPriceToggle.style.backgroundColor = 'rgba(67, 97, 238, 0.1)'; customPriceToggle.style.borderLeft = '3px solid var(--primary)'; customPriceToggle.style.padding = '0.5rem'; customPriceToggle.style.borderRadius = 'var(--border-radius)'; } } else { // For regular options, highlight the .payment-option container const paymentOption = e.target.closest('.payment-option'); if (paymentOption) { paymentOption.classList.add('selected'); // Reset custom price toggle styling if it was highlighted const customPriceToggle = document.getElementById('customPriceToggle'); if (customPriceToggle) { customPriceToggle.style.backgroundColor = ''; customPriceToggle.style.borderLeft = ''; customPriceToggle.style.padding = ''; } } } // Update price (this should work for all options) updatePaymentPrice(e.target.value); } }); }); // Update the updatePaymentPrice function to handle custom price: function updatePaymentPrice(price) { const modalPrice = document.getElementById('modalPrice'); const payButton = document.getElementById('payButton'); const customPriceRadio = document.getElementById('customPriceRadio'); // Uncheck custom price radio if regular option is selected if (customPriceRadio && customPriceRadio.checked === false) { document.getElementById('manualPriceGroup').style.display = 'none'; // Reset custom price toggle styling const customPriceToggle = document.getElementById('customPriceToggle'); if (customPriceToggle) { customPriceToggle.style.backgroundColor = ''; customPriceToggle.style.borderLeft = ''; customPriceToggle.style.padding = ''; } } modalPrice.textContent = 'Цена: $' + parseFloat(price).toFixed(2); payButton.textContent = 'Плащане $' + parseFloat(price).toFixed(2); payButton.disabled = false; payButton.style.opacity = '1'; } // Generate payment options based on config function generatePaymentOptions(optionsData, paymentType = 'balance', paymentItemId = '', config = {}) { const paymentOptionsContainer = document.getElementById('paymentOptions'); const paymentTypeInput = document.getElementById('paymentType'); const paymentItemIdInput = document.getElementById('paymentItemId'); const customPriceToggle = document.getElementById('customPriceToggle'); const manualPriceGroup = document.getElementById('manualPriceGroup'); // Set hidden fields paymentTypeInput.value = paymentType; paymentItemIdInput.value = paymentItemId; // Store config globally paymentConfig = { allowCustomAmount: config.allowCustomAmount || false, minAmount: config.minAmount || 1, maxAmount: config.maxAmount || 1000 }; // Clear container paymentOptionsContainer.innerHTML = ''; // Generate regular options if (optionsData && optionsData.length > 0) { optionsData.forEach((option, index) => { const optionElement = document.createElement('div'); optionElement.className = 'payment-option'; optionElement.innerHTML = ` <input type="radio" id="option${index}" name="paymentOption" value="${option.price}" data-label="${option.label}" ${option.default ? 'checked' : ''} onchange="updatePaymentPrice('${option.price}')"> <label for="option${index}">${option.label} - $${option.price}</label> `; paymentOptionsContainer.appendChild(optionElement); }); paymentOptionsContainer.style.display = 'block'; } else { paymentOptionsContainer.style.display = 'none'; } // Show/hide custom amount option based on config if (paymentConfig.allowCustomAmount) { customPriceToggle.style.display = 'block'; // Update manual price input constraints document.getElementById('manualPrice').min = paymentConfig.minAmount; document.getElementById('manualPrice').max = paymentConfig.maxAmount; document.getElementById('manualPrice').placeholder = 'от $${paymentConfig.minAmount} към $${paymentConfig.maxAmount}'; } else { customPriceToggle.style.display = 'none'; manualPriceGroup.style.display = 'none'; } // Select first option by default if none is marked as default if (optionsData && optionsData.length > 0) { const defaultOption = paymentOptionsContainer.querySelector('input[type="radio"][checked]') || paymentOptionsContainer.querySelector('input[type="radio"]'); if (defaultOption) { defaultOption.checked = true; defaultOption.closest('.payment-option').classList.add('selected'); updatePaymentPrice(defaultOption.value); } } else if (paymentConfig.allowCustomAmount) { // If no options but custom amount is allowed, show custom by default document.getElementById('customPriceRadio').checked = true; toggleCustomPriceSelection(true); } } // Also update the toggleCustomPriceSelection function: function toggleCustomPriceSelection(isSelected) { const manualPriceGroup = document.getElementById('manualPriceGroup'); const paymentOptions = document.getElementById('paymentOptions'); const customPriceRadio = document.getElementById('customPriceRadio'); if (isSelected) { manualPriceGroup.style.display = 'block'; // Deselect all regular options document.querySelectorAll('input[name="paymentOption"]').forEach(radio => { if (radio.id !== 'customPriceRadio') { radio.checked = false; const paymentOption = radio.closest('.payment-option'); if (paymentOption) { paymentOption.classList.remove('selected'); } } }); // Visual feedback for custom price selection const customPriceToggle = document.getElementById('customPriceToggle'); if (customPriceToggle) { customPriceToggle.style.backgroundColor = 'rgba(67, 97, 238, 0.1)'; customPriceToggle.style.borderLeft = '3px solid var(--primary)'; customPriceToggle.style.padding = '0.5rem'; customPriceToggle.style.borderRadius = 'var(--border-radius)'; customPriceToggle.style.transition = 'var(--transition)'; } // Focus on manual price input setTimeout(() => { const manualInput = document.getElementById('manualPrice'); manualInput.focus(); // Update price if there's already a value if (manualInput.value) { updateManualPrice(manualInput.value); } else { // Set default to min amount updateManualPrice(paymentConfig.minAmount); manualInput.value = paymentConfig.minAmount; } }, 100); } else { manualPriceGroup.style.display = 'none'; // Reset custom price toggle styling const customPriceToggle = document.getElementById('customPriceToggle'); if (customPriceToggle) { customPriceToggle.style.backgroundColor = ''; customPriceToggle.style.borderLeft = ''; customPriceToggle.style.padding = ''; } } } // Update price from manual input in real-time function updateManualPrice(price) { if (!price || price < paymentConfig.minAmount || price > paymentConfig.maxAmount) { const modalPrice = document.getElementById('modalPrice'); const payButton = document.getElementById('payButton'); if (price && (price < paymentConfig.minAmount || price > paymentConfig.maxAmount)) { modalPrice.textContent = 'Цена: от $${paymentConfig.minAmount} към $${paymentConfig.maxAmount}'; payButton.textContent = 'Невалидна сума'; payButton.disabled = true; payButton.style.opacity = '0.6'; } else { modalPrice.textContent = 'Цена: $0.00'; payButton.textContent = 'Плащане $0.00'; payButton.disabled = true; payButton.style.opacity = '0.6'; } return; } const modalPrice = document.getElementById('modalPrice'); const payButton = document.getElementById('payButton'); modalPrice.textContent = 'Цена: $' + parseFloat(price).toFixed(2); payButton.textContent = 'Плащане $' + parseFloat(price).toFixed(2); payButton.disabled = false; payButton.style.opacity = '1'; } const paymentModal = document.getElementById('paymentModal'); const paymentCloseBtn = paymentModal.querySelector('.close-btn'); // Elements inside modal const modalTitle = document.getElementById('modalTitle'); const modalText = document.getElementById('modalText'); const modalPrice = document.getElementById('modalPrice'); const payButton = document.getElementById('payButton'); const paymentForm = document.getElementById('paymentForm'); const paymentSuccess = document.getElementById('paymentSuccess'); // Updated modal opening handler document.querySelectorAll('[data-modal="paymentModal"]').forEach(trigger => { trigger.addEventListener('click', function(e) { e.preventDefault(); // Get data attributes const title = this.getAttribute('data-title'); const text = this.getAttribute('data-text'); const price = this.getAttribute('data-price'); const paymentType = this.getAttribute('data-payment-type') || 'balance'; const paymentItemId = this.getAttribute('data-payment-item-id') || ''; // Get payment config from data attributes const allowCustomAmount = this.getAttribute('data-allow-custom') === 'true'; const minAmount = parseFloat(this.getAttribute('data-min-amount')) || 1; const maxAmount = parseFloat(this.getAttribute('data-max-amount')) || 1000; // Get payment options from data attribute const optionsData = this.getAttribute('data-options'); let paymentOptions = []; if (optionsData) { try { paymentOptions = JSON.parse(optionsData); } catch (e) { console.error('Грешка при анализа на опциите за плащане:', e); } } // Fill modal content document.getElementById('modalTitle').textContent = title; document.getElementById('modalText').textContent = text; // Generate payment options with config const config = { allowCustomAmount: allowCustomAmount, minAmount: minAmount, maxAmount: maxAmount }; generatePaymentOptions(paymentOptions, paymentType, paymentItemId, config); // If no options and custom amount not allowed, use default price if (paymentOptions.length === 0 && !allowCustomAmount) { updatePaymentPrice(price); } // Reset form resetForm('paymentModal'); document.getElementById('paymentForm').style.display = 'block'; document.getElementById('paymentSuccess').style.display = 'none'; // Show modal document.getElementById('paymentModal').style.display = 'flex'; }); }); // Helper function to reset form function resetPaymentForm() { document.getElementById('modalName').value = ''; document.getElementById('modalEmail').value = ''; document.getElementById('manualPrice').value = ''; if (document.getElementById('customPriceRadio')) { document.getElementById('customPriceRadio').checked = false; } clearErrors(['nameError', 'emailError', 'manualPriceError']); } // Close payment modal paymentCloseBtn.addEventListener('click', function() { paymentModal.style.display = 'none'; }); // Close on outside click window.addEventListener('click', function(e) { if (e.target === paymentModal) { paymentModal.style.display = 'none'; } }); // Fill tooltip with alt text automatically document.querySelectorAll('.popup-trigger').forEach(trigger => { const altText = trigger.getAttribute('alt'); const popupText = trigger.querySelector('.popup-text'); if (popupText && altText) { popupText.textContent = altText; } }); </script> <script> // Fill tooltip with alt text automatically document.querySelectorAll('.popup-trigger').forEach(trigger => { const altText = trigger.getAttribute('alt'); const popupText = trigger.querySelector('.popup-text'); if (popupText && altText) { popupText.textContent = altText; } }); // Auth Modal const authModal = document.getElementById('authModal'); const authCloseBtn = authModal.querySelector('.close-btn'); const loginBtn = document.getElementById('loginBtn'); const registerBtn = document.getElementById('registerBtn'); const authTabs = document.querySelectorAll('.auth-tab'); const authForms = document.querySelectorAll('.auth-form'); // Open auth modal loginBtn.addEventListener('click', function() { authModal.style.display = 'flex'; switchAuthTab('login'); resetForm('authModal'); }); registerBtn.addEventListener('click', function() { authModal.style.display = 'flex'; switchAuthTab('register'); resetForm('authModal'); }); // Close auth modal authCloseBtn.addEventListener('click', function() { authModal.style.display = 'none'; }); // Close on outside click window.addEventListener('click', function(e) { if (e.target === authModal) { authModal.style.display = 'none'; } }); // Switch auth tabs function switchAuthTab(tabName) { authTabs.forEach(tab => { if (tab.getAttribute('data-tab') === tabName) { tab.classList.add('active'); } else { tab.classList.remove('active'); } }); authForms.forEach(form => { if (form.id === tabName + 'Form') { form.classList.add('active'); } else { form.classList.remove('active'); } }); } // Add click events to auth tabs authTabs.forEach(tab => { tab.addEventListener('click', function() { const tabName = this.getAttribute('data-tab'); switchAuthTab(tabName); resetForm('authModal'); }); }); // Reset form and errors function resetForm(modalType) { if (modalType === 'paymentModal') { document.getElementById('modalName').value = ''; document.getElementById('modalEmail').value = ''; clearErrors(['nameError', 'emailError']); } else if (modalType === 'authModal') { document.getElementById('loginEmail').value = ''; document.getElementById('loginPassword').value = ''; document.getElementById('registerName').value = ''; document.getElementById('registerEmail').value = ''; if(document.getElementById('registerPassword'))document.getElementById('registerPassword').value = ''; if(document.getElementById('registerConfirmPassword'))document.getElementById('registerConfirmPassword').value = ''; clearErrors(['loginEmailError', 'loginPasswordError', 'registerNameError', 'registerEmailError', 'registerPasswordError', 'registerConfirmPasswordError']); } } // Clear error messages function clearErrors(errorIds) { errorIds.forEach(id => { const errorElement = document.getElementById(id); if (errorElement) { errorElement.textContent = ''; errorElement.classList.remove('show'); const inputElement = document.getElementById(id.replace('Error', '')); if (inputElement) { inputElement.classList.remove('error'); } } }); } // Show error message function showError(fieldId, message) { const errorElement = document.getElementById(fieldId + 'Error'); const inputElement = document.getElementById(fieldId); if (errorElement) { errorElement.textContent = message; errorElement.classList.add('show'); } if (inputElement) { inputElement.classList.add('error'); } } // Enhanced close modal function for general use function closeModal(modalId) { // Clear timers based on which modal is being closed switch(modalId) { case 'telegramVerifyModal': clearTelegramTimers(); break; case 'authModal': // Clear any potential timers in auth modal break; } document.getElementById(modalId).style.display = 'none'; } // Enhanced processPayment function function processPayment(event) { if (event) event.preventDefault(); const name = document.getElementById('modalName').value; const email = document.getElementById('modalEmail').value; const paymentType = document.getElementById('paymentType').value; const paymentItemId = document.getElementById('paymentItemId').value; // Get price - check if using custom amount or selected option let price; let optionLabel = ''; const customPriceRadio = document.getElementById('customPriceRadio'); if (customPriceRadio && customPriceRadio.checked) { // Custom amount price = document.getElementById('manualPrice').value; optionLabel = 'custom_amount'; // Validate custom amount if (!price || price < paymentConfig.minAmount || price > paymentConfig.maxAmount) { showError('manualPrice', 'Сумата трябва да е между $${paymentConfig.minAmount} и $${paymentConfig.maxAmount}'); return false; } } else { // Regular option const selectedOption = document.querySelector('input[name="paymentOption"]:checked'); if (selectedOption) { price = selectedOption.value; optionLabel = selectedOption.getAttribute('data-label') || 'selected_option'; } else { // Fallback to default price price = document.getElementById('modalPrice').textContent.match(/\d+\.\d+/)?.[0] || '0'; optionLabel = 'default'; } } const service = document.getElementById('modalTitle').textContent; // Clear previous errors clearErrors(['nameError', 'emailError', 'manualPriceError']); // Validate form let hasErrors = false; if (!name) { showError('modalName', 'Моля, въведете вашето име'); hasErrors = true; } if (!email) { showError('modalEmail', 'Моля, въведете своя имейл адрес'); hasErrors = true; } else if (!/\S+@\S+\.\S+/.test(email)) { showError('modalEmail', 'Моля, въведете валиден имейл адрес'); hasErrors = true; } if (hasErrors) return false; // Prepare data for API request const data = { type: 'payment', name: name, email: email, price: parseFloat(price).toFixed(2), service: service, payment_type: paymentType, payment_item_id: paymentItemId, option: optionLabel, is_custom_amount: (customPriceRadio && customPriceRadio.checked) ? 1 : 0 }; // Send request to API sendRequest('/ajax/', data, function(response) { if (response.status === 'ok') { if (response.redirect_url) { handlePaymentRedirect(response.redirect_url, response.redirect_type); } else { // Show success message document.getElementById('paymentForm').style.display = 'none'; document.getElementById('paymentSuccess').style.display = 'block'; } } else { // Show error message showToast('Грешка: ' + response.error, 'error'); } }); return false; } // Handle payment redirect function handlePaymentRedirect(redirectUrl, redirectType) { switch (redirectType) { case 'new_window': window.open(redirectUrl, '_blank'); closeModal('paymentModal'); break; case 'iframe': document.getElementById('redirectIframe').src = redirectUrl; closeModal('paymentModal'); document.getElementById('redirectModal').style.display = 'flex'; break; case 'default': default: window.location.href = redirectUrl; break; } } const serpulseCsrfToken = "6e0437942a02896e4469a7dc392e9dd246171de87022e3da84a340cf9b7754f2"; // Send AJAX request function sendRequest(url, data, callback,need_loading_overlay=true) { // Show loading overlay when request starts if(need_loading_overlay)showLoading(); //console.log("XMLHttpRequest started"); const xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('X-CSRF-Token', serpulseCsrfToken); xhr.onreadystatechange = function() { //console.log("XMLHttpRequest onready fired, readyState:", xhr.readyState); if (xhr.readyState === 4) { // Hide loading overlay when request completes if(need_loading_overlay)hideLoading(); //console.log("Request complete, status:", xhr.status); //console.log("responseText:", xhr.responseText); if (xhr.status === 200) { let is_good = true; let response; // Declare response variable outside try block try { response = JSON.parse(xhr.responseText); } catch (e) { is_good=false; console.error("JSON parse error:", e); callback({status: 'error', error: 'Невалиден отговор от сървъра'}); } if(is_good) callback(response); } else { callback({status: 'error', error: 'Грешка в сървъра: ' + xhr.status}); } } }; xhr.send(JSON.stringify(data)); } // Close redirect modal document.getElementById('redirectModal').querySelector('.close-btn').addEventListener('click', function() { document.getElementById('redirectModal').style.display = 'none'; }); // Mock chart data (in a real implementation, you would use Google Charts) function initCharts() { // This is a placeholder for actual chart implementation //console.log('Charts would be initialized here with Google Charts API'); } // Image modal functionality document.addEventListener('DOMContentLoaded', function() { // Image modal const imgModal = document.getElementById('imgModal'); const modalImg = document.getElementById('modalImg'); const imgCloseBtn = imgModal.querySelector('.close-btn'); // Open image modal document.querySelectorAll('[data-modal="imgModal"]').forEach(img => { img.addEventListener('click', function() { modalImg.src = this.src; imgModal.style.display = 'flex'; }); }); // Close image modal imgCloseBtn.addEventListener('click', function() { imgModal.style.display = 'none'; }); // Close on outside click window.addEventListener('click', function(e) { if (e.target === imgModal) { imgModal.style.display = 'none'; } }); }); </script> <script> //console.log("herei"); // Analysis functionality document.addEventListener('DOMContentLoaded', function() { //console.log("DOMContentLoaded"); const analyzeBtn = document.getElementById('analyzeNowBtn'); const analysisStatus = document.getElementById('analysisStatus'); if (analyzeBtn) { analyzeBtn.addEventListener('click', function() { //console.log("click"); // Show captcha modal showCaptchaModal(); }); } }); function showCaptchaModal() { //console.log("showCaptchaModal"); // Create modal for captcha const modal = document.createElement('div'); modal.className = 'modal'; modal.id = 'captchaModal'; modal.style.display = 'flex'; modal.innerHTML = ` <div class="modal-content"> <span class="close-btn" onclick="closeCaptchaModal()">×</span> <h2 class="modal-title">Потвърдете, че не сте робот</h2> <p class="modal-text">Въведете текста от картинката:</p> <div class="captcha-container"> <img src="/captcha.php?t=` + new Date().getTime() + `" alt="CAPTCHA" class="captcha-image" id="captchaImage"> <button type="button" class="refresh-captcha" onclick="refreshCaptcha()">Опресняване на кода</button> <input type="text" class="captcha-input" id="captchaInput" placeholder="Въведете код" maxlength="6"> </div> <button class="cta-button" onclick="submitAnalysis()" style="margin-top: 1rem;">Изпратете за анализ</button> </div> `; document.body.appendChild(modal); } function closeCaptchaModal() { const modal = document.getElementById('captchaModal'); if (modal) { modal.remove(); } } function refreshCaptcha() { const captchaImage = document.getElementById('captchaImage'); if (captchaImage) { // Add timestamp to prevent caching captchaImage.src = '/captcha.php?t=' + new Date().getTime(); } } function submitAnalysis() { const captchaInput = document.getElementById('captchaInput'); const captchaCode = captchaInput ? captchaInput.value.trim() : ''; const analysisStatus = document.getElementById('analysisStatus'); if (!captchaCode) { showToast('Моля, въведете кода от изображението', 'error'); return; } // Show loading state analysisStatus.textContent = 'Заявката се изпраща...'; analysisStatus.className = 'analysis-status'; analysisStatus.style.display = 'block'; // Prepare data for API request type = "none"; if(document.getElementById('domain_name')){ type = "domain_analysis"; sendData = document.getElementById('domain_name').value; } if(document.getElementById('phrase')){ type = "phrase_analysis"; sendData = document.getElementById('phrase').value; } if(type=="none"){ // Show error message closeCaptchaModal(); analysisStatus.textContent = 'Грешка: типът е никакъв'; analysisStatus.className = 'analysis-status error'; analysisStatus.style.display = 'block'; return false; } const data = { type: type, data: sendData, captcha_code: captchaCode }; // Send request to API sendRequest('/ajax/', data, function(response) { // Close captcha modal closeCaptchaModal(); if (response.status === 'ok') { // Show success message analysisStatus.textContent = 'Вашата заявка е изпратена, моля, изчакайте проверката на домейна'; analysisStatus.className = 'analysis-status success'; } else { // Show error message analysisStatus.textContent = 'Грешка: ' + response.error; analysisStatus.className = 'analysis-status error'; // Refresh captcha on error if (response.refresh_captcha) { setTimeout(refreshCaptcha, 500); } } analysisStatus.style.display = 'block'; // Clear captcha input if (captchaInput) { captchaInput.value = ''; } }); } </script> <script> // Full screenshot functionality function openFullScreenshot(src) { const modal = document.getElementById('fullScreenshotModal'); const img = document.getElementById('fullScreenshotImg'); img.src = src; modal.style.display = 'flex'; // Prevent body scroll when modal is open document.body.style.overflow = 'hidden'; } function closeFullScreenshot() { const modal = document.getElementById('fullScreenshotModal'); modal.style.display = 'none'; // Restore body scroll document.body.style.overflow = 'auto'; } function downloadScreenshot() { const img = document.getElementById('fullScreenshotImg'); const link = document.createElement('a'); link.download = `screenshot-${document.getElementById('domain_name').value}-${new Date().getTime()}.jpg`; link.href = img.src; link.click(); } // Close modal on outside click document.addEventListener('click', function(e) { const modal = document.getElementById('fullScreenshotModal'); if (e.target === modal) { closeFullScreenshot(); } }); // Close modal on Escape key document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeFullScreenshot(); } }); </script> <script> // Unified authentication controller. All user-visible source strings are English and translated server-side. let telegramStatusInterval = null; let telegramTimerInterval = null; let qrcodeInstance = null; window.telegramBotUrl = null; function authText(key) { const messages = { registrationVerification: 'Акаунтът е създаден. Проверете имейла си и щракнете върху връзката за потвърждение, преди да влезете.', loginSuccess: 'Влезте успешно.', emailVerified: 'Имейл потвърден. Вашият акаунт е активен и сте влезли.', emailTelegramPending: 'Имейл потвърден. Завършете проверката на Telegram, за да активирате акаунта си.', emailTelegramReady: 'Email verified. Continue with Telegram to connect your Telegram account.', emailVerificationFailed: 'Тази връзка за потвърждение на имейл е невалидна или изтекла.', googleSuccess: 'Влезли сте с Google.', githubSuccess: 'Влезли сте с GitHub.', oauthCancelled: 'Външното влизане бе отменено.', oauthFailed: 'Неуспешно външно влизане. Моля, опитайте отново.', telegramVerifiedEmailPending: 'Проверката на Telegram е завършена. Проверете имейла си и щракнете върху връзката за потвърждение, за да активирате акаунта си.', telegramRegistrationComplete: 'Регистрацията приключи. Вече сте влезли.', telegramLoginSuccess: 'Влезли сте с Telegram.', invalidResponse: 'Невалиден отговор от сървъра' }; return messages[key] || key; } function startOAuth(provider) { if (!['google', 'github'].includes(provider)) { showToast('Неподдържан доставчик за влизане.', 'error'); return; } window.location.href = '/auth/' + provider + '.php?lang=' + encodeURIComponent(window.currentLang || 'en'); } function showEmailVerificationModal(email, message) { const normalizedEmail = (email || '').trim().toLowerCase(); if (normalizedEmail) sessionStorage.setItem('pending_verification_email', normalizedEmail); const address = document.getElementById('emailVerifyAddress'); const messageEl = document.getElementById('emailVerifyMessage'); if (address) address.textContent = normalizedEmail; if (messageEl) messageEl.textContent = message || 'Проверете входящата си поща и щракнете върху връзката за потвърждение, която ви изпратихме. Не можете да влезете с парола, докато имейлът ви не бъде потвърден.'; const modal = document.getElementById('emailVerifyModal'); if (modal) modal.style.display = 'flex'; } function closeEmailVerifyModal() { const modal = document.getElementById('emailVerifyModal'); if (modal) modal.style.display = 'none'; } function resendEmailVerification() { const email = (sessionStorage.getItem('pending_verification_email') || '').trim().toLowerCase(); if (!email) { showToast('Въведете своя имейл отново, за да изпратите отново съобщението за потвърждение.', 'error'); closeEmailVerifyModal(); return; } const button = document.getElementById('resendEmailBtn'); if (button) button.disabled = true; sendRequest('/ajax/', {type: 'resend_email_verification', email: email}, function(response) { if (button) button.disabled = false; if (response.status === 'ok') showToast(response.message, 'success'); else showToast(response.error || 'Не можахме да изпратим имейла за потвърждение в момента. Моля, опитайте отново по-късно.', 'error'); }); } function processLogin() { const email = document.getElementById('loginEmail').value.trim().toLowerCase(); const password = document.getElementById('loginPassword').value; clearErrors(['loginEmail', 'loginPassword']); let hasErrors = false; if (!email) { showError('loginEmail', 'Моля, въведете своя имейл'); hasErrors = true; } else if (!/\S+@\S+\.\S+/.test(email)) { showError('loginEmail', 'Моля, въведете валиден имейл адрес'); hasErrors = true; } if (!password) { showError('loginPassword', 'Моля, въведете вашата парола'); hasErrors = true; } if (hasErrors) return; sendRequest('/ajax/', {type: 'login', email: email, password: password}, function(response) { if (response.status === 'ok') { closeModal('authModal'); showToast(response.message || authText('loginSuccess'), 'success'); window.setTimeout(function(){ window.location.href = '/' + window.currentLang + '/profile/'; }, 250); return; } if (response.status === 'verification_required') { closeModal('authModal'); showEmailVerificationModal(response.email || email, response.message); showToast(response.message, 'info'); return; } showError('loginPassword', response.error || 'Невалиден имейл или парола.'); }); } function processRegistration() { const name = document.getElementById('registerName').value.trim(); const email = document.getElementById('registerEmail').value.trim().toLowerCase(); const password = document.getElementById('registerPassword').value; const confirmPassword = document.getElementById('registerConfirmPassword').value; clearErrors(['registerName', 'registerEmail', 'registerPassword', 'registerConfirmPassword']); let hasErrors = false; if (!name) { showError('registerName', 'Моля, въведете вашето име'); hasErrors = true; } if (!email) { showError('registerEmail', 'Моля, въведете своя имейл'); hasErrors = true; } else if (!/\S+@\S+\.\S+/.test(email)) { showError('registerEmail', 'Моля, въведете валиден имейл адрес'); hasErrors = true; } if (!password) { showError('registerPassword', 'Моля, създайте парола'); hasErrors = true; } else if (password.length < 8) { showError('registerPassword', 'Паролата трябва да е поне 8 знака'); hasErrors = true; } if (!confirmPassword) { showError('registerConfirmPassword', 'Моля, потвърдете паролата си'); hasErrors = true; } else if (password !== confirmPassword) { showError('registerConfirmPassword', 'Паролите не съвпадат'); hasErrors = true; } if (hasErrors) return; sendRequest('/ajax/', {type: 'registration', name: name, email: email, password: password}, function(response) { if (response.status === 'verification_required') { sessionStorage.setItem('pending_verification_email', response.email || email); closeModal('authModal'); showEmailVerificationModal(response.email || email, response.message || authText('registrationVerification')); showToast(response.message || authText('registrationVerification'), response.email_sent === false ? 'info' : 'success'); return; } if (response.status === 'ok') { showToast(response.message || authText('registrationVerification'), 'success'); return; } showToast(response.error || 'Регистрацията не можа да бъде завършена. Моля, опитайте отново.', 'error'); }); } function clearRegistrationForm() { ['registerName','registerEmail','registerPassword','registerConfirmPassword'].forEach(function(id){ const el=document.getElementById(id); if (el) el.value=''; }); clearErrors(['registerName','registerEmail','registerPassword','registerConfirmPassword']); } function generateQRCode(url) { const container = document.getElementById('qrcode'); if (!container) return; container.innerHTML = ''; if (typeof QRCode === 'undefined') return; qrcodeInstance = new QRCode(container, {text: url, width: 200, height: 200, correctLevel: QRCode.CorrectLevel.M}); } function openTelegramBot() { if (window.telegramBotUrl) window.open(window.telegramBotUrl, '_blank', 'noopener'); } function refreshTelegramQR() { if (window.telegramBotUrl) generateQRCode(window.telegramBotUrl); } function clearTelegramTimers() { if (telegramTimerInterval) { clearInterval(telegramTimerInterval); telegramTimerInterval = null; } if (telegramStatusInterval) { clearInterval(telegramStatusInterval); telegramStatusInterval = null; } } function startTelegramTimer(duration) { clearInterval(telegramTimerInterval); let remaining = Math.max(0, parseInt(duration || 300, 10)); const timer = document.getElementById('timer'); function render() { if (!timer) return; const m = Math.floor(remaining / 60), s = remaining % 60; timer.textContent = String(m).padStart(2,'0') + ':' + String(s).padStart(2,'0'); } render(); telegramTimerInterval = setInterval(function(){ remaining -= 1; render(); if (remaining < 0) { clearTelegramTimers(); showToast('Връзката за потвърждение на Telegram е изтекла. Моля, започнете отново.', 'error'); } }, 1000); } function showTelegramVerifyModal(botUsername, telegramToken, deepLink, expiresRemaining) { window.telegramBotUrl = deepLink || ('https://t.me/' + botUsername + '?start=' + encodeURIComponent(telegramToken)); const title = document.querySelector('#telegramVerifyModal .modal-title'); const text = document.getElementById('telegramModalText'); if (title) title.textContent = 'Пълна регистрация в Telegram'; if (text) text.innerHTML = 'Telegram беше отворен в нов раздел.<br>Share your own phone number with the bot, then return here. Your email has already been verified.'; generateQRCode(window.telegramBotUrl); startTelegramTimer(expiresRemaining || 600); const modal = document.getElementById('telegramVerifyModal'); if (modal) modal.style.display='flex'; window.open(window.telegramBotUrl, '_blank', 'noopener'); } function closeTelegramVerifyModal() { clearTelegramTimers(); const modal = document.getElementById('telegramVerifyModal'); if (modal) modal.style.display='none'; } function startTelegramRegistration() { const nameEl = document.getElementById('registerName'); const emailEl = document.getElementById('registerEmail'); const name = nameEl ? nameEl.value.trim() : ''; const email = emailEl ? emailEl.value.trim().toLowerCase() : ''; clearErrors(['registerName','registerEmail']); let hasErrors=false; if (!name) { showError('registerName','Моля, въведете вашето име'); hasErrors=true; } if (!email) { showError('registerEmail','Моля, въведете своя имейл'); hasErrors=true; } else if (!/\S+@\S+\.\S+/.test(email)) { showError('registerEmail','Моля, въведете валиден имейл адрес'); hasErrors=true; } if (hasErrors) return; sessionStorage.setItem('pending_verification_email', email); sendRequest('/ajax/', {type:'telegram_registration_start',name:name,email:email}, function(response){ if (response.status === 'verification_required') { closeModal('authModal'); showEmailVerificationModal(response.email || email, response.message); showToast(response.message || 'Verify your email before connecting Telegram.', response.email_sent === false ? 'info' : 'success'); return; } if (response.status !== 'ok') { showToast(response.error || 'Регистрацията на Telegram не можа да бъде стартирана.','error'); return; } beginTelegramBinding(response, email); }); } function startTelegramBindingAfterEmail() { sendRequest('/ajax/', {type:'telegram_registration_start'}, function(response){ if (response.status !== 'ok') { showToast(response.error || 'Telegram connection could not be started.','error'); return; } beginTelegramBinding(response, sessionStorage.getItem('pending_verification_email') || ''); }); } function beginTelegramBinding(response, email) { sessionStorage.setItem('current_user_token', response.user_token); closeModal('authModal'); showTelegramVerifyModal(response.bot_username,response.telegram_token,response.deep_link,response.telegram_token_expires_remaining); startTelegramRegistrationStatusCheck(response.user_token, email || ''); } function startTelegramRegistrationStatusCheck(userToken, email) { clearInterval(telegramStatusInterval); telegramStatusInterval=setInterval(function(){ checkTelegramVerificationStatus(userToken,email); }, 4000); checkTelegramVerificationStatus(userToken,email); } function checkTelegramVerificationStatus(userToken, email) { sendRequest('/ajax/', {type:'check_telegram_verification',user_token:userToken}, function(response){ if (response.status !== 'ok') return; if (response.account_verified) { clearTelegramTimers(); completeRegistration(); return; } if (response.telegram_verified && !response.email_verified) { clearTelegramTimers(); closeTelegramVerifyModal(); showEmailVerificationModal(email, authText('telegramVerifiedEmailPending')); showToast(authText('telegramVerifiedEmailPending'),'info'); } }, false); } function completeRegistration() { const token=sessionStorage.getItem('current_user_token') || ''; if (!token) return; sendRequest('/ajax/', {type:'complete_registration',user_token:token}, function(response){ if (response.status !== 'ok') { showToast(response.error || 'Регистрацията не можа да бъде завършена.','error'); return; } sessionStorage.removeItem('current_user_token'); sessionStorage.removeItem('pending_verification_email'); closeTelegramVerifyModal(); closeEmailVerifyModal(); showToast(response.message || authText('telegramRegistrationComplete'),'success'); window.setTimeout(function(){window.location.href='/' + window.currentLang + '/profile/';},250); }); } function loginWithTelegram() { const email=document.getElementById('loginEmail').value.trim().toLowerCase(); clearErrors(['loginEmail']); if (!email) { showError('loginEmail','Моля, въведете своя имейл'); return; } if (!/\S+@\S+\.\S+/.test(email)) { showError('loginEmail','Моля, въведете валиден имейл адрес'); return; } generateTelegramLoginLink(email); } function generateTelegramLoginLink(email) { sendRequest('/ajax/', {type:'generate_telegram_login_link',email:email}, function(response){ if (response.status !== 'ok') { showToast(response.error || 'Не може да се стартира влизане в Telegram','error'); return; } sessionStorage.setItem('current_user_token',response.user_token); closeModal('authModal'); showTelegramLoginModal(response.bot_username,response.deep_link,response.telegram_token_expires_remaining); }); } function showTelegramLoginModal(botUsername,deepLink,expiresRemaining) { window.telegramBotUrl=deepLink; const title=document.querySelector('#telegramVerifyModal .modal-title'); const text=document.getElementById('telegramModalText'); if (title) title.textContent='Влезте чрез Telegram'; if (text) text.innerHTML='Отворете бота на Telegram, за да получите вашия код за вход, след което въведете кода на този уебсайт.'; generateQRCode(window.telegramBotUrl); startTelegramTimer(expiresRemaining || 300); const modal=document.getElementById('telegramVerifyModal'); if (modal) modal.style.display='flex'; window.open(window.telegramBotUrl,'_blank','noopener'); startTelegramLoginStatusCheck(); } function startTelegramLoginStatusCheck() { const token=sessionStorage.getItem('current_user_token') || ''; clearInterval(telegramStatusInterval); telegramStatusInterval=setInterval(function(){checkTelegramLoginStatus(token);},3000); checkTelegramLoginStatus(token); } function checkTelegramLoginStatus(userToken) { if (!userToken) return; sendRequest('/ajax/', {type:'check_telegram_login_status',user_token:userToken}, function(response){ if (response.status==='ok' && response.login_code_sended) { clearTelegramTimers(); closeTelegramVerifyModal(); const codeModal=document.getElementById('telegramCodeModal'); if (codeModal) codeModal.style.display='flex'; startLoginCodeTimer(300); } },false); } function startLoginCodeTimer(duration) { let remaining=parseInt(duration||300,10); const timer=document.getElementById('loginTimer'); if (window.loginCodeTimer) clearInterval(window.loginCodeTimer); window.loginCodeTimer=setInterval(function(){ const m=Math.max(0,Math.floor(remaining/60)), s=Math.max(0,remaining%60); if (timer) timer.textContent=String(m).padStart(2,'0')+':'+String(s).padStart(2,'0'); remaining--; if (remaining<0) { clearInterval(window.loginCodeTimer); showToast('Кодът е изтекъл. Моля, започнете отначало.','error'); } },1000); } function verifyTelegramLoginCode() { const token=sessionStorage.getItem('current_user_token') || ''; const input=document.getElementById('telegramLoginCode'); const code=input ? input.value.trim() : ''; clearErrors(['telegramLoginCode']); if (!/^\\d{7}$/.test(code)) { showError('telegramLoginCode','Кодът трябва да съдържа 7 цифри'); return; } sendRequest('/ajax/', {type:'verify_telegram_login_code',user_token:token,verify_code:code}, function(response){ if (response.status!=='ok') { showError('telegramLoginCode',response.error || 'Лош код или изтекъл'); return; } if (window.loginCodeTimer) clearInterval(window.loginCodeTimer); sessionStorage.removeItem('current_user_token'); closeModal('telegramCodeModal'); showToast(response.message || authText('telegramLoginSuccess'),'success'); window.setTimeout(function(){window.location.href='/' + window.currentLang + '/profile/';},250); }); } // Old registration-status probing is intentionally disabled: it leaked account state and duplicated the new verification flow. function checkRegistrationStatus() { return; } function closeVisibleAuthModal() { for (const id of ['telegramCodeModal','telegramVerifyModal','emailVerifyModal','authModal']) { const modal=document.getElementById(id); if (!modal || modal.style.display !== 'flex') continue; if (id === 'telegramVerifyModal') closeTelegramVerifyModal(); else if (id === 'emailVerifyModal') closeEmailVerifyModal(); else closeModal(id); return; } } document.addEventListener('click', function(event){ const target=event.target; if (!(target instanceof HTMLElement) || !target.classList.contains('modal')) return; const id=target.id; if (!['telegramCodeModal','telegramVerifyModal','emailVerifyModal'].includes(id)) return; if (id === 'telegramVerifyModal') closeTelegramVerifyModal(); else if (id === 'emailVerifyModal') closeEmailVerifyModal(); else closeModal(id); }); document.addEventListener('keydown', function(event){ if (event.key === 'Escape') closeVisibleAuthModal(); }); function handleAuthNotice() { const url=new URL(window.location.href); const notice=url.searchParams.get('auth'); if (!notice) return; const map={ email_verified:[authText('emailVerified'),'success'], email_verified_telegram_pending:[authText('emailTelegramPending'),'info'], email_verified_telegram_ready:[authText('emailTelegramReady'),'success'], email_verification_failed:[authText('emailVerificationFailed'),'error'], google_success:[authText('googleSuccess'),'success'], github_success:[authText('githubSuccess'),'success'], oauth_cancelled:[authText('oauthCancelled'),'info'], oauth_failed:[authText('oauthFailed'),'error'] }; if (map[notice]) showToast(map[notice][0],map[notice][1]); if (notice === 'email_verified_telegram_ready') window.setTimeout(startTelegramBindingAfterEmail, 150); url.searchParams.delete('auth'); window.history.replaceState({},document.title,url.pathname+url.search+url.hash); } document.addEventListener('DOMContentLoaded',handleAuthNotice); </script> <!-- Add this JavaScript before closing body tag --> <script> // Animation on scroll document.addEventListener('DOMContentLoaded', function() { const fadeElements = document.querySelectorAll('.fade-in'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); } }); }, { threshold: 0.1 }); fadeElements.forEach(element => { observer.observe(element); }); // Add click handlers for project CTAs document.querySelectorAll('.project-cta').forEach(button => { button.addEventListener('click', function() { const projectTitle = this.closest('.project-card').querySelector('.project-title').textContent; // In production, this would navigate to the appropriate page }); }); }); </script> <script> // Cookie consent management (function() { // Configuration const COOKIE_CONSENT_KEY = 'serpuls_cookie_consent'; const COOKIE_EXPIRY_DAYS = 365; // Create cookie consent banner if not already accepted function initCookieConsent() { // Check if user has already made a choice const consent = getCookieConsent(); if (consent !== null) { // User already made a choice, apply preferences applyCookiePreferences(consent); return; } // Create banner createCookieBanner(); } // Create the cookie banner HTML function createCookieBanner() { const banner = document.createElement('div'); banner.id = 'cookie-consent-banner'; banner.style.cssText = ` position: fixed; bottom: 0; left: 0; right: 0; background: white; box-shadow: 0 -4px 20px rgba(0,0,0,0.15); padding: 1.5rem; z-index: 9999; border-top: 3px solid var(--primary, #4361ee); animation: slideUp 0.5s ease; `; // Add animation style if not exists if (!document.getElementById('cookie-animation-style')) { const style = document.createElement('style'); style.id = 'cookie-animation-style'; style.textContent = ` @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } } `; document.head.appendChild(style); } banner.innerHTML = ` <div style="max-width: 1200px; margin: 0 auto; display: flex; flex-wrap: wrap; gap: 2rem; align-items: center; justify-content: space-between;"> <div style="flex: 2; min-width: 280px;"> <h3 style="margin: 0 0 0.5rem 0; color: var(--dark, #212529); font-size: 1.25rem;">🍪 Съгласие за бисквитки</h3> <p style="margin: 0; color: var(--gray, #6c757d); line-height: 1.5;"> Използваме бисквитки, за да подобрим вашето сърфиране, да предоставяме персонализирано съдържание и да анализираме нашия трафик. Щраквайки върху Приемам всички, вие се съгласявате с използването на бисквитки. Прочетете нашите <a href="/en/cookie/" style="color: var(--primary, #4361ee); text-decoration: underline;">Политика за бисквитки</a> и <a href="/en/privacy/" style="color: var(--primary, #4361ee); text-decoration: underline;">Политика за поверителност</a>. </p> </div> <div style="display: flex; gap: 1rem; flex-wrap: wrap; align-items: center;"> <button id="cookie-accept-all" style="padding: 0.75rem 1.5rem; background: linear-gradient(135deg, var(--primary, #4361ee), var(--secondary, #7209b7)); color: white; border: none; border-radius: var(--border-radius, 8px); font-weight: 600; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 6px rgba(67,97,238,0.3);"> Приеми всички </button> <button id="cookie-accept-essential" style="padding: 0.75rem 1.5rem; background: white; color: var(--dark, #212529); border: 1px solid var(--gray, #6c757d); border-radius: var(--border-radius, 8px); font-weight: 600; cursor: pointer; transition: all 0.3s ease;"> Само от съществено значение </button> <button id="cookie-settings" style="padding: 0.75rem 1.5rem; background: transparent; color: var(--primary, #4361ee); border: none; font-weight: 600; cursor: pointer; text-decoration: underline;"> Персонализиране </button> </div> </div> `; document.body.appendChild(banner); // Add event listeners document.getElementById('cookie-accept-all').addEventListener('click', function() { acceptAllCookies(); }); document.getElementById('cookie-accept-essential').addEventListener('click', function() { acceptEssentialCookies(); }); document.getElementById('cookie-settings').addEventListener('click', function() { showCookieSettings(); }); } // Show cookie settings modal function showCookieSettings() { // Remove existing modal if any const existingModal = document.getElementById('cookie-settings-modal'); if (existingModal) existingModal.remove(); const modal = document.createElement('div'); modal.id = 'cookie-settings-modal'; modal.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 10000; animation: fadeIn 0.3s ease; `; modal.innerHTML = ` <div style="background: white; border-radius: var(--border-radius, 8px); max-width: 500px; width: 90%; max-height: 80vh; overflow-y: auto; box-shadow: 0 10px 25px rgba(0,0,0,0.2);"> <div style="padding: 1.5rem; border-bottom: 1px solid #e9ecef;"> <h3 style="margin: 0; color: var(--dark, #212529);">Предпочитания за бисквитки</h3> </div> <div style="padding: 1.5rem;"> <p style="color: var(--gray, #6c757d); margin-bottom: 1.5rem;">Изберете кои бисквитки разрешавате. Можете да промените тези настройки по всяко време.</p> <div style="margin-bottom: 1rem; padding: 1rem; background: #f8f9fa; border-radius: var(--border-radius, 8px);"> <label style="display: flex; align-items: center; gap: 0.75rem; cursor: not-allowed;"> <input type="checkbox" checked disabled style="width: 18px; height: 18px;"> <div> <strong style="color: var(--dark, #212529);">Основни бисквитки</strong> <p style="margin: 0.25rem 0 0; font-size: 0.9rem; color: var(--gray, #6c757d);">Изисква се за функционирането на уебсайта. Не може да бъде деактивиран.</p> </div> </label> </div> <div style="margin-bottom: 1rem; padding: 1rem; background: #f8f9fa; border-radius: var(--border-radius, 8px);"> <label style="display: flex; align-items: center; gap: 0.75rem; cursor: pointer;"> <input type="checkbox" id="cookie-pref-analytics" checked style="width: 18px; height: 18px;"> <div> <strong style="color: var(--dark, #212529);">Аналитични бисквитки</strong> <p style="margin: 0.25rem 0 0; font-size: 0.9rem; color: var(--gray, #6c757d);">Помогнете ни да разберем как посетителите взаимодействат с нашия сайт.</p> </div> </label> </div> <div style="margin-bottom: 1rem; padding: 1rem; background: #f8f9fa; border-radius: var(--border-radius, 8px);"> <label style="display: flex; align-items: center; gap: 0.75rem; cursor: pointer;"> <input type="checkbox" id="cookie-pref-functional" checked style="width: 18px; height: 18px;"> <div> <strong style="color: var(--dark, #212529);">Функционални бисквитки</strong> <p style="margin: 0.25rem 0 0; font-size: 0.9rem; color: var(--gray, #6c757d);">Запомнете вашите предпочитания и настройки.</p> </div> </label> </div> <div style="margin-bottom: 1rem; padding: 1rem; background: #f8f9fa; border-radius: var(--border-radius, 8px);"> <label style="display: flex; align-items: center; gap: 0.75rem; cursor: pointer;"> <input type="checkbox" id="cookie-pref-targeting" checked style="width: 18px; height: 18px;"> <div> <strong style="color: var(--dark, #212529);">Насочване на бисквитки</strong> <p style="margin: 0.25rem 0 0; font-size: 0.9rem; color: var(--gray, #6c757d);">Използва се за предоставяне на подходящо съдържание и реклами.</p> </div> </label> </div> </div> <div style="padding: 1.5rem; border-top: 1px solid #e9ecef; display: flex; gap: 1rem; justify-content: flex-end;"> <button id="cookie-save-preferences" style="padding: 0.75rem 1.5rem; background: linear-gradient(135deg, var(--primary, #4361ee), var(--secondary, #7209b7)); color: white; border: none; border-radius: var(--border-radius, 8px); font-weight: 600; cursor: pointer;"> Запазване на предпочитанията </button> <button id="cookie-close-modal" style="padding: 0.75rem 1.5rem; background: white; color: var(--dark, #212529); border: 1px solid var(--gray, #6c757d); border-radius: var(--border-radius, 8px); font-weight: 600; cursor: pointer;"> Отказ </button> </div> </div> `; document.body.appendChild(modal); // Close modal handlers document.getElementById('cookie-close-modal').addEventListener('click', function() { modal.remove(); }); document.getElementById('cookie-save-preferences').addEventListener('click', function() { const preferences = { essential: true, // Always true analytics: document.getElementById('cookie-pref-analytics').checked, functional: document.getElementById('cookie-pref-functional').checked, targeting: document.getElementById('cookie-pref-targeting').checked }; saveCookiePreferences(preferences); modal.remove(); removeCookieBanner(); }); // Click outside to close modal.addEventListener('click', function(e) { if (e.target === modal) { modal.remove(); } }); } // Accept all cookies function acceptAllCookies() { const preferences = { essential: true, analytics: true, functional: true, targeting: true }; saveCookiePreferences(preferences); removeCookieBanner(); } // Accept only essential cookies function acceptEssentialCookies() { const preferences = { essential: true, analytics: false, functional: false, targeting: false }; saveCookiePreferences(preferences); removeCookieBanner(); } // Save cookie preferences function saveCookiePreferences(preferences) { // Save to cookie const expiryDate = new Date(); expiryDate.setDate(expiryDate.getDate() + COOKIE_EXPIRY_DAYS); const cookieValue = JSON.stringify(preferences); document.cookie = `${COOKIE_CONSENT_KEY}=${encodeURIComponent(cookieValue)}; expires=${expiryDate.toUTCString()}; path=/; SameSite=Lax`; // Apply preferences applyCookiePreferences(preferences); // Store in localStorage for quick access localStorage.setItem(COOKIE_CONSENT_KEY, cookieValue); // Optional: send event to analytics if (typeof window.gtag !== 'undefined' && preferences.analytics) { window.gtag('consent', 'update', { 'analytics_storage': 'granted' }); } } // Get cookie consent from cookie or localStorage function getCookieConsent() { // Try to get from cookie first const match = document.cookie.match(new RegExp('(^| )' + COOKIE_CONSENT_KEY + '=([^;]+)')); if (match) { try { return JSON.parse(decodeURIComponent(match[2])); } catch (e) { console.error('Грешка при анализа на съгласието за бисквитки:', e); } } // Try localStorage const stored = localStorage.getItem(COOKIE_CONSENT_KEY); if (stored) { try { return JSON.parse(stored); } catch (e) { console.error('Грешка при анализиране на съгласието на localStorage:', e); } } return null; } // Apply cookie preferences (enable/disable features) function applyCookiePreferences(preferences) { if (!preferences) return; // Example: Disable analytics if not consented if (!preferences.analytics) { // Disable Google Analytics if present if (typeof window.gtag !== 'undefined') { window.gtag('consent', 'default', { 'analytics_storage': 'denied' }); } // Optionally remove existing analytics cookies // This is handled by the analytics script itself usually } // Disable targeting cookies if (!preferences.targeting) { // Disable ad-related tracking // Remove Facebook pixel, etc. } // Store preferences in data attribute for other scripts to check document.documentElement.setAttribute('data-cookie-consent', JSON.stringify(preferences)); // Dispatch event for other scripts to react window.dispatchEvent(new CustomEvent('cookieConsentChanged', { detail: preferences })); } // Remove cookie banner function removeCookieBanner() { const banner = document.getElementById('cookie-consent-banner'); if (banner) { banner.style.animation = 'slideDown 0.5s ease forwards'; setTimeout(() => { if (banner.parentNode) banner.remove(); }, 500); // Add slideDown animation if not exists if (!document.getElementById('cookie-animation-slidedown')) { const style = document.createElement('style'); style.id = 'cookie-animation-slidedown'; style.textContent = ` @keyframes slideDown { from { transform: translateY(0); } to { transform: translateY(100%); } } `; document.head.appendChild(style); } } } // Add a floating button to reopen cookie settings (optional) function addCookieSettingsButton() { const btn = document.createElement('button'); btn.id = 'cookie-settings-button'; btn.innerHTML = '🍪'; btn.setAttribute('aria-label', 'Настройки на бисквитките'); btn.style.cssText = ` position: fixed; bottom: 20px; left: 20px; width: 48px; height: 48px; border-radius: 50%; background: white; border: 1px solid var(--gray, #6c757d); box-shadow: var(--box-shadow, 0 4px 6px rgba(0,0,0,0.1)); cursor: pointer; font-size: 1.2rem; display: flex; align-items: center; justify-content: center; z-index: 9998; transition: var(--transition, all 0.3s ease); opacity: 0.8; `; btn.addEventListener('mouseenter', function() { this.style.opacity = '1'; this.style.transform = 'scale(1.1)'; }); btn.addEventListener('mouseleave', function() { this.style.opacity = '0.8'; this.style.transform = 'scale(1)'; }); btn.addEventListener('click', function() { showCookieSettings(); }); document.body.appendChild(btn); } // Initialize on DOM ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initCookieConsent); } else { initCookieConsent(); } // Optional: Add settings button after consent is given window.addEventListener('cookieConsentChanged', function() { // Only show settings button if not already present if (!document.getElementById('cookie-settings-button')) { //addCookieSettingsButton(); } }); // If consent already exists, add settings button const existingConsent = getCookieConsent(); if (existingConsent && !document.getElementById('cookie-settings-button')) { document.addEventListener('DOMContentLoaded', function() { //addCookieSettingsButton(); }); } })(); </script> </body> </html>