// https://hyperborea.org/tech-tips/fediverse-feeds/ room.registerElement('rss', { index: 0, columns: 2, rows: 1, maxitems: 6, items: [], emoji: "💬", borderspacing: 20, padding: 20, cell_col: "#222", cell_radius: 15, cell_col_font: "#FDF", images: true, css: "", cellwidth: 290, cellheight: 220, overflow: true, cssDefault(){ return ` .paragraphcontainer{ } table{ border-spacing: ${this.borderspacing}px; table-layout: fixed; } a{ color: ${this.cell_col_font}; } td > div, th > div, .grid > div{ vertical-align:top; padding: ${this.padding}px; ${this.overflow ? `min-height: ${this.cellheight}px;` : `height: ${this.cellheight}px !important` }; width: ${this.cellwidth}px !important; background: ${this.cell_col}; border-radius: ${this.cell_radius}px; color: ${this.cell_col_font}; ${this.overflow ? 'overflow: hidden !important' : ''} } .light{ font-size:12px; font-face: monospace; opacity:0.66; } .grid { -webkit-column-count: ${this.columns}; -webkit-column-gap: ${this.borderspacing}px; -moz-column-count: ${this.columns}; -moz-column-gap: ${this.borderspacing}px; column-count: ${this.columns}; column-gap: ${this.borderspacing}px; } .grid > div{ margin-bottom: ${this.borderspacing}px; } h1{ font-size:16px; font-weight:bold; } ` }, createChildren(){ this.paragraph = this.children.find( (el) => el.tag == 'PARAGRAPH' ) if( !this.paragraph ) throw 'dialog-rss: error need paragraph child' if( !this.url ) throw 'dialog-rss: error need url' this.fetchRSS() }, fetchRSS(){ try { let url = new URL(this.url) fetch(this.url) .then( res => res.text() ) .then( xml => { this.items = this.rssToHTML( xml, url.origin ) this.render() }) }catch(e){ console.error("dialog-rss: invalid URL: "+this.url) } }, render(){ this.index = this.index % this.items.length // to be sure let maxCells = this.columns > 1 && this.rows > 1 ? this.columns * this.rows : 99999 if( maxCells > this.maxitems ) maxCells = this.maxitems const css = this.cssDefault() + this.css const cssgridAvailable = 'grid' in document.documentElement.style; let html = `` html += cssgridAvailable ? this.getCSSgrid(maxCells) : this.getTable() this.paragraph.text = html this.paragraph.visible = true }, getCSSgrid: function(maxCells){ let i = 0 let html = `
` while( this.items[ this.index + i ] && i < maxCells ){ let content = this.items[ this.index + i ] html += `
${content}
` i++ } html += `
` return html }, getTable: function(maxCells){ let i = 0 let html = `` while( this.items[ this.index + i ] && i < maxCells ){ let content = this.items[ this.index + i ] if( i > 0 && (i % this.columns) == 0 ) html += `` html += `` i++ } html += `
${content}
` return html }, rssToHTML(xmlString, baseUrl){ const doc = new DOMParser().parseFromString(xmlString, "text/xml"); return [...doc.querySelectorAll("item")].map(item => { const title = String( item.querySelector("title")?.textContent || item.querySelector("pubDate")?.textContent || doc.querySelector("title")?.textContent || "" ).replace(/ \+.*/,'') const server = doc.querySelector("link")?.textContent || baseUrl.replace(/.*\/\//,'') const link = item.querySelector("link")?.textContent || "#"; let desc = item.querySelector("description")?.textContent || ""; // Fix relative paths desc = desc.replace(/(src|href)="\/([^"]+)"/g, `$1="${baseUrl}/$2"`); // Extract media:content or media:thumbnail URLs const media = [ ...item.getElementsByTagNameNS("*", "content"), ...item.getElementsByTagNameNS("*", "thumbnail") ] .filter( (m) => m.getAttribute("url")?.match(/\.(png|jpg|jpeg|gif)/gi) ) .map(m => `\n`).join(""); return `

${this.emoji}${baseUrl.replace(/.*\/\//,'')}

${title} ${desc}
${ this.images ? media : ''} ` }) }, })