mirror of
https://gitlab.com/octospacc/octospacc.gitlab.io
synced 2025-02-08 07:58:46 +01:00
Update MBViewer, with Trasformapi
This commit is contained in:
parent
ed9bdaf9fa
commit
0cf7946e6d
139
public/Assets/Lib/Trasformapi.js
Normal file
139
public/Assets/Lib/Trasformapi.js
Normal file
@ -0,0 +1,139 @@
|
||||
(function(){
|
||||
|
||||
const Exp = {};
|
||||
let MakeTreeFromXml;
|
||||
|
||||
const platformIsNode = (typeof module === 'object' && typeof module.exports === 'object');
|
||||
const platformIsBrowser = (typeof window !== 'undefined' && typeof window.document !== 'undefined');
|
||||
|
||||
if (platformIsNode && !platformIsBrowser) {
|
||||
MakeTreeFromXml = (xml) => new require('jsdom').JSDOM(xml);
|
||||
}
|
||||
|
||||
if (platformIsBrowser) {
|
||||
MakeTreeFromXml = (xml) => new DOMParser().parseFromString(xml, 'text/xml');
|
||||
}
|
||||
|
||||
Exp.Trasformapi = (transformerXml, initOptions={}) => {
|
||||
var transformerTree = MakeTreeFromXml(transformerXml);
|
||||
return {
|
||||
TransformForInput: (entityName, upstreamName, dataIn, transformOptions) => _TransformForInput(transformerTree, initOptions, entityName, upstreamName, dataIn, transformOptions),
|
||||
TransformForOutput: (entityName, upstreamName, dataIn, transformOptions) => _TransformForOutput(transformerTree, initOptions, entityName, upstreamName, dataIn, transformOptions),
|
||||
};
|
||||
}
|
||||
|
||||
function _TransformForInput (transformerTree, initOptions, entityName, upstreamName, dataIn, transformOptions={}) {
|
||||
// TODO: restructure prototype
|
||||
// TODO: make the propDataType inside this function, for both main and secondary
|
||||
function temp1 (upstreamName, propName, propType, propDataType, propContent, dataIn, dataOut, propNameSecondary, propTypeSecondary, propDataTypeSecondary) {
|
||||
// const propDataType =
|
||||
// const propDataTypeSecondary =
|
||||
const dataKey = propContent.getAttribute('key');
|
||||
//console.log(propName, propType, propDataType, propContent, dataIn, dataOut, propNameSecondary, propTypeSecondary, propDataTypeSecondary)
|
||||
// TODO: inside here somehow happens the array error with prop > content > prop nestings, probably we need to handle secondary and primary types separately
|
||||
const dataAttr = propContent.getAttribute('attr');
|
||||
let dataInContent;
|
||||
if (dataIn instanceof Node) {
|
||||
// TODO: 'document' won't work on nodejs, must change it
|
||||
//const dataNode = document.evaluate(dataKey, dataIn, ((ns) => ns), XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
//dataInContent = (dataAttr ? dataNode?.getAttribute(dataAttr) : dataNode?.textContent);
|
||||
// TODO: finish this to actually handle arrays properly (even below)
|
||||
const dataNodes = getElementsByXPath(dataKey, dataIn);
|
||||
if (!Array.isArray(propDataTypeSecondary || propDataType) && dataNodes.length > 0) {
|
||||
dataInContent = (dataAttr ? dataNodes[0].getAttribute(dataAttr) : dataNodes[0].textContent);
|
||||
} else {
|
||||
dataInContent = [];
|
||||
for (const dataNode of dataNodes) {
|
||||
// ... TODO push every item //dataInContent = (dataAttr ? dataNode?.getAttribute(dataAttr) : dataNode?.textContent);
|
||||
dataInContent.push(dataAttr ? dataNodes[0].getAttribute(dataAttr) : dataNodes[0].textContent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dataInContent = (dataKey ? _.get(dataIn, dataKey) : dataIn);
|
||||
}
|
||||
//const dataInContent = (dataIn instanceof Node
|
||||
// ? (document.evaluate(dataKey, dataIn, (ns) => ns, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue || {}
|
||||
// )['getAttribute' || 'textContent'](dataAttr)
|
||||
// : (dataKey ? _.get(dataIn, dataKey) : dataIn));
|
||||
// TODO: make this readable lmao
|
||||
// TODO: make no type mean any/object type and remove the distinction maybe
|
||||
// TODO: readd type casting
|
||||
const result = (["any", "object", "string", "number", "int", "float"].includes(propTypeSecondary || propType)
|
||||
? SetOrPush(dataInContent, (propDataTypeSecondary || propDataType))
|
||||
// ? SetOrPush((["any", "object"].includes(propType)
|
||||
// ? dataInContent
|
||||
// : { int: parseInt, float: parseFloat, string: String, number: Number }[propType](dataInContent)
|
||||
// ), propDataType)
|
||||
: SetOrPush(MakeApiEntityObject((propTypeSecondary || propType), upstreamName, dataInContent), (propDataTypeSecondary || propDataType)));
|
||||
!propNameSecondary ? (dataOut[propName] = result) : (dataOut[propName][propNameSecondary] = result);
|
||||
}
|
||||
function MakeApiEntityObject (entityName, upstreamName, dataIn) {
|
||||
let dataOut = {};
|
||||
const entitySchema = transformerTree.querySelector(`:scope > entity[name="${entityName}"]`);
|
||||
for (const propSchema of entitySchema.querySelectorAll(':scope > prop')) {
|
||||
const propName = propSchema.getAttribute('name');
|
||||
const propType = propSchema.getAttribute('type').split('[]')[0];
|
||||
const propDataType = (propSchema.getAttribute('type').endsWith('[]') ? [] : {});
|
||||
const propContent = propSchema.querySelector(`:scope > content[upstream="${upstreamName}"]`);
|
||||
if (!propContent) {
|
||||
// property is not implemented for the current upstream, skip it
|
||||
continue;
|
||||
}
|
||||
const propContentChildren = propContent.querySelectorAll(`:scope > prop`); // TODO
|
||||
if (propContentChildren.length === 0) {
|
||||
//const dataKey = propContent.getAttribute('key');
|
||||
//const dataAttr = propContent.getAttribute('attr');
|
||||
//const dataInContent = (dataIn instanceof Node
|
||||
// // TODO: 'document' won't work on nodejs, must change it
|
||||
// ? (document.evaluate(dataKey, dataIn, (ns) => ns, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue || {}
|
||||
// )[dataAttr || 'textContent']
|
||||
// : (dataKey ? _.get(dataIn, dataKey) : dataIn));
|
||||
//dataOut[propName] = (["string", "number", "int", "float"].includes(propType)
|
||||
// ? SetOrPush(dataInContent, propDataType)
|
||||
// : SetOrPush(MakeApiEntityObject(propType, upstreamName, dataInContent), propDataType));
|
||||
temp1(upstreamName, propName, propType, propDataType, propContent, dataIn, dataOut);
|
||||
} else {
|
||||
dataOut[propName] = {}; // should this be an array in some cases or not?
|
||||
// TODO: wrap this and the above in a function, to allow for code reuse, right now the else condition does less things than what it should because of the duplication
|
||||
for (const propChildSchema of propContentChildren) {
|
||||
const entityChildSchema = transformerTree.querySelector(`:scope > entity[name="${propType}"]`);
|
||||
const propChildName = propChildSchema.getAttribute('name');
|
||||
const propChildProp = entityChildSchema.querySelector(`:scope > prop[name="${propChildName}"]`);
|
||||
const propChildType = propChildProp.getAttribute('type').split('[]')[0];
|
||||
const propChildDataType = (propChildProp.getAttribute('type').endsWith('[]') ? [] : {});
|
||||
//const childDataKey = propChildSchema.getAttribute('key');
|
||||
//const childDataInContent = childDataKey ? _.get(dataIn, childDataKey) : dataIn;
|
||||
//dataOut[propName][propChildName] = (["string", "number", "int", "float"].includes(propChildType)
|
||||
// ? SetOrPush(childDataInContent, propDataType)
|
||||
// : null); // TODO other recursions? //SetOrPush(MakeApiEntityObject(propType, upstreamName, childDataInContent), childDataInContent));
|
||||
temp1(upstreamName, propName, propType, propDataType, propChildSchema, dataIn, dataOut, propChildName, propChildType, propChildDataType);
|
||||
}
|
||||
}
|
||||
}
|
||||
//console.log(dataOut);
|
||||
return dataOut;
|
||||
}
|
||||
return MakeApiEntityObject (entityName, upstreamName, dataIn);
|
||||
}
|
||||
|
||||
function _TransformForOutput (transformerTree, initOptions, entityName, upstreamName, dataIn, transformOptions={}) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// <https://stackoverflow.com/questions/36303869/how-to-use-document-evaluate-and-xpath-to-get-a-list-of-elements/42600459#42600459>
|
||||
// TODO: 'document' won't work on nodejs, must change it
|
||||
function getElementsByXPath (xpath, parent) {
|
||||
let results = [];
|
||||
let query = document.evaluate(xpath, parent || document, ((ns) => ns), XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
||||
for (let i=0, length=query.snapshotLength; i<length; ++i) {
|
||||
results.push(query.snapshotItem(i));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
const SetOrPush = (item, dest) => Array.isArray(dest) ? [...dest, item] : item;
|
||||
|
||||
if (platformIsNode) module.exports = Exp;
|
||||
if (platformIsBrowser) window.Trasformapi = Exp.Trasformapi;
|
||||
|
||||
})();
|
14
public/Assets/Lib/lodash.custom.min.js
vendored
Normal file
14
public/Assets/Lib/lodash.custom.min.js
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @license
|
||||
* Lodash (Custom Build) lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
|
||||
* Build: `lodash include="get"`
|
||||
*/
|
||||
;(function(){function t(){}function e(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}function n(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}function r(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}function o(t,e){for(var n=t.length;n--;)if(f(t[n][0],e))return n;return-1}function i(t){if(null==t)t=t===b?"[object Undefined]":"[object Null]";else if(I&&I in Object(t)){var e=T.call(t,I),n=t[I];
|
||||
try{t[I]=b;var r=true}catch(t){}var o=P.call(t);r&&(e?t[I]=n:delete t[I]),t=o}else t=P.call(t);return t}function a(t){if(typeof t=="string")return t;if(M(t)){for(var e=-1,n=null==t?0:t.length,r=Array(n);++e<n;)r[e]=a(t[e]);return r+""}return y(t)?G?G.call(t):"":(e=t+"","0"==e&&1/t==-g?"-0":e)}function c(t,e){var n=t.__data__,r=typeof e;return("string"==r||"number"==r||"symbol"==r||"boolean"==r?"__proto__"!==e:null===e)?n[typeof e=="string"?"string":"hash"]:n.map}function u(t,e){var n=null==t?b:t[e];
|
||||
return(!h(n)||C&&C in n?0:(p(n)?k:z).test(s(n)))?n:b}function s(t){if(null!=t){try{return E.call(t)}catch(t){}return t+""}return""}function l(t,e){function n(){var r=arguments,o=e?e.apply(this,r):r[0],i=n.cache;return i.has(o)?i.get(o):(r=t.apply(this,r),n.cache=i.set(o,r)||i,r)}if(typeof t!="function"||null!=e&&typeof e!="function")throw new TypeError("Expected a function");return n.cache=new(l.Cache||r),n}function f(t,e){return t===e||t!==t&&e!==e}function p(t){return!!h(t)&&(t=i(t),"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t);
|
||||
}function h(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}function _(t){return null!=t&&typeof t=="object"}function y(t){return typeof t=="symbol"||_(t)&&"[object Symbol]"==i(t)}function d(t){return null==t?"":a(t)}var b,g=1/0,v=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,j=/^\w*$/,m=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,O=/\\(\\)?/g,z=/^\[object .+?Constructor\]$/,S=typeof self=="object"&&self&&self.Object===Object&&self,S=typeof global=="object"&&global&&global.Object===Object&&global||S||Function("return this")(),w=typeof exports=="object"&&exports&&!exports.nodeType&&exports,x=w&&typeof module=="object"&&module&&!module.nodeType&&module,$=Array.prototype,A=Object.prototype,F=S["__core-js_shared__"],E=Function.prototype.toString,T=A.hasOwnProperty,C=function(){
|
||||
var t=/[^.]+$/.exec(F&&F.keys&&F.keys.IE_PROTO||"");return t?"Symbol(src)_1."+t:""}(),P=A.toString,k=RegExp("^"+E.call(T).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),A=S.Symbol,R=$.splice,I=A?A.toStringTag:b,N=u(S,"Map"),q=u(Object,"create"),G=($=A?A.prototype:b)?$.toString:b;e.prototype.clear=function(){this.__data__=q?q(null):{},this.size=0},e.prototype.delete=function(t){return t=this.has(t)&&delete this.__data__[t],this.size-=t?1:0,
|
||||
t},e.prototype.get=function(t){var e=this.__data__;return q?(t=e[t],"__lodash_hash_undefined__"===t?b:t):T.call(e,t)?e[t]:b},e.prototype.has=function(t){var e=this.__data__;return q?e[t]!==b:T.call(e,t)},e.prototype.set=function(t,e){var n=this.__data__;return this.size+=this.has(t)?0:1,n[t]=q&&e===b?"__lodash_hash_undefined__":e,this},n.prototype.clear=function(){this.__data__=[],this.size=0},n.prototype.delete=function(t){var e=this.__data__;return t=o(e,t),!(0>t)&&(t==e.length-1?e.pop():R.call(e,t,1),
|
||||
--this.size,true)},n.prototype.get=function(t){var e=this.__data__;return t=o(e,t),0>t?b:e[t][1]},n.prototype.has=function(t){return-1<o(this.__data__,t)},n.prototype.set=function(t,e){var n=this.__data__,r=o(n,t);return 0>r?(++this.size,n.push([t,e])):n[r][1]=e,this},r.prototype.clear=function(){this.size=0,this.__data__={hash:new e,map:new(N||n),string:new e}},r.prototype.delete=function(t){return t=c(this,t).delete(t),this.size-=t?1:0,t},r.prototype.get=function(t){return c(this,t).get(t)},r.prototype.has=function(t){
|
||||
return c(this,t).has(t)},r.prototype.set=function(t,e){var n=c(this,t),r=n.size;return n.set(t,e),this.size+=n.size==r?0:1,this};var L=function(t){t=l(t,function(t){return 500===e.size&&e.clear(),t});var e=t.cache;return t}(function(t){var e=[];return 46===t.charCodeAt(0)&&e.push(""),t.replace(m,function(t,n,r,o){e.push(r?o.replace(O,"$1"):n||t)}),e});l.Cache=r;var M=Array.isArray;t.memoize=l,t.eq=f,t.get=function(t,e,n){if(null==t)t=b;else{var r=t;if(!M(e)){if(M(e))r=false;else var o=typeof e,r=!("number"!=o&&"symbol"!=o&&"boolean"!=o&&null!=e&&!y(e))||(j.test(e)||!v.test(e)||null!=r&&e in Object(r));
|
||||
e=r?[e]:L(d(e))}for(r=0,o=e.length;null!=t&&r<o;){var i;if(i=e[r++],typeof i!="string"&&!y(i)){var a=i+"";i="0"==a&&1/i==-g?"-0":a}t=t[i]}t=r&&r==o?t:b}return t===b?n:t},t.isArray=M,t.isFunction=p,t.isObject=h,t.isObjectLike=_,t.isSymbol=y,t.toString=d,t.VERSION="4.17.5",typeof define=="function"&&typeof define.amd=="object"&&define.amd?(S._=t, define(function(){return t})):x?((x.exports=t)._=t,w._=t):S._=t}).call(this);
|
@ -204,7 +204,10 @@
|
||||
<script src="js/tgsticker.js"></script>
|
||||
<script src="js/widget-frame.js"></script>
|
||||
<script src="js/telegram-web.js"></script>
|
||||
<script src="js/MBViewer.js"></script>
|
||||
<script src="../Assets/Lib/lodash.custom.min.js"></script>
|
||||
<script src="../Assets/Lib/Trasformapi.js"></script>
|
||||
<script src="./js/TrasformapiSchema.js.xml"></script>
|
||||
<script src="./js/MBViewer.js"></script>
|
||||
<script>
|
||||
TWeb.init();
|
||||
ResizeLayouts();
|
||||
|
@ -19,6 +19,7 @@
|
||||
// * show, and/or sort by, posts tags/categories
|
||||
|
||||
let MbState = {};
|
||||
let MbApiTransformer;
|
||||
|
||||
function ArgsRewrite (props={}) {
|
||||
for (const key in props) {
|
||||
@ -71,9 +72,9 @@ function MakeSiteRestUrl (path='') {
|
||||
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 (MbState.platform === 'wordpress.org') {
|
||||
if (["atom", "rss", "wordpress.org"].includes(MbState.platform)) {
|
||||
const proxies = ["corsproxy.io", "corsproxy.org"];
|
||||
return `https://${proxies[~~(Math.random() * proxies.length)]}/?${siteUrl}/wp-json/${path}`;
|
||||
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}`;
|
||||
}
|
||||
@ -89,7 +90,7 @@ function MakeApiEndpoint (type, options={}) {
|
||||
"wordpress.com": {
|
||||
count: "number",
|
||||
orderBy: "order_by",
|
||||
}
|
||||
},
|
||||
}
|
||||
let query = '';
|
||||
for (const option in options) {
|
||||
@ -117,6 +118,9 @@ async function MbViewerInit () {
|
||||
if (!location.hash) {
|
||||
location.hash = '/';
|
||||
}
|
||||
if (!MbApiTransformer) {
|
||||
MbApiTransformer = Trasformapi(MbViewerTrasformapiSchema).TransformForInput;
|
||||
}
|
||||
MbState = {
|
||||
args: {},
|
||||
siteData: {
|
||||
@ -194,7 +198,9 @@ async function MbViewerInit () {
|
||||
}
|
||||
try {
|
||||
const siteRequest = await fetch(MakeSiteRestUrl());
|
||||
MbState.siteData = await siteRequest.json();
|
||||
MbState.siteData = (["atom", "rss"].includes(MbState.platform)
|
||||
? new DOMParser().parseFromString(await siteRequest.text(), 'text/xml')
|
||||
: await siteRequest.json());
|
||||
} catch(err) {
|
||||
setTimeout(MbViewerInit, 1000);
|
||||
return;
|
||||
@ -213,7 +219,7 @@ async function MbViewerInit () {
|
||||
setTimeout(MbViewerInit, 1000);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
} else if (!["atom", "rss"].includes(MbState.platform)) {
|
||||
$('section.tgme_channel_history.js-message_history').html(MakeMoreWrapperHtml('before'));
|
||||
TWeb.loadMore($('.js-messages_more_wrap > a'));
|
||||
}
|
||||
@ -223,11 +229,15 @@ async function MbViewerInit () {
|
||||
$('a[name="goBack"]')[0].hidden = false;
|
||||
}
|
||||
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.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 (["atom", "rss"].includes(MbState.platform)) {
|
||||
$('section.tgme_channel_history.js-message_history').html(MakeMoreWrapperHtml());
|
||||
TWeb.loadMore($('.js-messages_more_wrap > a'), MbState.siteData);
|
||||
}
|
||||
if (!MbState.siteUrl) {
|
||||
$('a[name="goBack"]')[0].hidden = true;
|
||||
$('section.tgme_channel_history.js-message_history').html(MakeMoreWrapperHtml());
|
||||
@ -236,7 +246,7 @@ async function MbViewerInit () {
|
||||
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-13T21:00' }, { content: `<p>
|
||||
</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.
|
||||
@ -245,7 +255,7 @@ async function MbViewerInit () {
|
||||
<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>`, date: '2024-01-14T02:00' }, { content: `<p>
|
||||
</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
|
||||
@ -255,13 +265,22 @@ async function MbViewerInit () {
|
||||
<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>
|
||||
</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>`, date: '2024-01-16T00:00' }, { content: `<p>
|
||||
</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 Transformapi (very broken)
|
||||
</p>`, time: '2024-01-23T01: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/>
|
||||
@ -298,27 +317,39 @@ function MakeMoreWrapperHtml (wrapType) {
|
||||
relativeOpts[wrapType] = MbState.startingPost.date;
|
||||
}
|
||||
return `<div class="tgme_widget_message_centered js-messages_more_wrap">
|
||||
<a href="${MbState.siteUrl && MakeSiteRestUrl(MakeApiEndpoint('posts', { count: 1, offset: offset, orderBy: "date", ...(MbState.startingPost && relativeOpts) }))}" data-${wrapType}="" class="tme_messages_more js-messages_more"></a>
|
||||
<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')).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))) {
|
||||
const postLink = (postData.link || postData.URL);
|
||||
const authorId = (postData.author?.ID || postData.author || postData._links?.author[0]?.href?.split('/')?.slice(-1)[0]);
|
||||
if (MbState.platform) {
|
||||
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'
|
||||
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];
|
||||
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);
|
||||
const iconUrl = (Object.values(authorData?.avatar_urls || {}).slice(-1)[0] || authorData?.icon?.url || MbState.siteData.iconUrl);
|
||||
//let attachmentsHtml = '';
|
||||
// TODO change this after fixing Trasformapi
|
||||
//for (const attachment of postData.attachments?.url) {
|
||||
//for (const attachment of postData.attachments) {
|
||||
// TODO more media types
|
||||
// attachmentsHtml += `<img src="${attachment.url}"/>`;
|
||||
//}
|
||||
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}">
|
||||
@ -352,14 +383,15 @@ async function MakeMbHtml (postData, makeMoreWrap) {
|
||||
</div>
|
||||
<div class="tgme_widget_message_text js-message_text before_footer" dir="auto">
|
||||
<div class="MbPost">
|
||||
<!--${/*attachmentsHtml*/JSON.stringify(postData.attachments)}-->
|
||||
${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" ${postLink ? `href="${postLink}"` : ''}>
|
||||
<time datetime="${postData.date}" class="time"></time>
|
||||
<a class="tgme_widget_message_date" ${postData.url ? `href="${postData.url}"` : ''}>
|
||||
<time datetime="${postData.time}" class="time"></time>
|
||||
<!-- TODO: show edited status -->
|
||||
</a>
|
||||
</span>
|
||||
@ -393,22 +425,24 @@ function ReformatPostHtml (html) {
|
||||
videoElem.preload = 'none';
|
||||
const frameElem = document.createElement('iframe');
|
||||
frameElem.style = 'border: none; width: 100%;';
|
||||
frameElem.allow = 'fullscreen';
|
||||
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(){
|
||||
top.postMessage((videoElem.src + ' ' + getComputedStyle(videoElem).height), '*');
|
||||
var buttonElem = document.createElement('button');
|
||||
buttonElem.style = 'position: absolute; top: 0; right: 0; z-index: 1;';
|
||||
buttonElem.innerHTML = 'Fullscreen';
|
||||
buttonElem.onclick = function(){ videoElem.requestFullscreen(); };
|
||||
document.body.appendChild(buttonElem);
|
||||
};
|
||||
videoElem.load();
|
||||
</script>
|
||||
@ -438,7 +472,7 @@ function ResizeLayouts () {
|
||||
}
|
||||
|
||||
$('a[name="goBack"]')[0].onclick = function(){
|
||||
ArgsRewrite({ siteurl: null, postid: null, /*postslug: null*/ });
|
||||
ArgsRewrite({ siteurl: null, postid: null, platform: null, /*postslug: null*/ });
|
||||
};
|
||||
|
||||
window.onmessage = function(event){
|
||||
|
101
public/MBViewer/js/TrasformapiSchema.js.xml
Normal file
101
public/MBViewer/js/TrasformapiSchema.js.xml
Normal file
@ -0,0 +1,101 @@
|
||||
const MbViewerTrasformapiSchema = `<schema>
|
||||
|
||||
<!-- 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" key="./guid"/>
|
||||
<content upstream="wordpress.com" key="ID"/>
|
||||
<content upstream="wordpress.org" key="id"/>
|
||||
<content upstream="mastodon" key="id"/>
|
||||
</prop>
|
||||
<prop name="url" type="string">
|
||||
<content upstream="rss" key="./link"/>
|
||||
<content upstream="wordpress.com" key="URL"/>
|
||||
<content upstream="wordpress.org" key="link"/>
|
||||
<content upstream="mastodon" key="url"/>
|
||||
</prop>
|
||||
<prop name="title" type="string">
|
||||
<content upstream="rss" key="title"/>
|
||||
<content upstream="wordpress.com" key="title"/>
|
||||
<content upstream="wordpress.org" key="title.rendered"/>
|
||||
</prop>
|
||||
<prop name="content" type="string">
|
||||
<!-- TODO optional multiple 'key' attrs -->
|
||||
<!--<content upstream="rss" key="content:encoded"/>-->
|
||||
<content upstream="rss" key="./description"/>
|
||||
<content upstream="wordpress.com" key="content"/>
|
||||
<content upstream="wordpress.org" key="content.rendered"/>
|
||||
<content upstream="mastodon" key="url"/>
|
||||
</prop>
|
||||
<!-- TODO: fix this, it's broken with somehow we ending up with an object with urls array, not an attachments array -->
|
||||
<prop name="attachments" type="file[]">
|
||||
<!--<content upstream="rss" key="./media:content"/>-->
|
||||
<content upstream="rss">
|
||||
<prop name="url" key="*[name()='media:content']" attr="url"/>
|
||||
</content>
|
||||
<!--<content upstream="mastodon" key="media_attachments"/>-->
|
||||
</prop>
|
||||
<prop name="author" type="profile">
|
||||
<content upstream="rss"/>
|
||||
<content upstream="wordpress.com" key="author"/>
|
||||
<content upstream="wordpress.org"/>
|
||||
<content upstream="mastodon" key="account"/>
|
||||
</prop>
|
||||
<prop name="time" type="string">
|
||||
<content upstream="rss" key="pubDate"/>
|
||||
<content upstream="wordpress.com" key="date"/>
|
||||
<content upstream="wordpress.org" key="date"/>
|
||||
<content upstream="mastodon" key="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" key="reblog"/>
|
||||
</prop>
|
||||
<!--<prop name="replying" type="message">
|
||||
<content upstream="mastodon" key=""/>
|
||||
</prop>-->
|
||||
</entity>
|
||||
|
||||
<entity name="revision">
|
||||
<prop name="time" type="string">
|
||||
<content upstream="wordpress.com" key="modified"/>
|
||||
<content upstream="wordpress.org" key="modified"/>
|
||||
<content upstream="mastodon" key="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">
|
||||
<content upstream="wordpress.com" key="ID"/>
|
||||
<content upstream="wordpress.org" key="author"/>
|
||||
</prop>
|
||||
<prop name="url" type="string">
|
||||
<content upstream="wordpress.com" key="profile_URL"/>
|
||||
</prop>
|
||||
<prop name="name" type="string">
|
||||
<!--<content upstream="rss" key="dc:creator"/>-->
|
||||
<content upstream="rss" key="dc:creator"/>
|
||||
<content upstream="wordpress.com" key="name"/>
|
||||
</prop>
|
||||
<prop name="icon" type="file">
|
||||
<content upstream="wordpress.com">
|
||||
<prop name="url" key="avatar_URL"/>
|
||||
</content>
|
||||
</prop>
|
||||
</entity>
|
||||
|
||||
<entity name="file">
|
||||
<prop name="url" type="string"/>
|
||||
</entity>
|
||||
|
||||
</schema>`;
|
Loading…
x
Reference in New Issue
Block a user