Update MBViewer, Trasformapi
This commit is contained in:
parent
0cf7946e6d
commit
3eeb0c1807
|
@ -1,5 +1,7 @@
|
|||
(function(){
|
||||
|
||||
// NOTE: using defiant.js gives us '[undefined]' arrays instead of '[]' ones sometimes, should be fixed
|
||||
|
||||
const Exp = {};
|
||||
let MakeTreeFromXml;
|
||||
|
||||
|
@ -8,6 +10,7 @@ const platformIsBrowser = (typeof window !== 'undefined' && typeof window.docume
|
|||
|
||||
if (platformIsNode && !platformIsBrowser) {
|
||||
MakeTreeFromXml = (xml) => new require('jsdom').JSDOM(xml);
|
||||
// TODO load all other dependencies
|
||||
}
|
||||
|
||||
if (platformIsBrowser) {
|
||||
|
@ -16,6 +19,10 @@ if (platformIsBrowser) {
|
|||
|
||||
Exp.Trasformapi = (transformerXml, initOptions={}) => {
|
||||
var transformerTree = MakeTreeFromXml(transformerXml);
|
||||
initOptions.sets ||= {};
|
||||
for (const attr of transformerTree.querySelector(':scope > set')?.attributes) {
|
||||
initOptions.sets[attr.name] = attr.value;
|
||||
}
|
||||
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),
|
||||
|
@ -23,51 +30,23 @@ Exp.Trasformapi = (transformerXml, initOptions={}) => {
|
|||
}
|
||||
|
||||
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);
|
||||
const globalSets = { ...initOptions.sets, ...transformOptions.sets };
|
||||
// due to a bug in defiant, we need to prefix something to any key starting with '@'...
|
||||
// <https://stackoverflow.com/questions/68903102/renaming-object-keys-which-are-nested/68903897#68903897>
|
||||
function JsonObjectKeysFix (obj) {
|
||||
// TODO avoid collisions? (even if they're unlikely with what we're doing)
|
||||
return (obj !== undefined && obj !== null ? Object.fromEntries(Object.entries(obj).map( ([key,value]) => {
|
||||
const newKey = (key.startsWith('@') ? `_${key}` : key);
|
||||
return typeof value == "object"
|
||||
? [newKey, JsonObjectKeysFix(value)]
|
||||
: [newKey, value]
|
||||
})) : obj);
|
||||
}
|
||||
function MakeApiEntityObject (entityName, upstreamName, dataIn) {
|
||||
if (!dataIn) {
|
||||
// nothing to do
|
||||
return;
|
||||
};
|
||||
let dataOut = {};
|
||||
const entitySchema = transformerTree.querySelector(`:scope > entity[name="${entityName}"]`);
|
||||
for (const propSchema of entitySchema.querySelectorAll(':scope > prop')) {
|
||||
|
@ -79,41 +58,74 @@ function _TransformForInput (transformerTree, initOptions, entityName, upstreamN
|
|||
// property is not implemented for the current upstream, skip it
|
||||
continue;
|
||||
}
|
||||
const propContentChildren = propContent.querySelectorAll(`:scope > prop`); // TODO
|
||||
const propContentChildren = propContent.querySelectorAll(`:scope > prop`);
|
||||
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);
|
||||
const dataKey = SubstituteStringSets(propContent.getAttribute('query'), globalSets);
|
||||
const dataInContent = (dataIn instanceof Node
|
||||
? GetElementsByXPath(dataKey, dataIn)[0]?.textContent
|
||||
: (dataKey ? /*_.get*/defiant.search(dataIn, dataKey)[0] : dataIn)
|
||||
);
|
||||
// I don't know if this is fully correct
|
||||
if (Array.isArray(propDataType) && Array.isArray(dataInContent)) {
|
||||
for (const itemContent of dataInContent) {
|
||||
dataOut[propName] = (["string", "number", "int", "float"].includes(propType)
|
||||
? SetOrPush(itemContent, propDataType)
|
||||
: SetOrPush(MakeApiEntityObject(propType, upstreamName, itemContent), propDataType)
|
||||
);
|
||||
}
|
||||
} 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
|
||||
dataOut[propName] = (["string", "number", "int", "float"].includes(propType)
|
||||
? SetOrPush(dataInContent, propDataType)
|
||||
: SetOrPush(MakeApiEntityObject(propType, upstreamName, dataInContent), propDataType)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
dataOut[propName] = {}; // NOTE: in some cases, this should be an array, I guess, or maybe not?
|
||||
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);
|
||||
const childDataKey = SubstituteStringSets(propChildSchema.getAttribute('query'), globalSets);
|
||||
let childDataInContent = [];
|
||||
if (dataIn instanceof Node) {
|
||||
const nodes = GetElementsByXPath(childDataKey, dataIn);
|
||||
if (nodes.length === 1) {
|
||||
childDataInContent = nodes[0]?.textContent
|
||||
} else {
|
||||
for (const node of nodes) {
|
||||
childDataInContent.push(node?.textContent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
childDataInContent = (childDataKey ? /*_.get*/defiant.search(dataIn, childDataKey)[0] : dataIn);
|
||||
}
|
||||
const childResult = (["string", "number", "int", "float"].includes(propChildType)
|
||||
? childDataInContent
|
||||
: MakeApiEntityObject(propChildType, upstreamName, childDataInContent)
|
||||
);
|
||||
if (Array.isArray(propDataType)) {
|
||||
if (!Array.isArray(dataOut[propName])) {
|
||||
dataOut[propName] = [];
|
||||
}
|
||||
const childItems = SureArray(childResult);
|
||||
for (const childItemIndex in childItems) {
|
||||
const childItem = childItems[childItemIndex];
|
||||
if (!dataOut[propName][childItemIndex]) {
|
||||
dataOut[propName][childItemIndex] = {};
|
||||
}
|
||||
dataOut[propName][childItemIndex][propChildName] = childItem;
|
||||
}
|
||||
} else {
|
||||
dataOut[propName][propChildName] = childResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//console.log(dataOut);
|
||||
return dataOut;
|
||||
}
|
||||
return MakeApiEntityObject (entityName, upstreamName, dataIn);
|
||||
return MakeApiEntityObject (entityName, upstreamName, (dataIn instanceof Node ? dataIn : JsonObjectKeysFix(dataIn)));
|
||||
}
|
||||
|
||||
function _TransformForOutput (transformerTree, initOptions, entityName, upstreamName, dataIn, transformOptions={}) {
|
||||
|
@ -122,7 +134,7 @@ function _TransformForOutput (transformerTree, initOptions, entityName, upstream
|
|||
|
||||
// <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) {
|
||||
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) {
|
||||
|
@ -131,7 +143,16 @@ function getElementsByXPath (xpath, parent) {
|
|||
return results;
|
||||
}
|
||||
|
||||
const SetOrPush = (item, dest) => Array.isArray(dest) ? [...dest, item] : item;
|
||||
const SetOrPush = (item, dest) => (Array.isArray(dest) ? [...dest, item] : item);
|
||||
|
||||
const SureArray = (item) => (Array.isArray(item) ? item : [item]);
|
||||
|
||||
const SubstituteStringSets = (string, sets) => {
|
||||
for (const set in sets) {
|
||||
string = string?.replaceAll(`{${set}}`, sets[set]);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
if (platformIsNode) module.exports = Exp;
|
||||
if (platformIsBrowser) window.Trasformapi = Exp.Trasformapi;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -205,6 +205,7 @@
|
|||
<script src="js/widget-frame.js"></script>
|
||||
<script src="js/telegram-web.js"></script>
|
||||
<script src="../Assets/Lib/lodash.custom.min.js"></script>
|
||||
<script src="../Assets/Lib/defiant.min.js"></script>
|
||||
<script src="../Assets/Lib/Trasformapi.js"></script>
|
||||
<script src="./js/TrasformapiSchema.js.xml"></script>
|
||||
<script src="./js/MBViewer.js"></script>
|
||||
|
|
|
@ -77,12 +77,15 @@ function MakeSiteRestUrl (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}`;
|
||||
} 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",
|
||||
|
@ -100,6 +103,13 @@ function MakeApiEndpoint (type, options={}) {
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
@ -201,7 +211,18 @@ async function MbViewerInit () {
|
|||
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;
|
||||
}
|
||||
|
@ -216,6 +237,7 @@ async function MbViewerInit () {
|
|||
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;
|
||||
}
|
||||
|
@ -228,16 +250,17 @@ async function MbViewerInit () {
|
|||
$('.tgme_channel_info_header_username').html(`<a href="${siteLink}">${GetDomainFromUrl(siteLink).toLowerCase()}</a>`);
|
||||
$('a[name="goBack"]')[0].hidden = false;
|
||||
}
|
||||
MbState.siteData.iconUrl = (MbState.siteData.site_icon_url || MbState.siteData.icon?.img || MbState.siteData.icon?.ico);
|
||||
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 (["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());
|
||||
|
@ -281,6 +304,12 @@ async function MbViewerInit () {
|
|||
<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>
|
||||
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)
|
||||
</p>`, time: '2024-01-24T01: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/>
|
||||
|
@ -288,6 +317,7 @@ async function MbViewerInit () {
|
|||
all rights upon the original materials (which are: everything not strictly related to the "MBViewer" mod) belong to the original owners.
|
||||
</p>` }]);
|
||||
}
|
||||
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);
|
||||
|
@ -324,7 +354,10 @@ function MakeMoreWrapperHtml (wrapType) {
|
|||
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();
|
||||
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);
|
||||
|
@ -343,13 +376,15 @@ async function MakeMbHtml (postData, makeMoreWrap) {
|
|||
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 = '';
|
||||
// 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}"/>`;
|
||||
//}
|
||||
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('"', '"') || ''}"/>`;
|
||||
}
|
||||
}
|
||||
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}">
|
||||
|
@ -383,8 +418,9 @@ 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)}
|
||||
${attachmentsHtml}
|
||||
${ReformatPostHtml(postData.content)}
|
||||
${postData.quoting ? `[♻️ Reblog]: ${ReformatPostHtml(postData.quoting.content)}` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tgme_widget_message_footer compact js-message_footer">
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
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[]"/>
|
||||
|
@ -8,49 +12,55 @@ const MbViewerTrasformapiSchema = `<schema>
|
|||
|
||||
<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"/>
|
||||
<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" key="./link"/>
|
||||
<content upstream="wordpress.com" key="URL"/>
|
||||
<content upstream="wordpress.org" key="link"/>
|
||||
<content upstream="mastodon" key="url"/>
|
||||
<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" key="title"/>
|
||||
<content upstream="wordpress.com" key="title"/>
|
||||
<content upstream="wordpress.org" key="title.rendered"/>
|
||||
<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 '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"/>
|
||||
<!-- 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>
|
||||
<!-- 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"/>
|
||||
<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" key="media_attachments"/>-->
|
||||
<!--
|
||||
<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" key="author"/>
|
||||
<content upstream="wordpress.com" query="//author"/>
|
||||
<content upstream="wordpress.org"/>
|
||||
<content upstream="mastodon" key="account"/>
|
||||
<content upstream="mastodon" query="//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"/>
|
||||
<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"/>
|
||||
|
@ -58,44 +68,63 @@ const MbViewerTrasformapiSchema = `<schema>
|
|||
<content upstream="mastodon"/>
|
||||
</prop>
|
||||
<prop name="quoting" type="message">
|
||||
<content upstream="mastodon" key="reblog"/>
|
||||
<content upstream="mastodon" query="//reblog"/>
|
||||
</prop>
|
||||
<!--<prop name="replying" type="message">
|
||||
<content upstream="mastodon" key=""/>
|
||||
<content upstream="mastodon" query=""/>
|
||||
</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"/>
|
||||
<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">
|
||||
<content upstream="wordpress.com" key="ID"/>
|
||||
<content upstream="wordpress.org" key="author"/>
|
||||
<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="wordpress.com" key="profile_URL"/>
|
||||
<content upstream="rss" query="link"/>
|
||||
<content upstream="wordpress.com" query="//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"/>
|
||||
<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" key="avatar_URL"/>
|
||||
<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"/>
|
||||
<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>`;
|
||||
|
|
Loading…
Reference in New Issue