diff --git a/Web/Presenters/OpenVKPresenter.php b/Web/Presenters/OpenVKPresenter.php index 6df21bd2..359ab8b6 100644 --- a/Web/Presenters/OpenVKPresenter.php +++ b/Web/Presenters/OpenVKPresenter.php @@ -283,10 +283,11 @@ abstract class OpenVKPresenter extends SimplePresenter } } - /*if($this->queryParam('al') == '1') { + if($this->queryParam('al') == '1') { + error_reporting(0); $this->assertNoCSRF(); header('Content-Type: text/plain; charset=UTF-8'); - }*/ + } parent::onStartup(); } diff --git a/Web/Presenters/templates/@layout.xml b/Web/Presenters/templates/@layout.xml index ce6c080c..b660cb53 100644 --- a/Web/Presenters/templates/@layout.xml +++ b/Web/Presenters/templates/@layout.xml @@ -446,6 +446,7 @@ {ifset bodyScripts} {include bodyScripts} {/ifset} + {script "js/router.js"} {/if} diff --git a/Web/Presenters/templates/Audio/Upload.xml b/Web/Presenters/templates/Audio/Upload.xml index 8151de2e..61d5904b 100644 --- a/Web/Presenters/templates/Audio/Upload.xml +++ b/Web/Presenters/templates/Audio/Upload.xml @@ -242,7 +242,7 @@ if(response.success) { u("#backToUpload").trigger("click") NewNotification(tr("success"), tr("audio_successfully_uploaded"), null, () => { - window.location.assign(response.redirect_link) + window.router.route(response.redirect_link) }) } else { fastError(response.flash.message) diff --git a/Web/Presenters/templates/Gifts/Pick.xml b/Web/Presenters/templates/Gifts/Pick.xml index cb01b944..22345389 100644 --- a/Web/Presenters/templates/Gifts/Pick.xml +++ b/Web/Presenters/templates/Gifts/Pick.xml @@ -52,7 +52,7 @@ let link = "/gifts?act=confirm&user={$user->getId()}&pack={$cat->getId()}&elid="; let gift = el.data("gift"); - window.location.assign(link + gift); + window.router.route(link + gift); }); {/block} \ No newline at end of file diff --git a/Web/static/css/main.css b/Web/static/css/main.css index 17c61ec4..afeb2c12 100644 --- a/Web/static/css/main.css +++ b/Web/static/css/main.css @@ -2831,6 +2831,10 @@ a.poll-retract-vote { z-index: 2; } +.page_header.search_expanded_at_all .header_navigation #search_box #searchBoxFastTips.shown { + display: none; +} + .page_header.search_expanded .link { display: none; } diff --git a/Web/static/js/al_feed.js b/Web/static/js/al_feed.js index 7ca8a798..87981a04 100644 --- a/Web/static/js/al_feed.js +++ b/Web/static/js/al_feed.js @@ -157,7 +157,7 @@ u(document).on('click', '#__feed_settings_link', (e) => { FINAL_URL.searchParams.delete('return_banned') } - window.location.assign(FINAL_URL.href) + window.router.route(FINAL_URL.href) }) COUNT.forEach(item => { diff --git a/Web/static/js/al_music.js b/Web/static/js/al_music.js index 4821d2ef..f2bb7f4f 100644 --- a/Web/static/js/al_music.js +++ b/Web/static/js/al_music.js @@ -664,7 +664,7 @@ u(document).on('keyup', (e) => { return } - if(!window.player) { + if(!window.player || !window.player.isAtAudiosPage()) { return } @@ -1294,7 +1294,7 @@ $(document).on("click", "#_deletePlaylist", (e) => { }, success: (response) => { if(response.success) { - window.location.assign("/playlists" + response.id) + window.router.route("/playlists" + response.id) } else { fastError(response.flash.message) } diff --git a/Web/static/js/al_photos.js b/Web/static/js/al_photos.js index b4632d60..e8b0d8ef 100644 --- a/Web/static/js/al_photos.js +++ b/Web/static/js/al_photos.js @@ -108,7 +108,7 @@ $(document).on("click", "#endUploading", (e) => { document.querySelector(".page_content .insertPhotos").innerHTML = "" document.getElementById("endUploading").style.display = "none" - NewNotification(tr("photos_successfully_uploaded"), tr("click_to_go_to_album"), null, () => {window.location.assign(`/album${result.owner}_${result.album}`)}) + NewNotification(tr("photos_successfully_uploaded"), tr("click_to_go_to_album"), null, () => {window.router.route({url:`/album${result.owner}_${result.album}`})}) document.querySelector(".whiteBox").style.display = "block" document.querySelector(".insertAgain").append(document.getElementById("fakeButton")) diff --git a/Web/static/js/al_wall.js b/Web/static/js/al_wall.js index 4b2df257..a4123c63 100644 --- a/Web/static/js/al_wall.js +++ b/Web/static/js/al_wall.js @@ -53,8 +53,9 @@ u(document).on('click', '.menu_toggler', (e) => { } }) -$(document).on("click", ".post-like-button", function(e) { +u(document).on("click", ".post-like-button", function(e) { e.preventDefault(); + e.stopPropagation() var thisBtn = u(this).first(); var link = u(this).attr("href"); diff --git a/Web/static/js/router.js b/Web/static/js/router.js new file mode 100644 index 00000000..595df21d --- /dev/null +++ b/Web/static/js/router.js @@ -0,0 +1,239 @@ +window.router = new class { + skeletons = { + + } + + get csrf() { + return u("meta[name=csrf]").attr("value") + } + + __isScriptAlreadyLoaded(script) { + if(script.src) { + const script_url = new URL(script.src) + const script_main_part = script_url.pathname + console.log(script_main_part) + return u(`script[src^='${script_main_part}']`).length > 0 + } + + return false + } + + __appendScript(script) { + const _t_scr = document.createElement('script') + if(script.src) { + _t_scr.src = script.src + } else { + _t_scr.textContent = script.textContent + } + + document.body.appendChild(_t_scr) + } + + __clearScripts() { + u(`script:not([src])`).remove() + } + + __appendPage(parsed_content) { + if(u('.paginator:not(.paginator-at-top)').length > 0) { + showMoreObserver.unobserve(u('.paginator:not(.paginator-at-top)').nodes[0]) + } + + const page_body = u(parsed_content.querySelector('.page_body')) + const sidebar = u(parsed_content.querySelector('.sidebar')) + const page_header = u(parsed_content.querySelector('.page_header')) + + if(page_body.length < 1) { + makeError('Err') + return + } + + this.__clearScripts() + parsed_content.querySelectorAll('script').forEach(script => { + console.log(script) + if(!this.__isScriptAlreadyLoaded(script)) { + this.__appendScript(script) + } + }) + u('.page_body').html(page_body.html()) + u('.sidebar').html(sidebar.html()) + if(u('.page_header #search_box select').length > 0 && page_header.find('#search_box select').length > 0) { + u('.page_header #search_box select').nodes[0].value = page_header.find('#search_box select').nodes[0].value + } + + if(page_header.hasClass('search_expanded_at_all')) { + u('.page_header').addClass('search_expanded_at_all').addClass('search_expanded') + } else { + if(u('.page_header').hasClass('search_expanded_at_all')) { + u('.page_header').removeClass('search_expanded_at_all').removeClass('search_expanded') + } + } + + u("meta[name=csrf]").attr("value", u(parsed_content.querySelector('meta[name=csrf]')).attr('value')) + + document.title = parsed_content.title + window.scrollTo(0, 0) + bsdnHydrate() + + if(u('.paginator:not(.paginator-at-top)').length > 0) { + showMoreObserver.observe(u('.paginator:not(.paginator-at-top)').nodes[0]) + } + } + + checkUrl(url) { + if((localStorage.getItem('ux.disable_ajax_routing') ?? 0) == 1 || window.openvk.current_id == 0) { + return false + } + + if(!url || url == '') { + return false + } + + if(url.indexOf(location.origin) == -1) { + return false + } + + return true + } + + async route(params = {}) { + if(typeof params == 'string') { + params = { + url: params + } + } + + const old_url = location.href + let url = params.url + if(url.indexOf(location.origin)) { + url = location.origin + url + } + + if((localStorage.getItem('ux.disable_ajax_routing') ?? 0) == 1 || window.openvk.current_id == 0) { + window.location.assign(url) + return + } + + const push_url = params.push_state ?? true + const next_page_url = new URL(url) + next_page_url.searchParams.append('al', 1) + next_page_url.searchParams.append('hash', this.csrf) + if(push_url) { + history.pushState({'from_router': 1}, '', url) + } else { + history.replaceState({'from_router': 1}, '', url) + } + + const parser = new DOMParser + const next_page_request = await fetch(next_page_url, { + method: 'GET' + }) + const next_page_text = await next_page_request.text() + const parsed_content = parser.parseFromString(next_page_text, 'text/html') + if(next_page_request.redirected) { + history.replaceState({'from_router': 1}, '', next_page_request.url) + } + + this.__appendPage(parsed_content) + } +} + +u(document).on('click', 'a', async (e) => { + const target = u(e.target).closest('a') + const dom_url = target.attr('href') + const id = target.attr('id') + let url = target.nodes[0].href + + if(id) { + if(['act_tab_a', 'ki'].indexOf(id) == -1) { + console.log('AJAX | Skipping cuz maybe its function call link.') + return + } + } + + if(!dom_url || dom_url == '#' || dom_url.indexOf('javascript:') != -1) { + console.log('AJAX | Skipped cuz its anchor or function call') + return + } + + if(target.attr('target') == '_blank') { + console.log('AJAX | Skipping cuz its _blank.') + return + } + + if(!window.router.checkUrl(url)) { + return + } + + e.preventDefault() + + console.log(`AJAX | Going to URL ${url}`) + await window.router.route({ + url: url, + }) +}) + +u(document).on('submit', 'form', async (e) => { + if(u('#ajloader').hasClass('shown')) { + e.preventDefault() + return + } + + if((localStorage.getItem('ux.disable_ajax_routing') ?? 0) == 1 || window.openvk.current_id == 0) { + return false + } + + e.preventDefault() + u('#ajloader').addClass('shown') + + const form = e.target + const method = form.method ?? 'get' + const url = form.action + + const url_object = new URL(url) + url_object.searchParams.append('al', 1) + if(method == 'get' || method == 'GET') { + url_object.searchParams.append('hash', window.router.csrf) + $(form).serializeArray().forEach(param => { + url_object.searchParams.append(param.name, param.value) + }) + } + + if(!url) { + return + } + + const form_data = serializeForm(form) + const request_object = { + method: method + } + + if(method != 'GET' && method != 'get') { + request_object.body = form_data + } + + const form_res = await fetch(url_object, request_object) + const form_result = await form_res.text() + switch(form_res.status) { + case 500: + makeError(form_res.statusText) + break + } + + const parser = new DOMParser + const parsed_content = parser.parseFromString(form_result, 'text/html') + + if(form_res.redirected) { + history.replaceState({'from_router': 1}, '', form_res.url) + } else { + const __new_url = new URL(form_res.url) + __new_url.searchParams.delete('al') + __new_url.searchParams.delete('hash') + + history.pushState({'from_router': 1}, '', __new_url) + } + + console.log(form_res) + window.router.__appendPage(parsed_content) + + u('#ajloader').removeClass('shown') +}) diff --git a/Web/static/js/utils.js b/Web/static/js/utils.js index afa2bd69..cc83b470 100644 --- a/Web/static/js/utils.js +++ b/Web/static/js/utils.js @@ -181,3 +181,37 @@ function getRemainingTime(fullTime, time) { return "-" + fmtTime(timer) } + +function serializeForm(form) +{ + const u_ = u(form) + const inputs = u_.find('input, textarea') + console.log(inputs) + let fd = new FormData() + inputs.nodes.forEach(inp => { + if(!inp || !inp.name) { + return + } + + if(inp.type == 'submit') { + return + } + + switch(inp.type) { + case 'submit': + return + case 'hidden': + case 'text': + case 'textarea': + fd.append(inp.name, inp.value) + break + case 'file': + for(const __file of inp.files) { + fd.append(inp.name, __file) + } + break + } + }) + + return fd +}