mirror of
https://gitlab.com/octospacc/octospacc.gitlab.io
synced 2025-06-05 21:59:15 +02:00
Update site structure, update global js to add domain warning, update SpiderADB
This commit is contained in:
676
static/MBViewer/js/MBViewer.js
Normal file
676
static/MBViewer/js/MBViewer.js
Normal file
@@ -0,0 +1,676 @@
|
||||
// TODO:
|
||||
// * support opening posts from not only id but also permalink
|
||||
// * custom colors
|
||||
// * 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
|
||||
// * fetch and compile to show Markdown WordPress export pages from Git?
|
||||
// * app info in main page without JS?
|
||||
// * fix some messages being skipped when connection errors happen (already done?)
|
||||
// * optionally show post titles?
|
||||
// * fix some unfinished tasks still executing when clicking back
|
||||
// * I think we might need to handle acronicized names for users when needed?
|
||||
// * show, and/or sort by, posts tags/categories
|
||||
// * scroll to post id when loading from dataInject or RSS
|
||||
|
||||
let MbState = {};
|
||||
let MbApiTransformer;
|
||||
|
||||
function ArgsRewrite (props={}, navigate=true) {
|
||||
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]}|`;
|
||||
}
|
||||
if (navigate) {
|
||||
location.hash = hash;
|
||||
}
|
||||
return 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) {
|
||||
if (0 === el[scrollAxis]) {
|
||||
el[scrollAxis] = 1;
|
||||
if (1 === el[scrollAxis]) {
|
||||
el[scrollAxis] = 0;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
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];
|
||||
}
|
||||
|
||||
function MakeSiteRestUrl (path='') {
|
||||
const siteUrl = MbState.siteUrl;
|
||||
if (GetDomainFromUrl(siteUrl).toLowerCase() === 'octospacc.altervista.org') {
|
||||
return `${siteUrl}/wp-content/uploads/${siteUrl.split('.').slice(0, 1)[0].split('//')[1]}/scripts/stuff.php?&Thing=SiteWpJsonCors&AccessToken=9ab6e20c&$Query=${encodeURIComponent(path)}`;
|
||||
} else {
|
||||
if (["atom", "rss", "wordpress.org"].includes(MbState.platform)) {
|
||||
const proxies = ["corsproxy.io", "corsproxy.org"];
|
||||
return `https://${proxies[~~(Math.random() * proxies.length)]}/?${siteUrl}/${MbState.platform === 'wordpress.org' ? `wp-json/${path}` : ''}`;
|
||||
} else if (MbState.platform === 'wordpress.com') {
|
||||
return `https://public-api.wordpress.com/rest/v1.1/sites/${GetDomainFromUrl(siteUrl)}/${path}`;
|
||||
} else if (MbState.platform === 'mastodon') {
|
||||
return `${MbState.siteUrl.split('/').slice(0, 3).join('/')}/api/${path || 'v2/instance'}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function MakeApiEndpoint (type, options={}) {
|
||||
const translations = {
|
||||
"mastodon": {},
|
||||
"wordpress.org": {
|
||||
count: "per_page",
|
||||
orderBy: "orderby",
|
||||
},
|
||||
"wordpress.com": {
|
||||
count: "number",
|
||||
orderBy: "order_by",
|
||||
},
|
||||
}
|
||||
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 'mastodon':
|
||||
switch (type) {
|
||||
case 'acct' : query = `v1/accounts/lookup?acct=${options.username}`; break;
|
||||
case 'default':
|
||||
case 'posts': query = `v1/accounts/${MbState.userId}/statuses?exclude_replies=true`; break;
|
||||
}
|
||||
break;
|
||||
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 () {
|
||||
if (!location.hash) {
|
||||
location.hash = '/';
|
||||
}
|
||||
if (!MbApiTransformer) {
|
||||
MbApiTransformer = Trasformapi(MbViewerTrasformapiSchema).TransformForInput;
|
||||
}
|
||||
MbState = {
|
||||
args: {},
|
||||
siteData: {
|
||||
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>!
|
||||
`,
|
||||
},
|
||||
authors: {},
|
||||
lastPostOffsetAfter: 0,
|
||||
lastPostOffsetBefore: 0,
|
||||
lastMustScroll: true,
|
||||
internalIdCount: 0,
|
||||
};
|
||||
$('form.tgme_header_search_form')[0].action = '';
|
||||
$('form.tgme_header_search_form')[0].onsubmit = function(event){
|
||||
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].onclick = function(){
|
||||
if (window.innerWidth <= 720 ) { // .tgme_header_right_column @media max-width
|
||||
if (!$('.tgme_header_right_column')[0].style.display) {
|
||||
$('body')[0].style.overflow = 'hidden';
|
||||
$('main.tgme_main')[0].style.visibility = 'hidden';
|
||||
$('.tgme_header_search')[0].style.display = 'none';
|
||||
$('.tgme_header_right_column')[0].style.display = 'revert';
|
||||
$('.tgme_header_right_column')[0].style.width = 'calc(100% - 24px)';
|
||||
$('.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;
|
||||
$('.tgme_channel_info_header_username').html('');
|
||||
$('.tgme_page_photo_image').html('');
|
||||
$('.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('|')) {
|
||||
if (arg) {
|
||||
const argTokens = arg.split('=');
|
||||
const valueItems = argTokens.slice(1).join('=').split(',');
|
||||
MbState.args[argTokens[0].toLowerCase()] = (valueItems.length > 1 ? valueItems : valueItems[0]);
|
||||
}
|
||||
}
|
||||
MbState.siteUrl = MbState.args.siteurl;
|
||||
MbState.platform = /*SureArray(*/MbState.args.platform/*)*/;
|
||||
MbState.postId = MbState.args.postid;
|
||||
//MbState.postSlug = MbState.args.postslug;
|
||||
RefreshIncludeStyle();
|
||||
RefreshIncludeScript();
|
||||
if (MbState.args.dataurl) {
|
||||
// TODO initially remove built-in site data?
|
||||
MbState.dataInject = {};
|
||||
try {
|
||||
const fileUrlPrefix = (MbState.args.dataurl.split('/').slice(0, -1).join('/') || '.');
|
||||
const dataRequest = await fetch(MbState.args.dataurl);
|
||||
MbState.dataInject = await dataRequest.json();
|
||||
let messagesNew = [];
|
||||
if (MbState.platform === 'telegram.export') {//(["telegram.export", "telegram.json"].includes(MbState.platform)) {
|
||||
MbState.siteData = {
|
||||
name: MbState.dataInject.name,
|
||||
description: `${MbState.dataInject.type} ${MbState.dataInject.id}`,
|
||||
acroName: (!MbState.siteData.iconUrl ? MbState.dataInject.name && MakeAcroName(MbState.dataInject.name) : ''),
|
||||
bgColor: ~~(Math.random() * 7),
|
||||
};
|
||||
const textEncoder = document.createElement('textarea');
|
||||
for (const message of MbState.dataInject.messages) {
|
||||
if (message.type !== 'message') {
|
||||
continue;
|
||||
}
|
||||
messagesNew.push({
|
||||
content: '',
|
||||
quoting: (message.forwarded_from && {
|
||||
author: {
|
||||
name: `Forwarded from ${message.forwarded_from}`,
|
||||
},
|
||||
}),
|
||||
time: message.date,
|
||||
url: `#${ArgsRewrite({ postid: null }, false)}PostId=${message.id}`,
|
||||
});
|
||||
//for (const piece of (Array.isArray(message.text) ? message.text : [message.text])) {
|
||||
// messagesNew[messagesNew.length - 1].content += (piece.text || piece);
|
||||
//}
|
||||
//const encoder = document.createElement('textarea');
|
||||
//encoder.innerHTML = messagesNew[messagesNew.length - 1].content;
|
||||
//messagesNew[messagesNew.length - 1].content = encoder.innerHTML.replaceAll('\n', '<br/>');
|
||||
for (const entity of message.text_entities) {
|
||||
const entityTag = { bold: "b", italic: "i", link: "a", text_link: "a", pre: "pre", blockquote: "blockquote" }[entity.type];
|
||||
const entityHref = (entityTag === 'a' && (entity.href || entity.text));
|
||||
textEncoder.innerHTML = entity.text;
|
||||
entity.text = textEncoder.innerHTML.replaceAll('\n', '<br/>');
|
||||
messagesNew[messagesNew.length - 1].content += (entityTag
|
||||
? `<${entityTag} ${entityHref ? `href="${entityHref}"` : ''}>${entity.text}</${entityTag}>`
|
||||
: entity.text);
|
||||
}
|
||||
if (messagesNew[messagesNew.length - 1].content) {
|
||||
messagesNew[messagesNew.length - 1].content = `<p>${messagesNew[messagesNew.length - 1].content}</p>`;
|
||||
}
|
||||
if (message.photo) {
|
||||
messagesNew[messagesNew.length - 1].content = `<img src="${fileUrlPrefix}/${message.photo}"/>${messagesNew[messagesNew.length - 1].content}`;
|
||||
} else if (message.file && message.mime_type?.split('/')[0] === 'video') {
|
||||
messagesNew[messagesNew.length - 1].content = `<video controls="true" src="${fileUrlPrefix}/${message.file}"></video>${messagesNew[messagesNew.length - 1].content}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
messagesNew = MbApiTransformer('message[]', MbState.platform, MbState.dataInject);
|
||||
}
|
||||
$('section.tgme_channel_history.js-message_history').html(MakeMoreWrapperHtml());
|
||||
TWeb.loadMore($('.js-messages_more_wrap > a'), messagesNew);
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
setTimeout(MbViewerInit, 1000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else 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 = (["atom", "rss"].includes(MbState.platform)
|
||||
? new DOMParser().parseFromString(await siteRequest.text(), 'text/xml')
|
||||
: await siteRequest.json());
|
||||
if (MbState.platform === 'mastodon') {
|
||||
MbState.siteData = MbApiTransformer('profile', MbState.platform, MbState.siteData);
|
||||
let username = MbState.siteUrl;
|
||||
if (username.endsWith('/')) username = username.slice(0, -1);
|
||||
username = username.split('/').slice(-1)[0];
|
||||
if (username.startsWith('@')) username = username.slice(1);
|
||||
const userRequest = await fetch(MakeSiteRestUrl(MakeApiEndpoint('acct', { username })));
|
||||
const userData = await userRequest.json();
|
||||
MbState.authors[MbState.userId = userData.id] = MbApiTransformer('profile', MbState.platform, userData);
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
setTimeout(MbViewerInit, 1000);
|
||||
return;
|
||||
}
|
||||
const siteLink = (MbState.siteData.url || MbState.siteData.URL || MbState.siteUrl);
|
||||
MbState.startingPost = MbState.postId;//!!(MbState.postId || MbState.postSlug);
|
||||
if (MbState.startingPost) {
|
||||
try {
|
||||
const postRequest = await fetch(MakeSiteRestUrl(MakeApiEndpoint('posts', { id: MbState.postId })));
|
||||
MbState.startingPost = await postRequest.json();
|
||||
$('section.tgme_channel_history.js-message_history').append(MakeMoreWrapperHtml('after'));
|
||||
MbState.lastPostOffsetAfter = 0; // for some reason we need to clear this after making the wrapper or else we lose a post
|
||||
TWeb.loadMore($('.js-messages_more_wrap > a[data-after]'), MbState.startingPost);
|
||||
$('section.tgme_channel_history.js-message_history').prepend(MakeMoreWrapperHtml('before'));
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
setTimeout(MbViewerInit, 1000);
|
||||
return;
|
||||
}
|
||||
} else if (!["atom", "rss"].includes(MbState.platform)) {
|
||||
$('section.tgme_channel_history.js-message_history').html(MakeMoreWrapperHtml('before'));
|
||||
TWeb.loadMore($('.js-messages_more_wrap > a'));
|
||||
}
|
||||
$('form.tgme_header_search_form')[0].action = `${siteLink}/?s`;
|
||||
$('form.tgme_header_search_form')[0].onsubmit = null;
|
||||
$('.tgme_channel_info_header_username').html(`<a href="${siteLink}">${GetDomainFromUrl(siteLink).toLowerCase()}</a>`);
|
||||
}
|
||||
if (MbState.siteUrl || MbState.dataInject) {
|
||||
$('a[name="goBack"]')[0].hidden = false;
|
||||
}
|
||||
if (["atom", "rss"].includes(MbState.platform)) {
|
||||
$('section.tgme_channel_history.js-message_history').html(MakeMoreWrapperHtml());
|
||||
TWeb.loadMore($('.js-messages_more_wrap > a'), MbState.siteData);
|
||||
MbState.siteData = MbApiTransformer('profile', MbState.platform, MbState.siteData.querySelector(':scope > channel'));
|
||||
}
|
||||
MbState.siteData.iconUrl = (MbState.siteData.icon?.url || MbState.siteData.site_icon_url || MbState.siteData.icon?.img || MbState.siteData.icon?.ico);
|
||||
MbState.siteData.acroName ||= (!MbState.siteData.iconUrl ? MbState.siteData.name && 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 && !MbState.dataInject) {
|
||||
$('a[name="goBack"]')[0].hidden = true;
|
||||
$('section.tgme_channel_history.js-message_history').html(MakeMoreWrapperHtml());
|
||||
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>`, time: '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.
|
||||
<br/> * Added CORS proxies to handle sites normally inaccessible.
|
||||
<br/> * Hey there, this text and everything is new...
|
||||
<br/>
|
||||
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>`, time: '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>`, time: '2024-01-15T01:00' }, { content: `<p>
|
||||
New changes:
|
||||
<br/> * Adapt newly-added icons for dark mode
|
||||
<br/> * Improved visualization of info column for small screens
|
||||
<br/> * Improved video anti-hotlinking bypass, added fullscreen button for browsers which wouldn't otherwise show the native one
|
||||
<br/> * Allow opening the stream at the point in time of a specific post ID for a website
|
||||
</p>`, time: '2024-01-16T00:00' }, { content: `<p>
|
||||
I was thinking this tool would now just start to die,
|
||||
since I should try to get some time to develop my actual well-made and non-kanged frontend,
|
||||
but I will need a few libraries and things first, that I can actually already start developing and introduce here.
|
||||
<br/>
|
||||
So, here are some new changes:
|
||||
<br/> * Fixed video embed fullscreen, and added a reload button in case load fails
|
||||
<br/> * Initial support for handling data via Trasformapi lib
|
||||
<br/> * Initial, experimental support for RSS feeds specifically, via Trasformapi (very broken)
|
||||
</p>`, time: '2024-01-23T01:00' }, { content: `<p>
|
||||
New changes:
|
||||
<br/> * Updated Trasformapi.js with misc fixes, query constants, and streamlined/powerful data querying
|
||||
(XPath support for both XML sources, and JSON sources via defiant.js)
|
||||
<br/> * Only slightly better RSS support
|
||||
<br/> * Initial, experimental support for Mastodon profiles (broken)
|
||||
<br/> * Hotfixed a defiant parsing bug on Firefox
|
||||
</p>`, time: '2024-01-24T01:00' }, { content: `<p>
|
||||
New changes:
|
||||
<br/> * Read Telegram's JSON chat exports (experimental, very slow and resource-heavy)
|
||||
<br/>
|
||||
Regarding Trasformapi, I transformed some of my development tears into words, read here if you're curious:
|
||||
<a href="https://octospacc.altervista.org/2024/01/25/mbviewer-per-distrarci/">https://octospacc.altervista.org/2024/01/25/mbviewer-per-distrarci/</a>.
|
||||
</p>`, time: '2024-01-25T01:00' }, { content: `<p>
|
||||
Some small things:
|
||||
<br/> * Fixed RSS feeds parsing on Firefox (mentioned in the post linked above), by fixing a bug in Trasformapi
|
||||
<br/> * HTML is now sanitized for removal of dangerous tags and attributes before displaying
|
||||
<br/> * Support including user-defined CSS rules from URL (<code>data:</code> supported) via the <code>includeStyle</code> argument
|
||||
</p>`, time: '2024-01-27T20:00' }, { content: `<p>
|
||||
New changes:
|
||||
<br/> * Support including user-defined JS scripts from URL (<code>data:</code> supported) via the <code>includeScript</code> argument. A script must expose a <code>MbViewerFunction(data)</code> function to be invoked by the main application to do useful operations, and then return data by calling the <code>MbViewerReturn(data)</code> API function.
|
||||
<br/>
|
||||
...I will probably need to write actual documentation about this, but for sure I will post about this on <a href="https://octospacc.altervista.org/?p=1416">https://octospacc.altervista.org/?p=1416</a>.
|
||||
</p>`, time: '2024-02-01T00:00' }, { content: `<p>
|
||||
Updates:
|
||||
<br/> * Include special CSS for optimized PDF/paper printing
|
||||
</p>`, time: '2024-02-05T11: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/>
|
||||
This webapp is not affiliated with Telegram (Telegram FZ LLC, Telegram Messenger Inc.), and
|
||||
all rights upon the original materials (which are: everything not strictly related to the "MBViewer" mod) belong to the original owners.
|
||||
</p>` }]);
|
||||
}
|
||||
document.title = `${MbState.siteData.name} — 👁️🗨️️ MBViewer`;
|
||||
$('.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}`);
|
||||
}
|
||||
}
|
||||
|
||||
function RefreshIncludeStyle () {
|
||||
document.querySelector('link[href][rel="stylesheet"]#MbViewerIncludeStyle')?.remove();
|
||||
if (MbState.args.includestyle) {
|
||||
const linkElem = document.createElement('link');
|
||||
linkElem.id = 'MbViewerIncludeStyle';
|
||||
linkElem.rel = 'stylesheet';
|
||||
linkElem.href = MbState.args.includestyle;
|
||||
document.body.appendChild(linkElem);
|
||||
}
|
||||
}
|
||||
|
||||
function RefreshIncludeScript () {
|
||||
document.querySelector('iframe#MbViewerIncludeScript')?.remove();
|
||||
if (MbState.args.includescript) {
|
||||
const frameElement = document.createElement('iframe');
|
||||
frameElement.id = 'MbViewerIncludeScript';
|
||||
frameElement.sandbox = 'allow-scripts';
|
||||
frameElement.src = `data:text/html;utf8,
|
||||
<script>
|
||||
function MbViewerReturn (data) {
|
||||
/* data.type = 'IncludeScriptResult'; */
|
||||
window.top.postMessage({ MbViewer: data }, '*');
|
||||
}
|
||||
</script>
|
||||
<script src="${MbState.args.includescript}"></script>
|
||||
<script>
|
||||
window.addEventListener('message', function(event){
|
||||
MbViewerFunction(event.data.MbViewer);
|
||||
});
|
||||
</script>
|
||||
`;
|
||||
frameElement.hidden = true;
|
||||
document.body.appendChild(frameElement);
|
||||
}
|
||||
}
|
||||
|
||||
function MakeMoreWrapperHtml (wrapType) {
|
||||
let offset, order, relativeOpts;
|
||||
switch (wrapType) {
|
||||
case 'after':
|
||||
offset = MbState.lastPostOffsetAfter;
|
||||
MbState.lastPostOffsetAfter--;
|
||||
order = 'asc';
|
||||
break;
|
||||
case 'before':
|
||||
offset = MbState.lastPostOffsetBefore;
|
||||
MbState.lastPostOffsetBefore++;
|
||||
order = 'desc';
|
||||
break;
|
||||
}
|
||||
if (MbState.startingPost) {
|
||||
relativeOpts = { order: order, exclude: (MbState.startingPost.id || MbState.startingPost.ID) };
|
||||
relativeOpts[wrapType] = MbState.startingPost.date;
|
||||
}
|
||||
return `<div class="tgme_widget_message_centered js-messages_more_wrap">
|
||||
<a href="${MbState.siteUrl && !["atom", "rss"].includes(MbState.platform) && MakeSiteRestUrl(MakeApiEndpoint('posts', { count: 1, offset: offset, orderBy: "date", ...(MbState.startingPost && relativeOpts) }))}" data-${wrapType}="" class="tme_messages_more js-messages_more"></a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
async function MakeMbHtml (postData, makeMoreWrap) {
|
||||
postData = (typeof(postData) === 'string' ? JSON.parse(postData) : postData);
|
||||
if (["atom", "rss"].includes(MbState.platform)) {
|
||||
postData = Array.from(postData.querySelectorAll(':scope > channel > item'));
|
||||
}
|
||||
if (["atom", "rss", "mastodon"].includes(MbState.platform)) {
|
||||
postData.reverse();
|
||||
}
|
||||
let html = '';
|
||||
const siteLink = (MbState.siteData.url || MbState.siteData.URL || MbState.siteLink);
|
||||
const siteHref = (siteLink ? `href="${siteLink}"` : '');
|
||||
for (postData of (postData.posts ? postData.posts : SureArray(postData))) {
|
||||
if (MbState.platform && MbState.platform !== 'telegram.export') {
|
||||
postData = MbApiTransformer('message', MbState.platform, postData);
|
||||
}
|
||||
const authorId = (postData.author?.id || postData._links?.author[0]?.href?.split('/')?.slice(-1)[0]);
|
||||
if (authorId && !MbState.authors[authorId]) {
|
||||
MbState.authors[authorId] = (typeof(postData.author) === 'object' && Object.keys(postData.author).join(' ') !== 'id'
|
||||
? postData.author
|
||||
: await (await fetch(MakeSiteRestUrl(MakeApiEndpoint('users', { id: authorId })))).json());
|
||||
}
|
||||
const authorData = (MbState.authors[authorId] || postData.author || (postData.quoting?.author && !postData.quoting?.content));
|
||||
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?.icon?.url || MbState.siteData.iconUrl);
|
||||
let attachmentsHtml = '';
|
||||
for (const attachment of (postData.attachments || postData.quoting?.attachments || [])) {
|
||||
if (attachment) {
|
||||
const mediaKind = attachment.type?.split('/')[0];
|
||||
const elemTag = (mediaKind === 'image' ? 'img' : mediaKind);
|
||||
const elemClosing = (mediaKind === 'image' ? '/>' : `></${elemTag}>`);
|
||||
attachmentsHtml += `<${elemTag} controls="true" src="${attachment.url}" alt="${attachment.description?.replaceAll('&', '&')?.replaceAll('"', '"') || ''}"/>`;
|
||||
}
|
||||
}
|
||||
const postInternalId = MbState.internalIdCount++;
|
||||
const postInnerHtml = `
|
||||
${attachmentsHtml}
|
||||
${ReformatPostHtml(postData.content)}
|
||||
${postData.quoting?.content ? `[♻️ Reblog]: ${ReformatPostHtml(postData.quoting.content)}` : ''}
|
||||
`;
|
||||
html += `
|
||||
<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="${postData.id || postData.ID}">
|
||||
<div class="tgme_widget_message_user">
|
||||
<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>
|
||||
<div class="tgme_widget_message_bubble">
|
||||
<i class="tgme_widget_message_bubble_tail">
|
||||
<svg class="bubble_icon" width="9px" height="20px" viewBox="0 0 9 20">
|
||||
<g fill="none">
|
||||
<path class="background" fill="#ffffff" d="M8,1 L9,1 L9,20 L8,20 L8,18 C7.807,15.161 7.124,12.233 5.950,9.218 C5.046,6.893 3.504,4.733 1.325,2.738 L1.325,2.738 C0.917,2.365 0.89,1.732 1.263,1.325 C1.452,1.118 1.72,1 2,1 L8,1 Z"></path>
|
||||
<path class="border_1x" fill="#d7e3ec" d="M9,1 L2,1 C1.72,1 1.452,1.118 1.263,1.325 C0.89,1.732 0.917,2.365 1.325,2.738 C3.504,4.733 5.046,6.893 5.95,9.218 C7.124,12.233 7.807,15.161 8,18 L8,20 L9,20 L9,1 Z M2,0 L9,0 L9,20 L7,20 L7,20 L7.002,18.068 C6.816,15.333 6.156,12.504 5.018,9.58 C4.172,7.406 2.72,5.371 0.649,3.475 C-0.165,2.729 -0.221,1.464 0.525,0.649 C0.904,0.236 1.439,0 2,0 Z"></path>
|
||||
<path class="border_2x" d="M9,1 L2,1 C1.72,1 1.452,1.118 1.263,1.325 C0.89,1.732 0.917,2.365 1.325,2.738 C3.504,4.733 5.046,6.893 5.95,9.218 C7.124,12.233 7.807,15.161 8,18 L8,20 L9,20 L9,1 Z M2,0.5 L9,0.5 L9,20 L7.5,20 L7.5,20 L7.501,18.034 C7.312,15.247 6.64,12.369 5.484,9.399 C4.609,7.15 3.112,5.052 0.987,3.106 C0.376,2.547 0.334,1.598 0.894,0.987 C1.178,0.677 1.579,0.5 2,0.5 Z"></path>
|
||||
<path class="border_3x" d="M9,1 L2,1 C1.72,1 1.452,1.118 1.263,1.325 C0.89,1.732 0.917,2.365 1.325,2.738 C3.504,4.733 5.046,6.893 5.95,9.218 C7.124,12.233 7.807,15.161 8,18 L8,20 L9,20 L9,1 Z M2,0.667 L9,0.667 L9,20 L7.667,20 L7.667,20 L7.668,18.023 C7.477,15.218 6.802,12.324 5.64,9.338 C4.755,7.064 3.243,4.946 1.1,2.983 C0.557,2.486 0.52,1.643 1.017,1.1 C1.269,0.824 1.626,0.667 2,0.667 Z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</i>
|
||||
<div class="tgme_widget_message_author accent_color">
|
||||
<a class="tgme_widget_message_owner_name" ${authorHref || siteHref}>
|
||||
<span dir="auto">
|
||||
${authorData?.name
|
||||
? `${authorData.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" data-internal-id="${postInternalId}">
|
||||
${postInnerHtml}
|
||||
</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.url ? `href="${postData.url}"` : ''}>
|
||||
<time datetime="${postData.time}" class="time"></time>
|
||||
<!-- TODO: show edited status -->
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
if (document.querySelector('iframe#MbViewerIncludeScript')) {
|
||||
function pollPostElement () {
|
||||
const postElement = document.querySelector(`div.tgme_widget_message_text > div.MbPost[data-internal-id="${postInternalId}"]`);
|
||||
if (postElement) {
|
||||
document.querySelector('iframe#MbViewerIncludeScript').contentWindow.postMessage({ MbViewer: { id: postInternalId, html: postInnerHtml } }, '*');
|
||||
} else {
|
||||
setTimeout(pollPostElement, 75);
|
||||
}
|
||||
}
|
||||
pollPostElement();
|
||||
}
|
||||
}
|
||||
if (!html) {
|
||||
// no more messages?
|
||||
return;
|
||||
}
|
||||
if (makeMoreWrap && MbState.siteUrl) {
|
||||
const wrapHtml = MakeMoreWrapperHtml(makeMoreWrap);
|
||||
switch (makeMoreWrap) {
|
||||
case 'after': html = `${html}${wrapHtml}`; break;
|
||||
case 'before': html = `${wrapHtml}${html}`; break;
|
||||
}
|
||||
}
|
||||
MbState.lastMoreWrap = makeMoreWrap;
|
||||
return html;
|
||||
}
|
||||
|
||||
function ReformatPostHtml (html) {
|
||||
const content = $(`<div>${cleanHTML(html, false)}</div>`);
|
||||
// bypass Altervista's anti-hotlinking protection by hiding our HTTP Referer header
|
||||
// TODO: only do this for altervista sites maybe
|
||||
if (MbState.platform === 'wordpress.org') {
|
||||
for (const videoElem of content.find('video').toArray()) {
|
||||
videoElem.preload = 'none';
|
||||
const frameElem = document.createElement('iframe');
|
||||
frameElem.style = 'border: none; width: 100%;';
|
||||
frameElem.allowFullscreen = true;
|
||||
frameElem.src = `data:text/html;utf8,<!DOCTYPE html><body>
|
||||
<style>
|
||||
html, body { margin: 0; overflow: hidden; }
|
||||
video { max-width: 100%; }
|
||||
</style>
|
||||
${encodeURIComponent(videoElem.outerHTML)}
|
||||
<button style="position: absolute; top: 0; right: 0; z-index: 1;">
|
||||
Reload Media
|
||||
</button>
|
||||
<script>
|
||||
var videoElem = document.querySelector('video');
|
||||
var buttonElem = document.querySelector('button');
|
||||
buttonElem.onclick = function(){
|
||||
videoElem.load();
|
||||
};
|
||||
videoElem.onloadedmetadata = function(){
|
||||
window.top.postMessage((videoElem.src + ' ' + getComputedStyle(videoElem).height), '*');
|
||||
};
|
||||
videoElem.load();
|
||||
</script>
|
||||
</body>`;
|
||||
videoElem.replaceWith(frameElem);
|
||||
}
|
||||
}
|
||||
return content.html();
|
||||
}
|
||||
|
||||
function HideMobileRightColumn () {
|
||||
$('body')[0].style.overflow = '';
|
||||
$('main.tgme_main')[0].style.visibility = '';
|
||||
$('.tgme_header_search')[0].style.display = '';
|
||||
$('.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(){
|
||||
ArgsRewrite({ dataurl: null, siteurl: null, postid: null, platform: null, includestyle: null, includescript: null /*postslug: null*/ });
|
||||
};
|
||||
|
||||
// TODO: we should check origin
|
||||
window.addEventListener('message', function(event){
|
||||
// TODO edit the video embed function to send objects for consistency
|
||||
if (typeof(event.data) === 'string') {
|
||||
const tokens = event.data.split(' ');
|
||||
$(`iframe[src*="${encodeURIComponent(tokens[0])}"]`).height(tokens[1]);
|
||||
}
|
||||
else if (event.data.MbViewer) {
|
||||
const data = event.data.MbViewer;
|
||||
//switch (data.type) {
|
||||
// case 'IncludeScriptResult':
|
||||
document.querySelector(`div.tgme_widget_message_text > div.MbPost[data-internal-id="${parseInt(data.id)}"]`).innerHTML = cleanHTML(data.html);
|
||||
// break;
|
||||
//}
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('resize', ResizeLayouts);
|
||||
window.addEventListener('hashchange', MbViewerInit);
|
130
static/MBViewer/js/TrasformapiSchema.xml.js
Normal file
130
static/MBViewer/js/TrasformapiSchema.xml.js
Normal file
@@ -0,0 +1,130 @@
|
||||
const MbViewerTrasformapiSchema = `<schema>
|
||||
|
||||
<set
|
||||
rss-media-query="*[name()='media:content' or name()='enclosure']"
|
||||
/>
|
||||
|
||||
<!-- WIP, find out how to structure this -->
|
||||
<endpoint name="messages">
|
||||
<method name="GET" args="" returns="message[]"/>
|
||||
<!-- ... -->
|
||||
</endpoint>
|
||||
|
||||
<entity name="message">
|
||||
<prop name="id" type="int">
|
||||
<content upstream="rss" query="./guid"/>
|
||||
<content upstream="wordpress.com" query="//ID"/>
|
||||
<content upstream="wordpress.org" query="//id"/>
|
||||
<content upstream="mastodon" query="//id"/>
|
||||
</prop>
|
||||
<prop name="url" type="string">
|
||||
<content upstream="rss" query="./link"/>
|
||||
<content upstream="wordpress.com" query="//URL"/>
|
||||
<content upstream="wordpress.org" query="//link"/>
|
||||
<content upstream="mastodon" query="//url"/>
|
||||
</prop>
|
||||
<prop name="title" type="string">
|
||||
<content upstream="rss" query="title"/>
|
||||
<content upstream="wordpress.com" query="//title"/>
|
||||
<content upstream="wordpress.org" query="//title/rendered"/>
|
||||
</prop>
|
||||
<prop name="content" type="string">
|
||||
<!-- TODO optional multiple 'query' attrs -->
|
||||
<!--<content upstream="rss" query="content:encoded"/>-->
|
||||
<content upstream="rss" query="./description"/>
|
||||
<content upstream="wordpress.com" query="//content"/>
|
||||
<content upstream="wordpress.org" query="//content/rendered"/>
|
||||
<content upstream="mastodon" query="//content"/>
|
||||
</prop>
|
||||
<prop name="attachments" type="file[]">
|
||||
<content upstream="rss">
|
||||
<prop name="url" query="{rss-media-query}/@url"/>
|
||||
<prop name="type" query="{rss-media-query}/@type"/>
|
||||
<prop name="description" query="{rss-media-query}/*[name()='media:description']"/>
|
||||
</content>
|
||||
<!--
|
||||
<content upstream="mastodon">
|
||||
<prop name="url" query="media_attachments.url"/>
|
||||
<prop name="type" query="media_attachments.type"/>
|
||||
</content>
|
||||
-->
|
||||
<content upstream="mastodon" query="//media_attachments"/>
|
||||
</prop>
|
||||
<prop name="author" type="profile">
|
||||
<content upstream="rss"/>
|
||||
<content upstream="wordpress.com" query="//author"/>
|
||||
<content upstream="wordpress.org"/>
|
||||
<content upstream="mastodon" query="//account"/>
|
||||
</prop>
|
||||
<prop name="time" type="string">
|
||||
<content upstream="rss" query="pubDate"/>
|
||||
<content upstream="wordpress.com" query="//date"/>
|
||||
<content upstream="wordpress.org" query="//date"/>
|
||||
<content upstream="mastodon" query="//created_at"/>
|
||||
</prop>
|
||||
<prop name="revisions" type="revision[]">
|
||||
<content upstream="wordpress.com"/>
|
||||
<content upstream="wordpress.org"/>
|
||||
<content upstream="mastodon"/>
|
||||
</prop>
|
||||
<prop name="quoting" type="message">
|
||||
<content upstream="mastodon" query="//reblog"/>
|
||||
</prop>
|
||||
<!--<prop name="replying" type="message">
|
||||
<content upstream="mastodon" query=""/>
|
||||
</prop>-->
|
||||
</entity>
|
||||
|
||||
<entity name="revision">
|
||||
<prop name="time" type="string">
|
||||
<content upstream="wordpress.com" query="//modified"/>
|
||||
<content upstream="wordpress.org" query="//modified"/>
|
||||
<content upstream="mastodon" query="//edited_at"/>
|
||||
</prop>
|
||||
</entity>
|
||||
|
||||
<!-- TODO (for wordpress) how to handle both authors and sites as a profile type? maybe add a 'variant' attr for 'content' tags? -->
|
||||
<entity name="profile">
|
||||
<prop name="id" type="int"> <!-- TODO fix type -->
|
||||
<content upstream="rss" query="link"/>
|
||||
<content upstream="wordpress.com" query="//ID"/>
|
||||
<content upstream="wordpress.org" query="//author"/>
|
||||
</prop>
|
||||
<prop name="url" type="string">
|
||||
<content upstream="rss" query="link"/>
|
||||
<content upstream="wordpress.com" query="//profile_URL"/>
|
||||
</prop>
|
||||
<prop name="name" type="string">
|
||||
<content upstream="rss" query="*[name()='title' or name()='dc:creator']"/>
|
||||
<content upstream="wordpress.com" query="//name"/>
|
||||
<content upstream="mastodon" query="//title"/>
|
||||
</prop>
|
||||
<prop name="description" type="string">
|
||||
<content upstream="rss" query="description"/>
|
||||
<content upstream="mastodon" query="//description"/>
|
||||
</prop>
|
||||
<prop name="icon" type="file">
|
||||
<content upstream="rss">
|
||||
<prop name="url" query="image/url"/>
|
||||
</content>
|
||||
<content upstream="wordpress.com">
|
||||
<prop name="url" query="//avatar_URL"/>
|
||||
</content>
|
||||
<content upstream="mastodon"> <!-- TODO read user avatars -->
|
||||
<!--<prop name="url" query="//thumbnail/url"/>-->
|
||||
<prop name="url" query="//contact/account/avatar"/>
|
||||
</content>
|
||||
</prop>
|
||||
</entity>
|
||||
|
||||
<entity name="file">
|
||||
<prop name="url" type="string">
|
||||
<content upstream="mastodon" query="//url"/>
|
||||
</prop>
|
||||
<prop name="type" type="string">
|
||||
<content upstream="mastodon" query="//type"/>
|
||||
</prop>
|
||||
<prop name="description" type="string"/>
|
||||
</entity>
|
||||
|
||||
</schema>`;
|
9
static/MBViewer/js/jquery-ui.min.js
vendored
Normal file
9
static/MBViewer/js/jquery-ui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
static/MBViewer/js/jquery.min.js
vendored
Normal file
4
static/MBViewer/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
379
static/MBViewer/js/telegram-web.js
Normal file
379
static/MBViewer/js/telegram-web.js
Normal file
@@ -0,0 +1,379 @@
|
||||
(function($) {
|
||||
$.fn.redraw = function() {
|
||||
return this.map(function(){ this.offsetTop; return this; });
|
||||
};
|
||||
$.fn.scrollIntoView = function(options) {
|
||||
options = options || {}
|
||||
return this.first().each(function() {
|
||||
var position = options.position || 'auto',
|
||||
padding = options.padding || 0,
|
||||
duration = options.duration || 0;
|
||||
var $item = $(this),
|
||||
$cont = $item.scrollParent(),
|
||||
scrollTop = $cont.scrollTop(),
|
||||
positionTop = 0,
|
||||
paddingTop = 0,
|
||||
itemHeight = $item.outerHeight(),
|
||||
isBody = false;
|
||||
if ($cont.get(0) === document) {
|
||||
isBody = true;
|
||||
$cont = $(window);
|
||||
positionTop = $item.offset().top;
|
||||
paddingTop = $('header').height() + 1;
|
||||
} else {
|
||||
positionTop = $item.offset().top - $cont.offset().top + scrollTop;
|
||||
}
|
||||
if (options.slidedEl) {
|
||||
if (options.slidedEl === 'this') {
|
||||
options.slidedEl = this;
|
||||
}
|
||||
$(options.slidedEl, this).each(function() {
|
||||
itemHeight += (this.scrollHeight - this.clientHeight);
|
||||
});
|
||||
}
|
||||
var itemTop = positionTop,
|
||||
itemBottom = itemTop + itemHeight,
|
||||
contHeight = $cont.height(),
|
||||
contTop = scrollTop + padding + paddingTop,
|
||||
contBottom = scrollTop + contHeight - padding,
|
||||
scrollTo = null;
|
||||
if (position == 'auto') {
|
||||
if (itemTop < contTop) {
|
||||
scrollTo = itemTop - padding - paddingTop;
|
||||
} else if (itemBottom > contBottom) {
|
||||
if (itemHeight > contHeight - padding - padding) {
|
||||
scrollTo = itemTop - padding - paddingTop;
|
||||
} else {
|
||||
scrollTo = itemBottom - contHeight + padding;
|
||||
}
|
||||
}
|
||||
} else if (position == 'top' || position == 'center') {
|
||||
if (contHeight > itemHeight) {
|
||||
padding = (contHeight - paddingTop - itemHeight) / 2;
|
||||
}
|
||||
scrollTo = itemTop - padding - paddingTop;
|
||||
} else if (position == 'bottom') {
|
||||
if (itemHeight > contHeight - padding - padding) {
|
||||
scrollTo = itemTop - padding - paddingTop;
|
||||
} else {
|
||||
scrollTo = itemBottom - contHeight + padding;
|
||||
}
|
||||
}
|
||||
if (scrollTo) {
|
||||
if (duration) {
|
||||
if (isBody) {
|
||||
$cont = $('html');
|
||||
}
|
||||
$cont.stop().animate({scrollTop: scrollTo}, duration);
|
||||
} else {
|
||||
$cont.scrollTop(scrollTo);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
function doesSupportThinBoxShadow() {
|
||||
if (!window.getComputedStyle) return;
|
||||
var div = document.createElement('div');
|
||||
div.style.boxShadow = '0 0 0 0.5px black';
|
||||
div.style.display = 'none';
|
||||
document.body.appendChild(div);
|
||||
var box_shadow = window.getComputedStyle(div).boxShadow;
|
||||
document.body.removeChild(div);
|
||||
return box_shadow.indexOf('0.5') >= 0;
|
||||
}
|
||||
|
||||
function formatDate(datetime) {
|
||||
var date = new Date(datetime);
|
||||
var cur_date = new Date();
|
||||
var j = date.getDate();
|
||||
var M = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][date.getMonth()];
|
||||
var Y = date.getFullYear();
|
||||
if (cur_date.getFullYear() == date.getFullYear()) {
|
||||
return M + ' ' + j;
|
||||
}
|
||||
if (!j && !M && !Y) {
|
||||
return 'Undefined';
|
||||
}
|
||||
return M + ' ' + j + ', ' + Y;
|
||||
}
|
||||
|
||||
function getCssProperty(el, prop) {
|
||||
if (window.getComputedStyle) {
|
||||
return window.getComputedStyle(el, '').getPropertyValue(prop) || null;
|
||||
} else if (el.currentStyle) {
|
||||
return el.currentStyle[prop] || null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isVisible(el, padding) {
|
||||
var node = el, val;
|
||||
var visibility = getCssProperty(node, 'visibility');
|
||||
if (visibility == 'hidden') return false;
|
||||
while (node) {
|
||||
if (node === document.documentElement) break;
|
||||
var display = getCssProperty(node, 'display');
|
||||
if (display == 'none') return false;
|
||||
var opacity = getCssProperty(node, 'opacity');
|
||||
if (opacity !== null && opacity < 0.1) return false;
|
||||
node = node.parentNode;
|
||||
}
|
||||
if (el.getBoundingClientRect) {
|
||||
padding = +padding || 0;
|
||||
var rect = el.getBoundingClientRect();
|
||||
var html = document.documentElement;
|
||||
if (rect.bottom < padding ||
|
||||
rect.right < padding ||
|
||||
rect.top > (window.innerHeight || html.clientHeight) - padding ||
|
||||
rect.left > (window.innerWidth || html.clientWidth) - padding) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var TWeb = {
|
||||
init: function(options) {
|
||||
options = options || {};
|
||||
if (!doesSupportThinBoxShadow()) {
|
||||
$('body').addClass('thin_box_shadow');
|
||||
}
|
||||
$('.js-widget_message').each(function() {
|
||||
TPost.init(this);
|
||||
});
|
||||
TWeb.updateServiceDate($('.js-widget_message_wrap'));
|
||||
if (options.scrollToPost) {
|
||||
TWeb.highlightPost(options.scrollToPost, true);
|
||||
} else {
|
||||
$('.js-widget_message_wrap').last().scrollIntoView({position: 'top'});
|
||||
}
|
||||
$('body').removeClass('no_transitions');
|
||||
$('.js-header_search').on('focus', function() {
|
||||
$('header.tgme_header').removeClass('search_collapsed');
|
||||
$(this).select();
|
||||
});
|
||||
$('.js-header_search').on('blur', function() {
|
||||
$('header.tgme_header').addClass('search_collapsed');
|
||||
});
|
||||
TWeb.initScroll();
|
||||
TWeb.initViews();
|
||||
if (window.matchMedia) {
|
||||
var darkMedia = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
TWeb.toggleTheme(darkMedia.matches);
|
||||
darkMedia.addListener(function(e) {
|
||||
TWeb.toggleTheme(e.matches);
|
||||
});
|
||||
}
|
||||
if (true) { // wallpaper supported
|
||||
$('body').addClass('twallpaper');
|
||||
TWallpaper.init($('#tgme_background').get(0));
|
||||
$(window).on('focus', function() { // chrome fix
|
||||
TWallpaper.update();
|
||||
});
|
||||
var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||
reduceMotion.addEventListener('change', function() {
|
||||
TWallpaper.scrollAnimate(!reduceMotion.matches);
|
||||
});
|
||||
TWallpaper.scrollAnimate(!reduceMotion.matches);
|
||||
}
|
||||
},
|
||||
toggleTheme: function(dark) {
|
||||
$('html').toggleClass('theme_dark', dark);
|
||||
},
|
||||
initScroll: function() {
|
||||
var $document = $(document);
|
||||
$document.on('scroll', function() {
|
||||
$before = $('.js-messages_more[data-before]');
|
||||
$after = $('.js-messages_more[data-after]');
|
||||
var wheight = $(window).height();
|
||||
var scrollTop = $(window).scrollTop();
|
||||
if ($before.length) {
|
||||
var bottom = $before.offset().top + $before.height() - scrollTop;
|
||||
if (bottom > -wheight * 3) {
|
||||
TWeb.loadMore($before);
|
||||
}
|
||||
}
|
||||
if ($after.length) {
|
||||
var top = $after.offset().top - scrollTop;
|
||||
if (top < wheight * 3) {
|
||||
TWeb.loadMore($after);
|
||||
}
|
||||
}
|
||||
});
|
||||
$document.on('click', '.js-messages_more', function() {
|
||||
var $el = $(this);
|
||||
TWeb.loadMore($el);
|
||||
});
|
||||
},
|
||||
initViews: function() {
|
||||
TWeb.viewsMap = {};
|
||||
TWeb.viewsQueue = [];
|
||||
TWeb.viewsLastLoad = 0;
|
||||
var $document = $(document), $window = $(window);
|
||||
$document.ready(function() {
|
||||
$window.on('scroll resize', TWeb.checkVisiblePosts);
|
||||
TWeb.checkVisiblePosts();
|
||||
});
|
||||
},
|
||||
checkVisiblePosts: function() {
|
||||
$('.js-widget_message[data-view]').each(function() {
|
||||
if (isVisible(this, 50)) {
|
||||
var view = this.getAttribute('data-view');
|
||||
if (view) {
|
||||
TWeb.addViewToQueue(view);
|
||||
}
|
||||
this.removeAttribute('data-view');
|
||||
}
|
||||
});
|
||||
},
|
||||
addViewToQueue: function(view) {
|
||||
if (!TWeb.viewsMap[view]) {
|
||||
TWeb.viewsMap[view] = true;
|
||||
TWeb.viewsQueue.push(view);
|
||||
TWeb.sendViewsMaybe();
|
||||
}
|
||||
},
|
||||
sendViewsMaybe: function() {
|
||||
var now = +(new Date);
|
||||
if (now - TWeb.viewsLastLoad < 10000 && TWeb.viewsQueue.length < 50) {
|
||||
return setTimeout(TWeb.sendViewsMaybe, 10000);
|
||||
}
|
||||
if (TWeb.viewsQueue.length > 0) {
|
||||
var views = TWeb.viewsQueue.join(';');
|
||||
TWeb.viewsQueue = [];
|
||||
$.ajax('/v/', {type: 'POST', data: {views: views}});
|
||||
TWeb.viewsLastLoad = now;
|
||||
}
|
||||
},
|
||||
highlightPost: function(post_id, scroll) {
|
||||
var $postWrap = $('.js-widget_message[data-post="' + post_id + '"]').parents('.js-widget_message_wrap');
|
||||
if (scroll) {
|
||||
$postWrap.scrollIntoView({position: 'top'});
|
||||
}
|
||||
$postWrap.addClass('prepare_highlight').redraw().addClass('highlight');
|
||||
setTimeout(function() {
|
||||
$postWrap.removeClass('highlight');
|
||||
setTimeout(function() {
|
||||
$postWrap.removeClass('prepare_highlight');
|
||||
}, 300);
|
||||
}, 1500);
|
||||
},
|
||||
updateServiceDate: function($wrapEls, skip_first) {
|
||||
$wrapEls.each(function() {
|
||||
if (!$(this).data('msg_date')) {
|
||||
var datetime = $('time[datetime]', this).attr('datetime');
|
||||
if (datetime) {
|
||||
var date_formatted = formatDate(datetime);
|
||||
$('<div class="tgme_widget_message_service_date_wrap"><div class="tgme_widget_message_service_date">' + date_formatted + '</div></div>').appendTo(this);
|
||||
$(this).data('msg_date', date_formatted);
|
||||
}
|
||||
}
|
||||
});
|
||||
var len = $wrapEls.size();
|
||||
for (var i = len - 1; i >= 0; i--) {
|
||||
var $wrapEl = $wrapEls.eq(i);
|
||||
var $prevWrapEl = i > 0 ? $wrapEls.eq(i - 1) : null;
|
||||
if (!$prevWrapEl && skip_first) continue;
|
||||
var date_visible = !$prevWrapEl || $prevWrapEl.data('msg_date') != $wrapEl.data('msg_date');
|
||||
$wrapEl.toggleClass('date_visible', date_visible);
|
||||
}
|
||||
},
|
||||
loadMore: function($moreEl, dataOverride) {
|
||||
var loading = $moreEl.data('loading');
|
||||
if (loading) {
|
||||
return false;
|
||||
}
|
||||
var wrapType = '';
|
||||
var after = $moreEl.attr('data-after');
|
||||
var before = $moreEl.attr('data-before');
|
||||
if (after !== undefined) {
|
||||
wrapType = 'after';
|
||||
} else if (before !== undefined) {
|
||||
wrapType = 'before';
|
||||
}
|
||||
var url = $moreEl.attr('href');
|
||||
$moreEl.data('loading', true);
|
||||
$moreEl.addClass('dots-animated');
|
||||
var _load = function(url, before, after) {
|
||||
if (dataOverride) {
|
||||
_loadContinue(dataOverride);
|
||||
} else if (url) {
|
||||
$.ajax(url, {
|
||||
success: function(data) { _loadContinue(data) },
|
||||
error: function(data) {
|
||||
var timeout = $moreEl.data('timeout') || 1000;
|
||||
$moreEl.data('timeout', timeout > 60000 ? timeout : timeout * 2);
|
||||
setTimeout(function(){ _load(url, before, after); }, timeout);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
var _loadContinue = async function(data) {
|
||||
var messageHistoryCountBefore = $('section.tgme_channel_history.js-message_history .tgme_widget_message_wrap.js-widget_message_wrap').length;
|
||||
var [initialHtmlScroll, initialHtmlHeight] = [$('html').scrollTop(), $('html').height()];
|
||||
var $data = $(await MakeMbHtml(data, wrapType));
|
||||
var $helper = $('<div class="tgme_widget_messages_helper"></div>');
|
||||
$helper.append($data);
|
||||
$('.js-message_history').append($helper);
|
||||
$helper.find('.js-widget_message').each(function() {
|
||||
TPost.init(this);
|
||||
});
|
||||
$helper.remove();
|
||||
var wrapEls = $data.filter('.js-widget_message_wrap').get();
|
||||
var $moreElWrap = $moreEl.parents('.js-messages_more_wrap');
|
||||
if (before) {
|
||||
var firstWrapEl = $moreElWrap.next('.js-widget_message_wrap').get();
|
||||
var $wrapEls = $(wrapEls.concat(firstWrapEl));
|
||||
TWeb.updateServiceDate($wrapEls);
|
||||
var y = $moreElWrap.offset().top + $moreElWrap.outerHeight(true) - $(document).scrollTop();
|
||||
$data.insertBefore($moreElWrap);
|
||||
var st = $moreElWrap.offset().top - y;
|
||||
$moreElWrap.remove();
|
||||
$(window).scrollTop(st);
|
||||
} else {
|
||||
var lastWrapEl = $moreElWrap.prev('.js-widget_message_wrap').get();
|
||||
var $wrapEls = $(lastWrapEl.concat(wrapEls));
|
||||
TWeb.updateServiceDate($wrapEls, lastWrapEl.length > 0);
|
||||
$data.insertBefore($moreElWrap);
|
||||
$moreElWrap.remove();
|
||||
}
|
||||
// load more messages if the current viewport is not tall enough to be scrolled
|
||||
if (!IsScrollableY($('html')[0])) {
|
||||
MbState.wasEverNonScrollable = true;
|
||||
MbState.lastMustScroll = true;
|
||||
TWeb.loadMore($('.js-messages_more_wrap > a'));
|
||||
return;
|
||||
}
|
||||
if (MbState.lastMustScroll) {
|
||||
return _scrollToLastMessage();
|
||||
}
|
||||
if (MbState.wasEverNonScrollable) {
|
||||
return _scrollToLastMessage();
|
||||
}
|
||||
if (wrapType === 'before') {
|
||||
$('html').scrollTop(initialHtmlScroll + $('html').height() - initialHtmlHeight);
|
||||
}
|
||||
if (MbState.startingPost && messageHistoryCountBefore === 1) {
|
||||
TWeb.highlightPost(MbState.startingPost?.id || MbState.startingPost?.ID);
|
||||
}
|
||||
};
|
||||
var _scrollToLastMessage = function() {
|
||||
//$('#BottomAnchor')[0].scrollIntoView();
|
||||
var lastMessageElem = $('.tgme_widget_message_wrap').last()[0];
|
||||
lastMessageElem.scrollIntoView();
|
||||
// scroll a bit more to show the message nicely if it's taller than viewport
|
||||
if (lastMessageElem.clientHeight > ($('html')[0].clientHeight - 48)) {
|
||||
$('html')[0].scrollTop -= 48;
|
||||
}
|
||||
MbState.lastMustScroll = false;
|
||||
};
|
||||
// avoid automatic infinite upscrolling
|
||||
if ($('html')[0].scrollTop === 0) {
|
||||
$('html')[0].scrollTop = 16;
|
||||
}
|
||||
_load(url, before, after);
|
||||
},
|
||||
}
|
||||
window.TWeb = TWeb;
|
808
static/MBViewer/js/tgsticker.js
Normal file
808
static/MBViewer/js/tgsticker.js
Normal file
@@ -0,0 +1,808 @@
|
||||
var RLottie = (function () {
|
||||
var rlottie = {}, apiInitStarted = false, apiInited = false, initCallbacks = [];
|
||||
var deviceRatio = window.devicePixelRatio || 1;
|
||||
|
||||
var startTime = +(new Date());
|
||||
function dT() {
|
||||
return '[' + ((+(new Date()) - startTime)/ 1000.0) + '] ';
|
||||
}
|
||||
|
||||
rlottie.Api = {};
|
||||
rlottie.players = Object.create(null);;
|
||||
rlottie.WORKERS_LIMIT = 4;
|
||||
|
||||
var reqId = 0;
|
||||
var mainLoopAf = false;
|
||||
var mainLoopTo = false;
|
||||
var mainLoopInited = false;
|
||||
var checkViewportDate = false;
|
||||
var lastRenderDate = false;
|
||||
|
||||
var userAgent = window.navigator.userAgent;
|
||||
var isSafari = !!window.safari ||
|
||||
!!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome'))));
|
||||
var isRAF = isSafari;
|
||||
rlottie.isSafari = isSafari;
|
||||
|
||||
function wasmIsSupported() {
|
||||
try {
|
||||
if (typeof WebAssembly === 'object' &&
|
||||
typeof WebAssembly.instantiate === 'function') {
|
||||
const module = new WebAssembly.Module(Uint8Array.of(
|
||||
0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00
|
||||
));
|
||||
if (module instanceof WebAssembly.Module) {
|
||||
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isSupported() {
|
||||
return (
|
||||
wasmIsSupported() &&
|
||||
typeof Uint8ClampedArray !== 'undefined' &&
|
||||
typeof Worker !== 'undefined' &&
|
||||
typeof ImageData !== 'undefined'
|
||||
);
|
||||
}
|
||||
|
||||
rlottie.isSupported = isSupported();
|
||||
|
||||
function mainLoop() {
|
||||
var key, rlPlayer, delta, rendered;
|
||||
var isEmpty = true;
|
||||
var now = +Date.now();
|
||||
var checkViewport = !checkViewportDate || (now - checkViewportDate) > 1000;
|
||||
for (key in rlottie.players) {
|
||||
rlPlayer = rlottie.players[key];
|
||||
if (rlPlayer &&
|
||||
rlPlayer.frameCount) {
|
||||
delta = now - rlPlayer.frameThen;
|
||||
if (delta > rlPlayer.frameInterval) {
|
||||
rendered = render(rlPlayer, checkViewport);
|
||||
if (rendered) {
|
||||
lastRenderDate = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// var delay = !lastRenderDate || now - lastRenderDate < 100 ? 16 : 500;
|
||||
var delay = 16;
|
||||
if (delay < 20 && isRAF) {
|
||||
mainLoopAf = requestAnimationFrame(mainLoop)
|
||||
} else {
|
||||
mainLoopTo = setTimeout(mainLoop, delay);
|
||||
}
|
||||
mainLoopInited = true;
|
||||
if (checkViewport) {
|
||||
checkViewportDate = now;
|
||||
}
|
||||
}
|
||||
function setupMainLoop() {
|
||||
var isEmpty = true, forceRender = false, rlPlayer;
|
||||
for (key in rlottie.players) {
|
||||
rlPlayer = rlottie.players[key];
|
||||
if (rlPlayer &&
|
||||
rlPlayer.frameCount) {
|
||||
if (rlPlayer.forceRender) {
|
||||
forceRender = true;
|
||||
}
|
||||
isEmpty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mainLoopInited === isEmpty || forceRender) {
|
||||
mainLoopAf && cancelAnimationFrame(mainLoopAf);
|
||||
mainLoopTo && clearTimeout(mainLoopTo);
|
||||
mainLoopInited = false;
|
||||
if (!isEmpty) {
|
||||
if (isRAF) {
|
||||
mainLoopAf = requestAnimationFrame(mainLoop);
|
||||
} else {
|
||||
mainLoopTo = setTimeout(mainLoop, 0);
|
||||
}
|
||||
mainLoopInited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initApi(callback) {
|
||||
if (apiInited) {
|
||||
callback && callback();
|
||||
} else {
|
||||
callback && initCallbacks.push(callback);
|
||||
if (!apiInitStarted) {
|
||||
console.log(dT(), 'tgsticker init');
|
||||
apiInitStarted = true;
|
||||
QueryableWorkerProxy.init('/js/tgsticker-worker.js?14', rlottie.WORKERS_LIMIT, function() {
|
||||
apiInited = true;
|
||||
for (var i = 0; i < initCallbacks.length; i++) {
|
||||
initCallbacks[i]();
|
||||
}
|
||||
initCallbacks = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function destroyWorkers() {
|
||||
QueryableWorkerProxy.destroy();
|
||||
apiInitStarted = apiInited = false;
|
||||
}
|
||||
|
||||
function initPlayer(el, options) {
|
||||
if (el.rlPlayer) return;
|
||||
if (el.tagName.toLowerCase() != 'picture') {
|
||||
console.warn('only picture tag allowed');
|
||||
return;
|
||||
}
|
||||
options = options || {};
|
||||
var rlPlayer = el.rlPlayer = {};
|
||||
rlPlayer.thumb = el.querySelector('img');
|
||||
var tgs_sources = el.querySelectorAll('source[type="application/x-tgsticker"]');
|
||||
var multi_source = el.hasAttribute('data-multi-source');
|
||||
var urls = [], urls_map = {};
|
||||
for (var i = 0; i < tgs_sources.length; i++) {
|
||||
var tgs_source = tgs_sources[i];
|
||||
var url = tgs_source && tgs_source.getAttribute('srcset') || '';
|
||||
var frames_align = tgs_source && tgs_source.getAttribute('data-frames-align') || '';
|
||||
if (url && !urls_map[url]) {
|
||||
urls_map[url] = true;
|
||||
urls.push({
|
||||
url: url,
|
||||
framesAlign: frames_align
|
||||
});
|
||||
if (!multi_source) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!urls.length) {
|
||||
console.warn('picture source application/x-tgsticker not found');
|
||||
return;
|
||||
}
|
||||
var pic_width = el.clientWidth || el.getAttribute('width');
|
||||
var pic_height = el.clientHeight || el.getAttribute('height');
|
||||
var curDeviceRatio = options.maxDeviceRatio ? Math.min(options.maxDeviceRatio, deviceRatio) : deviceRatio;
|
||||
if (!pic_width || !pic_height) {
|
||||
pic_width = pic_height = 256;
|
||||
}
|
||||
rlPlayer.reqId = ++reqId;
|
||||
rlottie.players[reqId] = rlPlayer;
|
||||
rlPlayer.el = el;
|
||||
rlPlayer.frameNo = false;
|
||||
rlPlayer.nextFrameNo = false;
|
||||
rlPlayer.frames = {};
|
||||
rlPlayer.width = Math.trunc(pic_width * curDeviceRatio);
|
||||
rlPlayer.height = Math.trunc(pic_height * curDeviceRatio);
|
||||
rlPlayer.workerProxy = QueryableWorkerProxy.create(rlPlayer.reqId, onFrame, onLoaded);
|
||||
rlPlayer.options = options;
|
||||
rlPlayer.isVisible = true;
|
||||
rlPlayer.paused = !!options.noAutoPlay;
|
||||
rlPlayer.needPlayOnce = !!options.playOnce;
|
||||
rlPlayer.needPlayUntilEnd = !!options.playUntilEnd;
|
||||
rlPlayer.repeatCount = false;
|
||||
rlPlayer.waitForFirstFrame = false;
|
||||
rlPlayer.stopOnFirstFrame = false;
|
||||
rlPlayer.stopOnLastFrame = false;
|
||||
rlPlayer.forcePlayFrames = 0;
|
||||
rlPlayer.times = [];
|
||||
rlPlayer.imageData = new ImageData(rlPlayer.width, rlPlayer.height);
|
||||
rlPlayer.workerProxy.loadFromData(urls, rlPlayer.width, rlPlayer.height);
|
||||
triggerEvent(rlPlayer.el, 'tg:init');
|
||||
}
|
||||
|
||||
function destroyPlayer(el) {
|
||||
if (!el.rlPlayer) return;
|
||||
var rlPlayer = el.rlPlayer;
|
||||
delete rlottie.players[rlPlayer.reqId];
|
||||
delete rlPlayer;
|
||||
setupMainLoop();
|
||||
}
|
||||
|
||||
function render(rlPlayer, checkViewport) {
|
||||
if (!rlPlayer.canvas ||
|
||||
rlPlayer.canvas.width == 0 ||
|
||||
rlPlayer.canvas.height == 0) {
|
||||
return false;
|
||||
}
|
||||
if (!rlPlayer.forceRender) {
|
||||
var focused = window.isFocused ? isFocused() : document.hasFocus();
|
||||
if (!focused ||
|
||||
rlPlayer.paused ||
|
||||
!rlPlayer.isVisible ||
|
||||
!rlPlayer.frameCount) {
|
||||
return false;
|
||||
}
|
||||
var isInViewport = rlPlayer.isInViewport;
|
||||
if (isInViewport === undefined || checkViewport) {
|
||||
var rect = rlPlayer.el.getBoundingClientRect();
|
||||
if (rect.bottom < 0 ||
|
||||
rect.right < 0 ||
|
||||
rect.top > (window.innerHeight || document.documentElement.clientHeight) ||
|
||||
rect.left > (window.innerWidth || document.documentElement.clientWidth)) {
|
||||
isInViewport = false;
|
||||
} else {
|
||||
isInViewport = true;
|
||||
}
|
||||
rlPlayer.isInViewport = isInViewport;
|
||||
}
|
||||
if (!isInViewport) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var frame = rlPlayer.frameQueue.shift();
|
||||
if (frame !== null) {
|
||||
doRender(rlPlayer, frame);
|
||||
var nextFrameNo = rlPlayer.nextFrameNo;
|
||||
if (rlPlayer.stopOnLastFrame &&
|
||||
frame.no == rlPlayer.frameCount - 1) {
|
||||
rlPlayer.stopOnLastFrame = false;
|
||||
if (!rlPlayer.paused) {
|
||||
rlPlayer.paused = true;
|
||||
triggerEvent(rlPlayer.el, 'tg:pause');
|
||||
}
|
||||
}
|
||||
if (rlPlayer.stopOnFirstFrame &&
|
||||
frame.no == 0) {
|
||||
if (rlPlayer.waitForFirstFrame) {
|
||||
rlPlayer.waitForFirstFrame = false;
|
||||
} else {
|
||||
rlPlayer.stopOnFirstFrame = false;
|
||||
if (!rlPlayer.paused) {
|
||||
rlPlayer.paused = true;
|
||||
triggerEvent(rlPlayer.el, 'tg:pause');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextFrameNo !== false) {
|
||||
rlPlayer.nextFrameNo = false;
|
||||
requestFrame(rlPlayer.reqId, nextFrameNo);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function doRender(rlPlayer, frame) {
|
||||
rlPlayer.forceRender = false;
|
||||
rlPlayer.imageData.data.set(frame.frame);
|
||||
rlPlayer.context.putImageData(rlPlayer.imageData, 0, 0);
|
||||
rlPlayer.frameNo = frame.no;
|
||||
var now = +(new Date());
|
||||
if (rlPlayer.frameThen) {
|
||||
rlPlayer.times.push(now - rlPlayer.frameThen)
|
||||
}
|
||||
rlPlayer.frameThen = now - (now % rlPlayer.frameInterval);
|
||||
if (rlPlayer.thumb) {
|
||||
rlPlayer.el.removeChild(rlPlayer.thumb);
|
||||
delete rlPlayer.thumb;
|
||||
}
|
||||
// console.log(dT(), '['+rlPlayer.reqId+']', 'render frame#'+frame.no);
|
||||
}
|
||||
|
||||
function requestFrame(reqId, frameNo) {
|
||||
var rlPlayer = rlottie.players[reqId];
|
||||
var frame = rlPlayer.frames[frameNo];
|
||||
if (frame) {
|
||||
// console.log(dT(), '['+reqId+']', 'request frame#'+frameNo+' (cache)');
|
||||
onFrame(reqId, frameNo, frame);
|
||||
} else {
|
||||
// console.log(dT(), '['+reqId+']', 'request frame#'+frameNo+' (worker)');
|
||||
rlPlayer.workerProxy.renderFrame(frameNo, !isSafari);
|
||||
}
|
||||
}
|
||||
|
||||
function onFrame(reqId, frameNo, frame) {
|
||||
var rlPlayer = rlottie.players[reqId];
|
||||
if (!rlPlayer || !rlPlayer.frames) {
|
||||
return;
|
||||
}
|
||||
if (!rlPlayer.frames[frameNo] &&
|
||||
(!frameNo || (rlPlayer.options.cachingModulo && ((reqId + frameNo) % rlPlayer.options.cachingModulo)))) {
|
||||
rlPlayer.frames[frameNo] = new Uint8ClampedArray(frame)
|
||||
}
|
||||
var prevNo = frameNo > 0 ? frameNo - 1 : rlPlayer.frameCount - 1;
|
||||
var lastQueueFrame = rlPlayer.frameQueue.last();
|
||||
if (lastQueueFrame &&
|
||||
lastQueueFrame.no != prevNo) {
|
||||
return;
|
||||
}
|
||||
rlPlayer.frameQueue.push({
|
||||
no: frameNo,
|
||||
frame: frame
|
||||
});
|
||||
var nextFrameNo = ++frameNo;
|
||||
if (nextFrameNo >= rlPlayer.frameCount) {
|
||||
nextFrameNo = 0;
|
||||
if (rlPlayer.times.length) {
|
||||
// var avg = 0;
|
||||
// for (var i = 0; i < rlPlayer.times.length; i++) {
|
||||
// avg += rlPlayer.times[i] / rlPlayer.times.length;
|
||||
// }
|
||||
// console.log('avg time: ' + avg + ', ' + rlPlayer.fps);
|
||||
rlPlayer.times = [];
|
||||
}
|
||||
}
|
||||
if (rlPlayer.frameQueue.needsMore()) {
|
||||
requestFrame(reqId, nextFrameNo)
|
||||
} else {
|
||||
rlPlayer.nextFrameNo = nextFrameNo;
|
||||
}
|
||||
}
|
||||
|
||||
function onLoaded(reqId, frameCount, fps) {
|
||||
var rlPlayer = rlottie.players[reqId];
|
||||
|
||||
rlPlayer.canvas = document.createElement('canvas');
|
||||
rlPlayer.canvas.width = rlPlayer.width;
|
||||
rlPlayer.canvas.height = rlPlayer.height;
|
||||
rlPlayer.el.appendChild(rlPlayer.canvas);
|
||||
rlPlayer.context = rlPlayer.canvas.getContext('2d');
|
||||
|
||||
rlPlayer.fps = fps;
|
||||
rlPlayer.frameInterval = 1000 / rlPlayer.fps;
|
||||
rlPlayer.frameThen = Date.now();
|
||||
rlPlayer.frameCount = frameCount;
|
||||
rlPlayer.forceRender = true;
|
||||
rlPlayer.frameQueue = new FrameQueue(fps / 4);
|
||||
setupMainLoop();
|
||||
requestFrame(reqId, 0);
|
||||
triggerEvent(rlPlayer.el, 'tg:load');
|
||||
if (frameCount > 0) {
|
||||
if (rlPlayer.needPlayOnce) {
|
||||
delete rlPlayer.needPlayOnce;
|
||||
delete rlPlayer.needPlayUntilEnd;
|
||||
rlPlayer.paused = false;
|
||||
rlPlayer.stopOnFirstFrame = true;
|
||||
rlPlayer.stopOnLastFrame = false;
|
||||
if (rlPlayer.frameNo === false ||
|
||||
rlPlayer.frameNo > 0) {
|
||||
rlPlayer.waitForFirstFrame = true;
|
||||
}
|
||||
} else if (rlPlayer.needPlayUntilEnd) {
|
||||
delete rlPlayer.needPlayOnce;
|
||||
delete rlPlayer.needPlayUntilEnd;
|
||||
rlPlayer.paused = false;
|
||||
rlPlayer.stopOnFirstFrame = false;
|
||||
rlPlayer.stopOnLastFrame = true;
|
||||
}
|
||||
}
|
||||
if (!rlPlayer.paused) {
|
||||
triggerEvent(rlPlayer.el, 'tg:play');
|
||||
}
|
||||
}
|
||||
|
||||
rlottie.init = function(el, options) {
|
||||
if (!rlottie.isSupported) {
|
||||
return false;
|
||||
}
|
||||
initApi(function() {
|
||||
el && initPlayer(el, options);
|
||||
});
|
||||
}
|
||||
|
||||
rlottie.destroy = function(el) {
|
||||
destroyPlayer(el);
|
||||
}
|
||||
|
||||
rlottie.playOnce = function(el) {
|
||||
if (el && el.rlPlayer) {
|
||||
var rlPlayer = el.rlPlayer;
|
||||
if (rlPlayer.frameCount > 0) {
|
||||
rlPlayer.stopOnFirstFrame = true;
|
||||
rlPlayer.stopOnLastFrame = false;
|
||||
if (rlPlayer.frameNo > 0) {
|
||||
rlPlayer.waitForFirstFrame = true;
|
||||
}
|
||||
if (rlPlayer.paused) {
|
||||
rlPlayer.paused = false;
|
||||
triggerEvent(el, 'tg:play');
|
||||
}
|
||||
} else {
|
||||
rlPlayer.needPlayOnce = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rlottie.playUntilEnd = function(el) {
|
||||
if (el && el.rlPlayer) {
|
||||
var rlPlayer = el.rlPlayer;
|
||||
if (rlPlayer.frameCount > 0) {
|
||||
rlPlayer.stopOnFirstFrame = false;
|
||||
rlPlayer.stopOnLastFrame = true;
|
||||
if (rlPlayer.paused) {
|
||||
rlPlayer.paused = false;
|
||||
triggerEvent(el, 'tg:play');
|
||||
}
|
||||
} else {
|
||||
rlPlayer.needPlayUntilEnd = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rlottie.play = function(el, reset) {
|
||||
if (el && el.rlPlayer) {
|
||||
if (reset) {
|
||||
rlottie.reset(el);
|
||||
}
|
||||
el.rlPlayer.paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
rlottie.pause = function(el) {
|
||||
if (el && el.rlPlayer) {
|
||||
el.rlPlayer.paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
rlottie.reset = function(el) {
|
||||
if (el && el.rlPlayer) {
|
||||
var rlPlayer = el.rlPlayer;
|
||||
rlPlayer.frameQueue.clear();
|
||||
rlPlayer.forceRender = true;
|
||||
requestFrame(rlPlayer.reqId, 0);
|
||||
setupMainLoop();
|
||||
}
|
||||
}
|
||||
|
||||
rlottie.destroyWorkers = function() {
|
||||
destroyWorkers();
|
||||
}
|
||||
|
||||
return rlottie;
|
||||
}());
|
||||
|
||||
|
||||
var QueryableWorkerProxy = (function() {
|
||||
var workerproxy = {};
|
||||
var proxyId = 0;
|
||||
var wReqId = 0;
|
||||
var rObjs = {};
|
||||
var wrMap = {};
|
||||
var proxies = {};
|
||||
var rlottieWorkers = [], curWorkerNum = 0;
|
||||
|
||||
var startTime = +(new Date());
|
||||
function dT() {
|
||||
return '[' + ((+(new Date()) - startTime)/ 1000.0) + '] ';
|
||||
}
|
||||
|
||||
function Proxy(playerId, onFrame, onLoaded) {
|
||||
this.proxyId = ++proxyId;
|
||||
this.playerId = playerId;
|
||||
this.onFrame = onFrame;
|
||||
this.onLoaded = onLoaded;
|
||||
this.items = [];
|
||||
this.itemsMap = {};
|
||||
proxies[this.proxyId] = this;
|
||||
return this;
|
||||
};
|
||||
Proxy.prototype.loadFromData = function(urls, width, height) {
|
||||
if (this.items.length > 0) {
|
||||
console.warn('already loaded');
|
||||
return;
|
||||
}
|
||||
this.clampedSize = width * height * 4;
|
||||
for (var i = 0; i < urls.length; i++) {
|
||||
var url = urls[i];
|
||||
var _wReqId = ++wReqId;
|
||||
var worker = rlottieWorkers[curWorkerNum++];
|
||||
if (curWorkerNum >= rlottieWorkers.length) {
|
||||
curWorkerNum = 0;
|
||||
}
|
||||
worker.sendQuery('loadFromData', _wReqId, url.url, width, height);
|
||||
var item = {
|
||||
reqId: _wReqId,
|
||||
worker: worker,
|
||||
url: url.url,
|
||||
loaded: false,
|
||||
clamped: new Uint8ClampedArray(this.clampedSize),
|
||||
frameLoaded: {}
|
||||
};
|
||||
if (url.framesAlign) {
|
||||
item.framesAlign = url.framesAlign;
|
||||
}
|
||||
this.items.push(item);
|
||||
this.itemsMap[_wReqId] = item;
|
||||
wrMap[_wReqId] = this.proxyId;
|
||||
}
|
||||
if (this.items.length > 1) {
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.canvas.width = width;
|
||||
this.canvas.height = height;
|
||||
this.context = this.canvas.getContext('2d');
|
||||
this.imageData = new ImageData(width, height);
|
||||
}
|
||||
};
|
||||
Proxy.prototype.renderFrame = function(frameNo, need_clamped) {
|
||||
for (var i = 0; i < this.items.length; i++) {
|
||||
var item = this.items[i];
|
||||
var realFrameNo = frameNo;
|
||||
if (item.framesAlign == 'right') {
|
||||
realFrameNo = frameNo - (this.frameCount - item.frameCount);
|
||||
}
|
||||
if (need_clamped) {
|
||||
if(!item.clamped.length) { // fix detached
|
||||
item.clamped = new Uint8ClampedArray(this.clampedSize);
|
||||
}
|
||||
item.worker.sendQuery('renderFrame', item.reqId, realFrameNo, item.clamped);
|
||||
} else {
|
||||
item.worker.sendQuery('renderFrame', item.reqId, realFrameNo);
|
||||
}
|
||||
// console.log(dT(), '['+this.playerId+'.'+item.reqId+']', 'request frame#'+frameNo+' (worker)');
|
||||
}
|
||||
};
|
||||
|
||||
function onFrame(wReqId, realFrameNo, frame) {
|
||||
var proxyId = wrMap[wReqId];
|
||||
var proxy = proxies[proxyId];
|
||||
var item = proxy.itemsMap[wReqId];
|
||||
var frameNo = realFrameNo;
|
||||
if (item.framesAlign == 'right') {
|
||||
frameNo = realFrameNo + (proxy.frameCount - item.frameCount);
|
||||
}
|
||||
// console.log(dT(), '['+proxy.playerId+'.'+item.reqId+']', 'onframe#'+frameNo+' (worker)');
|
||||
item.frameLoaded[frameNo] = frame;
|
||||
var finished = true;
|
||||
for (var i = 0; i < proxy.items.length; i++) {
|
||||
var item = proxy.items[i];
|
||||
var loadedFrame = item.frameLoaded[frameNo];
|
||||
if (!loadedFrame) {
|
||||
finished = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (finished) {
|
||||
if (proxy.items.length == 1) {
|
||||
var loadedFrame = proxy.items[0].frameLoaded[frameNo];
|
||||
proxy.onFrame(proxy.playerId, frameNo, loadedFrame);
|
||||
delete proxy.items[0].frameLoaded[frameNo];
|
||||
} else {
|
||||
var promises = [];
|
||||
for (var i = 0; i < proxy.items.length; i++) {
|
||||
var item = proxy.items[i];
|
||||
var loadedFrame = item.frameLoaded[frameNo];
|
||||
proxy.imageData.data.set(loadedFrame);
|
||||
var promise = createImageBitmap(proxy.imageData);
|
||||
promises.push(promise);
|
||||
delete item.frameLoaded[frameNo];
|
||||
}
|
||||
Promise.all(promises).then(function(bitmaps) {
|
||||
proxy.context.clearRect(0, 0, proxy.canvas.width, proxy.canvas.height);
|
||||
for (var i = 0; i < bitmaps.length; i++) {
|
||||
proxy.context.drawImage(bitmaps[i], 0, 0);
|
||||
}
|
||||
var imageData = proxy.context.getImageData(0, 0, proxy.canvas.width, proxy.canvas.height);
|
||||
proxy.onFrame(proxy.playerId, frameNo, imageData.data);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
delete frameDatas;
|
||||
}
|
||||
}
|
||||
|
||||
function onLoaded(wReqId, frameCount, fps) {
|
||||
var proxyId = wrMap[wReqId];
|
||||
var proxy = proxies[proxyId];
|
||||
var item = proxy.itemsMap[wReqId];
|
||||
item.loaded = true;
|
||||
item.frameCount = frameCount;
|
||||
item.fps = fps;
|
||||
var finished = true;
|
||||
frameCount = null; fps = null;
|
||||
for (var i = 0; i < proxy.items.length; i++) {
|
||||
var item = proxy.items[i];
|
||||
if (!item.framesAlign) {
|
||||
if (frameCount === null) {
|
||||
frameCount = item.frameCount;
|
||||
} else if (frameCount !== false && frameCount !== item.frameCount) {
|
||||
frameCount = false;
|
||||
}
|
||||
}
|
||||
if (fps === null) {
|
||||
fps = item.fps;
|
||||
} else if (fps !== false && fps !== item.fps) {
|
||||
fps = false;
|
||||
}
|
||||
if (!item.loaded) {
|
||||
finished = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (finished) {
|
||||
if (frameCount === null) {
|
||||
console.warn('Frame count not defined'); return;
|
||||
}
|
||||
if (frameCount === false) {
|
||||
console.warn('Frame count is different'); return;
|
||||
}
|
||||
if (fps === null) {
|
||||
console.warn('FPS not defined'); return;
|
||||
}
|
||||
if (fps === false) {
|
||||
console.warn('FPS is different'); return;
|
||||
}
|
||||
proxy.frameCount = frameCount;
|
||||
proxy.fps = fps;
|
||||
proxy.onLoaded(proxy.playerId, frameCount, fps);
|
||||
}
|
||||
}
|
||||
|
||||
workerproxy.init = function(worker_url, workers_limit, callback) {
|
||||
var workersRemain = workers_limit;
|
||||
var firstWorker = rlottieWorkers[0] = new QueryableWorker(worker_url);
|
||||
firstWorker.addListener('ready', function () {
|
||||
console.log(dT(), 'worker #0 ready');
|
||||
firstWorker.addListener('frame', onFrame);
|
||||
firstWorker.addListener('loaded', onLoaded);
|
||||
--workersRemain;
|
||||
if (!workersRemain) {
|
||||
console.log(dT(), 'workers ready');
|
||||
callback && callback();
|
||||
} else {
|
||||
for (var workerNum = 1; workerNum < workers_limit; workerNum++) {
|
||||
(function(workerNum) {
|
||||
var rlottieWorker = rlottieWorkers[workerNum] = new QueryableWorker(worker_url);
|
||||
rlottieWorker.addListener('ready', function () {
|
||||
console.log(dT(), 'worker #' + workerNum + ' ready');
|
||||
rlottieWorker.addListener('frame', onFrame);
|
||||
rlottieWorker.addListener('loaded', onLoaded);
|
||||
--workersRemain;
|
||||
if (!workersRemain) {
|
||||
console.log(dT(), 'workers ready');
|
||||
callback && callback();
|
||||
}
|
||||
});
|
||||
})(workerNum);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
workerproxy.create = function(playerId, onFrame, onLoaded) {
|
||||
return new Proxy(playerId, onFrame, onLoaded);
|
||||
};
|
||||
workerproxy.destroy = function() {
|
||||
for (var workerNum = 0; workerNum < rlottieWorkers.length; workerNum++) {
|
||||
rlottieWorkers[workerNum].terminate();
|
||||
console.log('worker #' + workerNum + ' terminated');
|
||||
}
|
||||
console.log('workers destroyed');
|
||||
rlottieWorkers = [];
|
||||
};
|
||||
|
||||
return workerproxy;
|
||||
}());
|
||||
|
||||
function QueryableWorker(url, defaultListener, onError) {
|
||||
var instance = this;
|
||||
var worker = new Worker(url);
|
||||
var listeners = {};
|
||||
|
||||
this.defaultListener = defaultListener || function() {};
|
||||
|
||||
if (onError) {worker.onerror = onError;}
|
||||
|
||||
this.postMessage = function(message) {
|
||||
worker.postMessage(message);
|
||||
}
|
||||
|
||||
this.terminate = function() {
|
||||
worker.terminate();
|
||||
}
|
||||
|
||||
this.addListener = function(name, listener) {
|
||||
listeners[name] = listener;
|
||||
}
|
||||
|
||||
this.removeListener = function(name) {
|
||||
delete listeners[name];
|
||||
}
|
||||
|
||||
/*
|
||||
This functions takes at least one argument, the method name we want to query.
|
||||
Then we can pass in the arguments that the method needs.
|
||||
*/
|
||||
this.sendQuery = function(queryMethod) {
|
||||
if (arguments.length < 1) {
|
||||
throw new TypeError('QueryableWorker.sendQuery takes at least one argument');
|
||||
return;
|
||||
}
|
||||
var queryMethod = arguments[0];
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (RLottie.isSafari) {
|
||||
worker.postMessage({
|
||||
'queryMethod': queryMethod,
|
||||
'queryMethodArguments': args
|
||||
});
|
||||
} else {
|
||||
var transfer = [];
|
||||
for(var i = 0; i < args.length; i++) {
|
||||
if(args[i] instanceof ArrayBuffer) {
|
||||
transfer.push(args[i]);
|
||||
}
|
||||
|
||||
if(args[i].buffer && args[i].buffer instanceof ArrayBuffer) {
|
||||
transfer.push(args[i].buffer);
|
||||
}
|
||||
}
|
||||
|
||||
worker.postMessage({
|
||||
'queryMethod': queryMethod,
|
||||
'queryMethodArguments': args
|
||||
}, transfer);
|
||||
}
|
||||
}
|
||||
|
||||
worker.onmessage = function(event) {
|
||||
if (event.data instanceof Object &&
|
||||
event.data.hasOwnProperty('queryMethodListener') &&
|
||||
event.data.hasOwnProperty('queryMethodArguments')) {
|
||||
listeners[event.data.queryMethodListener].apply(instance, event.data.queryMethodArguments);
|
||||
} else {
|
||||
this.defaultListener.call(instance, event.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function FrameQueue(maxLength) {
|
||||
this.queue = [];
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
FrameQueue.prototype.needsMore = function frameQueueNeedsMore() {
|
||||
return this.queue.length < this.maxLength;
|
||||
}
|
||||
|
||||
FrameQueue.prototype.empty = function frameQueueEmpty() {
|
||||
return !this.queue.length;
|
||||
}
|
||||
|
||||
FrameQueue.prototype.notEmpty = function frameQueueEmpty() {
|
||||
return this.queue.length > 0;
|
||||
}
|
||||
|
||||
FrameQueue.prototype.push = function frameQueuePush(element) {
|
||||
return this.queue.push(element);
|
||||
}
|
||||
|
||||
FrameQueue.prototype.shift = function frameQueueShift() {
|
||||
return this.queue.length ? this.queue.shift() : null;
|
||||
}
|
||||
|
||||
FrameQueue.prototype.last = function frameQueueLast(element) {
|
||||
return this.queue.length ? this.queue[this.queue.length - 1] : null;
|
||||
}
|
||||
|
||||
FrameQueue.prototype.clear = function frameQueueClear() {
|
||||
this.queue = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (!this.CustomEvent || typeof this.CustomEvent === "object") {
|
||||
(function() {
|
||||
this.CustomEvent = function CustomEvent(type, eventInitDict) {
|
||||
var event;
|
||||
eventInitDict = eventInitDict || {bubbles: false, cancelable: false, detail: undefined};
|
||||
|
||||
try {
|
||||
event = document.createEvent('CustomEvent');
|
||||
event.initCustomEvent(type, eventInitDict.bubbles, eventInitDict.cancelable, eventInitDict.detail);
|
||||
} catch (error) {
|
||||
event = document.createEvent('Event');
|
||||
event.initEvent(type, eventInitDict.bubbles, eventInitDict.cancelable);
|
||||
event.detail = eventInitDict.detail;
|
||||
}
|
||||
|
||||
return event;
|
||||
};
|
||||
})();
|
||||
}
|
||||
|
||||
function triggerEvent(el, event_type, init_dict) {
|
||||
var event = new CustomEvent(event_type, init_dict);
|
||||
el.dispatchEvent(event);
|
||||
}
|
1
static/MBViewer/js/tgwallpaper.min.js
vendored
Normal file
1
static/MBViewer/js/tgwallpaper.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
var TWallpaper=function(){function x(a){for(var b=[].concat(G);0<a;)b.push(b.shift()),a--;a=[];for(var c=0;c<b.length;c+=2)a.push(b[c]);return a}function B(a,b){b%=90;var c=x(a%p);if(b){var d=x(++a%p);return[{x:c[0].x+(d[0].x-c[0].x)/90*b,y:c[0].y+(d[0].y-c[0].y)/90*b},{x:c[1].x+(d[1].x-c[1].x)/90*b,y:c[1].y+(d[1].y-c[1].y)/90*b},{x:c[2].x+(d[2].x-c[2].x)/90*b,y:c[2].y+(d[2].y-c[2].y)/90*b},{x:c[3].x+(d[3].x-c[3].x)/90*b,y:c[3].y+(d[3].y-c[3].y)/90*b}]}return c}function H(a){for(l+=a;90<=l;)l-=90,g++,g>=p&&(g-=p);for(;0>l;)l+=90,g--,0>g&&(g+=p)}function I(a){C+=a.deltaY;D||(requestAnimationFrame(P),D=!0)}function P(){var a=C/50;C%=50;if(a=0<a?Math.floor(a):Math.ceil(a))H(a),a=B(g,l),y(z(a));D=!1}function Q(){if(0<A.length){var a=A.shift();y(a)}else clearInterval(E)}function z(a){for(var b=f._hctx.createImageData(50,50),c=b.data,d=0,q=0;50>q;q++)for(var h=q/50-.5,F=h*h,v=0;50>v;v++){var m=v/50-.5,e=.35*Math.sqrt(m*m+F);e=e*e*6.4;var r=Math.sin(e),w=Math.cos(e);e=Math.max(0,Math.min(1,.5+m*w-h*r));m=Math.max(0,Math.min(1,.5+m*r+h*w));for(var J=w=r=0,K=0,t=0;t<u.length;t++){var k=e-a[t].x,L=m-a[t].y;k=Math.max(0,.9-Math.sqrt(k*k+L*L));k*=k*k*k;r+=k;w+=k*u[t].r/255;J+=k*u[t].g/255;K+=k*u[t].b/255}c[d++]=w/r*255;c[d++]=J/r*255;c[d++]=K/r*255;c[d++]=255}return b}function y(a){f._hctx.putImageData(a,0,0);f._ctx.drawImage(f._hc,0,0,50,50)}function M(){var a=+Date.now();!document.hasFocus()||a-N<R||(N=a,H(1),a=B(g,l),y(z(a)));O=requestAnimationFrame(M)}var g=0,l=0,N=0,R=1E3/15,A=[],E=null,O=null,u=[],n=[0,.25,.5,.75,1,1.5,2,2.5,3,3.5,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18.3,18.6,18.9,19.2,19.5,19.8,20.1,20.4,20.7,21,21.3,21.6,21.9,22.2,22.5,22.8,23.1,23.4,23.7,24,24.3,24.6,24.9,25.2,25.5,25.8,26.1,26.3,26.4,26.5,26.6,26.7,26.8,26.9,27],G=[{x:.8,y:.1},{x:.6,y:.2},{x:.35,y:.25},{x:.25,y:.6},{x:.2,y:.9},{x:.4,y:.8},{x:.65,y:.75},{x:.75,y:.4}],p=G.length,D=!1,C=0,f={init:function(a){u=[];var b=a.getAttribute("data-colors")||"";b&&(b=b.split(","));for(var c=0;c<b.length;c++){var d=u,q=d.push;var h=(h=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(b[c]))?{r:parseInt(h[1],16),g:parseInt(h[2],16),b:parseInt(h[3],16)}:null;q.call(d,h)}f._hc||(f._hc=document.createElement("canvas"),f._hc.width=50,f._hc.height=50,f._hctx=f._hc.getContext("2d"));f._canvas=a;f._ctx=f._canvas.getContext("2d");f.update()},update:function(){var a=B(g,l);y(z(a))},toNextPosition:function(){clearInterval(E);A=[];var a=x(g%p);g++;var b=x(g%p),c=(b[0].x-a[0].x)/27,d=(b[0].y-a[0].y)/27,q=(b[1].x-a[1].x)/27,h=(b[1].y-a[1].y)/27,F=(b[2].x-a[2].x)/27,v=(b[2].y-a[2].y)/27,m=(b[3].x-a[3].x)/27;b=(b[3].y-a[3].y)/27;for(var e=0;60>e;e++)A.push(z([{x:a[0].x+c*n[e],y:a[0].y+d*n[e]},{x:a[1].x+q*n[e],y:a[1].y+h*n[e]},{x:a[2].x+F*n[e],y:a[2].y+v*n[e]},{x:a[3].x+m*n[e],y:a[3].y+b*n[e]}]));E=setInterval(Q,1E3/30)},animate:function(a){a?M():cancelAnimationFrame(O)},scrollAnimate:function(a){a?document.addEventListener("wheel",I):document.removeEventListener("wheel",I)}};return f}();
|
2728
static/MBViewer/js/widget-frame.js
Normal file
2728
static/MBViewer/js/widget-frame.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user