Thread viewer for Bluesky
at mastodon 123 lines 3.0 kB view raw
1class AtURI { 2 /** @param {string} uri */ 3 constructor(uri) { 4 if (!uri.startsWith('at://')) { 5 throw new URLError(`Not an at:// URI: ${uri}`); 6 } 7 8 let parts = uri.split('/'); 9 10 if (parts.length != 5) { 11 throw new URLError(`Invalid at:// URI: ${uri}`); 12 } 13 14 this.repo = parts[2]; 15 this.collection = parts[3]; 16 this.rkey = parts[4]; 17 } 18} 19 20/** @param {string} tag, @param {string | object} [params], @returns {any} */ 21 22function $tag(tag, params) { 23 let element; 24 let parts = tag.split('.'); 25 26 if (parts.length > 1) { 27 let tagName = parts[0]; 28 element = document.createElement(tagName); 29 element.className = parts.slice(1).join(' '); 30 } else { 31 element = document.createElement(tag); 32 } 33 34 if (typeof params === 'string') { 35 element.className = element.className + ' ' + params; 36 } else if (params) { 37 for (let key in params) { 38 if (key == 'text') { 39 element.innerText = params[key]; 40 } else if (key == 'html') { 41 element.innerHTML = params[key]; 42 } else { 43 element[key] = params[key]; 44 } 45 } 46 } 47 48 return element; 49} 50 51/** @param {string} name, @returns {any} */ 52 53function $id(name) { 54 return document.getElementById(name); 55} 56 57/** @param {string} uri, @returns {AtURI} */ 58 59function atURI(uri) { 60 return new AtURI(uri); 61} 62 63function castToInt(value) { 64 if (value === undefined || value === null || typeof value == "number") { 65 return value; 66 } else { 67 return parseInt(value, 10); 68 } 69} 70 71/** @param {string} html, @returns {string} */ 72 73function escapeHTML(html) { 74 return html.replace(/&/g, '&amp;') 75 .replace(/</g, '&lt;') 76 .replace(/>/g,'&gt;'); 77} 78 79/** @param {string} html, @returns {string} */ 80 81function sanitizeHTML(html) { 82 return DOMPurify.sanitize(html, { 83 ALLOWED_TAGS: [ 84 'a', 'b', 'blockquote', 'br', 'code', 'dd', 'del', 'div', 'dl', 'dt', 'em', 'font', 85 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'li', 'ol', 'p', 'q', 'pre', 's', 'span', 'strong', 86 'sub', 'sup', 'u', 'wbr', '#text' 87 ], 88 ALLOWED_ATTR: [ 89 'align', 'alt', 'class', 'clear', 'color', 'dir', 'href', 'lang', 'rel', 'title', 'translate' 90 ] 91 }); 92} 93 94/** @returns {string} */ 95 96function getLocation() { 97 return location.origin + location.pathname; 98} 99 100/** @param {Date} date1, @param {Date} date2, @returns {boolean} */ 101 102function sameDay(date1, date2) { 103 return ( 104 date1.getDate() == date2.getDate() && 105 date1.getMonth() == date2.getMonth() && 106 date1.getFullYear() == date2.getFullYear() 107 ); 108} 109 110/** @param {Post} post, @returns {string} */ 111 112function linkToPostThread(post) { 113 return linkToPostById(post.author.handle, post.rkey); 114} 115 116/** @param {string} handle, @param {string} postId, @returns {string} */ 117 118function linkToPostById(handle, postId) { 119 let url = new URL(getLocation()); 120 url.searchParams.set('author', handle); 121 url.searchParams.set('post', postId); 122 return url.toString(); 123}