From 968992f29c351d8796a8bde7a04262b9fe507b41 Mon Sep 17 00:00:00 2001 From: Tom Lutzenberger Date: Sat, 21 Oct 2017 00:09:49 +0200 Subject: [PATCH] [:sparkles: FEATURE] Implement site-specific selector blacklist Fixes #10 --- package.json | 2 +- readingDurationBookmarklet.js | 49 ++++++++++++++++++------------- readingDurationBookmarklet.min.js | 2 +- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 00601a2..d7b1851 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "reading-duration-bookmarklet", - "version": "0.1.6", + "version": "0.1.7", "description": "Dynamic Bookmarklet to detect the reading time of an article", "main": "readingDurationBookmarklet.js", "scripts": { diff --git a/readingDurationBookmarklet.js b/readingDurationBookmarklet.js index aa890b0..faf7b3d 100644 --- a/readingDurationBookmarklet.js +++ b/readingDurationBookmarklet.js @@ -26,7 +26,11 @@ const readingDurationBookmarklet = () => { const SEC_PER_HOUR = 3600; const TWO_DIGIT_SEC = 10; - const defaultSite = { name: 'default', selector: ['article', '.article', '#article', '.post'] }; + const defaultSite = { + name: 'default', + selector: ['article', '.article', '#article', '.post'], + elementBlacklist: [] + }; @@ -39,22 +43,24 @@ const readingDurationBookmarklet = () => { const getSite = () => { return [{ name: 'dev.to', - selector: '#article-body' + selector: '#article-body', + elementBlacklist: [] }, { name: 'medium.com', - selector: '.section-content' + selector: '.section-content', + elementBlacklist: ['.graf--trailing'] }]; }; /** - * @method getIgnoredTags + * @method getGlobalElementBlacklist * @description Get an array with all elements that should be ignored (stripped) from content * * @returns {String[]} */ - const getIgnoredTags = () => { + const getGlobalElementBlacklist = () => { return [ 'audio', 'aside', @@ -119,14 +125,14 @@ const readingDurationBookmarklet = () => { do { // Loop through selector fallbacks until an element is found article = document.querySelector(selector[index]); index++; - } while(article !== null && index < selector.length); + } while(article === null && index < selector.length); } else { article = document.querySelector(selector); } if (article !== null) { - return cleanupContent(stripIgnoredTags(article.cloneNode(true))); + return article.cloneNode(true); } return false; @@ -135,15 +141,16 @@ const readingDurationBookmarklet = () => { /** - * @method stripIgnoredTags - * @description Strip all ignored tags from content + * @method stripBlacklistedElements + * @description Strip all blacklisted elements from content * - * @param {Element} content - (DOM-)Element with all the content + * @param {Element} contentNode - (DOM-)Element with all the content + * @param {Array} siteBlacklist - Array of site-specific blacklisted elements * @returns {Element} */ - const stripIgnoredTags = (content) => { - const tagSelectorList = getIgnoredTags().join(','); - const nodeList = content.querySelectorAll(tagSelectorList); + const stripBlacklistedElements = (contentNode, siteBlacklist) => { + let elementBlacklist = getGlobalElementBlacklist().concat(siteBlacklist); + const nodeList = contentNode.querySelectorAll(elementBlacklist.join(',')); if (nodeList.length > ZERO) { nodeList.forEach((nodeElement) => { @@ -151,7 +158,7 @@ const readingDurationBookmarklet = () => { }); } - return content; + return contentNode; }; @@ -160,11 +167,11 @@ const readingDurationBookmarklet = () => { * @method cleanupContent * @description Remove all unnecessary whitespaces and HTML tags and return plain text * - * @param {Element} content - (DOM-)Element with all the content + * @param {Element} contentNode - (DOM-)Element with all the content * @returns {String} */ - const cleanupContent = (content) => { - let cleanedContent = content.textContent.trim(); + const cleanupContent = (contentNode) => { + let cleanedContent = contentNode.textContent.trim(); cleanedContent = cleanedContent.replace(/[^\w\s./-]/g, ' '); cleanedContent = cleanedContent.replace(/\s+/g, ' '); @@ -234,13 +241,15 @@ const readingDurationBookmarklet = () => { */ const execute = () => { const site = detectSite(); - const article = getArticleContent(site.selector); + const articleNode = getArticleContent(site.selector); + let articleContent = ''; let message = ''; - if(!article) { + if(!articleNode) { message = 'Article has not been found. Sorry!'; } else { - message = `${countWords(article)} Words, ${calculateReadDuration(article)}`; + articleContent = cleanupContent(stripBlacklistedElements(articleNode, site.elementBlacklist)); + message = `${countWords(articleContent)} Words ${calculateReadDuration(articleContent)}`; } alert(message); diff --git a/readingDurationBookmarklet.min.js b/readingDurationBookmarklet.min.js index 44bd0a5..d6829c7 100644 --- a/readingDurationBookmarklet.min.js +++ b/readingDurationBookmarklet.min.js @@ -1,2 +1,2 @@ -javascript:!(()=>{const readingDurationBookmarklet=()=>{'use strict';const a=0,b=60,c=3600,d={name:'default',selector:['article','.article','#article','.post']},e=()=>{return[{name:'dev.to',selector:'#article-body'},{name:'medium.com',selector:'.section-content'}]},f=()=>{return['audio','aside','button','code','fieldset','form','iframe','input','label','legend','menu','nav','noscript','object','pre','progress','script','style','video','#disqus_thread','#comments','.comments','.comment']},g=()=>{const b=e().filter((a)=>{const b=new RegExp(a.name,'im');return b.test(window.location.host)});return b.length>a?b[0]:d},h=(a)=>{let b=null;if(Array.isArray(a)){let c=0;do b=document.querySelector(a[c]),c++;while(null!=b&&c{const c=f().join(','),d=b.querySelectorAll(c);return d.length>a&&d.forEach((a)=>{a.remove()}),b},j=(a)=>{let b=a.textContent.trim();return b=b.replace(/[^\w\s./-]/g,' '),b=b.replace(/\s+/g,' '),b},k=(a)=>{return a.split(' ').length},l=(a)=>{const c=k(a)/220*b;return m(c)},m=(d)=>{let e=0,f=0,g=0;return da&&e<10?'0'+e:e,g>a?`${g}hrs ${f}min`:`${f}min`)};return{execute:()=>{const a=g(),b=h(a.selector);let c='';c=b?`${k(b)} Words, ${l(b)}`:'Article has not been found. Sorry!',alert(c)}}};readingDurationBookmarklet().execute(); +javascript:!(()=>{const readingDurationBookmarklet=()=>{'use strict';const a=0,b=60,c=3600,d={name:'default',selector:['article','.article','#article','.post'],elementBlacklist:[]},e=()=>{return[{name:'dev.to',selector:'#article-body',elementBlacklist:[]},{name:'medium.com',selector:'.section-content',elementBlacklist:['.graf--trailing']}]},f=()=>{return['audio','aside','button','code','fieldset','form','iframe','input','label','legend','menu','nav','noscript','object','pre','progress','script','style','video','#disqus_thread','#comments','.comments','.comment']},g=()=>{const b=e().filter((a)=>{const b=new RegExp(a.name,'im');return b.test(window.location.host)});return b.length>a?b[0]:d},h=(a)=>{let b=null;if(Array.isArray(a)){let c=0;do b=document.querySelector(a[c]),c++;while(null==b&&c{let d=f().concat(c);const e=b.querySelectorAll(d.join(','));return e.length>a&&e.forEach((a)=>{a.remove()}),b},j=(a)=>{let b=a.textContent.trim();return b=b.replace(/[^\w\s./-]/g,' '),b=b.replace(/\s+/g,' '),b},k=(a)=>{return a.split(' ').length},l=(a)=>{const c=k(a)/220*b;return m(c)},m=(d)=>{let e=0,f=0,g=0;return da&&e<10?'0'+e:e,g>a?`${g}hrs ${f}min`:`${f}min`)};return{execute:()=>{const a=g(),b=h(a.selector);let c='',d='';b?(c=j(i(b,a.elementBlacklist)),d=`${k(c)} Words ${l(c)}`):d='Article has not been found. Sorry!',alert(d)}}};readingDurationBookmarklet().execute(); })(); \ No newline at end of file