Update MBViewer

This commit is contained in:
octospacc 2024-01-15 01:11:43 +01:00
parent 58819c8c97
commit 064d9c7c40
5 changed files with 284 additions and 76 deletions

View File

@ -1,8 +1,8 @@
.MBPost * {
.MbPost * {
max-width: 100%;
height: auto;
}
.MBPost figure {
.MbPost figure {
margin: 1em;
}

View File

@ -2,18 +2,18 @@
<html>
<head>
<meta charset="utf-8">
<title>MBViewer</title>
<title>👁️‍🗨️️ MBViewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<meta name="format-detection" content="telephone=no"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="MobileOptimized" content="176"/>
<meta name="HandheldFriendly" content="True"/>
<meta property="og:title" content="MBViewer"/>
<meta property="og:title" content="👁️‍🗨️️ MBViewer"/>
<!--
<meta property="og:image" content=""/>
<meta property="og:description" content=""/>
-->
<meta property="twitter:title" content="MBViewer"/>
<meta property="twitter:title" content="👁️‍🗨️️ MBViewer"/>
<!--
<meta property="twitter:image" content=""/>
<meta name="twitter:card" content="summary"/>
@ -26,12 +26,14 @@
document.documentElement.classList &&
document.documentElement.classList.add('theme_dark');
</script>
<!--
<link rel="icon" type="image/svg+xml" href="img/website_icon.svg"/>
<link rel="apple-touch-icon" sizes="180x180" href="img/apple-touch-icon.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="img/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="img/favicon-16x16.png"/>
<link rel="alternate icon" href="img/favicon.ico" type="image/x-icon"/>
<link href="css/font-roboto.css" rel="stylesheet" type="text/css"/>
-->
<link href="css/widget-frame.css" rel="stylesheet" media="screen"/>
<link href="css/telegram-web.css" rel="stylesheet" media="screen"/>
<link rel="stylesheet" href="css/MBViewer.css"/>
@ -74,6 +76,11 @@
<section class="tgme_right_column">
<div class="tgme_channel_info">
<div class="tgme_channel_info_header">
<a href="javascript:;" name="closeColumn" style="float: right;" hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32">
<path d="M23.057 7.057a1.333 1.333 0 1 1 1.886 1.886L17.886 16l7.057 7.057a1.333 1.333 0 1 1-1.886 1.886L16 17.886l-7.057 7.057a1.333 1.333 0 1 1-1.886-1.886L14.114 16 7.057 8.943a1.333 1.333 0 1 1 1.886-1.886L16 14.114l7.057-7.057z"/>
</svg>
</a>
<i class="tgme_page_photo_image"></i>
<div class="tgme_channel_info_header_title_wrap">
<div class="tgme_channel_info_header_title"></div>
@ -181,7 +188,13 @@
</header>
<main class="tgme_main">
<div class="tgme_container">
<section class="tgme_channel_history js-message_history"></section>
<section class="tgme_channel_history js-message_history">
<noscript>
<p>
This application requires modern JavaScript.
</p>
</noscript>
</section>
</div>
</main>
<div id="BottomAnchor"></div>
@ -194,6 +207,7 @@
<script src="js/MBViewer.js"></script>
<script>
TWeb.init();
ResizeLayouts();
MbViewerInit();
</script>
</body>

View File

@ -1,20 +1,41 @@
// TODO:
// * handle opening the feed at a specific post id in time
// ** support opening from not only id but also permalink
// * custom colors
// * author profiles, bios, and showing only their messages instead of the full site
// * reduce lag on mobile somehow
// * open author profiles/bios as a channel and show only their messages instead of the full site
// * show site/profile info on click of navbar for mobile
// * in-app search of site content
// * homepage with history and sponsored sources
// * don't show redundant day markers
// * navigate markdown Wordpress export pages from Git
// * app info in page without JS
// * other supported sources
// ** Markdown WordPress export pages from Git?
// ** Atom/RSS feeds
// * app info in page without JS?
// * fix some messages being skipped when connection errors happen
// * optionally show post titles?
// * fix unfinished tasks still executing when clicking back
// * fix imported SVG buttons not fitting with dark theme
// * I think we might need to handle acronicized names for users when needed?
let MbState = {};
function ArgsRewrite (props={}) {
for (const key in props) {
const value = props[key];
value ? (MbState.args[key] = value) : (delete MbState.args[key]);
}
let hash = '/';
for (const arg in MbState.args) {
hash += `${arg}=${MbState.args[arg]}|`;
}
location.hash = hash;
}
const SureArray = (obj) => (Array.isArray(obj) ? obj : [obj]);
// <https://stackoverflow.com/questions/29956338/how-to-accurately-determine-if-an-element-is-scrollable/71170105#71170105>
function CanScrollEl(el, scrollAxis) {
function CanScrollEl (el, scrollAxis) {
if (0 === el[scrollAxis]) {
el[scrollAxis] = 1;
if (1 === el[scrollAxis]) {
@ -26,10 +47,21 @@ function CanScrollEl(el, scrollAxis) {
}
return false;
}
function IsScrollableY(el) {
function IsScrollableY (el) {
return (el.scrollHeight > el.clientHeight) && CanScrollEl(el, 'scrollTop') && ('hidden' !== getComputedStyle(el).overflowY);
}
// <https://www.javascripttutorial.net/dom/css/check-if-an-element-is-visible-in-the-viewport/>
function IsElemInViewport (elem) {
const rect = elem.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
function GetDomainFromUrl (url) {
return url.split('//')[1].split('/')[0];
}
@ -48,8 +80,35 @@ function MakeSiteRestUrl (path='') {
}
}
function MakeApiEndpoint (platform, options) {
// ...
function MakeApiEndpoint (type, options={}) {
const translations = {
"wordpress.org": {
count: "per_page",
},
"wordpress.com": {
count: "number",
}
}
let query = '';
for (const option in options) {
if (option !== 'id') {
query += `&${translations[MbState.platform][option] || option}=${options[option]}`;
}
}
query = `${options.id || ''}?${query.slice(1)}`;
switch (MbState.platform) {
case 'wordpress.org': query = `wp/v2/${type}/${query}`; break;
case 'wordpress.com': query = `${type}/${query}`; break;
}
return query;
}
function MakeAcroName(name) {
let acro = '';
for (const word of name.split(' ').slice(0,3)) {
acro += word[0].toUpperCase();
}
return acro;
}
async function MbViewerInit () {
@ -57,58 +116,105 @@ async function MbViewerInit () {
location.hash = '/';
}
MbState = {
args: {},
siteData: {
name: "MBViewer",
name: "👁️‍🗨️️ MBViewer",
acroName: '👁️‍🗨️️ MBV',
description: `
The messages of this channel are baked inside the app,
and serve as the centralized place for all kinds of information about it,
while at the same time acting as a demo.
Please enjoy your time here, or use the search bar to input a supported URL.
<br/>
For other projects, visit the Octo Hub at <a href="https://hub.octt.eu.org">hub.octt.eu.org</a>!
`,
},
platform: "wordpress.org",
authors: {},
};
$('form.tgme_header_search_form')[0].action = '';
$('form.tgme_header_search_form')[0].onsubmit = function(event){
location.hash = `/siteUrl=${event.target.querySelector('input').value}`;
let url = event.target.querySelector('input').value;
const urlLow = url.toLowerCase();
if (!urlLow.startsWith('http://') && !urlLow.startsWith('https://')) {
url = `https://${url}`;
}
if (["t.me", "telegram.me"].includes(url.toLowerCase().split('://')[1].split('/')[0])) {
location = url;
} else {
ArgsRewrite({ siteurl: url });
}
event.preventDefault();
};
$('a.tgme_header_link')[0].href = '';
$('a.tgme_header_link')[0].onclick = function(){
if (window.innerWidth <= 720 ) { // .tgme_header_right_column @media max-width
if (!$('.tgme_header_right_column')[0].style.display) {
$('main.tgme_main')[0].style.visibility = 'hidden';
$('.tgme_header_right_column')[0].style.display = 'revert';
$('.tgme_header_right_column')[0].style.width = 'revert';
$('.tgme_header_right_column .tgme_channel_info')[0].style.height = 'calc(100% - 32px)';
$('.tgme_header_right_column a[name="closeColumn"]')[0].hidden = false;
} else {
HideMobileRightColumn();
}
}
};
$('.tgme_header_right_column a[name="closeColumn"]')[0].onclick = HideMobileRightColumn;
//$('a.tgme_header_link')[0].removeAttribute('href');
$('.tgme_channel_info_header_username').html('');
$('.tgme_page_photo_image').html('');
$('.tgme_page_photo_image').removeClass('bgcolor0');
$('.tgme_page_photo_image').removeClass('bgcolor0 bgcolor1 bgcolor2 bgcolor3 bgcolor4 bgcolor5 bgcolor6');
$('.tgme_page_photo_image').attr('data-content', '');
$('.tgme_header_title, .tgme_channel_info_header_title').html('');
$('.tgme_channel_info_description').html('');
$('section.tgme_channel_history.js-message_history').html('');
for (const arg of location.hash.split('/').slice(1).join('/').split('|')) {
const argTokens = arg.split('=');
MbState[argTokens[0]] = argTokens.slice(1).join('=');
}
if (MbState.siteUrl) {
if (GetDomainFromUrl(MbState.siteUrl).toLowerCase().endsWith('wordpress.com')) {
MbState.platform = 'wordpress.com';
if (arg) {
const argTokens = arg.split('=');
const valueItems = argTokens.slice(1).join('=').split(',');
MbState.args[argTokens[0].toLowerCase()] = (valueItems.length > 1 ? valueItems : valueItems[0]);
}
const siteRequest = await fetch(MakeSiteRestUrl());
MbState.siteData = await siteRequest.json();
$('form.tgme_header_search_form')[0].action = `${MbState.siteUrl}/?s`;
}
MbState.siteUrl = MbState.args.siteurl;
MbState.platform = /*SureArray(*/MbState.args.platform/*)*/;
if (MbState.siteUrl) {
if (!MbState.platform) {
if (GetDomainFromUrl(MbState.siteUrl).toLowerCase().endsWith('wordpress.com')) {
MbState.platform = 'wordpress.com';
} else {
MbState.platform = 'wordpress.org';
}
}
try {
const siteRequest = await fetch(MakeSiteRestUrl());
MbState.siteData = await siteRequest.json();
} catch(err) {
setTimeout(MbViewerInit, 1000);
}
const siteLink = (MbState.siteData.url || MbState.siteData.URL || MbState.siteUrl);
$('form.tgme_header_search_form')[0].action = `${siteLink}/?s`;
$('form.tgme_header_search_form')[0].onsubmit = null;
$('a.tgme_header_link')[0].href = MbState.siteUrl;
$('.tgme_channel_info_header_username').html(`<a href="${MbState.siteUrl}">${GetDomainFromUrl(MbState.siteUrl).toLowerCase()}</a>`);
$('.tgme_page_photo_image').html(`<img src="${MbState.siteUrl}${MbState.siteData.site_icon_url}"/>`);
//$('a.tgme_header_link')[0].href = siteLink;
$('.tgme_channel_info_header_username').html(`<a href="${siteLink}">${GetDomainFromUrl(siteLink).toLowerCase()}</a>`);
$('a[name="goBack"]')[0].hidden = false;
$('section.tgme_channel_history.js-message_history').html(MakeMoreWrapperHtml(0, 'before'));
MbState.lastMustScroll = 3; // Firefox fix
TWeb.loadMore($('.js-messages_more_wrap > a'), true);
} else {
MbState.lastMustScroll = true;
TWeb.loadMore($('.js-messages_more_wrap > a'));
}
MbState.siteData.iconUrl = (MbState.siteData.site_icon_url || MbState.siteData.icon?.img || MbState.siteData.icon?.ico);
MbState.siteData.acroName ||= (!MbState.siteData.iconUrl ? MakeAcroName(MbState.siteData.name) : '');
MbState.siteData.bgColor = ~~(Math.random() * 7);
if (MbState.siteData.iconUrl && !["http", "https"].includes(MbState.siteData.iconUrl.split('://')[0])) {
MbState.siteData.iconUrl = `${MbState.siteUrl}${MbState.siteData.iconUrl}`;
}
if (!MbState.siteUrl) {
$('a[name="goBack"]')[0].hidden = true;
$('.tgme_page_photo_image').addClass('bgcolor0');
$('.tgme_page_photo_image').attr('data-content', 'MBV');
$('section.tgme_channel_history.js-message_history').html(MakeMoreWrapperHtml());
TWeb.loadMore($('.js-messages_more_wrap > a'), true, [{ content: `<p>
Here I am doing, another strange thing of mine.
TWeb.loadMore($('.js-messages_more_wrap > a'), [{ content: `<p>
Here I am, doing another strange thing of mine.
This is my personal experiment to make an MB-style frontend for sources that are by default not really friendly to that concept.
Since this first day, we will start with just WordPress, and we'll see what comes from that.
See <a href="https://octospacc.altervista.org/2024/01/13/wordpress-che-non-e/">https://octospacc.altervista.org/2024/01/13/wordpress-che-non-e/</a>.
</p>`, date: '2024-01-13' }, { content: `<p>
</p>`, date: '2024-01-13T21:00' }, { content: `<p>
After fixing a few post-release issues driving me insane (scrolling cough cough), here are some new improvements:
<br/> * Handling of posts without date is just a bit nicer.
<br/> * Added a back button to return to this page here from a real site stream.
@ -118,6 +224,16 @@ async function MbViewerInit () {
I also just now realized that wordpress.com uses a different REST API with different endpoints and parameters,
so I will need to handle that...
</p>`, date: '2024-01-14T02:00' }, { content: `<p>
New changes:
<br/> * Correctly handle wordpress.com blogs
<br/> * Show specific users as post authors whenever possible
<br/> * Made the navigation bar smarter: now handles URLs without schema, and t.me links (redirects to official site)
<br/> * Made the info box (right column on desktop) visible on small screens (by clicking the screen header)
<br/> * Added an Altervista workaround for videos not loading (bypass anti-hotlinking)
<br/> * Made URL hash parameter names case-insensitive
<br/> * Now sites without an icon will display a random color and their acronicized name
<br/> * Hopefully fixed all the scrolling-loading issues for real this time...
</p>`, date: '2024-01-15T01:00' }, { content: `<p>
Copyright notice: MBViewer uses code borrowed from <a href="https://t.me">t.me</a>,
specially modified to handle customized data visualizations in an MB-style.
<br/>
@ -125,9 +241,15 @@ async function MbViewerInit () {
all rights upon the original materials (which are: everything not strictly related to the "MBViewer" mod) belong to the original owners.
</p>` }]);
}
$('.tgme_page_photo_image').attr('data-content', MbState.siteData.acroName);
$('.tgme_header_title, .tgme_channel_info_header_title').html(MbState.siteData.name);
$('.tgme_channel_info_description').html(MbState.siteData.description);
if (MbState.siteData.iconUrl) {
$('.tgme_page_photo_image').html(`<img src="${MbState.siteData.iconUrl}"/>`);
} else {
$('.tgme_page_photo_image').addClass(`bgcolor${MbState.siteData.bgColor}`);
}
MbState.lastMustScroll = true;
}
function MakeMoreWrapperHtml (postOffset, wrapType) {
@ -135,23 +257,34 @@ function MakeMoreWrapperHtml (postOffset, wrapType) {
MbState.lastPostOffset = (postOffset + 1);
}
return `<div class="tgme_widget_message_centered js-messages_more_wrap">
<a href="${MbState.siteUrl && MakeSiteRestUrl(`wp/v2/posts/?offset=${postOffset}&per_page=1`)}" data-${wrapType}="" class="tme_messages_more js-messages_more"></a>
<a href="${MbState.siteUrl && MakeSiteRestUrl(MakeApiEndpoint('posts', { offset: postOffset, count: 1 }))}" data-${wrapType}="" class="tme_messages_more js-messages_more"></a>
</div>`;
}
function MakeMbHtml (postData) {
async function MakeMbHtml (postData) {
postData = (typeof(postData) === 'string' ? JSON.parse(postData) : postData);
let html = '';
const siteHref = (MbState.siteUrl ? `href="${MbState.siteUrl}"` : '');
for (postData of SureArray(postData)) {
let html = (MbState.siteUrl ? MakeMoreWrapperHtml(MbState.lastPostOffset, 'before') : '');
const siteLink = (MbState.siteData.url || MbState.siteData.URL || MbState.siteLink);
const siteHref = (siteLink ? `href="${siteLink}"` : '');
for (postData of (postData.posts ? postData.posts : SureArray(postData))) {
const postLink = (postData.link || postData.URL);
const authorId = (postData.author?.ID || postData.author || postData._links?.author[0]?.href?.split('/')?.slice(-1)[0]);
if (authorId && !MbState.authors[authorId]) {
MbState.authors[authorId] = (typeof(postData.author) === 'object'
? postData.author
: await (await fetch(MakeSiteRestUrl(MakeApiEndpoint('users', { id: authorId })))).json());
}
const authorData = MbState.authors[authorId];
const authorLink = (authorData?.link || (siteLink && `${siteLink}/author/${authorData?.name}`));
const authorHref = (authorLink ? `href="${authorLink}"` : '');
const iconUrl = (Object.values(authorData?.avatar_urls || {}).slice(-1)[0] || authorData?.avatar_URL || MbState.siteData.iconUrl);
html += `
${MbState.siteUrl ? MakeMoreWrapperHtml(MbState.lastPostOffset, 'before') : ''}
<div class="tgme_widget_message_wrap js-widget_message_wrap date_visible">
<div class="tgme_widget_message text_not_supported_wrap js-widget_message" data-post="">
<div class="tgme_widget_message_user">
<a ${siteHref}>
<i class="tgme_widget_message_user_photo ${MbState.siteUrl ? '' : 'bgcolor0'}" style="background-color: unset;" data-content="${MbState.siteUrl ? '' : 'MBV'}">
${MbState.siteUrl ? `<img src="${MbState.siteUrl}${MbState.siteData.site_icon_url}"/>` : ''}
<a ${authorHref || siteHref}>
<i class="tgme_widget_message_user_photo ${iconUrl ? '' : `bgcolor${MbState.siteData.bgColor}`}" style="background-color: unset;" data-content="${MbState.siteData.acroName}">
${iconUrl ? `<img src="${iconUrl}"/>` : ''}
</i>
</a>
</div>
@ -167,21 +300,24 @@ function MakeMbHtml (postData) {
</svg>
</i>
<div class="tgme_widget_message_author accent_color">
<a class="tgme_widget_message_owner_name" ${siteHref}>
<a class="tgme_widget_message_owner_name" ${authorHref || siteHref}>
<span dir="auto">
${MbState.siteData.name}
${MbState.authors[authorId]?.name
? `${MbState.authors[authorId]?.name} [${MbState.siteData.name}]`
: MbState.siteData.name
}
</span>
</a>
</div>
<div class="tgme_widget_message_text js-message_text before_footer" dir="auto">
<div class="MBPost">
${postData.content?.rendered || postData.content}
<div class="MbPost">
${ReformatPostHtml(postData.content?.rendered || postData.content)}
</div>
</div>
<div class="tgme_widget_message_footer compact js-message_footer">
<div class="tgme_widget_message_info short js-message_info">
<span class="tgme_widget_message_meta">
<a class="tgme_widget_message_date" ${postData.link ? `href="${postData.link}"` : ''}>
<a class="tgme_widget_message_date" ${postLink ? `href="${postLink}"` : ''}>
<time datetime="${postData.date}" class="time"></time>
<!-- TODO: show edited status -->
</a>
@ -196,8 +332,62 @@ function MakeMbHtml (postData) {
return html;
}
function ReformatPostHtml (html) {
const content = $(`<div>${html}</div>`);
// bypass Altervista's anti-hotlinking protection by hiding our HTTP Referer header
// TODO: only do this for altervista sites maybe
for (const videoElem of content.find('video').toArray()) {
const frameElem = document.createElement('iframe');
frameElem.style = 'border: none; width: 100%;';
frameElem.allow = 'fullscreen';
frameElem.src = `data:text/html;utf8,
<style>
html, body { margin: 0; overflow: hidden; }
video { max-width: 100%; }
</style>
${encodeURIComponent(videoElem.outerHTML)}
<script>
var videoElem = document.querySelector('video');
var oldVideoHeight = getComputedStyle(videoElem).height;
setInterval(function(){
var newVideoHeight = getComputedStyle(videoElem).height;
if (newVideoHeight !== oldVideoHeight) {
top.postMessage((videoElem.src + ' ' + newVideoHeight), '*');
oldVideoHeight = oldVideoHeight;
}
}, 750);
</script>
`;
videoElem.replaceWith(frameElem);
}
return content.html();
}
function HideMobileRightColumn () {
$('main.tgme_main')[0].style.visibility = '';
$('.tgme_header_right_column')[0].style.display = '';
$('.tgme_header_right_column')[0].style.width = '';
$('.tgme_header_right_column .tgme_channel_info')[0].style.height = '';
$('.tgme_header_right_column a[name="closeColumn"]')[0].hidden = true;
}
function ResizeLayouts () {
if (window.innerWidth <= 720 ) { // .tgme_header_right_column @media max-width
$('a.tgme_header_link')[0].href = 'javascript:;';
} else {
HideMobileRightColumn();
$('a.tgme_header_link')[0].removeAttribute('href');
}
}
$('a[name="goBack"]')[0].onclick = function(){
location.hash = '/';
ArgsRewrite({ siteurl: null, postid: null });
};
window.onmessage = function(event){
const tokens = event.data.split(' ');
$(`iframe[src*="${encodeURIComponent(tokens[0])}"]`).height(tokens[1]);
};
window.addEventListener('resize', ResizeLayouts);
window.addEventListener('hashchange', MbViewerInit);

View File

@ -192,19 +192,19 @@ var TWeb = {
if ($before.length) {
var bottom = $before.offset().top + $before.height() - scrollTop;
if (bottom > -wheight * 3) {
TWeb.loadMore($before, false);
TWeb.loadMore($before);
}
}
if ($after.length) {
var top = $after.offset().top - scrollTop;
if (top < wheight * 3) {
TWeb.loadMore($after, false);
TWeb.loadMore($after);
}
}
});
$document.on('click', '.js-messages_more', function() {
var $el = $(this);
TWeb.loadMore($el, false);
TWeb.loadMore($el);
});
},
initViews: function() {
@ -280,7 +280,7 @@ var TWeb = {
$wrapEl.toggleClass('date_visible', date_visible);
}
},
loadMore: function($moreEl, scrollToLast, dataOverride) {
loadMore: function($moreEl, dataOverride) {
var loading = $moreEl.data('loading');
if (loading) {
return false;
@ -290,8 +290,6 @@ var TWeb = {
var url = $moreEl.attr('href');
$moreEl.data('loading', true);
$moreEl.addClass('dots-animated');
var time0 = +(new Date);
//console.log('loading...', before, after);
var _load = function(url, before, after) {
if (dataOverride) {
_loadContinue(dataOverride);
@ -306,10 +304,9 @@ var TWeb = {
});
}
};
function _loadContinue(data) {
var time1 = +(new Date);
//console.log('loaded ' + (time1 - time0) + 'ms');
var $data = $(MakeMbHtml(data));
async function _loadContinue(data) {
var [initialHtmlScroll, initialHtmlHeight] = [$('html').scrollTop(), $('html').height()];
var $data = $(await MakeMbHtml(data));
var $helper = $('<div class="tgme_widget_messages_helper"></div>');
$helper.append($data);
$('.js-message_history').append($helper);
@ -318,8 +315,6 @@ var TWeb = {
});
$helper.remove();
var wrapEls = $data.filter('.js-widget_message_wrap').get();
var time2 = +(new Date);
//console.log('prepared ' + (time2 - time1) + 'ms');
var $moreElWrap = $moreEl.parents('.js-messages_more_wrap');
if (before) {
var firstWrapEl = $moreElWrap.next('.js-widget_message_wrap').get();
@ -337,17 +332,25 @@ var TWeb = {
$data.insertBefore($moreElWrap);
$moreElWrap.remove();
}
var time3 = +(new Date);
//console.log('inserted ' + (time3 - time2) + 'ms');
if (scrollToLast || MbState.lastMustScroll > 0) {
$('#BottomAnchor')[0].scrollIntoView();
$('.tgme_widget_message_wrap').last().scrollIntoView();
MbState.lastMustScroll--;
}
if (!IsScrollableY($('html')[0])) {
TWeb.loadMore($('.js-messages_more_wrap > a'), true);
MbState.lastMustScroll--;
MbState.wasEverNonScrollable = true;
MbState.lastMustScroll = true;
TWeb.loadMore($('.js-messages_more_wrap > a'));
return;
}
if (MbState.lastMustScroll) {
//$('#BottomAnchor')[0].scrollIntoView();
$('.tgme_widget_message_wrap').last().scrollIntoView();
MbState.lastMustScroll = false;
return;
}
if (MbState.wasEverNonScrollable) {
//$('#BottomAnchor')[0].scrollIntoView();
$('.tgme_widget_message_wrap').last().scrollIntoView();
MbState.wasEverNonScrollable = false;
return;
}
$('html').scrollTop(initialHtmlScroll + $('html').height() - initialHtmlHeight);
};
_load(url, before, after);
},

View File

@ -33,7 +33,7 @@
<h4><!--
--><a href="https://kb.octt.eu.org">📝 OcttKB</a>
<small>(knowledge base)</small><!--
<small>(knowledge base & personal wiki)</small><!--
--></h4>
<h4><!--
@ -48,6 +48,7 @@
<h4><a href="./Ecoji">🦜 Ecoji v1</a> <small>(webapp fork)</small></h4>
<h4><a href="./FramesBrowser">🪟️ Frames Browser</a></h4>
<h4><a href="./MatrixStickerHelper">🃏️ [Matrix] Sticker Helper</a></h4>
<h4><a href="./MBViewer">👁️‍🗨️️ MBViewer</a> <small>(chat-like UI for WordPress & co.)</small></h4>
<br/>
<h4><a href="https://octospacc.gitlab.io/FumoPrisms">🔺️ Fumo Prisms (!)</a></h4>
</div></div>