You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			177 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			177 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
| /* global URL */
 | |
| 
 | |
| const he = require('he');
 | |
| const LinkifyIt = require('linkify-it');
 | |
| 
 | |
| const linkify = LinkifyIt();
 | |
| const { concatenateBytes, getViewOfArrayBuffer } = require('./crypto');
 | |
| 
 | |
| module.exports = {
 | |
|   assembleChunks,
 | |
|   findLinks,
 | |
|   getChunkPattern,
 | |
|   getDomain,
 | |
|   getTitleMetaTag,
 | |
|   getImageMetaTag,
 | |
|   isLinkInWhitelist,
 | |
|   isMediaLinkInWhitelist,
 | |
| };
 | |
| 
 | |
| const SUPPORTED_DOMAINS = [
 | |
|   'youtube.com',
 | |
|   'www.youtube.com',
 | |
|   'm.youtube.com',
 | |
|   'youtu.be',
 | |
|   'reddit.com',
 | |
|   'www.reddit.com',
 | |
|   'm.reddit.com',
 | |
|   'imgur.com',
 | |
|   'www.imgur.com',
 | |
|   'm.imgur.com',
 | |
|   'instagram.com',
 | |
|   'www.instagram.com',
 | |
|   'm.instagram.com',
 | |
| ];
 | |
| function isLinkInWhitelist(link) {
 | |
|   try {
 | |
|     const url = new URL(link);
 | |
| 
 | |
|     if (url.protocol !== 'https:') {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     if (!url.pathname || url.pathname.length < 2) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     const lowercase = url.host.toLowerCase();
 | |
|     if (!SUPPORTED_DOMAINS.includes(lowercase)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
|   } catch (error) {
 | |
|     return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| const SUPPORTED_MEDIA_DOMAINS = /^([^.]+\.)*(ytimg.com|cdninstagram.com|redd.it|imgur.com|fbcdn.net)$/i;
 | |
| function isMediaLinkInWhitelist(link) {
 | |
|   try {
 | |
|     const url = new URL(link);
 | |
| 
 | |
|     if (url.protocol !== 'https:') {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     if (!url.pathname || url.pathname.length < 2) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     if (!SUPPORTED_MEDIA_DOMAINS.test(url.host)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
|   } catch (error) {
 | |
|     return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| const META_TITLE = /<meta\s+property="og:title"\s+content="([\s\S]+?)"\s*\/?\s*>/im;
 | |
| const META_IMAGE = /<meta\s+property="og:image"\s+content="([\s\S]+?)"\s*\/?\s*>/im;
 | |
| function _getMetaTag(html, regularExpression) {
 | |
|   const match = regularExpression.exec(html);
 | |
|   if (match && match[1]) {
 | |
|     return he.decode(match[1]).trim();
 | |
|   }
 | |
| 
 | |
|   return null;
 | |
| }
 | |
| 
 | |
| function getTitleMetaTag(html) {
 | |
|   return _getMetaTag(html, META_TITLE);
 | |
| }
 | |
| function getImageMetaTag(html) {
 | |
|   return _getMetaTag(html, META_IMAGE);
 | |
| }
 | |
| 
 | |
| function findLinks(text) {
 | |
|   const matches = linkify.match(text || '') || [];
 | |
|   return matches.map(match => match.text);
 | |
| }
 | |
| 
 | |
| function getDomain(url) {
 | |
|   try {
 | |
|     const urlObject = new URL(url);
 | |
|     return urlObject.hostname;
 | |
|   } catch (error) {
 | |
|     return null;
 | |
|   }
 | |
| }
 | |
| 
 | |
| const MB = 1024 * 1024;
 | |
| const KB = 1024;
 | |
| 
 | |
| function getChunkPattern(size) {
 | |
|   if (size > MB) {
 | |
|     return _getRequestPattern(size, MB);
 | |
|   } else if (size > 500 * KB) {
 | |
|     return _getRequestPattern(size, 500 * KB);
 | |
|   } else if (size > 100 * KB) {
 | |
|     return _getRequestPattern(size, 100 * KB);
 | |
|   } else if (size > 50 * KB) {
 | |
|     return _getRequestPattern(size, 50 * KB);
 | |
|   } else if (size > 10 * KB) {
 | |
|     return _getRequestPattern(size, 10 * KB);
 | |
|   } else if (size > KB) {
 | |
|     return _getRequestPattern(size, KB);
 | |
|   }
 | |
| 
 | |
|   throw new Error(`getChunkPattern: Unsupported size: ${size}`);
 | |
| }
 | |
| 
 | |
| function _getRequestPattern(size, increment) {
 | |
|   const results = [];
 | |
| 
 | |
|   let offset = 0;
 | |
|   while (size - offset > increment) {
 | |
|     results.push({
 | |
|       start: offset,
 | |
|       end: offset + increment - 1,
 | |
|       overlap: 0,
 | |
|     });
 | |
|     offset += increment;
 | |
|   }
 | |
| 
 | |
|   if (size - offset > 0) {
 | |
|     results.push({
 | |
|       start: size - increment,
 | |
|       end: size - 1,
 | |
|       overlap: increment - (size - offset),
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   return results;
 | |
| }
 | |
| 
 | |
| function assembleChunks(chunkDescriptors) {
 | |
|   const chunks = chunkDescriptors.map((chunk, index) => {
 | |
|     if (index !== chunkDescriptors.length - 1) {
 | |
|       return chunk.data;
 | |
|     }
 | |
| 
 | |
|     if (!chunk.overlap) {
 | |
|       return chunk.data;
 | |
|     }
 | |
| 
 | |
|     return getViewOfArrayBuffer(
 | |
|       chunk.data,
 | |
|       chunk.overlap,
 | |
|       chunk.data.byteLength
 | |
|     );
 | |
|   });
 | |
| 
 | |
|   return concatenateBytes(...chunks);
 | |
| }
 |