Български | 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 | 汉语 | 日本語 |
M

maksim.by

Bejegyzett

A maksim.by webhely elemzése: Mit tanulhat meg a főoldalról? A maksim.by webhely főoldalán közölt adatok alapján arra a következtetésre juthatunk, hogy az erőforrás minim...

Az oldal elemzésének frissítési dátuma: 2026/06/03 22:24:49
Az utolsó whois frissítés dátuma: 2026/06/09 21:54:04
Domain állapota
Bejegyzett
Addig fizetve
13.08.2027
Elérhető től
12.09.2027

Weboldal leírása

📝

A maksim.by webhely elemzése: Mit tanulhat meg a főoldalról?

A maksim.by webhely főoldalán közölt adatok alapján arra a következtetésre juthatunk, hogy az erőforrás minimális funkcionalitással rendelkezik, és nem tartalmaz jelentős információt a céljának megértéséhez.

Webhelytéma

A maksim.by webhely rendkívül korlátozott formában jelenik meg. A domain név személyes jelleget jelez – valószínűleg egy személyes projektet vagy a tulajdonos nevét. A szöveges adatokban azonban nincs szó az oldal tevékenységeiről, szolgáltatásairól, termékeiről vagy céljairól. Az egyetlen elérhető szöveg a "Click me 18+" kifejezés, amely felnőtt közönségnek szánt tartalmat jelezhet, azonban a további információk hiánya spekulatívsá teszi ezt az értelmezést.

Az oldal leírása üresként jelenik meg, ami azt jelzi, hogy a description metacímke üres. Ez negatívan befolyásolja a SEO optimalizálását, és csökkenti a keresési eredmények közé jutás esélyét. Hiányzik az oldal címe is (</code> címke) – ez súlyos technikai hiba, mivel enélkül a keresőmotorok nem tudják meghatározni az oldal témáját.</p> <h2>Kimenet</h2> <p>A megadott adatok alapján a <strong>maksim.by</strong> webhely nem működik teljes értékű erőforrásként. Vagy fejlesztés alatt áll, ideiglenes csonk, vagy nem támogatott. A tartalom, a leírás, a cím és a linkek hiánya miatt alkalmatlan információs, kereskedelmi vagy szakmai oldalként való használatra.</p> <p>Nem zárhatjuk ki annak lehetőségét sem, hogy a domaint későbbi bevételszerzés céljából vásárolták meg, de jelenleg nem tartalmaz semmilyen tevékenységet vagy tartalmat.</p> <div class="faq-accordion"> <h2>Gyakran ismételt kérdések</h2> <részletek> <summary>Mit jelent a „Click me 18+” kifejezés a maksim.by webhelyen?</summary> <p>A „Click me 18+” kifejezés felnőtt tartalom jelenlétére utalhat, azonban az egyéb adatok teljes hiánya miatt ezt nem lehet biztosan megerősíteni. Ez lehet tesztszöveg, csonk vagy egy kísérleti projekt része.</p> </details> <részletek> <summary>Miért nincs a webhelynek címe és leírása?</summary> <p>A <code><title></code> és a <code>description</code> metacímke hiánya a webhely fejlesztésének hibáját vagy hiányos állapotát jelzi. Az ilyen webhelyek nem jelennek meg hatékonyan a keresési eredmények között, és hibásnak tűnhetnek.</p> </details> <részletek> <summary>A maksim.by webhely működőképesnek tekinthető?</summary> <p>Nem. A megadott adatok alapján az oldal nem működik megfelelően: hiányoznak olyan kulcselemek, mint a cím, a leírás és a tartalom. Ez azt jelzi, hogy az erőforrás átmenetileg nem elérhető, vagy technikai csonk.</p> </details> </div></p> </div> </div> <!-- Archive Section --> <!-- SEO Score --> <!-- Meta Tags --> <!-- Whois Information --> <div class="card"> <div class="card-header"> <h2 class="card-title">Whois információ</h2> <div class="card-icon">📄</div> </div> <div class="card-content"> <div class="info-grid"> <div class="info-item"><strong>domain_name:</strong> maksim.by</div><div class="info-item"><strong>creation_date:</strong> 2007-08-09</div><div class="info-item"><strong>creation_time:</strong> 1186617600</div><div class="info-item"><strong>expiration_date:</strong> 2027-08-13</div><div class="info-item"><strong>expiration_time:</strong> 1818115200</div><div class="info-item"><strong>registrar:</strong> Reliable Software, Ltd</div> </div> </div> </div> <!-- Whois Raw Data --> <div class="card"> <div class="card-header"> <h2 class="card-title">Whois nyers adatok</h2> <div class="card-icon">📋</div> </div> <div class="card-content"> <pre id="whoisRaw" style="white-space: pre-wrap; background: #f8f9fa; padding: 1rem; border-radius: var(--border-radius); max-height: 200px; overflow-y: auto;"> Domain name: maksim.by<br />Registrar: Reliable Software, Ltd<br />Person: HIDDEN!<br />Email: HIDDEN! Details are available at https://whois.cctld.by<br />Name Server: justin.ns.cloudflare.com<br />Name Server: rita.ns.cloudflare.com<br />Update Date: 2026-04-11<br />Creation Date: 2007-08-09<br />Expiration Date: 2027-08-13<br />-------------------------------------------<br />Service provided by Belarusian Cloud Technologies LLC<br /> </pre> <button class="toggle-btn" onclick="toggleRawData('whoisRaw', this)">Teljes adat megjelenítése</button> </div> </div> <!-- SEO Audit --> <!-- Load Google Charts at the top of the search engine section --> <script> // Load Google Charts google.charts.load('current', {'packages':['corechart', 'line']}); </script> <!-- Google Positions --> <div class="chart-container"> <h2 class="chart-title">Pozíciók be Google</h2> <div id="chart_div_google" style="width: 100%; height: 400px;"></div> </div> <!-- Google Search Phrases --> <div class="card"> <div class="card-header"> <h2 class="card-title">Keresési kifejezések - Google</h2> <div class="card-icon">🔍</div> </div> <div class="card-content"> <div class="filters"> <div class="filter-group"> <label class="filter-label" for="googlePhraseFilter">Kifejezés:</label> <input type="text" id="googlePhraseFilter" class="filter-input" placeholder="Szűrés kifejezés szerint"> </div> <div class="filter-group"> <label class="filter-label" for="googlePageFilter">oldal:</label> <input type="text" id="googlePageFilter" class="filter-input" placeholder="Szűrés oldal szerint"> </div> <div class="filter-group"> <label class="filter-label"> </label> <button class="cta-button" onclick="applyFilters('google')">Szűrő alkalmazása</button> </div> </div> <div class="table-responsive"> <table class="data-table" id="googlePhrasesTable"> <thead> <tr> <th>Pozíció</th> <th>Kifejezés</th> <th>oldal</th> <th>Töredék</th> </tr> </thead> <tbody id="googlePhrasesBody"> <tr><td><span class="badge badge-success">1</span></td><td><noindex><a href="/hu/phrase/%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D0%B0-%D0%BF%D0%B5%D1%81%D0%BD%D0%B8-%D0%BA%D0%BE%D0%BB%D1%8B%D0%B1%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F/" rel="nofollow">максим певица песни колыбельная</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-success">1</span></td><td><noindex><a href="/hu/phrase/%D0%BA%D0%BE%D0%BB%D1%8B%D0%B1%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D0%BF%D0%B5%D1%81%D0%BD%D1%8F-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D1%8B-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D0%BF%D1%80%D0%BE%D1%81%D0%BB%D1%83%D1%88%D0%B0%D1%82%D1%8C/" rel="nofollow">колыбельная песня певицы максим прослушать</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-success">1</span></td><td><noindex><a href="/hu/phrase/%D0%BF%D0%B5%D1%81%D0%BD%D1%8F-%D0%BA%D0%BE%D0%BB%D1%8B%D0%B1%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D1%8B-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C/" rel="nofollow">песня колыбельная певицы максим скачать</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-success">1</span></td><td><noindex><a href="/hu/phrase/%D0%BA%D0%BE%D0%BB%D1%8B%D0%B1%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D0%BF%D0%B5%D1%81%D0%BD%D1%8F-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D1%8B-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC/" rel="nofollow">колыбельная песня певицы максим</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-success">2</span></td><td><noindex><a href="/hu/phrase/%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D0%BA%D0%BE%D0%BB%D1%8B%D0%B1%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE/" rel="nofollow">максим колыбельная видео</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-success">2</span></td><td><noindex><a href="/hu/phrase/%D0%BA%D0%BB%D0%B8%D0%BF-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D0%BA%D0%BE%D0%BB%D1%8B%D0%B1%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F/" rel="nofollow">клип максим колыбельная</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-success">2</span></td><td><noindex><a href="/hu/phrase/%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D0%BA%D0%BE%D0%BB%D1%8B%D0%B1%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D1%82%D0%B0%D1%80%D0%B0%D1%81-%D0%B1%D1%83%D0%BB%D1%8C%D0%B1%D0%B0-%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C/" rel="nofollow">максим колыбельная тарас бульба скачать</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-success">2</span></td><td><noindex><a href="/hu/phrase/%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D0%B0%D0%BA%D0%BA%D0%BE%D1%80%D0%B4%D1%8B-%D0%BF%D0%B5%D1%81%D0%B5%D0%BD/" rel="nofollow">максим аккорды песен</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-success">2</span></td><td><noindex><a href="/hu/phrase/%D1%81%D0%BB%D1%83%D1%88%D0%B0%D1%82%D1%8C-%D0%BF%D0%B5%D1%81%D0%BD%D1%8E-%D0%BA%D0%BE%D0%BB%D1%8B%D0%B1%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC/" rel="nofollow">слушать песню колыбельная максим</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-success">2</span></td><td><noindex><a href="/hu/phrase/%D1%82%D0%B5%D0%BA%D1%81%D1%82-%D0%BF%D0%B5%D1%81%D0%BD%D0%B8-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D1%8B-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D0%BA%D0%BE%D0%BB%D1%8B%D0%B1%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F/" rel="nofollow">текст песни певицы максим колыбельная</a></noindex></td><td>/</td><td></td></tr> </tbody> </table> </div> <div class="pagination" id="googlePagination"> <button disabled onclick="loadSearchPhrases('google', 0)">«</button> <button class="active" onclick="loadSearchPhrases('google', 1)">1</button> <button class="" onclick="loadSearchPhrases('google', 2)">2</button> <button class="" onclick="loadSearchPhrases('google', 3)">3</button> <button disabled="">...</button> <button class="" onclick="loadSearchPhrases('google', 19)">19</button> <button class="" onclick="loadSearchPhrases('google', 20)">20</button> <button onclick="loadSearchPhrases('google', 2)">»</button> </div> </div> </div> <script> // Google Charts for google google.charts.setOnLoadCallback(drawChart_google); function drawChart_google() { var data = new google.visualization.DataTable(); data.addColumn('date', 'Nap'); data.addColumn("number", "максим певица песни колыбельная");data.addColumn("number", "колыбельная песня певицы максим прослушать");data.addColumn("number", "песня колыбельная певицы максим скачать");data.addColumn("number", "колыбельная песня певицы максим");data.addColumn("number", "максим колыбельная видео");data.addColumn("number", "клип максим колыбельная");data.addColumn("number", "максим колыбельная тарас бульба скачать");data.addColumn("number", "максим аккорды песен");data.addColumn("number", "слушать песню колыбельная максим");data.addColumn("number", "текст песни певицы максим колыбельная"); data.addRows([ [new Date(2011, 1, 16),null,null,1,1,null,null,null,null,null,null,],[new Date(2011, 1, 3),1,null,null,null,2,2,2,null,null,null,],[new Date(2011, 1, 4),null,null,null,null,null,null,null,2,null,null,],[new Date(2011, 1, 7),null,1,null,null,null,null,null,null,2,2,], ]); var options = { chart: { title: 'Olyan keresési kifejezések, amelyeknél a domain a tetején található google keresőmotor: "maksim.by"', subtitle: 'Kifejezéspozíció története' }, width: '100%', height: 400, curveType: 'function', legend: { position: 'bottom' }, pointSize: 5, pointsVisible: true, interpolateNulls: true, selectionMode: 'multiple', vAxis: { direction: '-1', title: 'Pozíció', }, hAxis: { title: 'Dátum', }, }; var chart = new google.visualization.LineChart(document.getElementById("chart_div_google")); chart.draw(data, options); } </script> <!-- Yandex Positions --> <div class="chart-container"> <h2 class="chart-title">Pozíciók be Yandex</h2> <div id="chart_div_yandex" style="width: 100%; height: 400px;"></div> </div> <!-- Yandex Search Phrases --> <div class="card"> <div class="card-header"> <h2 class="card-title">Keresési kifejezések - Yandex</h2> <div class="card-icon">🔍</div> </div> <div class="card-content"> <div class="filters"> <div class="filter-group"> <label class="filter-label" for="yandexPhraseFilter">Kifejezés:</label> <input type="text" id="yandexPhraseFilter" class="filter-input" placeholder="Szűrés kifejezés szerint"> </div> <div class="filter-group"> <label class="filter-label" for="yandexPageFilter">oldal:</label> <input type="text" id="yandexPageFilter" class="filter-input" placeholder="Szűrés oldal szerint"> </div> <div class="filter-group"> <label class="filter-label"> </label> <button class="cta-button" onclick="applyFilters('yandex')">Szűrő alkalmazása</button> </div> </div> <div class="table-responsive"> <table class="data-table" id="yandexPhrasesTable"> <thead> <tr> <th>Pozíció</th> <th>Kifejezés</th> <th>oldal</th> <th>Töredék</th> </tr> </thead> <tbody id="yandexPhrasesBody"> <tr><td><span class="badge badge-warning">4</span></td><td><noindex><a href="/hu/phrase/%D0%BC%D0%B0%D0%BAs%D0%B8%D0%BC/" rel="nofollow">макsим</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-warning">5</span></td><td><noindex><a href="/hu/phrase/%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C-%D0%BF%D0%B5%D1%81%D0%BD%D1%8E-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D1%8B-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC/" rel="nofollow">скачать песню певицы максим</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-warning">5</span></td><td><noindex><a href="/hu/phrase/%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C-%D0%BF%D0%B5%D1%81%D0%BD%D0%B8-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D1%8B-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC/" rel="nofollow">скачать песни певицы максим</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-warning">6</span></td><td><noindex><a href="/hu/phrase/%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D0%B0-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D0%BF%D0%B5%D1%81%D0%BD%D0%B8-%D0%B1%D0%B5%D1%81%D0%BF%D0%BB%D0%B0%D1%82%D0%BD%D0%BE/" rel="nofollow">певица максим песни бесплатно</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-warning">6</span></td><td><noindex><a href="/hu/phrase/%D1%84%D0%BE%D1%82%D0%BE-%D1%80%D0%B5%D0%B1%D0%B5%D0%BD%D0%BA%D0%B0-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D1%8B-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC/" rel="nofollow">фото ребенка певицы максим</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-warning">6</span></td><td><noindex><a href="/hu/phrase/%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C-%D0%B1%D0%B5%D1%81%D0%BF%D0%BB%D0%B0%D1%82%D0%BD%D0%BE-%D0%BF%D0%B5%D1%81%D0%BD%D1%8E-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D1%8B-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC/" rel="nofollow">скачать бесплатно песню певицы максим</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-warning">6</span></td><td><noindex><a href="/hu/phrase/%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D0%B0-%D0%BA%D0%BE%D0%BB%D1%8B%D0%B1%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F/" rel="nofollow">максим певица колыбельная</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-warning">7</span></td><td><noindex><a href="/hu/phrase/%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D0%B0-%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C-%D0%B1%D0%B5%D1%81%D0%BF%D0%BB%D0%B0%D1%82%D0%BD%D0%BE/" rel="nofollow">максим певица скачать бесплатно</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-warning">10</span></td><td><noindex><a href="/hu/phrase/%D0%BC%D1%83%D0%B7%D1%8B%D0%BA%D0%B0-%D0%BF%D0%B5%D0%B2%D0%B8%D1%86%D1%8B-%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC/" rel="nofollow">музыка певицы максим</a></noindex></td><td>/</td><td></td></tr><tr><td><span class="badge badge-danger">11</span></td><td><noindex><a href="/hu/phrase/%D0%BC%D1%83%D0%B7-%D1%82%D0%B2-2008/" rel="nofollow">муз тв 2008</a></noindex></td><td>/</td><td></td></tr> </tbody> </table> </div> <div class="pagination" id="yandexPagination"> <button disabled onclick="loadSearchPhrases('yandex', 0)">«</button> <button class="active" onclick="loadSearchPhrases('yandex', 1)">1</button> <button class="" onclick="loadSearchPhrases('yandex', 2)">2</button> <button class="" onclick="loadSearchPhrases('yandex', 3)">3</button> <button class="" onclick="loadSearchPhrases('yandex', 4)">4</button> <button class="" onclick="loadSearchPhrases('yandex', 5)">5</button> <button onclick="loadSearchPhrases('yandex', 2)">»</button> </div> </div> </div> <script> // Google Charts for yandex google.charts.setOnLoadCallback(drawChart_yandex); function drawChart_yandex() { var data = new google.visualization.DataTable(); data.addColumn('date', 'Nap'); data.addColumn("number", "макsим");data.addColumn("number", "скачать песню певицы максим");data.addColumn("number", "скачать песни певицы максим");data.addColumn("number", "певица максим песни бесплатно");data.addColumn("number", "фото ребенка певицы максим");data.addColumn("number", "скачать бесплатно песню певицы максим");data.addColumn("number", "максим певица колыбельная");data.addColumn("number", "максим певица скачать бесплатно");data.addColumn("number", "музыка певицы максим");data.addColumn("number", "муз тв 2008"); data.addRows([ [new Date(2011, 1, 26),null,null,null,6,null,null,null,null,null,null,],[new Date(2011, 1, 27),null,null,null,null,6,null,null,null,null,11,],[new Date(2011, 2, 17),null,5,5,null,null,6,6,7,10,null,],[new Date(2011, 4, 10),4,null,null,null,null,null,null,null,null,null,], ]); var options = { chart: { title: 'Olyan keresési kifejezések, amelyeknél a domain a tetején található yandex keresőmotor: "maksim.by"', subtitle: 'Kifejezéspozíció története' }, width: '100%', height: 400, curveType: 'function', legend: { position: 'bottom' }, pointSize: 5, pointsVisible: true, interpolateNulls: true, selectionMode: 'multiple', vAxis: { direction: '-1', title: 'Pozíció', }, hAxis: { title: 'Dátum', }, }; var chart = new google.visualization.LineChart(document.getElementById("chart_div_yandex")); chart.draw(data, options); } </script> <!-- Additional Services --> <div class="card" id="domain-additional-services"> <div class="card-header"> <h2 class="card-title">További szolgáltatások</h2> <div class="card-icon">💎</div> </div> <div class="card-content"> <div class="info-grid"> <div class="info-item"> <span class="popup-trigger" alt="Teljes SEO audit részletes ajánlásokkal"> <button class="cta-button" data-modal="paymentModal" data-payment-type="domain_seo_pay" data-payment-item-id="484838" data-title="Teljes SEO audit maksim.by" data-text="Teljes SEO audit részletes javaslatokkal a keresőmotorok pozícióinak javítására." data-price="99.99"> Teljes SEO audit </button> <span class="popup-text"></span> </span> </div> <div class="info-item"> <span class="popup-trigger" alt="Havi pozíciófigyelés 100 kulcsmondathoz"> <button class="cta-button" data-modal="paymentModal" data-payment-type="unsupported" disabled title="Payment flow is not implemented for this service" data-payment-item-id="484838" data-title="Pozíciófigyelés maksim.by" data-text="Havi pozíciófigyelés 100 kulcskifejezéshez a Google-ban és a Yandexben." data-price="49.99"> Pozíciófigyelés </button> <span class="popup-text"></span> </span> </div> <div class="info-item"> <span class="popup-trigger" alt="A versenytársak elemzése és promóciós stratégiáik meghatározása"> <button class="cta-button" data-modal="paymentModal" data-payment-type="domain_analiz_pay" data-payment-item-id="484838" data-title="Versenytárs elemzés maksim.by" data-text="A versenytársak elemzése és promóciós stratégiáik meghatározása." data-price="79.99"> Versenytárs elemzés </button> <span class="popup-text"></span> </span> </div> </div> </div> </div> <!-- Search Engine Positions --> <!-- Load Google Charts --> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script> // Load Google Charts google.charts.load('current', {'packages':['corechart', 'line']}); </script> <script> // Google Charts loader (already in your HTML) google.charts.load('current', {'packages':['corechart', 'line']}); // Global variable to store chart instances let chartInstances = {}; // AJAX function for loading search phrases with pagination/filtering function loadSearchPhrases(engine, page) { const phraseFilterEl = document.getElementById(`${engine}PhraseFilter`); const pageFilterEl = document.getElementById(`${engine}PageFilter`); const phraseFilter = phraseFilterEl ? phraseFilterEl.value : ''; const pageFilter = pageFilterEl ? pageFilterEl.value : ''; const domain = document.getElementById('domain_name').value; const data = { type: 'search_phrases', engine: engine, page: page, domain: domain, phrase_filter: phraseFilter, page_filter: pageFilter }; // Show loading state for table const tbody = document.getElementById(`${engine}PhrasesBody`); if (tbody) { tbody.innerHTML = ` <tr> <td colspan="4" style="text-align: center; padding: 2rem;"> <div class="loading-spinner"></div> <p>Loading data...</p> </td> </tr> `; } // Show loading state for chart const chartContainer = document.getElementById(`chart_div_${engine}`); if (chartContainer) { chartContainer.innerHTML = ` <div style="text-align: center; padding: 4rem;"> <div class="loading-spinner"></div> <p>Updating chart...</p> </div> `; } sendRequest('/ajax/', data, function(response) { if (response.status === 'ok') { updateTableContent(engine, response.data); updatePagination(engine, response.pagination); // Generate and update chart with new data if (response.data && response.data.length > 0) { const chartData = generateChartData(response.data, engine); updateChart(engine, chartData); } else { // Hide chart if no data const chartContainerDiv = document.getElementById(`chart_container_${engine}`); if (chartContainerDiv) { chartContainerDiv.style.display = 'none'; } } } else { showErrorInTable(engine, response.error); } }); } // Generate chart data from API response function generateChartData(phrasesData, engine) { const chartData = { js_addColumn_str: '', js_dataRows_str: '' }; if (!phrasesData || phrasesData.length === 0) { return chartData; } // Parse all phrases and their history const allPhrases = []; const allHistory = []; phrasesData.forEach(item => { const pages = parsePageData(item.page_data); const titles = parseContentData(item.title_data); const snippets = parseContentData(item.snippet_data); const history = parsePositionHistory(item.position_history, pages, titles, snippets); if (history.length > 0) { allPhrases.push(item.phrase); allHistory.push({ phrase: item.phrase, history: history }); } }); if (allHistory.length === 0) { return chartData; } // Collect all unique days const allDays = new Set(); allHistory.forEach(item => { item.history.forEach(h => { allDays.add(h.day); }); }); // Sort days in ascending order const sortedDays = Array.from(allDays).sort((a, b) => a - b); // Generate columns chartData.js_addColumn_str = 'data.addColumn("date", "Day")\n'; allPhrases.forEach(phrase => { const label = phrase.length > 30 ? phrase.substring(0, 27) + '...' : phrase; chartData.js_addColumn_str += `data.addColumn("number", "${label}")\n`; }); // Count how many columns we should have const expectedColumnCount = 1 + allPhrases.length; // Generate data rows const dataRows = []; sortedDays.forEach(day => { // Convert day number to Date object const date = new Date(day * 86400 * 1000); // Create row array const row = [date]; // Add position for EACH phrase on this day allHistory.forEach(item => { const history = item.history; const entry = history.find(h => h.day === day); row.push(entry ? entry.position : null); }); // Adjust row length if needed if (row.length !== expectedColumnCount) { while (row.length < expectedColumnCount) { row.push(null); } while (row.length > expectedColumnCount) { row.pop(); } } dataRows.push(row); }); // Format rows for Google Charts if (dataRows.length > 0) { const rowStrings = dataRows.map(row => { const formattedRow = row.map((value, index) => { if (index === 0 && value instanceof Date) { const year = value.getFullYear(); const month = value.getMonth(); const day = value.getDate(); return `new Date(${year}, ${month}, ${day})`; } return value; }); return JSON.stringify(formattedRow); }); chartData.js_dataRows_str = rowStrings.join(',\n'); chartData.js_dataRows_str = chartData.js_dataRows_str.replace(/"new Date\((\d+), (\d+), (\d+)\)"/g, 'new Date($1, $2, $3)'); } return chartData; } // Update chart with new data function updateChart(engine, chartData) { if (!chartData || !chartData.js_dataRows_str || !chartData.js_addColumn_str) { console.warn(`No chart data for ${engine}`); const chartContainerDiv = document.getElementById(`chart_container_${engine}`); if (chartContainerDiv) { chartContainerDiv.style.display = 'none'; } return; } // Show chart container const chartContainerDiv = document.getElementById(`chart_container_${engine}`); if (chartContainerDiv) { chartContainerDiv.style.display = 'block'; } // Check if Google Charts is loaded if (typeof google === 'undefined' || typeof google.visualization === 'undefined') { console.warn('Google Charts not loaded yet'); google.charts.load('current', {'packages':['corechart', 'line']}); google.charts.setOnLoadCallback(function() { setTimeout(() => { drawChart(engine, chartData); }, 100); }); } else { setTimeout(() => { drawChart(engine, chartData); }, 100); } } // Draw chart function function drawChart(engine, chartData) { try { const chartContainer = document.getElementById(`chart_div_${engine}`); // Check if chart container exists if (!chartContainer) { console.warn(`Chart container for ${engine} not found`); return; } var data = new google.visualization.DataTable(); // Add date column data.addColumn('date', 'Day'); // Parse and add data columns const columns = chartData.js_addColumn_str.split('\n').filter(line => line.trim() !== ''); columns.forEach(col => { const match = col.match(/data\.addColumn\("(\w+)",\s*"([^"]+)"\)/); if (match) { const type = match[1]; const label = match[2]; if (type === 'date' && label === 'Day') { return; } data.addColumn(type, label); } }); // Parse data rows const rowsStr = chartData.js_dataRows_str.trim(); if (rowsStr && rowsStr !== '[]') { try { const rows = eval(`[${rowsStr}]`); // Adjust rows if needed const adjustedRows = rows.map(row => { if (!Array.isArray(row)) { return new Array(data.getNumberOfColumns()).fill(null); } if (row.length === data.getNumberOfColumns()) { return row; } const adjusted = new Array(data.getNumberOfColumns()).fill(null); // Copy available values const copyCount = Math.min(row.length, data.getNumberOfColumns()); for (let i = 0; i < copyCount; i++) { adjusted[i] = row[i]; } return adjusted; }); data.addRows(adjustedRows); } catch (e) { console.error(`Error parsing data rows:`, e); return; } } const domainName = document.getElementById('domain_name').value; const engineName = engine.charAt(0).toUpperCase() + engine.slice(1); var options = { chart: { title: `Search phrases for which the domain is at the top of the ${engine} search engine: "${domainName}"`, subtitle: 'Phrase position history' }, width: '100%', height: 400, curveType: 'function', legend: { position: 'bottom', maxLines: 3 }, pointSize: 3, pointsVisible: true, interpolateNulls: true, selectionMode: 'multiple', vAxis: { direction: -1, title: 'Position', viewWindow: { min: 1, max: 100 }, ticks: [ {v: 1, f: '1'}, {v: 10, f: '10'}, {v: 20, f: '20'}, {v: 30, f: '30'}, {v: 50, f: '50'}, {v: 100, f: '100'} ] }, hAxis: { title: 'Date', format: 'MMM yyyy' }, }; // Store chart instance for potential future updates const chart = new google.visualization.LineChart(chartContainer); chartInstances[engine] = chart; chart.draw(data, options); } catch (error) { console.error(`Error drawing chart for ${engine}:`, error); } } // Parse functions (same as before) function parsePageData(pageDataStr) { const pages = {}; if (!pageDataStr || pageDataStr.trim() === '') return pages; try { const pageItems = pageDataStr.split(';'); pageItems.forEach(item => { if (item.trim()) { const parts = item.split(':'); if (parts.length >= 2) { const id = parts[0].trim(); const page = parts.slice(1).join(':'); if (id) { pages[id] = decodeURIComponent(page).replace(/\+/g, ' '); } } } }); } catch (e) { console.error('Error parsing page data:', e); } return pages; } function parseContentData(contentDataStr) { const content = {}; if (!contentDataStr || contentDataStr.trim() === '') return content; try { const items = contentDataStr.split(';'); items.forEach(item => { if (item.trim()) { const parts = item.split(':'); if (parts.length >= 2) { const id = parts[0].trim(); const value = parts.slice(1).join(':'); if (id) { let decodedValue; try { decodedValue = decodeURIComponent(value); decodedValue = decodedValue.replace(/\\x([0-9A-Fa-f]{2})/g, (match, hex) => String.fromCharCode(parseInt(hex, 16)) ); } catch (e) { decodedValue = value; } content[id] = decodedValue; } } } }); } catch (e) { console.error('Error parsing content data:', e); } return content; } function parsePositionHistory(historyStr, pages, titles, snippets) { const history = []; if (!historyStr || historyStr.trim() === '') return history; try { const entries = historyStr.split(';'); entries.forEach(entry => { if (entry.trim()) { const parts = entry.split(':'); if (parts.length >= 3) { const day = parseInt(parts[0]); const position = parseInt(parts[1]); const pageId = parts[2]; const titleId = parts[3] || ''; const snippetId = parts[4] || ''; if (!isNaN(day) && !isNaN(position)) { history.push({ day: day, position: position, page: pages[pageId] || '', title: titles[titleId] || '', snippet: snippets[snippetId] || '' }); } } } }); history.sort((a, b) => a.day - b.day); } catch (e) { console.error('Error parsing position history:', e); } return history; } // Update table content with AJAX response function updateTableContent(engine, data) { const tbody = document.getElementById(`${engine}PhrasesBody`); if (!tbody) return; let tableRows = ''; const domain = document.getElementById('domain_name').value; data.forEach(item => { const pages = parsePageData(item.page_data); const titles = parseContentData(item.title_data); const snippets = parseContentData(item.snippet_data); const history = parsePositionHistory(item.position_history, pages, titles, snippets); const latestEntry = history.length > 0 ? history[history.length - 1] : null; const position = latestEntry ? latestEntry.position : null; const page = latestEntry ? latestEntry.page : ''; const title = latestEntry ? latestEntry.title : ''; const snippet = latestEntry ? latestEntry.snippet : ''; let change = null; if (history.length >= 2) { const previousEntry = history[history.length - 2]; if (previousEntry && position !== null && previousEntry.position !== null) { change = previousEntry.position - position; } } const badgeClass = getPositionBadgeClass(position); const positionHtml = position ? `<span class="badge ${badgeClass}">${position}</span>` : '<span class="badge">N/A</span>'; const changeHtml = change !== null && change !== 0 ? `<span class="position-change ${change > 0 ? 'positive' : 'negative'}">(${change > 0 ? '+' : ''}${change})</span>` : ''; const hasTitle = title && title.trim() !== ''; const hasSnippet = snippet && snippet.trim() !== ''; const showSnippet = hasTitle || hasSnippet; let snippetHtml = ''; if (showSnippet) { snippetHtml = ` <button class="show-snippet-btn" onclick="toggleSnippet(this)">Show snippet</button> <div class="snippet" style="display: none;"> <noindex><a href="https://${domain}${escapeHtml(page)}" rel="nofollow" target="_blank"> ${hasTitle ? `<strong>${escapeHtml(title)}</strong><br>` : ''} </a></noindex> ${hasSnippet ? escapeHtml(snippet) : ''} </div> `; } else { snippetHtml = '<span style="color: var(--gray);">-</span>'; } tableRows += ` <tr> <td>${positionHtml}${changeHtml}</td> <td><noindex><a href="/en/phrase/${encodeURIComponent(item.phrase)}/" rel="nofollow">${escapeHtml(item.phrase)}</a></noindex></td> <td>${escapeHtml(page)}</td> <td>${snippetHtml}</td> </tr> `; }); tbody.innerHTML = tableRows; } // Update pagination with AJAX response function updatePagination(engine, pagination) { const paginationContainer = document.getElementById(`${engine}Pagination`); if (!paginationContainer) return; const { current_page, total_pages, has_previous, has_next } = pagination; let paginationButtons = ''; if (total_pages > 1) { // Previous button paginationButtons += `<button ${!has_previous ? 'disabled' : ''} onclick="loadSearchPhrases('${engine}', ${current_page - 1})">«</button>`; // First page paginationButtons += `<button class="${current_page === 1 ? 'active' : ''}" ${current_page === 1 ? 'disabled' : ''} onclick="loadSearchPhrases('${engine}', 1)">1</button>`; // Calculate range let startPage = Math.max(2, current_page - 2); let endPage = Math.min(total_pages - 1, current_page + 2); if (current_page <= 3) { endPage = Math.min(total_pages - 1, 5); } if (current_page >= total_pages - 2) { startPage = Math.max(2, total_pages - 4); } if (startPage > 2) { paginationButtons += `<button disabled>...</button>`; } for (let i = startPage; i <= endPage; i++) { const activeClass = i === current_page ? 'active' : ''; const disabledAttr = i === current_page ? 'disabled' : ''; paginationButtons += `<button class="${activeClass}" ${disabledAttr} onclick="loadSearchPhrases('${engine}', ${i})">${i}</button>`; } if (endPage < total_pages - 1) { paginationButtons += `<button disabled>...</button>`; } if (total_pages > 1) { paginationButtons += `<button class="${current_page === total_pages ? 'active' : ''}" ${current_page === total_pages ? 'disabled' : ''} onclick="loadSearchPhrases('${engine}', ${total_pages})">${total_pages}</button>`; } // Next button paginationButtons += `<button ${!has_next ? 'disabled' : ''} onclick="loadSearchPhrases('${engine}', ${current_page + 1})">»</button>`; } paginationContainer.innerHTML = paginationButtons; } // Show error in table function showErrorInTable(engine, errorMessage) { const tbody = document.getElementById(`${engine}PhrasesBody`); if (tbody) { tbody.innerHTML = ` <tr> <td colspan="4" style="text-align: center; padding: 2rem; color: var(--danger);"> <div style="margin-bottom: 1rem;"> <span style="font-size: 2rem;">❌</span> </div> <p><strong>Error loading data:</strong></p> <p>${escapeHtml(errorMessage)}</p> <button class="toggle-btn" onclick="loadSearchPhrases('${engine}', 1)" style="margin-top: 1rem;"> Retry </button> </td> </tr> `; } } // Helper functions function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function getPositionBadgeClass(position) { if (!position) return 'badge'; if (position <= 3) return 'badge-success'; if (position <= 10) return 'badge-warning'; return 'badge-danger'; } function toggleSnippet(button) { const snippet = button.nextElementSibling; const isVisible = snippet.style.display === 'block'; snippet.style.display = isVisible ? 'none' : 'block'; button.textContent = isVisible ? 'Show snippet' : 'Hide snippet'; } // Filter functions function applyFilters(engine) { loadSearchPhrases(engine, 1); } function clearFilters(engine) { const phraseFilterEl = document.getElementById(`${engine}PhraseFilter`); const pageFilterEl = document.getElementById(`${engine}PageFilter`); if (phraseFilterEl) phraseFilterEl.value = ''; if (pageFilterEl) pageFilterEl.value = ''; loadSearchPhrases(engine, 1); } // Initialize filter events on page load document.addEventListener('DOMContentLoaded', function() { // Initialize enter key for filter inputs ['google', 'yandex'].forEach(engine => { const phraseFilter = document.getElementById(`${engine}PhraseFilter`); const pageFilter = document.getElementById(`${engine}PageFilter`); if (phraseFilter) { phraseFilter.addEventListener('keypress', function(e) { if (e.key === 'Enter') { applyFilters(engine); } }); } if (pageFilter) { pageFilter.addEventListener('keypress', function(e) { if (e.key === 'Enter') { applyFilters(engine); } }); } }); // Load Google Charts google.charts.load('current', {'packages':['corechart', 'line']}); // Initial chart drawing for server-side data google.charts.setOnLoadCallback(function() { // You might want to extract chart data from initial server-side render // or make an initial AJAX call to get chart data for page 1 }); }); // AJAX request function function sendRequest(url, data, callback) { const xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { try { const response = JSON.parse(xhr.responseText); callback(response); } catch (e) { console.error("JSON parse error:", e); callback({status: 'error', error: 'Invalid response from server'}); } } else { callback({status: 'error', error: 'Server error: ' + xhr.status}); } } }; xhr.send(JSON.stringify(data)); } </script> <script> (function() { 'use strict'; // --- Configuration --- const STORAGE_KEY = 'serpuls_domain_sections_state'; const SECTIONS_TO_COLLAPSE = [ { id: 'domain-main-info', title: 'Fő információk', btnText: 'Info' }, { id: 'domain-server-info', title: 'Szerver információ', btnText: 'Szerver' }, { id: 'domain-meta-tags', title: 'Meta címkék listája', btnText: 'Meta' }, { id: 'domain-internal-links', title: 'Belső hivatkozások', btnText: 'Belső' }, { id: 'domain-external-links', title: 'Külső hivatkozások', btnText: 'Külső' }, { id: 'domain-whois-info', title: 'Whois információ', btnText: 'Whois' }, { id: 'domain-seo-audit', title: 'SEO audit', btnText: 'SEO audit' } ]; // --- Helper Functions --- 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 const sections = []; // Add main sections from the page const sectionSelectors = [ { id: 'domain-header', title: 'Domain információk', selector: '.domain-header' }, { id: 'domain-description', title: 'Weboldal leírása', selector: '.card:has(.card-title:contains("Weboldal leírása"))' }, { id: 'domain-backup', title: 'Domain biztonsági mentése', selector: '.card:has(.card-title:contains("Domain biztonsági mentése"))' }, { id: 'domain-seo-score', title: 'SEO pontszám', selector: '.seo-score' }, { id: 'domain-main-info', title: 'Fő információk', selector: '.card:has(.card-title:contains("Fő információk"))' }, { id: 'domain-server-info', title: 'Szerver információ', selector: '.card:has(.card-title:contains("Szerver információ"))' }, { id: 'domain-meta-tags', title: 'Meta címkék listája', selector: '.card:has(.card-title:contains("Meta címkék listája"))' }, { id: 'domain-internal-links', title: 'Belső hivatkozások', selector: '.card:has(.card-title:contains("Belső hivatkozások"))' }, { id: 'domain-external-links', title: 'Külső hivatkozások', selector: '.card:has(.card-title:contains("Külső hivatkozások"))' }, { id: 'domain-whois-info', title: 'Whois információ', selector: '.card:has(.card-title:contains("Whois információ"))' }, { id: 'domain-whois-raw', title: 'Whois nyers adatok', selector: '.card:has(.card-title:contains("Whois nyers adatok"))' }, { id: 'domain-robots', title: 'Robots.txt', selector: '.card:has(.card-title:contains("Robots.txt"))' }, { id: 'domain-seo-audit', title: 'SEO audit', selector: '.card:has(.card-title:contains("SEO audit"))' }, { id: 'domain-google-positions', title: 'Pozíciók be Google', selector: '.chart-container:has(h2:contains("Google"))' }, { id: 'domain-google-phrases', title: 'Keresési kifejezések - Google', selector: '.card:has(.card-title:contains("Google"))' }, { id: 'domain-yandex-positions', title: 'Pozíciók be Yandex', selector: '.chart-container:has(h2:contains("Yandex"))' }, { id: 'domain-yandex-phrases', title: 'Keresési kifejezések - Yandex', selector: '.card:has(.card-title:contains("Yandex"))' }, { id: 'domain-additional-services', title: 'További szolgáltatások', selector: '.card:has(.card-title:contains("További szolgáltatások"))' } ]; sectionSelectors.forEach(section => { let element = null; // Try to find by selector if (section.selector) { if (section.selector.includes(':contains')) { // Simple contains selector const containsText = section.selector.match(/:contains\("([^"]+)"\)/); if (containsText) { const searchText = containsText[1]; const cards = document.querySelectorAll('.card, .chart-container, .seo-score, .domain-header'); for (const card of cards) { if (card.textContent.includes(searchText) && !card.id) { element = card; break; } } } } else { element = document.querySelector(section.selector); } } if (element && !element.id) { element.id = section.id; sections.push({ id: section.id, title: section.title, element: element }); } else if (element && element.id) { sections.push({ id: element.id, title: section.title, element: element }); } }); // Also add any card with an ID that wasn't captured document.querySelectorAll('.card[id], .chart-container[id], .seo-score[id]').forEach(el => { if (!sections.find(s => s.id === el.id)) { const titleEl = el.querySelector('.card-title, h2'); if (titleEl) { sections.push({ id: el.id, title: titleEl.innerText.trim(), element: el }); } } }); 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>📑 Tartalomjegyzék</span> <button id="toc-toggle-btn" class="toc-toggle-btn" aria-label="A tartalomjegyzék összecsukása">−</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) { if (!section) continue; 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 ID or by title let targetCard = document.getElementById(section.id); if (!targetCard) { 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', 'Szakasz összecsukása/kibontása'); // 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')) { content.style.display = ''; card.classList.remove('section-collapsed'); button.innerHTML = '➖'; saveSectionState(sectionId, false); } else { 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', 'Vissza az elejére'); 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 --- 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 = '← → Továbbiak megtekintéséhez húzza el az ujját'; 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); 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 --- function addCustomStyles() { const style = document.createElement('style'); style.textContent = ` .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-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-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); } @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; } } html { scroll-behavior: smooth; } .card .card-content { transition: display 0.2s ease; } [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 --- 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>Professzionális eszköz keresőelemzéshez, pozíciókövetéshez és SEO optimalizáláshoz.</p> <p style="margin-top: 1rem;"><strong>Skynet360 LP.</strong></p> <p><strong>Cégszám:</strong> SL027564</p> <p><strong>Cím:</strong> Suite 1 4 Queen Street, Edinburgh, Scotland, EH2 1JE</p> </div> </div> <div class="footer-section"> <h3>Gyors linkek</h3> <ul class="footer-links"> <li><a href="/hu/">Otthon</a></li> <li><a href="/hu/profile/">Profil</a></li> <li><a href="/hu/tasks/">Monitoring feladatok</a></li> <li><a href="/hu/api_docs/">API dokumentáció</a></li> <li><a href="/hu/team/">Csapatunk</a></li> </ul> </div> <div class="footer-section"> <h3>Erőforrás</h3> <ul class="footer-links"> <li><a href="/hu/blogs/">Blog</a></li> <li><a href="/hu/faq/">GYIK</a></li> <li><a href="/hu/support/">Támogatás</a></li> </ul> </div> <div class="footer-section"> <h3>Kapcsolatok</h3> <div class="footer-info"> <p>Email: <a href="mailto:info@serpuls.com">info@serpuls.com</a></p> <p>Távirat: <a href="https://t.me/serpulse_support">@serpulse_support</a></p> <div class="social-links"> <a href="#" class="social-link" title="Távirat">📱</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. Minden jog fenntartva. Skynet360 LP.</p> <div class="footer-bottom-links"> <a href="/hu/privacy/">Adatvédelmi szabályzat</a> <a href="/hu/terms/">Felhasználási feltételek</a> <a href="/hu/agreement/">Felhasználói szerződés</a> <a href="/hu/cookie/">Cookie-kra vonatkozó szabályzat</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>Sikeres fizetés!</h3> <p>Köszönjük megrendelését. Hamarosan felvesszük Önnel a kapcsolatot.</p> <button class="cta-button" onclick="closeModal('paymentModal')">Közeli</button> </div> <form id="paymentForm" onsubmit="return processPayment(event)"> <h2 class="modal-title" id="modalTitle">Fizetés a szolgáltatásért</h2> <p class="modal-text" id="modalText">Szolgáltatás leírása</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">Adja meg az összeget (USD):</label> <input type="number" id="manualPrice" class="form-control" placeholder="Adja meg az összeget" 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)"> Adja meg az egyéni összeget </label> </div> <div class="form-group"> <label for="modalName">Név:</label> <input type="text" id="modalName" class="form-control" placeholder="Írja be a nevét" required> <div class="error-message" id="modalNameError"></div> </div> <div class="form-group"> <label for="modalEmail">Email:</label> <input type="email" id="modalEmail" class="form-control" placeholder="Adja meg e-mail címét" required> <div class="error-message" id="modalEmailError"></div> </div> <p class="modal-price" id="modalPrice">Ár: $0.00</p> <button type="submit" class="cta-button" id="payButton">Fizetés $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">Fizetés</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">Bejelentkezés</div> <div class="auth-tab" data-tab="register">Nyilvántartás</div> </div> <div class="auth-form active" id="loginForm"> <h2 class="modal-title">Jelentkezzen be fiókjába</h2> <div class="form-group"> <label for="loginEmail">Email:</label> <input type="email" id="loginEmail" class="form-control" autocomplete="email" placeholder="Adja meg e-mail címét"> <div class="error-message" id="loginEmailError"></div> </div> <div class="form-group"> <label for="loginPassword">Jelszó:</label> <input type="password" id="loginPassword" class="form-control" autocomplete="current-password" placeholder="Adja meg jelszavát"> <div class="error-message" id="loginPasswordError"></div> </div> <button class="cta-button" style="width:100%" onclick="processLogin()">Jelentkezzen be</button> <div style="text-align:center;margin:1rem 0;color:var(--gray);border-top:1px solid #e9ecef;padding-top:1rem;">Vagy folytassa ezzel</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()">📱 Jelentkezzen be a Telegram segítségével</button> <p style="margin-top:.5rem;color:var(--gray);font-size:.9rem">A Telegram-bejelentkezéshez először adja meg a Telegramhoz kapcsolódó Serpulse-fiók e-mail-címét.</p> </div> <div class="auth-form" id="registerForm"> <h2 class="modal-title">Hozzon létre fiókot</h2> <div class="form-group"><label for="registerName">Név:</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">Email:</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">Jelszó:</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">Jelszó megerősítése:</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()">Hozzon létre fiókot</button> <div style="text-align:center;margin:1rem 0;color:var(--gray);border-top:1px solid #e9ecef;padding-top:1rem;">Vagy hozzon létre egy fiókot</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()">📱 Regisztráljon a Telegramon</button> <p style="margin-top:1rem;color:var(--gray);font-size:.9rem">A jelszóhoz és a Telegram regisztrációhoz e-mail ellenőrzés szükséges. A Google és a GitHub regisztráció a szolgáltató által visszaküldött ellenőrzött e-mailt használja.</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">Ellenőrző kód</h2> <p class="modal-text">A kódot elküldték a Telegramnak. Írja be alább:</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()"> Erősítse meg a bejelentkezést </button> <div class="timer-container" id="loginCodeTimer"> A kód érvényes: <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">Teljes regisztráció a Telegramban</h2> <p class="modal-text" id="telegramModalText"> A Telegram új lapon nyílt meg.<br> Ha a bot nem nyílt meg automatikusan, használja az alábbi gombot, vagy olvassa be a QR-kódot. </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;"> Olvassa be a QR-kódot a bot megnyitásához </p> </div> <!-- Timer and Status --> <div id="telegramTimer" class="timer-container"> Megerősítésre vár... <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"> 📱 Nyissa meg újra a Telegramot </button> <button class="cta-button secondary" onclick="refreshTelegramQR()" id="refreshQRBtn"> 🔄 Frissítse a QR-kódot </button> </div> <!-- Status Info --> <div class="info-box" style="margin-top: 1.5rem;"> <p style="margin: 0; font-size: 0.9rem;"> <strong>Hogyan működik:</strong><br> 1. Kattintson a Telegram megnyitása lehetőségre, vagy olvassa be a QR-kódot<br> 2. A robotban kattintson a START gombra, és ossza meg telefonszámát<br> 3. A megerősítés állapota automatikusan frissül </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">Igazolja e-mail-címét</h2> <p class="modal-text" id="emailVerifyMessage">Ellenőrizze a beérkezett üzeneteket, és kattintson az általunk küldött ellenőrző linkre. Addig nem tud bejelentkezni jelszóval, amíg az e-mail címét meg nem erősíti.</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()">Ellenőrző e-mail újraküldése</button> <button class="cta-button" style="width:100%;margin-top:.75rem" onclick="closeEmailVerifyModal()">Közeli</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 || 'Profil'; } // 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('Hiba a bejelentkezési állapot ellenőrzése során:', 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'); localStorage.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 = 'Elrejt'; } else { // Hide all beyond the first 5 items.forEach((el, index) => { if (index >= max_count) el.classList.add('hidden'); }); btn.textContent = 'Mutasson többet'; } } // Toggle raw data visibility function toggleRawData(elementId, btn) { const element = document.getElementById(elementId); if (element.style.maxHeight === '200px') { element.style.maxHeight = 'none'; btn.textContent = 'Teljes adat elrejtése'; } else { element.style.maxHeight = '200px'; btn.textContent = 'Teljes adat megjelenítése'; } } </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 = 'Ár: $' + parseFloat(price).toFixed(2); payButton.textContent = 'Fizetés $' + 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 = 'Tól $${paymentConfig.minAmount} hogy $${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 = 'Ár: -tól $${paymentConfig.minAmount} hogy $${paymentConfig.maxAmount}'; payButton.textContent = 'Érvénytelen összeg'; payButton.disabled = true; payButton.style.opacity = '0.6'; } else { modalPrice.textContent = 'Ár: $0.00'; payButton.textContent = 'Fizetés $0.00'; payButton.disabled = true; payButton.style.opacity = '0.6'; } return; } const modalPrice = document.getElementById('modalPrice'); const payButton = document.getElementById('payButton'); modalPrice.textContent = 'Ár: $' + parseFloat(price).toFixed(2); payButton.textContent = 'Fizetés $' + 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('Hiba a fizetési lehetőségek elemzésekor:', 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', 'Az összegnek között kell lennie $${paymentConfig.minAmount} és $${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', 'Kérjük, adja meg a nevét'); hasErrors = true; } if (!email) { showError('modalEmail', 'Kérjük, adja meg e-mail címét'); hasErrors = true; } else if (!/\S+@\S+\.\S+/.test(email)) { showError('modalEmail', 'Kérjük, adjon meg egy érvényes e-mail címet'); 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('Hiba: ' + 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 = "58356daef1aec1dde49b28350f7582007ccb5b945c576cbcb804a5cb33d0cfb9"; // 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: 'Érvénytelen válasz a szervertől'}); } if(is_good) callback(response); } else { callback({status: 'error', error: 'Szerver hiba: ' + 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">Erősítsd meg, hogy nem vagy robot</h2> <p class="modal-text">Írja be a kép szövegét:</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()">Frissítse a kódot</button> <input type="text" class="captcha-input" id="captchaInput" placeholder="Írja be a kódot" maxlength="6"> </div> <button class="cta-button" onclick="submitAnalysis()" style="margin-top: 1rem;">Küldje be elemzésre</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('Kérjük, írja be a képen látható kódot', 'error'); return; } // Show loading state analysisStatus.textContent = 'Kérés küldése...'; 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 = 'Hiba: típusa nincs'; 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 = 'Kérését elküldtük, kérjük, várja meg a domain ellenőrzését'; analysisStatus.className = 'analysis-status success'; } else { // Show error message analysisStatus.textContent = 'Hiba: ' + 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: 'Fiók létrehozva. Ellenőrizze e-mailjeit, és a bejelentkezés előtt kattintson az ellenőrző linkre.', loginSuccess: 'Sikeresen bejelentkezett.', emailVerified: 'E-mail igazolva. Fiókja aktív, és be van jelentkezve.', emailTelegramPending: 'E-mail igazolva. Fiókja aktiválásához fejezze be a Telegram-ellenőrzést.', emailVerificationFailed: 'Ez az e-mail-ellenőrző link érvénytelen vagy lejárt.', googleSuccess: 'Google-lal bejelentkezve.', githubSuccess: 'Bejelentkezve a GitHub használatával.', oauthCancelled: 'A külső bejelentkezést törölték.', oauthFailed: 'A külső bejelentkezés nem sikerült. Kérjük, próbálja újra.', telegramVerifiedEmailPending: 'A táviratos ellenőrzés befejeződött. Ellenőrizze e-mailjét, és kattintson az ellenőrző linkre fiókja aktiválásához.', telegramRegistrationComplete: 'A regisztráció befejeződött. Ön most bejelentkezett.', telegramLoginSuccess: 'Bejelentkezve a Telegram segítségével.', invalidResponse: 'Érvénytelen válasz a szervertől' }; return messages[key] || key; } function startOAuth(provider) { if (!['google', 'github'].includes(provider)) { showToast('Nem támogatott bejelentkezési szolgáltató.', 'error'); return; } window.location.href = '/auth/' + provider + '.php?lang=' + encodeURIComponent(window.currentLang || 'en'); } function showEmailVerificationModal(email, message) { const normalizedEmail = (email || '').trim().toLowerCase(); if (normalizedEmail) localStorage.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 || 'Ellenőrizze a beérkezett üzeneteket, és kattintson az általunk küldött ellenőrző linkre. Addig nem tud bejelentkezni jelszóval, amíg az e-mail címét meg nem erősíti.'; 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 = (localStorage.getItem('pending_verification_email') || '').trim().toLowerCase(); if (!email) { showToast('Az ellenőrző üzenet újraküldéséhez adja meg újra az e-mail-címét.', '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 || 'Jelenleg nem tudtuk elküldeni az ellenőrző e-mailt. Kérjük, próbálja újra később.', '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', 'Kérjük, adja meg e-mail címét'); hasErrors = true; } else if (!/\S+@\S+\.\S+/.test(email)) { showError('loginEmail', 'Kérjük, adjon meg egy érvényes e-mail címet'); hasErrors = true; } if (!password) { showError('loginPassword', 'Kérjük, adja meg jelszavát'); 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 || 'Érvénytelen e-mail cím vagy jelszó.'); }); } 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', 'Kérjük, adja meg a nevét'); hasErrors = true; } if (!email) { showError('registerEmail', 'Kérjük, adja meg e-mail címét'); hasErrors = true; } else if (!/\S+@\S+\.\S+/.test(email)) { showError('registerEmail', 'Kérjük, adjon meg egy érvényes e-mail címet'); hasErrors = true; } if (!password) { showError('registerPassword', 'Kérjük, hozzon létre egy jelszót'); hasErrors = true; } else if (password.length < 8) { showError('registerPassword', 'A jelszónak legalább 8 karakterből kell állnia'); hasErrors = true; } if (!confirmPassword) { showError('registerConfirmPassword', 'Kérjük, erősítse meg jelszavát'); hasErrors = true; } else if (password !== confirmPassword) { showError('registerConfirmPassword', 'A jelszavak nem egyeznek'); hasErrors = true; } if (hasErrors) return; sendRequest('/ajax/', {type: 'registration', name: name, email: email, password: password}, function(response) { if (response.status === 'verification_required') { localStorage.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 || 'A regisztrációt nem sikerült befejezni. Kérjük, próbálja újra.', '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('A távirat-ellenőrző link lejárt. Kérjük, kezdje újra.', '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 = 'Teljes regisztráció a Telegramban'; if (text) text.innerHTML = 'A Telegram új lapon nyílt meg.<br>Ossza meg saját telefonszámát a bottal, majd térjen vissza ide. E-mail ellenőrzés is szükséges.'; 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 name = document.getElementById('registerName').value.trim(); const email = document.getElementById('registerEmail').value.trim().toLowerCase(); clearErrors(['registerName','registerEmail']); let hasErrors=false; if (!name) { showError('registerName','Kérjük, adja meg a nevét'); hasErrors=true; } if (!email) { showError('registerEmail','Kérjük, adja meg e-mail címét'); hasErrors=true; } else if (!/\S+@\S+\.\S+/.test(email)) { showError('registerEmail','Kérjük, adjon meg egy érvényes e-mail címet'); hasErrors=true; } if (hasErrors) return; localStorage.setItem('pending_verification_email', email); sendRequest('/ajax/', {type:'telegram_registration_start',name:name,email:email}, function(response){ if (response.status === 'continue_email_verification') { closeModal('authModal'); showEmailVerificationModal(email, 'A Telegram már ellenőrizve van ehhez a fiókhoz. A fiók aktiválásához igazolja e-mail-címét.'); showToast('Az e-mail ellenőrzés továbbra is szükséges.', 'info'); return; } if (response.status !== 'ok') { showToast(response.error || 'A távirat regisztrációt nem sikerült elindítani.','error'); return; } localStorage.setItem('current_user_token', response.user_token); closeModal('authModal'); if (response.email_sent === false) { showToast(response.message || 'Az ellenőrző e-mailt nem sikerült elküldeni. Kérjük, használja az Ellenőrző e-mail újraküldése.','info'); } 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=localStorage.getItem('current_user_token') || ''; if (!token) return; sendRequest('/ajax/', {type:'complete_registration',user_token:token}, function(response){ if (response.status !== 'ok') { showToast(response.error || 'A regisztrációt nem sikerült befejezni.','error'); return; } localStorage.removeItem('current_user_token'); localStorage.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','Kérjük, adja meg e-mail címét'); return; } if (!/\S+@\S+\.\S+/.test(email)) { showError('loginEmail','Kérjük, adjon meg egy érvényes e-mail címet'); return; } generateTelegramLoginLink(email); } function generateTelegramLoginLink(email) { sendRequest('/ajax/', {type:'generate_telegram_login_link',email:email}, function(response){ if (response.status !== 'ok') { showToast(response.error || 'Nem sikerült elindítani a Telegram bejelentkezést','error'); return; } localStorage.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='Bejelentkezés Telegramon keresztül'; if (text) text.innerHTML='Nyissa meg a Telegram botot, hogy megkapja bejelentkezési kódját, majd írja be a kódot ezen a webhelyen.'; 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=localStorage.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('A kód lejárt. Kérem kezdje elölről.','error'); } },1000); } function verifyTelegramLoginCode() { const token=localStorage.getItem('current_user_token') || ''; const input=document.getElementById('telegramLoginCode'); const code=input ? input.value.trim() : ''; clearErrors(['telegramLoginCode']); if (!/^\\d{7}$/.test(code)) { showError('telegramLoginCode','A kódnak 7 számjegyből kell állnia'); return; } sendRequest('/ajax/', {type:'verify_telegram_login_code',user_token:token,verify_code:code}, function(response){ if (response.status!=='ok') { showError('telegramLoginCode',response.error || 'Rossz kód vagy lejárt'); return; } if (window.loginCodeTimer) clearInterval(window.loginCodeTimer); localStorage.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_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]); 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;">🍪 Cookie hozzájárulás</h3> <p style="margin: 0; color: var(--gray, #6c757d); line-height: 1.5;"> Cookie-kat használunk a böngészési élmény fokozására, a személyre szabott tartalmak kiszolgálására és a forgalmunk elemzésére. Az Accept All gombra kattintva hozzájárul a cookie-k használatához. Olvassa el a mi <a href="/en/cookie/" style="color: var(--primary, #4361ee); text-decoration: underline;">Cookie-szabályzat</a> és <a href="/en/privacy/" style="color: var(--primary, #4361ee); text-decoration: underline;">Adatvédelmi szabályzat</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);"> Az összes elfogadása </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;"> Csak alapvető </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;"> Testreszabás </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);">Cookie-beállítások</h3> </div> <div style="padding: 1.5rem;"> <p style="color: var(--gray, #6c757d); margin-bottom: 1.5rem;">Válassza ki, mely sütiket engedélyezi. Ezeket a beállításokat bármikor módosíthatja.</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);">Essential Cookie-k</strong> <p style="margin: 0.25rem 0 0; font-size: 0.9rem; color: var(--gray, #6c757d);">A weboldal működéséhez szükséges. Nem lehet letiltani.</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);">Analytics cookie-k</strong> <p style="margin: 0.25rem 0 0; font-size: 0.9rem; color: var(--gray, #6c757d);">Segítsen nekünk megérteni, hogyan lépnek kapcsolatba a látogatók webhelyünkkel.</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);">Funkcionális sütik</strong> <p style="margin: 0.25rem 0 0; font-size: 0.9rem; color: var(--gray, #6c757d);">Ne feledje a preferenciáit és beállításait.</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);">Célzó cookie-k</strong> <p style="margin: 0.25rem 0 0; font-size: 0.9rem; color: var(--gray, #6c757d);">Releváns tartalom és hirdetések megjelenítésére szolgál.</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;"> Mentse a Beállításokat </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;"> Mégse </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('Hiba a cookie hozzájárulásának elemzésekor:', e); } } // Try localStorage const stored = localStorage.getItem(COOKIE_CONSENT_KEY); if (stored) { try { return JSON.parse(stored); } catch (e) { console.error('Hiba a localStorage hozzájárulás elemzésekor:', 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', 'Cookie-beállítások'); 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>