diff options
Diffstat (limited to 'view/js/main.js')
-rw-r--r-- | view/js/main.js | 1199 |
1 files changed, 905 insertions, 294 deletions
diff --git a/view/js/main.js b/view/js/main.js index 2ad7b86fb..5bf7234aa 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -25,6 +25,11 @@ var savedTitle = ''; var followUpPageLoad = false; var window_needs_alert = true; var expanded_items = []; +var updateTimeout = []; +const singlethread_modules = ['display', 'hq']; +const redirect_modules = ['display', 'notify']; +let b64mids = []; + var page_cache = {}; @@ -58,37 +63,13 @@ $.ajaxSetup({cache: false}); var tf = new Function('n', 's', 'var k = s.split("/")['+aStr['plural_func']+']; return (k ? k : s);'); -jQuery.timeago.settings.strings = { - prefixAgo : aStr['t01'], - prefixFromNow : aStr['t02'], - suffixAgo : aStr['t03'], - suffixFromNow : aStr['t04'], - seconds : aStr['t05'], - minute : aStr['t06'], - minutes : function(value){return tf(value, aStr['t07']);}, - hour : aStr['t08'], - hours : function(value){return tf(value, aStr['t09']);}, - day : aStr['t10'], - days : function(value){return tf(value, aStr['t11']);}, - month : aStr['t12'], - months : function(value){return tf(value, aStr['t13']);}, - year : aStr['t14'], - years : function(value){return tf(value, aStr['t15']);}, - wordSeparator : aStr['t16'], - numbers : aStr['t17'], -}; - -jQuery.timeago.settings.allowFuture = true; - $(document).ready(function() { $(document).on('click focus', '.comment-edit-form', handle_comment_form); $(document).on('click', '.conversation-settings-link', getConversationSettings); $(document).on('click', '#settings_module_ajax_submit', postConversationSettings); - $(document).on('click', '#expand-aside', function() { - toggleAside(); - }); + $(document).on('click', '#expand-aside', toggleAside); $(document).on('click focus', '.comment-edit-form textarea', function(e) { if(! this.autocomplete_handled) { @@ -108,13 +89,153 @@ $(document).ready(function() { } }); + document.addEventListener('click', function(event) { + // Only handle clicks on .wall-item-reaction or its children + const target = event.target.closest('.wall-item-reaction'); + if (!target) return; + + let doRequest = true; + const isUserClick = event.isTrusted; + + // Destructure relevant data attributes + const { itemId: id, itemMid: mid, itemParent: parentId, itemUuid: uuid, itemVerb: verb } = target.dataset; + const isCommentBtn = target.classList.contains('wall-item-comment'); + + if (isCommentBtn) { + if (id === parentId) { + // Handle blog mode + target.classList.add('disabled'); + document.getElementById(`load-more-progress-wrapper-${id}`).classList.remove('d-none'); + document.getElementById(`load-more-${id}`).classList.remove('d-none') + request(id, mid, 'load', parentId, uuid, isUserClick); + return; + } + + // Get relevant DOM elements + const threadWrapper = document.getElementById(`thread-wrapper-${id}`); + const parentWrapper = document.getElementById(`thread-wrapper-${parentId}`); + const subThreadWrapper = document.getElementById(`wall-item-sub-thread-wrapper-${id}`); + const parentSubThreadWrapper = document.getElementById(`wall-item-sub-thread-wrapper-${parentId}`); + + // Query related sub-thread and highlight elements + const parentIndentedThreads = document.querySelectorAll(`#wall-item-sub-thread-wrapper-${parentId} .wall-item-sub-thread-wrapper.item-indent`); + + let ancestorIds = []; + + doRequest = !subThreadWrapper.children.length; + + // Set visual styles using UUID + subThreadWrapper.style.setProperty('--hz-item-indent', stringToHslColor(uuid)); + threadWrapper.style.setProperty('--hz-item-highlight', stringToHslColor(uuid)); + threadWrapper.style.setProperty('--hz-wall-item-expanded-before-content', '"' + aStr.dblclick_to_exit_zoom + '"'); + + // Clear previous highlights + parentSubThreadWrapper.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => el.classList.remove('item-highlight')); + + if (isUserClick && parentIndentedThreads.length === 0 && !subThreadWrapper.children.length) { + // Handle first-time expansion and highlighting but not for toplevels (blog mode) + threadWrapper.classList.add('item-highlight'); + } else { + // Handle indentation and zooming + let ancestor = subThreadWrapper.parentElement; + ancestorIds.push(ancestor.id.slice(15)); // thread-wrapper-1234 + + while (ancestor) { + if (ancestor.classList.contains('item-indent') && ancestor.classList.contains('wall-item-sub-thread-wrapper')) { + ancestorIds.push(ancestor.parentElement.id.slice(15)); + } + ancestor = ancestor.parentElement; + } + + ancestorIds.reverse(); + + if (ancestorIds.length > 3) { + // Handle zooming in + let firstWrapper = document.getElementById('thread-wrapper-' + ancestorIds[0]); + let firstSubWrapper = document.getElementById('wall-item-sub-thread-wrapper-' + ancestorIds[0]); + + firstWrapper.querySelector('.wall-item-comment').classList.remove('indented'); + firstWrapper.classList.remove('wall-item-expanded', 'shadow'); + firstSubWrapper.classList.remove('item-indent'); + + let newFirstWrapper = document.getElementById('thread-wrapper-' + ancestorIds[1]) + let newFirstSubWrapper = document.getElementById('wall-item-sub-thread-wrapper-' + ancestorIds[1]) + + newFirstWrapper.classList.add('wall-item-expanded', 'shadow'); + parentWrapper.classList.add('wall-item-backdrop'); + + // Exit zoom on double-click + newFirstWrapper.addEventListener('dblclick', function() { + parentWrapper.querySelectorAll('.wall-item-comment.indented').forEach(el => el.classList.remove('indented')); + parentWrapper.querySelectorAll('.wall-item-comment.collapsed').forEach(el => el.classList.remove('collapsed')); + parentWrapper.classList.remove('wall-item-backdrop'); + parentWrapper.querySelectorAll('.wall-item-sub-thread-wrapper.item-indent').forEach(el => el.classList.remove('item-indent')); + parentWrapper.querySelectorAll('.wall-item-sub-thread-wrapper.d-none').forEach(el => el.classList.remove('d-none')); + parentWrapper.querySelectorAll('.thread-wrapper.wall-item-expanded').forEach(el => el.classList.remove('wall-item-expanded', 'shadow')); + }, { once: true }); + } + + // Toggle sub-thread visibility if indented + if (isUserClick && target.classList.contains('indented')) { + doRequest = false; + subThreadWrapper.classList.toggle('d-none'); + target.classList.toggle('collapsed'); + } + + // Indenting of already expanded but flattened items + if (isUserClick && subThreadWrapper.classList.contains('item-expanded') && !subThreadWrapper.classList.contains('item-indent')) { + doRequest = false; + + threadWrapper.querySelectorAll('.wall-item-sub-thread-wrapper.item-expanded').forEach(function (el, i) { + el.classList.add('item-indent'); + + el.querySelectorAll('.wall-item-comment.expanded').forEach(function (el, i) { + el.classList.add('collapsed', 'indented'); + }); + + // Collapse everything below the first level + if (i > 0) { + el.classList.add('d-none'); + } + }); + } + + // Indent the subthread + subThreadWrapper.classList.add('item-indent', 'item-expanded'); + + // Mark as indented after visibility toggle + target.classList.add('indented'); + } + + // Mark as expanded + target.classList.add('expanded'); + } + + if (doRequest) { + request(id, mid, verb, parentId, uuid, isUserClick); + } + }); + + document.addEventListener('click', function(event) { + const targetElement = event.target.closest('.dropdown-item-expand'); + if (!targetElement) return; + + event.preventDefault(); + + const id = targetElement.dataset.itemId; + const subWrapper = document.getElementById(`wall-item-sub-thread-wrapper-${id}`); + + subWrapper.innerHTML = ''; + autoExpand(id); + }); + // @hilmar |-> if ( typeof(window.tao) == 'undefined' ) { window.tao = {}; } if ( typeof(window.tao.zin) == 'undefined' ) { tao.zin = { syslc: '', syslcs: {}, htm: '', me: '', debug: '' }; - tao.zin.axim = '<div class="zinpax fa fa-sync fa-spin"> </div>'; + tao.zin.axim = '<i class="zinpax bi bi-arrow-repeat"></i>'; $('.navbar-app[href*="/lang"]').attr({"aria-expand": "true", "id": "zintog"}) .removeAttr('href').addClass('zinlcx zinlcxp dropdown dropdown-toggle').css('cursor','pointer'); $('.nav-link[href*="/lang"]').addClass('zinlcxmi zinlcx').removeAttr('href').css('cursor','pointer'); @@ -129,9 +250,9 @@ $(document).ready(function() { tao.zin.re = JSON.parse(re); tao.zin.syslc = tao.zin.re.lc; tao.zin.syslcs = tao.zin.re.lcs; - tao.zin.htm = '<ul class="zinlcs fa-ul">'; + tao.zin.htm = '<ul class="zinlcs">'; $.each( tao.zin.syslcs, function( k, v ) { - tao.zin.htm += '<li><a id="zinlc' + k + '" class="zinlc nav-link fakelink">' + k + ' ' + v + '</a></li>'; + tao.zin.htm += '<li><a id="zinlc' + k + '" class="zinlc fakelink">' + k + ' ' + v + '</a></li>'; }); tao.zin.htm += '</ul>'; $('.zinpax').remove(); @@ -172,17 +293,20 @@ $(document).ready(function() { let notify_id = this.dataset.notify_id; let path = $(this)[0].pathname.split('/')[1]; let stateObj = { b64mid: b64mid }; - let singlethread_modules = ['display', 'hq']; - let redirect_modules = ['display', 'notify']; - if(! b64mid && ! notify_id) + if (!b64mid && !notify_id) { return; + } + + if (document.querySelector('main').classList.contains('region_1-on')) { + toggleAside(); + } - if(localUser && redirect_modules.indexOf(path) !== -1) { + if (localUser && redirect_modules.indexOf(path) !== -1) { path = 'hq'; } - if(notify_id) { + if (notify_id) { $.ajax({ type: 'post', url: 'notify', @@ -198,21 +322,17 @@ $(document).ready(function() { window.location.href = path + '/' + b64mid; } else { - if (singlethread_modules.indexOf(module) !== -1) { history.pushState(stateObj, '', module + '/' + b64mid); - } if (b64mid) { - e.preventDefault(); - - if(! page_load) { + if (!page_load) { prepareLiveUpdate(b64mid, notify_id); $('.message').removeClass('active'); $('[data-b64mid="' + b64mid + '"].message').addClass('active'); - $('[data-b64mid="' + b64mid + '"].message .badge').remove(); + $('[data-b64mid="' + b64mid + '"].message .unseen_count').remove(); } } } @@ -231,20 +351,21 @@ $(document).ready(function() { updateInit(); - var e = document.getElementById('content-complete'); - if(e) + if (document.getElementById('content-complete')) { pageHasMoreContent = false; + } - $(document).on('hz:updateConvItems', function(event) { - if(!bParam_mid) + document.addEventListener('hz:updateConvItems', function(e) { + if (!bParam_mid) { cache_next_page(); + } }); - $(document).on('hz:handleNetworkNotificationsItems', function(e, obj) { + document.addEventListener('hz:handleNetworkNotificationsItems', function(e) { push_notification( - obj.name, - $('<p>' + obj.message + '</p>').text(), - obj.b64mid + e.detail.name, + $('<p>' + e.detail.message + '</p>').text(), + e.detail.b64mid ); }); }); @@ -256,19 +377,18 @@ function getConversationSettings() { } function postConversationSettings() { - $.post( - 'settings/conversation', - $('#settings_module_ajax_form').serialize() + "&auto_update=" + next_page - ); - if(next_page === 1) { page_load = true; } - $('#conversation_settings').modal('hide'); - - if(timer) clearTimeout(timer); - timer = setTimeout(updateInit,100); + $.post( + 'settings/conversation', + $('#settings_module_ajax_form').serialize() + "&auto_update=" + next_page + ).done(function() { + $('#conversation_settings').modal('hide'); + toast('Conversation features updated', 'info'); + updateInit(); + }); return false; } @@ -351,6 +471,13 @@ function handle_comment_form(e) { var commentSaveTimer = null; var emptyCommentElm = form.find('.comment-edit-text').attr('id'); var convId = emptyCommentElm.replace('comment-edit-text-',''); + + // in case parent input is set use it as convId + const parentInputVal = form.find(':input[name=parent]').val(); + if (parentInputVal) { + convId = parentInputVal; + } + $('#' + emptyCommentElm).on('focusout',function(e){ if(commentSaveTimer) clearTimeout(commentSaveTimer); @@ -469,16 +596,18 @@ function inserteditortag(BBcode, id) { } function insertCommentAttach(comment,id) { - activeCommentID = id; activeCommentText = comment; - $('body').css('cursor', 'wait'); - $('#invisible-comment-upload').trigger('click'); - return false; +} +function insertCommentEmbed(comment,id) { + activeCommentID = id; + activeCommentText = comment; + initializeEmbedPhotoDialog(); + return false; } function insertCommentURL(comment, id) { @@ -515,17 +644,22 @@ function viewsrc(id) { } function showHideComments(id) { - if( $('#collapsed-comments-' + id).is(':visible')) { - $('#collapsed-comments-' + id + ' .autotime').timeago('dispose'); - $('#collapsed-comments-' + id).hide(); - $('#hide-comments-' + id).html(aStr.showmore); - $('#hide-comments-total-' + id).show(); - } else { - $('#collapsed-comments-' + id + ' .autotime').timeago(); - $('#collapsed-comments-' + id).show(); - $('#hide-comments-' + id).html(aStr.showfewer); - $('#hide-comments-total-' + id).hide(); + let collapsedComments = document.getElementById('collapsed-comments-' + id); + let hideCommentsLabel = document.getElementById('hide-comments-label-' + id); + let hideCommentsTotal = document.getElementById('hide-comments-total-' + id); + let hideCommentsIcon = document.getElementById('hide-comments-icon-' + id); + let isCollapsed = collapsedComments.style.display === 'none'; + + collapsedComments.style.display = isCollapsed ? '' : 'none'; + hideCommentsLabel.textContent = isCollapsed ? hideCommentsLabel.dataset.expanded : hideCommentsLabel.dataset.collapsed; + + if (hideCommentsTotal) { + hideCommentsTotal.style.display = isCollapsed ? 'none' : ''; } + + let oldClass = isCollapsed ? 'bi-chevron-down' : 'bi-chevron-up'; + let newClass = isCollapsed ? 'bi-chevron-up' : 'bi-chevron-down'; + hideCommentsIcon.classList.replace(oldClass, newClass); } function openClose(theID, display) { @@ -648,201 +782,378 @@ function updatePageItems(mode, data) { collapseHeight(); } - -function updateConvItems(mode,data) { - let scroll_position = $(window).scrollTop(); +function updateConvItems(mode, data) { + let scroll_position = window.scrollY; let b64mids = []; - if(mode !== 'update') - $(document).trigger('hz:updateConvItems'); + // Parse the data string into a DOM object + let parser = new DOMParser(); + let doc = parser.parseFromString(data, 'text/html'); - if(mode === 'update' || mode === 'replace') { - prev = 'threads-begin'; + if (mode !== 'update') { + document.dispatchEvent(new Event('hz:updateConvItems')); } - if(mode === 'append') { - next = 'threads-end'; + + let prev, next; + if (mode === 'update' || mode === 'replace') { + prev = document.getElementById('threads-begin'); + } + if (mode === 'append') { + next = document.getElementById('threads-end'); } - $('.thread-wrapper', data).each(function() { - if(this.classList.contains('toplevel_item')) { - let ident = this.id; - let convId = ident.replace('thread-wrapper-',''); - let commentWrap = $('#'+ident+' .collapsed-comments').attr('id'); + doc.querySelectorAll('.thread-wrapper').forEach(function (elem) { + if (elem.classList.contains('toplevel_item')) { + let ident = elem.id; + let convId = ident.replace('thread-wrapper-', ''); + let commentWrap = elem.querySelector('.collapsed-comments')?.id; let itmId = 0; let isVisible = false; // figure out the comment state - if(typeof commentWrap !== 'undefined') - itmId = commentWrap.replace('collapsed-comments-',''); + if (commentWrap !== undefined) { + itmId = commentWrap.replace('collapsed-comments-', ''); + } - if($('#collapsed-comments-'+itmId).is(':visible')) + let collapsedComment = document.getElementById('collapsed-comments-' + itmId); + if (collapsedComment && collapsedComment.style.display !== 'none') { isVisible = true; + } // insert the content according to the mode and first_page // and whether or not the content exists already (overwrite it) - - if($('#' + ident).length == 0) { - if((mode === 'update' || mode === 'replace') && profile_page == 1) { - $('#' + prev).after($(this)); - prev = ident; + let existingElem = document.getElementById(ident); + if (!existingElem) { + if ((mode === 'update' || mode === 'replace') && profile_page == 1) { + if (prev) { + prev.after(elem); + prev = elem; + } } - if(mode === 'append') { - $('#' + next).before($(this)); + if (mode === 'append') { + if (next) { + next.before(elem); + } } - } - else { - $('#' + ident).replaceWith($(this)); + } else { + existingElem.replaceWith(elem); } - // set the comment state to the state we discovered earlier + // DOMParser will prevent scripts from execution for security reasons. + // We remove all scripts but possibly injected some from + // addons like for example gallery later. + // TODO: make the script run from the addon itself. + let scripts = elem.querySelectorAll('script'); + scripts.forEach(script => { + let scriptContent = script.textContent || script.innerText; + eval(scriptContent); // Execute the script + }); - if(isVisible) + // set the comment state to the state we discovered earlier + if (isVisible) { showHideComments(itmId); + } let commentBody = localStorage.getItem("comment_body-" + convId); - - if(commentBody) { - var commentElm = $('#comment-edit-text-' + convId); - if(auto_save_draft) { - if($(commentElm).val() === '') { - $('#comment-edit-form-' + convId).show(); - $(commentElm).addClass("expanded"); + if (commentBody) { + let commentElm = document.getElementById('comment-edit-text-' + convId); + if (auto_save_draft && commentElm) { + if (commentElm.value === '') { + let commentForm = document.getElementById('comment-edit-form-' + convId); + if (commentForm) { + commentForm.style.display = 'block'; + } + commentElm.classList.add("expanded"); openMenu("comment-tools-" + convId); - $(commentElm).val(commentBody); + commentElm.value = commentBody; } } else { localStorage.removeItem("comment_body-" + convId); } } - // trigger the autotime function on all newly created content - $("> .wall-item-outside-wrapper .autotime, > .thread-wrapper .autotime",this).timeago(); - $("> .shared_header .autotime",this).timeago(); - - if((mode === 'append' || mode === 'replace') && (loadingPage)) { + if ((mode === 'append' || mode === 'replace') && loadingPage) { loadingPage = false; } - // if single thread view and the item has a title, display it in the title bar - - if(mode === 'replace') { - if (window.location.search.indexOf("mid=") != -1 || window.location.pathname.indexOf("display") != -1) { - let title = $(".wall-item-title").text(); - title.replace(/^\s+/, ''); - title.replace(/\s+$/, ''); - if (title) { - savedTitle = title + " " + savedTitle; - document.title = title; + // if single thread view and the item has a title, display it in the title bar + if (mode === 'replace') { + if (window.location.search.includes("mid=") || window.location.pathname.includes("display")) { + let titleElem = document.querySelector(".wall-item-title"); + if (titleElem) { + let title = titleElem.textContent.trim(); + if (title) { + savedTitle = title + ' ' + savedTitle; + document.title = title; + } } } } } - $(this).data('b64mids').forEach((b64mid) => { - b64mids.push(b64mid); - }); + let data_json = JSON.parse(elem.dataset.b64mids); + + if (elem.parentNode.classList.contains('wall-item-sub-thread-wrapper') && elem.parentNode.children.length) { + // Set the highlight state + if (data_json.includes(bParam_mid) && !elem.parentNode.parentNode.classList.contains('toplevel_item')) { + elem.parentNode.parentNode.classList.add('item-highlight'); + document.documentElement.style.setProperty('--hz-item-highlight', stringToHslColor(JSON.parse(elem.parentNode.parentNode.dataset.b64mids)[0])); + } + + let elemSubThreadWrapper = elem.querySelector('.wall-item-sub-thread-wrapper'); + let elemCommentButton = elem.querySelector('.wall-item-comment'); + // Set the button and sub-thread-wrapper state + if (elemCommentButton && elemSubThreadWrapper.children.length) { + elemCommentButton.classList.add('expanded'); + } + + elem.parentNode.classList.add('item-expanded'); + } + + b64mids.push(...data_json); }); - $(document).trigger('hz:sse_setNotificationsStatus', [b64mids]); + document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids })); - $(window).scrollTop(scroll_position); + window.scrollTo(0, scroll_position); - if(followUpPageLoad) { - $(document).trigger('hz:sse_bs_counts'); - } - else { - $(document).trigger('hz:sse_bs_init'); + if (followUpPageLoad) { + document.dispatchEvent(new Event('hz:sse_bs_counts')); + } else { + document.dispatchEvent(new Event('hz:sse_bs_init')); } - if(commentBusy) { + if (commentBusy) { commentBusy = false; - $('body').css('cursor', 'auto'); + document.body.style.cursor = 'auto'; } - // Setup to determine if the media player is playing. This affects - // some content loading decisions. + // Setup to determine if the media player is playing. This affects some content loading decisions. + ['playing', 'pause'].forEach(event => { + document.querySelectorAll('video, audio').forEach(media => { + media.removeEventListener(event, mediaHandler); + media.addEventListener(event, mediaHandler); + }); + }); - $('video').off('playing'); - $('video').off('pause'); - $('audio').off('playing'); - $('audio').off('pause'); + function mediaHandler(event) { + mediaPlaying = event.type === 'playing'; + } - $('video').on('playing', function() { - mediaPlaying = true; - }); - $('video').on('pause', function() { - mediaPlaying = false; - }); - $('audio').on('playing', function() { - mediaPlaying = true; - }); - $('audio').on('pause', function() { - mediaPlaying = false; + imagesLoaded(document.querySelectorAll('.wall-item-body img, .wall-photo-item img'), function () { + if (bParam_mid && mode === 'replace') { + scrollToItem(); + } + else { + collapseHeight(); + } }); - if(! preloadImages) { - $('.wall-item-body, .wall-photo-item').imagesLoaded() - .always( function( instance ) { - //console.log('all images loaded'); - collapseHeight(); + // reset rotators and cursors we may have set before reaching this place + let pageSpinner = document.getElementById("page-spinner"); + if (pageSpinner) { + pageSpinner.style.display = 'none'; + } + let profileJotTextLoading = document.getElementById("profile-jot-text-loading"); + if (profileJotTextLoading) { + profileJotTextLoading.style.display = 'none'; + } - if(bParam_mid && mode === 'replace') - scrollToItem(); + followUpPageLoad = true; - }) - .done( function( instance ) { - //console.log('all images successfully loaded'); - }) - .fail( function() { - //console.log('all images loaded, at least one is broken'); - }) - .progress( function( instance, image ) { - //var result = image.isLoaded ? 'loaded' : 'broken'; - //console.log( 'image is ' + result + ' for ' + image.img.src ); - }); + + updateRelativeTime('.autotime'); +} + +function imagesLoaded(elements, callback) { + let loadedCount = 0; + let totalImages = 0; + let timeoutId; + const timeout = 10000; + const processed = new Set(); // Use a Set for efficient lookup + + // Helper function to extract img elements from an HTML string + function extractImagesFromHtml(htmlString) { + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = htmlString; + return tempDiv.querySelectorAll('.wall-item-body img, .wall-photo-item img'); } - else { - collapseHeight(); - if(bParam_mid && mode === 'replace') - scrollToItem(); + function checkComplete(src) { + // Skip processing if image has already been processed + if (processed.has(src)) return; + + processed.add(src); + loadedCount++; + + // Update progress + const progress = Math.round((loadedCount * 100) / totalImages); + document.getElementById('image_counter').innerText = `${progress}%`; + + // If all images are loaded, trigger the callback + if (loadedCount === totalImages) { + document.getElementById('image_counter').innerText = ''; + clearTimeout(timeoutId); + callback(); + } } - // reset rotators and cursors we may have set before reaching this place + // Convert HTML string to img elements if necessary + if (typeof elements === 'string') { + elements = extractImagesFromHtml(elements); + } + + // Exit early if there are no images to load + if (!elements || elements.length === 0) { + callback(); + return; + } - $("#page-spinner").hide(); - $("#profile-jot-text-loading").hide(); + // Filter valid image elements (only img with src attribute) + const images = Array.from(elements) + .filter((element) => element.tagName.toLowerCase() === 'img' && element.src) + .filter((element, index, self) => + index === self.findIndex(e => e.src === element.src) // Avoid duplicates + ); - followUpPageLoad = true; + // If no images are found, call the callback immediately + if (images.length === 0) { + callback(); + return; + } + + totalImages = images.length; + // Set timeout for the loading process + timeoutId = setTimeout(() => { + console.warn(`Image loading timed out after ${timeout}ms`); + callback(false); + }, timeout); + + // Iterate through images to add load and error event listeners + images.forEach((img) => { + let new_img = new Image(); + new_img.src = img.src; + + if (new_img.complete && new_img.naturalHeight > 0) { + // Image is already loaded, handle immediately + // console.log(`Image cached: ${new_img.src}`); + checkComplete(new_img.src); + } else { + // Add event listeners for load and error events + new_img.addEventListener('load', () => { + // console.log(`Image loaded: ${new_img.src}`); + checkComplete(new_img.src) + }); + new_img.addEventListener('error', () => { + console.log(`Image failed to load: ${new_img.src}`); + checkComplete(new_img.src); + }); + } + + }); +} + +function updateRelativeTime(selector) { + // Get all elements with the given selector + const timeElements = document.querySelectorAll(selector); + if (timeElements.length === 0) return; + + // Default time style and map for supported options + const styleMap = ['narrow', 'short', 'long']; + const style = styleMap.find(s => selector.includes(s)) || 'long'; + + // Create an instance of RelativeTimeFormat + const rtf = new Intl.RelativeTimeFormat(lang, { + localeMatcher: 'best fit', // 'best fit' or 'lookup' + numeric: 'always', // 'always' or 'auto' + style: style // 'long', 'short', or 'narrow' + }); + + const now = Date.now(); // Get the current time only once + + // Helper function to calculate the time difference in appropriate units + function getRelativeTime(diffInSeconds) { + const isFuture = diffInSeconds > 0; + const absDiffInSeconds = Math.abs(diffInSeconds); + + if (absDiffInSeconds < 60) return { value: absDiffInSeconds, unit: 'second' }; + if (absDiffInSeconds < 3600) return { value: Math.floor(absDiffInSeconds / 60), unit: 'minute' }; + if (absDiffInSeconds < 86400) return { value: Math.floor(absDiffInSeconds / 3600), unit: 'hour' }; + if (absDiffInSeconds < 2592000) return { value: Math.floor(absDiffInSeconds / 86400), unit: 'day' }; + if (absDiffInSeconds < 31536000) return { value: Math.floor(absDiffInSeconds / 2592000), unit: 'month' }; + return { value: Math.floor(absDiffInSeconds / 31536000), unit: 'year' }; + } + + // Process each element + timeElements.forEach(element => { + const timestamp = new Date(element.title).getTime(); + if (isNaN(timestamp)) return; // Skip invalid timestamps + + const diffInSeconds = Math.floor((timestamp - now) / 1000); // Time difference in seconds + const { value, unit } = getRelativeTime(diffInSeconds); + + // Format the relative time and set it as the element's text + const formattedTime = rtf.format(diffInSeconds > 0 ? value : -value, unit); + element.textContent = formattedTime; + }); + + // Avoid duplicate timeout registrations for the same selector + if (!updateTimeout.includes(selector)) { + updateTimeout.push(selector); + setTimeout(() => updateRelativeTime(selector), 60000); // Re-run the update every 60 seconds + } } function scrollToItem() { // auto-scroll to a particular comment in a thread (designated by mid) when in single-thread mode - if(justifiedGalleryActive) - return; + if (justifiedGalleryActive) return; let submid = ((bParam_mid.length) ? bParam_mid : 'abcdefg'); - //var encoded = ((submid.substr(0,4) == 'b64.') ? true : false); - //var submid_encoded = ((encoded) ? submid : window.btoa(submid)); - $('.thread-wrapper').filter(function() { - if($(this).data('b64mids').indexOf(submid) > -1 && !$(this).hasClass('toplevel_item')) { - if($('.collapsed-comments').length) { - var scrolltoid = $('.collapsed-comments').attr('id').substring(19); - $('#collapsed-comments-' + scrolltoid + ' .autotime').timeago(); - $('#collapsed-comments-' + scrolltoid).show(); - $('#hide-comments-' + scrolltoid).html(aStr.showfewer); - $('#hide-comments-total-' + scrolltoid).hide(); + // Select all thread wrappers + let threadWrappers = document.querySelectorAll('.thread-wrapper'); + + threadWrappers.forEach(thread => { + // Get the 'data-b64mids' attribute and check if it contains submid + let b64mids = thread.dataset.b64mids; + + if (b64mids && b64mids.includes(submid)) { + // Handle collapsed comments if any + let collapsedComments = document.querySelectorAll('.collapsed-comments'); + if (collapsedComments.length) { + let scrollToId = collapsedComments[0].id.substring(19); + showHideComments(scrollToId); + } + + collapseHeight(); + + if (!thread.classList.contains('toplevel_item')) { + // Scroll to the target element + let navHeight = document.getElementById('navbar-top') ? document.getElementById('navbar-top').offsetHeight : 0; + window.scrollTo({ + top: getOffsetTopRelativeToBody(thread) - navHeight, + behavior: 'smooth' + }); } - $('html, body').animate({ scrollTop: $(this).offset().top - $('nav').outerHeight(true) }, 'slow'); - $(this).addClass('item-highlight'); + + let id = thread.id.replace('thread-wrapper-', ''); + let content = document.getElementById('wall-item-content-wrapper-' + id); + content.classList.add('item-highlight-fade'); } }); +} +function getOffsetTopRelativeToBody(element) { + let offsetTop = 0; + while (element) { + offsetTop += element.offsetTop; + element = element.offsetParent; + } + return offsetTop; } function collapseHeight() { @@ -869,8 +1180,8 @@ function collapseHeight() { startOpen: open, heightMargin: 50, collapsedHeight: divmore_height, - moreLink: '<a href="#" class="divgrow-showmore fakelink">' + aStr.divgrowmore + '</a>', - lessLink: '<a href="#" class="divgrow-showmore fakelink">' + aStr.divgrowless + '</a>', + moreLink: '<a href="#" class="divgrow-showmore fakelink"><i class="bi bi-chevron-down align-middle divgrow-showmore-icon"></i> <span class="divgrow-showmore-label align-middle">' + aStr.divgrowmore + '</span></a>', + lessLink: '<a href="#" class="divgrow-showmore fakelink"><i class="bi bi-chevron-up align-middle divgrow-showmore-icon"></i> <span class="divgrow-showmore-label align-middle">' + aStr.divgrowless + '</span></a>', beforeToggle: function(trigger, element, expanded) { if(expanded) { if((($(element).offset().top + divmore_height) - $(window).scrollTop()) < 65 ) { @@ -906,7 +1217,7 @@ function updateInit() { liveUpdate(); } else { - $(document).trigger('hz:sse_bs_init'); + document.dispatchEvent(new Event('hz:sse_bs_init')); } if($('#live-photos').length || $('#live-cards').length || $('#live-articles').length ) { @@ -1030,45 +1341,16 @@ function liveUpdate(notify_id) { var dready = new Date(); console.log('DATA ready in: ' + (dready - dstart)/1000 + ' seconds.'); - if(update_mode === 'update' || preloadImages) { - console.log('LOADING images...'); - $('.wall-item-body, .wall-photo-item',data).imagesLoaded() - .always( function( instance ) { - //console.log('all images loaded'); - - var iready = new Date(); - console.log('IMAGES ready in: ' + (iready - dready)/1000 + ' seconds.'); - - page_load = false; - scroll_next = false; - updateConvItems(update_mode,data); - - in_progress = false; - $('#image_counter').html(''); - - // remove modal backdrop in case the update was triggered from a modal - $('.modal-backdrop').remove(); - }) - .done( function( instance ) { - //console.log('all images successfully loaded'); - }) - .fail( function() { - //console.log('all images loaded, at least one is broken'); - }) - .progress( function( instance, image ) { - $('#image_counter').html(Math.floor((instance.progressedCount*100)/instance.images.length) + '%'); - //var result = image.isLoaded ? 'loaded' : 'broken'; - //console.log( 'image is ' + result + ' for ' + image.img.src ); - }); + console.log('LOADING images...'); + imagesLoaded(data, function () { + var iready = new Date(); + console.log('IMAGES ready in: ' + (iready - dready)/1000 + ' seconds.'); - } - else { page_load = false; scroll_next = false; updateConvItems(update_mode,data); in_progress = false; - } - + }); }); } @@ -1102,9 +1384,8 @@ function cache_next_page() { console.log('cached: ' + update_url); - $('.wall-item-body, .wall-photo-item', data).imagesLoaded() - .always( function( instance ) { - console.log('page_cache images loaded:'); + imagesLoaded(data, function () { + console.log('page_cache: images loaded'); page_cache.data = data; page_cache.page = bParam_page; @@ -1112,19 +1393,7 @@ function cache_next_page() { bParam_page--; page_load = false; - }) - .done( function( instance ) { - console.log('success'); - }) - .fail( function() { - console.log('at least one is broken'); - }) - .progress( function( instance, image ) { - //console.log(instance.progressedCount + '/' + instance.images.length); - //var result = image.isLoaded ? 'loaded' : 'broken'; - //console.log( 'image is ' + result + ' for ' + image.img.src ); }); - }); } @@ -1154,7 +1423,7 @@ function pageUpdate() { scroll_next = false; updatePageItems(update_mode,data); $("#page-spinner").hide(); - $(".autotime").timeago(); + updateRelativeTime('.autotime'); in_progress = false; }); } @@ -1175,21 +1444,299 @@ function justifyPhotosAjax(id) { $('#' + id).justifiedGallery('norewind').on('jg.complete', function(e){ justifiedGalleryActive = false; }); } +function request(id, mid, verb, parent, uuid, userClick) { + + if (verb === 'load') { + const dots = document.getElementById('load-more-dots-' + parent); + dots.classList.add('jumping-dots'); + + const parent_sub = document.getElementById('wall-item-sub-thread-wrapper-' + parent); + const offset = parent_sub.children.length; + + fetch('/request?offset=' + offset + '&verb=' + verb + '&mid=' + mid + '&parent=' + parent + '&module=' + module) + .then(response => response.json()) + .then(obj => { + let parser = new DOMParser(); + let doc = parser.parseFromString(obj.html, 'text/html'); + let b64mids = []; + + doc.querySelectorAll('.thread-wrapper').forEach(function (e) { + let data = JSON.parse(e.dataset.b64mids); + b64mids.push(...data); + }); + + imagesLoaded(doc.querySelectorAll('.wall-item-body img'), function () { + injectWithAnimation('wall-item-sub-thread-wrapper-' + parent, doc); + dots.classList.remove('jumping-dots'); + + const loadmore_progress = document.getElementById('load-more-progress-' + parent); + loadmore_progress.style.width = Math.round(100 * parent_sub.children.length / loadmore_progress.dataset.commentsTotal) + '%'; + + if (Number(parent_sub.children.length) === Number(loadmore_progress.dataset.commentsTotal)) { + const loadmore = document.getElementById('load-more-' + parent); + loadmore.remove(); + } + + updateRelativeTime('.autotime'); + collapseHeight(); + + document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids })); + document.dispatchEvent(new Event('hz:sse_bs_counts')); + }); + + }) + .catch(error => { + console.error('Error fetching data:', error); + }); + + return; + } + + const loading = document.getElementById('like-rotator-' + id); + + if (userClick) { + loading.style.display = 'block'; + } + + if (verb === 'comment') { + if (userClick && singlethread_modules.indexOf(module) !== -1) { + let stateObj = { b64mid: uuid }; + history.pushState(stateObj, '', module + '/' + uuid); + } + + fetch('/request?verb=' + verb + '&mid=' + mid + '&parent=' + parent + '&module=' + module) + .then(response => response.json()) + .then(obj => { + let parser = new DOMParser(); + let doc = parser.parseFromString(obj.html, 'text/html'); + + doc.querySelectorAll('.thread-wrapper').forEach(function (e) { + let data = JSON.parse(e.dataset.b64mids); + b64mids.push(...data); + }); + + imagesLoaded(doc.querySelectorAll('.wall-item-body img'), function () { + injectWithAnimation('wall-item-sub-thread-wrapper-' + id, doc, true); + updateRelativeTime('.autotime'); + collapseHeight(); + + if (userClick) { + loading.style.display = 'none'; + document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids })); + document.dispatchEvent(new Event('hz:sse_bs_counts')); + } + }); + + }) + .catch(error => { + console.error('Error fetching data:', error); + }); + } + else { + fetch('/request?verb=' + verb + '&mid=' + mid + '&parent=' + parent) + .then(response => response.json()) + .then(obj => { + const modal = new bootstrap.Modal('#reactions'); + const modal_content = document.getElementById('reactions_body'); + const modal_title = document.getElementById('reactions_title'); + const modal_action = document.getElementById('reactions_action'); + modal_action.style.display = 'none'; + modal_title.innerHTML = obj.title; + modal_content.innerHTML = ''; + if (obj.action) { + modal_action.innerHTML = '<a href="#" onclick="' + obj.action + '(' + id + ',\'' + verb + '\'); return false;">' + obj.action_label + '</a>'; + modal_action.style.display = 'block'; + } + console.log(obj) + obj.result.forEach(e => { + let mod = ''; + if (e.item_blocked === 4) { + mod = '<span onclick="moderate_approve(' + e.id + '); return false;" class="text-success pe-4 d-inline-block"><i class="bi bi-check-lg" ></i></span><span onclick="moderate_drop(' + e.id + '); return false;" class="text-danger pe-4 d-inline-block"><i class="bi bi-trash" ></i></span>'; + } + modal_content.innerHTML += '<a href="' + e.url + '" class="list-group-item list-group-item-action border-0">' + mod + '<img src="' + e.photo + '" class="menu-img-1" loading="lazy"> ' + e.name + '</a>'; + + }); + + modal.show(); + loading.style.display = 'none'; + }) + .catch(error => { + console.error('Error fetching data:', error); + }); + } + +} + +function injectWithAnimation(containerId, parsedDoc, overwrite = false) { + const container = document.getElementById(containerId); + if (!container) return; + if (overwrite) container.innerHTML = ''; + + const newElements = Array.from(parsedDoc.body.children); + + for (let i = newElements.length - 1; i >= 0; i--) { + const el = newElements[i].cloneNode(true); + el.classList.add('item-fade-in'); + container.insertBefore(el, container.firstChild); + + // Remove classes after transition ends + const onTransitionEnd = (event) => { + el.classList.remove('item-fade-in', 'show'); + el.removeEventListener('transitionend', onTransitionEnd); + }; + el.addEventListener('transitionend', onTransitionEnd); + + setTimeout(() => { + el.classList.add('show'); + }, (newElements.length - 1 - i) * 30); + } +} + +const autoExpand = (function () { + const clickedElements = new Set(); // Stores clicked button references + + // We wait 10 seconds for images. Set the timeout here slightly higher. + function waitForElement(selector, timeout = 11000) { + return new Promise((resolve, reject) => { + // Check if the element already exists + const element = document.querySelector(selector); + + if (element) { + resolve(element); + return; + } + + // Set up a timeout to reject the promise if the element doesn't appear in time + const timer = setTimeout(() => { + observer.disconnect(); + reject(new Error(`Element "${selector}" not found within ${timeout}ms`)); + }, timeout); + + // Create a MutationObserver to watch for DOM changes + const observer = new MutationObserver(() => { + const el = document.querySelector(selector); + if (el) { + clearTimeout(timer); + observer.disconnect(); + resolve(el); + } + }); + + // Start observing the document for changes + observer.observe(document.documentElement, { + childList: true, + subtree: true + }); + }); + } + + async function autoExpand(id) { + const loading = document.getElementById('like-rotator-' + id); + let iteration = 0; + const maxIterations = 3; + clickedElements.clear(); + + try { + // Step 1: Ensure initial button is clicked + const initBtnSelector = '#wall-item-comment-' + id; + const initBtn = await waitForElement(initBtnSelector); + + if (!clickedElements.has(initBtn)) { + initBtn.click(); + clickedElements.add(initBtn); + iteration++; + } + + // Step 2: Loop until no new buttons are found + let newButtonsFound; + + const commentSelector = `#wall-item-sub-thread-wrapper-${id} .thread-wrapper`; + const commentBtnSelector = `#wall-item-sub-thread-wrapper-${id} .wall-item-comment`; + + do { + newButtonsFound = false; + + // Wait for any comment to appear + await waitForElement(commentSelector); + + const expandButtons = document.querySelectorAll(commentBtnSelector); + + for (const btn of expandButtons) { + if (!clickedElements.has(btn)) { + btn.click(); + clickedElements.add(btn); + newButtonsFound = true; + // Optional: await waitForElement(...) to wait for new content + } + } + + // Wait between iterations to allow UI to update + if (newButtonsFound) { + iteration++; + await new Promise(res => setTimeout(res, 700)); + } + + } while (newButtonsFound && iteration < maxIterations); + + console.log('Replies loaded!'); + + loading.style.display = 'none'; + + document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids })); + document.dispatchEvent(new Event('hz:sse_bs_counts')); + + } catch (error) { + loading.style.display = 'none'; + console.error("autoExpand failed:", error.message); + } + } + + return autoExpand; +})(); + + +function stringToHexColor(str) { + let hash = 0; + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + let color = "#"; + for (let i = 0; i < 3; i++) { + const value = (hash >> (i * 8)) & 0xFF; + color += value.toString(16).padStart(2, '0'); + } + return color; +} + +function stringToHslColor(str) { + let stringUniqueHash = [...str].reduce((acc, char) => { + return char.charCodeAt(0) + ((acc << 5) - acc); + }, 0); + return `hsl(${stringUniqueHash % 360}, 65%, 65%)`; +} + function dolike(ident, verb) { - $('#like-rotator-' + ident.toString()).show(); + $('#like-rotator-' + ident).show(); - if(typeof conv_mode == typeof undefined) + if (typeof conv_mode == typeof undefined) { conv_mode = ''; + } - if(typeof page_mode == typeof undefined) + if (typeof page_mode == typeof undefined) { page_mode = ''; + } + + let reload = 0; - var reload = ''; - if(module == 'photos') + if (module == 'photos') { reload = 1; + } - $.get('like/' + ident.toString() + '?verb=' + verb + '&conv_mode=' + conv_mode + '&page_mode=' + page_mode + '&reload=' + reload, function (data) { - if(data.success) { + + $.get('like/' + ident + '?verb=' + verb + '&conv_mode=' + conv_mode + '&page_mode=' + page_mode + '&reload=' + reload, function (data) { + if (data.success) { + + close_modal(); // mod photos if (data.reload) { @@ -1207,11 +1754,13 @@ function dolike(ident, verb) { else { $('#thread-wrapper-' + data.orig_id).replaceWith(data.html); } - $('#wall-item-ago-' + data.id + ' .autotime').timeago(); + + updateRelativeTime('.autotime'); collapseHeight(); liking = 0; } }); + liking = 1; } @@ -1221,17 +1770,73 @@ function doprofilelike(ident, verb) { function doreply(parent, ident, owner, hint) { - var form = $('#comment-edit-form-' + parent.toString()); - form.find('input[name=parent]').val(ident); - var i = form.find('button[type=submit]'); - var btn = i.html().replace(/<[^>]*>/g, '').trim(); - i.html('<i class="fa fa-reply" ></i> ' + btn); - var sel = 'wall-item-body-' + ident.toString(); - var quote = window.getSelection().toString().trim(); - form.find('textarea').val("@{" + owner + "}" + ((($(window.getSelection().anchorNode).closest("#" + sel).attr("id") != sel) || (quote.length === 0))? " " : "\n[quote]" + quote + "[/quote]\n")); - $('#comment-edit-text-' + parent.toString()).focus(); + const modal = new bootstrap.Modal('#reactions'); + const modal_content = document.getElementById('reactions_body'); + const modal_title = document.getElementById('reactions_title'); + const modal_action = document.getElementById('reactions_action'); + + modal_action.style.display = 'none'; + modal_title.innerHTML = hint; + + const preview = document.getElementById('comment-edit-preview-' + parent.toString()); + preview.innerHTML = ''; + + // Get the form element by ID + const form = document.getElementById('comment-edit-wrapper-' + parent.toString()); + if (!form) return; + + modal_content.innerHTML = ''; + modal_content.append(form); + + // Set the value of the input named 'parent' + const parentInput = form.querySelector('input[name=parent]'); + if (parentInput) { + parentInput.value = ident; + } + + // Find the submit button and update its HTML + const submitBtn = form.querySelector('button[type=submit]'); + if (submitBtn) { + const btnText = submitBtn.innerHTML.replace(/<[^>]*>/g, '').trim(); + submitBtn.innerHTML = '<i class="bi bi-arrow-90deg-left"></i> ' + btnText; + } + + // Prepare the quote logic + const sel = 'wall-item-body-' + ident.toString(); + const quote = window.getSelection().toString().trim(); + + // Check if the selection is inside the correct element + let isInSel = false; + const anchorNode = window.getSelection().anchorNode; + if (anchorNode) { + let node = anchorNode.nodeType === 3 ? anchorNode.parentNode : anchorNode; + while (node) { + if (node.id === sel) { + isInSel = true; + break; + } + node = node.parentNode; + } + } + + modal.show(); + + // Set the textarea value + const textarea = form.querySelector('textarea'); + if (textarea) { + let commentBody = localStorage.getItem('comment_body-' + ident); + if (commentBody) { + textarea.value = commentBody; + } + else { + textarea.value = "@{" + owner + "}" + ((!isInSel || quote.length === 0) ? " " : "\n[quote]" + quote + "[/quote]\n"); + } + + textarea.focus(); + } } + function doscroll(parent, hidden) { var id; var x = '#hide-comments-outer-' + hidden.toString(); @@ -1245,6 +1850,7 @@ function doscroll(parent, hidden) { var c = '#collapsed-comments-' + x; if($(c).length !== 0 && (! $(c).is(':visible'))) { showHideComments(x); + collapseHeight(); pos += $(c).height(); } } @@ -1257,7 +1863,7 @@ function doscroll(parent, hidden) { }); $('html, body').animate({scrollTop:(id.offset().top) - 50}, 'slow'); - $('<a href="javascript:doscrollback(' + pos + ');" id="back-to-reply" title="' + aStr['to_reply'] + '"><i class="fa fa-angle-double-down"></i></a>').insertAfter('#wall-item-ago-' + id.attr('id').replace(/\D/g,'')); + $('<a href="javascript:doscrollback(' + pos + ');" id="back-to-reply" title="' + aStr['to_reply'] + '"><i class="bi bi-chevron-double-down"></i></a>').insertAfter('#wall-item-ago-' + id.attr('id').replace(/\D/g,'')); } function doscrollback(pos) { @@ -1282,7 +1888,7 @@ function dopin(id) { $('.dropdown-item-pinnable').html($('.dropdown-item-pinnable').html().replace(aStr['unpin_item'],aStr['pin_item'])); $('.wall-item-pinned').remove() if(i.length == 0) { - $('<span class="wall-item-pinned" title="' + aStr['pinned'] + '" id="wall-item-pinned-' + id + '"><i class="fa fa-thumb-tack"> </i></span>').insertAfter('#wall-item-ago-' + id); + $('<span class="wall-item-pinned" title="' + aStr['pinned'] + '" id="wall-item-pinned-' + id + '"><i class="bi bi-pin"> </i></span>').insertAfter('#wall-item-ago-' + id); me.html(me.html().replace(aStr['pin_item'],aStr['unpin_item'])); }; }) @@ -1305,7 +1911,7 @@ function dropItem(url, object, b64mid) { $(object + ', #pinned-wrapper-' + id).remove(); $('body').css('cursor', 'auto'); - toast(aStr.itemdel, 'info') + toast(aStr.itemdel, 'info'); //$.jGrowl(aStr.itemdel, { sticky: false, theme: 'info', life: 3000 }); if (typeof b64mid !== typeof undefined) { @@ -1327,26 +1933,28 @@ function dropItem(url, object, b64mid) { } function dosubthread(ident) { - $('#like-rotator-' + ident.toString()).show(); - $.get('subthread/sub/' + ident.toString(), updateInit ); + $('#like-rotator-' + ident).show(); + $.get('subthread/sub/' + ident, updateInit ); liking = 1; } function dounsubthread(ident) { - $('#like-rotator-' + ident.toString()).show(); - $.get('subthread/unsub/' + ident.toString(), updateInit ); + $('#like-rotator-' + ident).show(); + $.get('subthread/unsub/' + ident, updateInit ); liking = 1; } -function moderate_approve(ident) { - $('#like-rotator-' + ident.toString()).show(); - $.get('moderate/' + ident.toString() + '/approve', updateInit ); +function moderate_approve(ident, verb) { + $('#like-rotator-' + ident).show(); + close_modal(); + $.get('moderate/' + ident + '/approve', updateInit ); liking = 1; } function moderate_drop(ident) { - $('#like-rotator-' + ident.toString()).show(); - $.get('moderate/' + ident.toString() + '/drop', $('#thread-wrapper-' + ident.toString()).fadeOut(function() { this.remove(); })); + $('#like-rotator-' + ident).show(); + close_modal(); + $.get('moderate/' + ident + '/drop', $('#thread-wrapper-' + ident).fadeOut(function() { this.remove(); })); liking = 1; } @@ -1357,18 +1965,18 @@ function dostar(ident) { if(data.result == 1) { $('#starred-' + ident).addClass('starred'); $('#starred-' + ident).removeClass('unstarred'); - $('#starred-' + ident).addClass('fa-star'); - $('#starred-' + ident).removeClass('fa-star-o'); + $('#starred-' + ident).addClass('bi-star-fill'); + $('#starred-' + ident).removeClass('bi-star'); $('#star-' + ident).addClass('hidden'); $('#unstar-' + ident).removeClass('hidden'); - var btn_tpl = '<div class="btn-group" id="star-button-' + ident + '"><button type="button" class="btn btn-outline-secondary border-0 btn-sm wall-item-star" onclick="dostar(' + ident + ');"><i class="fa fa-star"></i></button></div>' - $('#wall-item-tools-left-' + ident).prepend(btn_tpl); + var btn_tpl = '<div class="" id="star-button-' + ident + '"><button type="button" class="btn btn-sm btn-link link-secondary wall-item-star" onclick="dostar(' + ident + ');"><i class="bi bi-star generic-icons"></i></button></div>' + $('#wall-item-tools-right-' + ident).prepend(btn_tpl); } else { $('#starred-' + ident).addClass('unstarred'); $('#starred-' + ident).removeClass('starred'); - $('#starred-' + ident).addClass('fa-star-o'); - $('#starred-' + ident).removeClass('fa-star'); + $('#starred-' + ident).addClass('bi-star'); + $('#starred-' + ident).removeClass('bi-star-fill'); $('#star-' + ident).removeClass('hidden'); $('#unstar-' + ident).addClass('hidden'); $('#star-button-' + ident).remove(); @@ -1440,12 +2048,14 @@ function post_comment(id) { window.location.href = data.reload; } + close_modal(); localStorage.removeItem("comment_body-" + id); $("#comment-edit-preview-" + id).hide(); $("#comment-edit-text-" + id).val('').blur().attr('placeholder', aStr.comment); - $('#wall-item-comment-wrapper-' + id).before(data.html); - $('#wall-item-ago-' + data.id + ' .autotime').timeago(); + $('#wall-item-sub-thread-wrapper-' + data.thr_parent_id).append(data.html); + + updateRelativeTime('.autotime'); $('body').css('cursor', 'unset'); collapseHeight(); commentBusy = false; @@ -1475,7 +2085,7 @@ function preview_comment(id) { function(data) { if(data.preview) { $("#comment-edit-preview-" + id).html(data.preview); - $("#comment-edit-preview-" + id + " .autotime").timeago(); + updateRelativeTime('.autotime'); $("#comment-edit-preview-" + id + " a").click(function() { return false; }); } }, @@ -1505,7 +2115,7 @@ function preview_post() { function(data) { if(data.preview) { $("#jot-preview-content").html(data.preview); - $("#jot-preview-content .autotime").timeago(); + updateRelativeTime('.autotime'); $("#jot-preview-content" + " a").click(function() { return false; }); } }, @@ -1553,7 +2163,7 @@ function contactgroupChangeMember(gid, cid) { $('body').css('cursor', 'wait'); $.get('contactgroup/' + gid + '/' + cid, function(data) { $('body').css('cursor', 'auto'); - $('#group-' + gid).toggleClass('fa-check-square-o fa-square-o'); + $('#group-' + gid).toggleClass('bi-check-square bi-square'); }); } @@ -1627,13 +2237,6 @@ function loadText(textRegion,data) { $(textRegion).val(currentText + data); } -function addeditortext(data) { - if(plaintext == 'none') { - var currentText = $("#profile-jot-text").val(); - $("#profile-jot-text").val(currentText + data); - } -} - function makeid(length) { var result = ''; var characters = 'abcdef0123456789'; @@ -1679,7 +2282,7 @@ function push_notification(title, body, b64mid) { function toggleAside() { if ($('main.region_1-on').length) { - $('#expand-aside-icon').addClass('fa-arrow-circle-right').removeClass('fa-arrow-circle-left'); + $('#expand-aside-icon').addClass('bi-arrow-right-circle').removeClass('bi-arrow-left-circle'); $('html, body').css({ 'position': '', 'left': '' }); $('main').removeClass('region_1-on'); $('#region_1').addClass('d-none'); @@ -1687,7 +2290,7 @@ function toggleAside() { $('#overlay').remove(); } else { - $('#expand-aside-icon').removeClass('fa-arrow-circle-right').addClass('fa-arrow-circle-left'); + $('#expand-aside-icon').removeClass('bi-arrow-right-circle').addClass('bi-arrow-left-circle'); $('html, body').css({ 'position': 'sticky', 'left': '0px'}); $('main').addClass('region_1-on'); $('#region_1').removeClass('d-none'); @@ -1697,7 +2300,7 @@ function toggleAside() { } function toast(string, severity) { - let id = btoa(string); + let id = bin2hex(string); let container = document.getElementById('toast-container'); let toast = document.getElementById(id); @@ -1718,3 +2321,11 @@ function toast(string, severity) { toastInstance.show(); } + +function close_modal() { + let modal = bootstrap.Modal.getInstance(document.querySelector('.modal.show')); + + if (modal) { + modal.hide(); + } +} |