2020-04-29 08:37:43 +00:00
/ * *
* Make a page more readable by disabling all page styling and applying a
* bare minimum of our own . Go to the first thing that looks like the start
* of the actual content so no time is wasted scrolling past initial
* navigation etc .
*
* @ title Readable ++
* /
( function read ( ) {
/ * C r e a t e a n e w I F R A M E t o g e t a " c l e a n " W i n d o w o b j e c t , s o w e c a n u s e i t s
* console . Sometimes sites ( e . g . Twitter ) override console . log and even
* the entire console object . "delete console.log" or "delete console"
* does not always work , and messing with the prototype seemed more
* brittle than this . * /
var console = ( function ( ) {
var iframe = document . getElementById ( 'xxxJanConsole' ) ;
if ( ! iframe ) {
iframe = document . createElementNS ( 'http://www.w3.org/1999/xhtml' , 'iframe' ) ;
iframe . id = 'xxxJanConsole' ;
iframe . style . display = 'none' ;
( document . body || document . documentElement ) . appendChild ( iframe ) ;
}
return iframe && iframe . contentWindow && iframe . contentWindow . console || {
log : function ( ) { }
} ;
} ) ( ) ;
/* The style sheet for more readable content. */
var css = ( function ( ) { / * @ c h a r s e t " u t f - 8 " ;
@ namespace svg "http://www.w3.org/2000/svg" ;
- jancss - comment { content :
"General styles -------------------------------------------" ;
}
* {
line - height : 1.5 ;
}
html {
background : rgb ( 245 , 245 , 225 ) ;
color : rgb ( 0 , 0 , 30 ) ;
}
body {
max - width : 48 em ;
margin : 0 auto ;
padding : 1 em ;
font - family : "Calibri" , sans - serif ;
font - size : 1.05 rem ;
}
p {
line - height : 1.7 ;
}
: link {
color : # 00 e ;
}
: visited {
color : # 528 ;
}
: link : focus ,
: visited : focus ,
: link : hover ,
: visited : hover {
color : # e30 ;
}
: link : active ,
: visited : active {
color : # e00 ;
}
center ,
[ align ] {
text - align : left ;
}
b : not ( . jancss - probably - structure ) ,
u ,
blink {
font - weight : inherit ;
font - style : inherit ;
text - decoration : inherit
}
b . jancss - probably - structure {
font - size : larger ;
}
. jancss - probably - layout {
font : inherit ;
}
- jancss - comment { content :
"Headers --------------------------------------------------" ;
}
h1 : not ( . jancss - probably - layout ) ,
h2 : not ( . jancss - probably - layout ) ,
h3 : not ( . jancss - probably - layout ) {
font - family : "Cambria" , serif ;
}
h1 , h1 . jancss - probably - layout {
border - bottom : 1 px solid # 888 ;
font - size : 200 % ;
font - weight : 100 ;
}
h2 , h2 . jancss - probably - layout * {
border - bottom : 1 px solid # bbb ;
font - size : 150 % ;
font - weight : 100 ;
}
h3 , h3 . jancss - probably - layout {
border - bottom : 1 px dotted # bbb ;
font - size : 117 % ;
font - weight : 100 ;
}
h1 . jancss - probably - layout * ,
h2 . jancss - probably - layout * ,
h3 . jancss - probably - layout * {
font - size : 1 rem ;
}
- jancss - comment { content :
"Links in headers (probably permalinks) -------------------" ;
}
h1 a [ href ] : not ( : hover ) ,
h2 a [ href ] : not ( : hover ) ,
h3 a [ href ] : not ( : hover ) {
text - decoration : none ;
}
h1 a [ href ] : : after ,
h2 a [ href ] : : after ,
h3 a [ href ] : : after {
font - size : 75 % ;
content : " #" ;
}
- jancss - comment { content :
"Pre-formatted text and source code -----------------------" ;
}
pre {
padding : 1 ex ;
border : 1 px dotted ;
}
code ,
pre ,
. syntaxhighlighter ,
. dp - highlighter {
font - family : "Consolas" , monospace ;
font - size : small ;
background : # ffe ;
}
. dp - highlighter + pre [ name = "code" ] {
display : none ;
}
- jancss - comment { content :
"Forms ----------------------------------------------------" ;
}
textarea {
width : 100 % ;
height : 32 ex ;
}
- jancss - comment { content :
"Tables ---------------------------------------------------" ;
}
table . jancss - probably - for - data th ,
table . jancss - probably - for - data td {
vertical - align : top ;
text - align : left ;
padding : 0.5 ex ;
}
table . jancss - probably - for - data caption {
font - weight : bold ;
border - bottom : 1 px dotted ;
}
table . jancss - probably - for - layout td {
display : inline - block ;
}
table . jancss - probably - for - data tr : nth - child ( odd ) td : not ( . jancss - active - col ) {
background : # ffe ;
}
table . jancss - probably - for - data tr : hover td : not ( . code ) ,
table . jancss - probably - for - data . jancss - active - col {
background : # ffc ;
}
table . jancss - probably - for - data th ,
table . jancss - probably - for - data tr td : not ( . code ) : hover {
background : # ff9 ;
}
table . jancss - probably - for - data th code ,
table . jancss - probably - for - data td code {
background : inherit ;
}
- jancss - comment { content :
"Make images use the full page width ----------------------" ;
}
img ,
input [ type = "image" ] ,
object ,
embed ,
video ,
audio ,
iframe ,
canvas ,
: not ( svg | * ) > svg | * {
max - width : 100 % ;
}
figure {
margin : 0 ;
}
iframe {
width : 100 % ;
}
iframe [ class *= "twitter" ] {
min - height : 15 em ;
}
- jancss - comment { content :
"Dim images and media until :hover ------------------------" ;
}
body : not ( : hover ) img ,
body : not ( : hover ) input [ type = "image" ] ,
body : not ( : hover ) object ,
body : not ( : hover ) embed ,
body : not ( : hover ) video ,
body : not ( : hover ) audio ,
body : not ( : hover ) iframe ,
body : not ( : hover ) canvas ,
body : not ( : hover ) : not ( svg | * ) > svg | * {
opacity : 0.25 ;
}
- jancss - comment { content :
"Limit icon dimensions --------------------------------" ;
}
a [ href *= "facebook.com" ] svg ,
a [ href *= "instagram.com" ] svg ,
a [ href *= "twitter.com" ] svg ,
a [ href *= ".pinterest." ] svg ,
svg [ id *= "icon" ] ,
svg [ class *= "icon" ] ,
svg [ class *= "inline" ] ,
svg [ data - icon ] ,
svg [ role = "img" ] ,
[ class *= "icon" ] svg ,
[ class *= "Icon" ] svg ,
img [ class *= "icon" ] [ src *= ".svg" ] ,
img [ class *= "Icon" ] [ src *= ".svg" ] ,
span > svg ,
button svg ,
[ class *= "button" ] svg ,
[ role *= "button" ] svg ,
[ class *= "controls" ] svg ,
. svg - icon ,
. inline - icon ,
. wp - smiley ,
. smiley ,
. emoticon ,
: not ( html ) . emoji {
max - width : 1.4 em ;
max - height : 1.4 em ;
}
- jancss - comment { content :
"Make everything scrollable -------------------------------" ;
}
[ style *= "position: fixed" ] ,
[ style *= "position:fixed" ] {
position : static ! important ;
}
- jancss - comment { content :
"Make side notes and pull quotes less conspicuous ---------" ;
}
aside : not ( : hover ) ,
[ data - expander - id ] ,
[ id ^= "footnote_plugin_tooltip_text_" ] : not ( : hover ) ,
blockquote [ class *= "quote" ] : not ( : hover ) ,
q [ class *= "pull" ] : not ( : hover ) ,
blockquote [ class *= "pull" ] : not ( : hover ) ,
. quote - box : not ( : hover ) ,
. su - pullquote : not ( : hover ) ,
. pullquote : not ( : hover ) ,
. pullQuote : not ( : hover ) ,
. pull - quote : not ( : hover ) {
opacity : 0.25 ;
}
- jancss - comment { content :
"Decrease common forum and metadata font size -------------" ;
}
. postprofile ,
. signature {
font - size : smaller ;
border - top : 1 px dotted ;
opacity : 0.5 ;
}
- jancss - comment { content :
"Hide common social media elements ------------------------" ;
}
iframe [ src *= ".facebook.com/" ] ,
iframe [ src *= ".twitter.com/widgets/" ] ,
iframe [ src *= "//plusone.google.com/_/+1/" ] ,
iframe [ src *= "//www.reddit.com/static/button/" ] ,
iframe [ src *= "//s7.addthis.com/" ] ,
iframe [ src *= "//www.stumbleupon.com/badge/embed/" ] ,
iframe [ src *= "//widgets.bufferapp.com/" ] {
width : 12 em ;
height : 4 ex ;
border : 1 px dotted ;
}
. twtr - widget . twtr - scroll {
max - height : 30 ex ;
overflow : auto ;
}
. article _ _share {
display : none ;
}
# social _btns {
display : none ;
}
. taboola {
display : none ;
}
. social - media > . share {
display : none ;
}
: - moz - any (
div ,
ul ,
li
) : - moz - any (
: - moz - any (
[ id *= "social" ] ,
[ id *= "Social" ]
) : - moz - any (
[ id *= "media" ] ,
[ id *= "Media" ]
) : - moz - any (
[ id *= "share" ] ,
[ id *= "Share" ] ,
[ id *= "sharing" ] ,
[ id *= "Sharing" ]
) ,
: - moz - any (
[ id *= "social" ] ,
[ id *= "Social" ]
[ id *= "share" ] ,
[ id *= "Share" ] ,
[ id *= "sharing" ] ,
[ id *= "Sharing" ]
) : - moz - any (
[ id *= "toolbar" ] ,
[ id *= "Toolbar" ] ,
[ id *= "buttons" ] ,
[ id *= "Buttons" ] ,
) ,
: - moz - any (
[ class *= "social" ] ,
[ class *= "Social" ]
) : - moz - any (
[ class *= "media" ] ,
[ class *= "Media" ]
) : - moz - any (
[ class *= "share" ] ,
[ class *= "Share" ] ,
[ class *= "sharing" ] ,
[ class *= "Sharing" ]
) ,
: - moz - any (
[ class *= "social" ] ,
[ class *= "Social" ]
[ class *= "share" ] ,
[ class *= "Share" ] ,
[ class *= "sharing" ] ,
[ class *= "Sharing" ]
) : - moz - any (
[ class *= "toolbar" ] ,
[ class *= "Toolbar" ] ,
[ class *= "buttons" ] ,
[ class *= "Buttons" ] ,
)
) {
display : none ;
}
: matches (
div ,
ul ,
li
) : matches (
: matches (
[ id *= "social" ] ,
[ id *= "Social" ]
) : matches (
[ id *= "media" ] ,
[ id *= "Media" ]
) : matches (
[ id *= "share" ] ,
[ id *= "Share" ] ,
[ id *= "sharing" ] ,
[ id *= "Sharing" ]
) ,
: matches (
[ id *= "social" ] ,
[ id *= "Social" ]
[ id *= "share" ] ,
[ id *= "Share" ] ,
[ id *= "sharing" ] ,
[ id *= "Sharing" ]
) : matches (
[ id *= "toolbar" ] ,
[ id *= "Toolbar" ] ,
[ id *= "buttons" ] ,
[ id *= "Buttons" ] ,
) ,
: matches (
[ class *= "social" ] ,
[ class *= "Social" ]
) : matches (
[ class *= "media" ] ,
[ class *= "Media" ]
) : matches (
[ class *= "share" ] ,
[ class *= "Share" ] ,
[ class *= "sharing" ] ,
[ class *= "Sharing" ]
) ,
: matches (
[ class *= "social" ] ,
[ class *= "Social" ]
[ class *= "share" ] ,
[ class *= "Share" ] ,
[ class *= "sharing" ] ,
[ class *= "Sharing" ]
) : matches (
[ class *= "toolbar" ] ,
[ class *= "Toolbar" ] ,
[ class *= "buttons" ] ,
[ class *= "Buttons" ] ,
)
) {
display : none ;
}
- jancss - comment { content :
"Hide ad elements that slipped through my ad blocker ------" ;
}
iframe [ id ^= "google_ads_" ] {
display : none ;
}
- jancss - comment { content :
"Hide empty list items ------------------------------------" ;
}
li : empty , li . jancss - emptyish {
display : none ;
}
- jancss - comment { content :
"Make common navigation elements more compact -------------" ;
}
: - moz - any (
nav ,
body [ class *= "avigat" ] ,
body [ id *= "avigat" ] ,
body [ class *= "-nav-" ] ,
body [ class *= "nav-" ] ,
body [ class $ = "-nav" ] ,
body [ id *= "-nav-" ] ,
body [ id *= "nav-" ] ,
body [ id$ = "-nav" ] ,
body [ role = "navigation" ]
) ul {
display : inline ;
margin : 0 ;
padding : 0 ;
}
: - webkit - any (
nav ,
body [ class *= "avigat" ] ,
body [ id *= "avigat" ] ,
body [ class *= "-nav-" ] ,
body [ class *= "nav-" ] ,
body [ class $ = "-nav" ] ,
body [ id *= "-nav-" ] ,
body [ id *= "nav-" ] ,
body [ id$ = "-nav" ] ,
body [ role = "navigation" ]
) ul {
display : inline ;
margin : 0 ;
padding : 0 ;
}
: any (
nav ,
body [ class *= "avigat" ] ,
body [ id *= "avigat" ] ,
body [ class *= "-nav-" ] ,
body [ class *= "nav-" ] ,
body [ class $ = "-nav" ] ,
body [ id *= "-nav-" ] ,
body [ id *= "nav-" ] ,
body [ id$ = "-nav" ] ,
body [ role = "navigation" ]
) ul {
display : inline ;
margin : 0 ;
padding : 0 ;
}
: - moz - any (
nav ,
body [ class *= "avigat" ] ,
body [ id *= "avigat" ] ,
body [ class *= "-nav-" ] ,
body [ class *= "nav-" ] ,
body [ class $ = "-nav" ] ,
body [ id *= "-nav-" ] ,
body [ id *= "nav-" ] ,
body [ id$ = "-nav" ] ,
body [ role = "navigation" ]
) li {
display : inline ;
margin : 0 ;
padding : 0 . 5 em ;
border - right : 1 px dotted ;
}
: - webkit - any (
nav ,
body [ class *= "avigat" ] ,
body [ id *= "avigat" ] ,
body [ class *= "-nav-" ] ,
body [ class *= "nav-" ] ,
body [ class $ = "-nav" ] ,
body [ id *= "-nav-" ] ,
body [ id *= "nav-" ] ,
body [ id$ = "-nav" ] ,
body [ role = "navigation" ]
) li {
display : inline ;
margin : 0 ;
padding : 0 . 5 em ;
border - right : 1 px dotted ;
}
: any (
nav ,
body [ class *= "avigat" ] ,
body [ id *= "avigat" ] ,
body [ class *= "-nav-" ] ,
body [ class *= "nav-" ] ,
body [ class $ = "-nav" ] ,
body [ id *= "-nav-" ] ,
body [ id *= "nav-" ] ,
body [ id$ = "-nav" ] ,
body [ role = "navigation" ]
) li {
display : inline ;
margin : 0 ;
padding : 0 . 5 em ;
border - right : 1 px dotted ;
}
- jancss - comment { content :
"Hide old cufón text replacement --------------------------" ;
}
. cufon - canvas canvas {
display : none ;
}
- jancss - comment { content :
"Make notes on decorrespondent.nl less conspicuous --------" ;
}
. contentitem - sidenote : not ( : hover ) > : not ( . contentitem - sidenote - snippet ) ,
. contentitem - infocard - toggle - container + . contentitem - infocard - contents : not ( : hover ) {
opacity : 0.25 ;
}
. contentitem - sidenote : hover > : not ( . contentitem - sidenote - snippet ) ,
. contentitem - infocard - toggle - container + . contentitem - infocard - contents : hover {
background : # ffc ;
}
- jancss - comment { content :
"Hide the source text on Google Translate-d pages ---------" ;
}
. google - src - text {
display : none ;
}
- jancss - comment { content :
"Hide big ScrollMagic spacers, e.g. on Co.Design ----------" ;
}
. scrollmagic - pin - spacer {
display : none ;
}
- jancss - comment { content :
"Always hide our IFRAME used to restore console.log -------" ;
}
# xxxJanConsole {
display : none ;
}
* / ; } ) . t o S t r i n g ( )
. replace ( /^function\s*\(\s*\)\s*\{\s*\/\*/ , '' )
. replace ( /\*\/\s*\;?\s*\}\s*$/ , '' ) ;
/* The attributes to disable. */
var attrs = [
'style' ,
'background' , 'bgcolor' , 'color' , 'text' , 'link' , 'vlink' , 'alink' , 'hlink' ,
'table@width' , 'colgroup@width' , 'col@width' , 'tr@width' , 'td@width' , 'th@width' , 'table@height' , 'tr@height' , 'td@height' , 'th@height' ,
'img@width' , 'img@height' ,
'border' ,
'frameborder' ,
'align' ,
'face' , 'font@size' , 'basefont@size'
] ;
/* Elements to remove completely. */
var elementsToRemoveSelectors = [
'.bt-popin' /* Used on standaard.be. */
] ;
/* The selectors to try for the header elements, whose text content will be compared to the page title. The last match wins. */
var headerSelectors = [
'[class*="head"]:not(:empty)' ,
'[class*="Head"]:not(:empty)' ,
'[id*="head"]:not(:empty)' ,
'[id*="Head"]:not(:empty)' ,
'[role="heading"]' ,
'[class*="title"]:not(:empty)' ,
'[class*="Title"]:not(:empty)' ,
'h1:not(:empty), h2:not(:empty), h3:not(:empty)' ,
'h1:not(:empty)[itemprop~="name"], h2:not(:empty)[itemprop~="name"], h3:not(:empty)[itemprop~="name"]' ,
'h1:not(:empty)[itemprop~="headline"], h2:not(:empty)[itemprop~="headline"], h3:not(:empty)[itemprop~="headline"]'
] ;
var ancestorsForHeadersToIgnoreSelectors = [
'aside' ,
'.related-posts' ,
'.article-slider' ,
'.article-drawer'
] ;
/* The selectors to try (in this order) for the first content element to scroll to when no suitable header was found. */
var contentSelectors = [
/ * T h e m o s t s e m a n t i c a l l y r i c h e l e m e n t s s h o u l d b e u s e d c o r r e c t l y s o w e
* can ass - u - me them to be the main content element , right ?
* /
'main h1:not(:empty)' ,
'main header' ,
'main h2:not(:empty)' ,
'main' ,
'body [itemprop="blogPost"] h1:not(:empty)' ,
'body [itemprop="blogPost"] header' ,
'body [itemprop="blogPost"] h2:not(:empty)' ,
'body [itemprop="blogPost"]' ,
'body [role="main"] h1:not(:empty)' ,
'body [role="main"] header' ,
'body [role="main"] h2:not(:empty)' ,
'body [role="main"]' ,
'body [role="document"] h1:not(:empty)' ,
'body [role="document"] header' ,
'body [role="document"] h2:not(:empty)' ,
'body [role="document"]' ,
'body [role="article"] h1:not(:empty)' ,
'body [role="article"] header' ,
'body [role="article"] h2:not(:empty)' ,
'body [role="article"]' ,
'body #main h1:not(:empty)' ,
'body #main header' ,
'body #main h2:not(:empty)' ,
'body #main' ,
/ * < a r t i c l e > i s a l s o " s e m a n t i c a l l y r i c h " , b u t t h e r e a r e s e v e r a l s i t e s
* that have a list of related articles , each in its own < article > . A
* "real" article would not have any < article > siblings .
* /
':not(li) > article:only-of-type' ,
/ * C o m m o n I D s a n d c l a s s e s f o r t h e m a i n c o n t e n t e l e m e n t ( e . g . w e b l o g
* post IDs , newspaper articles , … )
* /
'body #article' ,
'body :not(#spotlight) > .article' ,
'body .articleContent' ,
'body #article_top' ,
'body #article_body' ,
'body #article_main' ,
'body .post-body:not(.field-item)' ,
':not(input):not(textarea).post' ,
':not(input):not(textarea).blogpost' ,
':not(input):not(textarea).blogPost' ,
'body [id^="post0"]' ,
'body [id^="post1"]' ,
'body [id^="post2"]' ,
'body [id^="post3"]' ,
'body [id^="post4"]' ,
'body [id^="post5"]' ,
'body [id^="post6"]' ,
'body [id^="post7"]' ,
'body [id^="post8"]' ,
'body [id^="post9"]' ,
'body [id^="post-0"]' ,
'body [id^="post-1"]' ,
'body [id^="post-2"]' ,
'body [id^="post-3"]' ,
'body [id^="post-4"]' ,
'body [id^="post-5"]' ,
'body [id^="post-6"]' ,
'body [id^="post-7"]' ,
'body [id^="post-8"]' ,
'body [id^="post-9"]' ,
'body #entry' ,
'body .entry' ,
'body #content' ,
'body .content' ,
'body [id^="content"]' ,
'body [class^="content"]' ,
'body #main' ,
'body .main' ,
/ * C o n s i d e r t h e f i r s t h e a d e r ( i n D O M o r d e r ) t o b e t h e m o s t i m p o r t a n t
* one and ass - u - me it is the start of the main content .
* /
'h1:not(:empty)' ,
'body #header' ,
'header' ,
'body .header' ,
'h2:not(:empty)' ,
/ * W h e n a l l e l s e f a i l s , j u s t l o o k f o r b i g g e r t e x t , w h i c h w o u l d
* probably be used instead of the appropriate header elements .
* /
'big'
] ;
/* Structure elements incorrectly used for layout purposes ("make it big and bold"). */
var structureElementsForLayoutSelectors = [
'//*[contains(" h1 h2 h3 h4 h5 h6 h7 ", concat(" ", local-name(), " ")) and string-length(normalize-space()) > 120]'
] ;
/* Layout elements incorrectly used for structure purposes ("bold means header"). */
var layoutElementsForStructureSelectors = [
/ * B e c a u s e t h e r e i s n o s u p p o r t f o r t h e S e l e c t o r s L e v e l 4 " s u b j e c t o f
* a selector " syntax yet ( or any definite syntax , for that matter ) ,
* I simply ass - u - me in the code below that the subject is a "B"
* element . Either the result of the selector , or the previous
* element sibling .
* /
'b:first-child + :empty' ,
':empty + b + :empty' ,
':empty + b:last-child' ,
'div > b:only-child, p > b:only-child'
] ;
/* URI pattern for syntax highlighting style sheets. */
var syntaxHighlightHrefRegex = /\b((syntax|pygments)(hi(ghlight(er)?|lite(r)?))?|sh(Core|Theme[^.]*)|geshi|codecolorer)[./]/i ;
/ * K e e p t r a c k o f w h i c h e l e m e n t s h a v e h a d t h e i r e v e n t h a n d l e r s
* disabled / re - enabled .
* /
var elementsWithToggledEventHandlers = { } ;
var eventHandlerAttributesToToggle = [
'oncontextmenu' ,
'onshow' ,
'oninput' ,
'onkeydown' ,
'onkeyup' ,
'onkeypress' ,
'onmousedown' ,
'onmouseup' ,
'onmouseenter' ,
'onmouseleave' ,
'onmouseover' ,
'onmouseout' ,
'onmousemove' ,
'onresize' ,
'onscroll' ,
'onwheel' ,
'onselect' ,
'onselectstart' ,
'onselectionchange'
] ;
/ * A l l m a i n c o n t e n t e l e m e n t s ( t h e o n e i n t h e m a i n d o c u m e n t a n d t h o s e
* nested in any IFRAMEs etc . ) * /
var contentElements = [ ] ;
/* The main function. */
( function execute ( document ) {
function addClass ( element , classNames ) {
/* HTMLElement.classList does not work on iOS 4's Safari, so this is a fallback. */
classNames . split ( /\s+/ ) . forEach ( function ( className ) {
element . className = ( ( ' ' + element . className + ' ' ) . replace ( ' ' + className . trim ( ) + ' ' , '' ) + ' ' + className ) . trim ( ) ;
} ) ;
}
function removeClass ( element , classNames ) {
classNames . split ( /\s+/ ) . forEach ( function ( className ) {
element . className = ( ' ' + element . className + ' ' ) . replace ( ' ' + className . trim ( ) + ' ' , '' ) . trim ( ) ;
} ) ;
}
function toArray ( arrayLike ) {
return Array . prototype . slice . call ( arrayLike ) ;
}
var all = toArray ( document . getElementsByTagName ( '*' ) ) ,
ourStyleSheet = document . getElementById ( 'jancss' ) ,
allStyleSheets = toArray ( document . styleSheets ) ,
prettyPrintStyleSheet ,
matches ;
/* Special hack for The Guardian (and possibly others), which re-enables the CSS because it detects a change in font size. */
window . TextResizeDetector && TextResizeDetector . stopDetector && TextResizeDetector . stopDetector ( ) ;
/ * C l e a r a l l s c h e d u l e d c a l l b a c k s . N a i v e l y a s s - u - m e t h a t a n y c a l l t o
* setTimeout / setInterval returns the next ID from a monotonically
* increasing function that is used for both timeout and interval
* IDs . Therefore , to clear all timeouts and intervals , it suffices
* to get a new timeout ID and then clear everything up to that ID .
*
* Though the HTML5 specification says nothing about the return value
* of setTimeout / setInterval , this appears to work in Firefox 22 ,
* Chrome 27 , Opera 12 and Safari 5.
* /
var maxTimeoutId = setTimeout ( function ( ) {
for ( var i = 1 ; i < maxTimeoutId ; i ++ ) {
clearTimeout ( i ) ;
clearInterval ( i ) ;
}
} , 4 ) ; /* 4 ms is the minimum timeout as per HTML5. */
/ * N o w c l e a r a l l t h e r e q u e s t e d a n i m a t i o n f r a m e c a l l b a c k s . A g a i n , t h i s
* naively assumes the ID will increment one by one . MDN explicitly
* advises against assumptions such as this one : https : //developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame#Return_value
* /
var requestAnimationFrame = window . requestAnimationFrame
|| window . mozRequestAnimationFrame
|| window . webkitRequestAnimationFrame
|| window . msRequestAnimationFrame
|| function ( ) { } ;
var cancelAnimationFrame = window . cancelAnimationFrame
|| window . mozCancelAnimationFrame
|| window . webkitCancelAnimationFrame
|| window . msCancelAnimationFrame
|| function ( ) { } ;
var maxAnimationFrameRequestId = requestAnimationFrame ( function ( ) {
for ( i = 1 ; i < maxAnimationFrameRequestId * 2 ; i ++ ) {
cancelAnimationFrame ( i ) ;
}
/* Log requests to the original requestAnimationFrame. */
window . requestAnimationFrame = function ( callback ) {
var callbackSource = callback . toSource && callback . toSource ( ) ;
if ( callbackSource && callbackSource . indexOf ( 'Readable++ requestAnimationFrame interceptor' ) === - 1 ) {
console . log ( 'Readable++: intercepted call to requestAnimationFrame at ' + new Date ( ) ) ;
console . log ( 'Readable++: callback for requestAnimationFrame: ' + callbackSource ) ;
}
} ;
} ) ;
/ * W h i l e i n R e a d a b l e + + m o d e , d i s a b l e s o m e e l e m e n t s ' e v e n t h a n d l e r s .
* This prevents hijacking events like "scroll" and "resize" , which
* can be abused to annoy me with visual effects and whatnot , and
* "contextmenu" and "selectstart" , which are clearly defined as
* universal and inalienable human rights . Look it up at your local
* Wikipedia office .
* /
[ window , document , document . documentElement , document . body ] . forEach ( function ( elem ) {
/ * I n n o n - H T M L d o c u m e n t s , e l e m e n t s l i k e d o c u m e n t . b o d y c a n b e
* null . * /
if ( ! elem ) {
return ;
}
/ * B e c a u s e t h e w i n d o w f o r I F R A M E s i s t h e s a m e a s t h e o u t e r
* document ' s window , we need to keep track of wether we
* have toggled the event handlers . Otherwise , they might
* get disabled and re - enabled immediately after .
* /
if ( elementsWithToggledEventHandlers [ elem ] ) {
return ;
}
elementsWithToggledEventHandlers [ elem ] = true ;
/ * T o g g l e s e l e c t e d e v e n t h a n d l e r s t h a t h a v e b e e n s e t u s i n g
* "elem.oneventx = function () { … };"
* /
eventHandlerAttributesToToggle . forEach ( function ( attrib ) {
if ( elem [ 'jancss-' + attrib ] ) {
elem [ attrib ] = elem [ 'jancss-' + attrib ] ;
delete elem [ 'jancss-' + attrib ] ;
} else if ( elem [ attrib ] ) {
elem [ 'jancss-' + attrib ] = elem [ attrib ] ;
elem [ attrib ] = function ( ) { } ;
}
} ) ;
/* Toggle all event handlers that have been set using jQuery. */
if ( typeof jQuery === 'function' ) {
/* Since jQuery 1.7. */
if ( typeof jQuery . hasData === 'function' && jQuery . hasData ( elem ) ) {
/* Spotted in the wild: "jQuery._data is undefined". */
if ( typeof jQuery . _data !== 'function' ) {
return ;
}
var data = jQuery . _data ( elem ) ;
if ( data . jancssEvents ) {
data . events = data . jancssEvents ;
delete data . jancssEvents ;
jQuery . _data ( elem , data ) ;
return ;
} else if ( data . events ) {
data . jancssEvents = data . events ;
delete data . events ;
jQuery . _data ( elem , data ) ;
return ;
}
}
/* Before jQuery 1.7 and after jQuery 1.2.3 */
if ( jQuery . fn . data ) {
var $elem = jQuery ( elem ) ;
var eventsData = $elem . data ( 'events' ) ;
var jancssEventsData = $elem . data ( 'jancssEvents' ) ;
if ( jancssEventsData ) {
$elem . data ( 'events' , jancssEventsData ) ;
$elem . removeData ( 'jancssEvents' ) ;
} else if ( eventsData ) {
$elem . data ( 'jancssEvents' , eventsData ) ;
$elem . removeData ( 'events' ) ;
}
}
}
} ) ;
/ * T h e c o d e a b o v e d o e s n o t w o r k f o r e v e n t l i s t e n e r s a d d e d u s i n g
* addEventListener . Some sites listen for the "resize" event and
* then reposition elements by setting their style directly . To
* counteract this , simply delete all "style" attributes that get set
* while our style sheet is enabled . That 'll show ' em !
* /
if ( typeof MutationObserver === 'function' && ! document . jancssHasMutationObserver ) {
document . jancssHasMutationObserver = true ;
var observer = new MutationObserver ( function ( mutations ) {
if ( ourStyleSheet . disabled ) {
return ;
}
mutations . forEach ( function ( mutation ) {
if ( ! mutation . target . hasAttribute ( 'style' ) || mutation . target . id === 'xxxJanConsole' || mutation . target . xxxJanReadableAllowStyle ) {
return ;
}
console . log ( 'Readable++: removing "style" attribute set while in Readable++ mode on element ' , mutation . target ) ;
mutation . target . removeAttribute ( mutation . attributeName ) ;
} ) ;
} ) ;
observer . observe ( document , {
attributes : true ,
attributeFilter : [ 'style' ] ,
subtree : true
} ) ;
}
/ * L o a d b L a z y . j s " r e t i n a " i m a g e s . T h i s i s m o r e s p e c i f i c t h a n t h e
* generic lazy - loading attributes below , and needs special handling
* because it specifies multiple sources in one attribute .
* /
toArray ( document . querySelectorAll ( 'img.b-lazy[data-src*="|"]' ) ) . forEach ( function ( img ) {
var attribute = 'data-src' ;
var sources = img . getAttribute ( attribute ) . split ( '|' ) ;
img . src = sources . pop ( ) ;
img . removeAttribute ( attribute ) ;
} ) ;
/ * L o a d R i l o a d r " < n o s c r i p t > " i m a g e s . T h i s i s m o r e s p e c i f i c t h a n t h e
* generic lazy - loading attributes below , and needs special handling
* because it would reset the images to blank when scrolling after
* Readable ++ has been applied .
* /
toArray ( document . querySelectorAll ( 'img[data-src] + noscript' ) ) . forEach ( function ( noscript ) {
var img = noscript . previousElementSibling ;
var placeholder = img . previousElementSibling ;
if ( placeholder && placeholder . tagName . toLowerCase ( ) === 'svg' ) {
placeholder . parentNode . removeChild ( placeholder ) ;
}
img . outerHTML = noscript . textContent ;
noscript . parentNode . removeChild ( noscript ) ;
} ) ;
/* Load images that are supposed to be loaded lazily. */
[
'data-original' ,
'data-lazyload' ,
'data-lazy-src' ,
'data-full-src' ,
'data-src'
] . forEach ( function ( attribute ) {
toArray ( document . querySelectorAll ( 'img[' + attribute + ']' ) ) . forEach ( function ( img ) {
img . src = img . getAttribute ( attribute ) ;
img . removeAttribute ( attribute ) ;
} ) ;
} ) ;
/* Show controls on AUDIO and VIDEO elements. */
[ ] . forEach . call ( document . querySelectorAll ( 'audio, video' ) , function ( element ) {
element . controls = true ;
} ) ;
/ * H i d e e m p t y l i s t i t e m s t h a t a r e n o t " : e m p t y " a s p e r C S S . S o ,
* "empty" as in " containing text - less placeholders typically used
* for social sharing by showing an icon or logo or whatnot "
* ( e . g . < li > < a href = "#" > < span class = "icon-fb" > < / s p a n > < / a > < / l i > ) .
* /
toArray ( document . querySelectorAll ( 'li :empty:not(img):not(input)' ) ) . forEach ( function ( elem ) {
while ( elem . tagName && elem . tagName . toLowerCase ( ) !== 'li' ) {
elem = elem . parentNode ;
}
if ( elem . textContent . trim ( ) === '' && ! elem . querySelector ( 'img, input' ) )
{
addClass ( elem , 'jancss-emptyish' ) ;
}
} ) ;
/* Remove known-bad elements. */
toArray ( document . querySelectorAll ( elementsToRemoveSelectors . join ( ', ' ) ) ) . forEach ( function ( element ) {
element . parentNode . removeChild ( element ) ;
} ) ;
/ * T h e f i r s t t i m e t h i s b o o k m a r k l e t i s c a l l e d , a d d o u r s t y l e s h e e t a n d
* check for layout elements . * /
if ( ! ourStyleSheet ) {
( ourStyleSheet = document . createElementNS ( 'http://www.w3.org/1999/xhtml' , 'style' ) ) . id = 'jancss' ;
ourStyleSheet . innerHTML = css ;
( document . head || document . body || document . documentElement ) . appendChild ( ourStyleSheet ) ;
ourStyleSheet . disabled = true ;
/ * H i g h l i g h t m a t c h i n g d a t a t a b l e c o l u m n s o n : h o v e r . I d o n o t
* know how to do this in pure CSS without COLGROUPs . * /
function columnMouseHandler ( e ) {
if ( ! /^t[dh]$/i . test ( '' + e . target . tagName ) ) {
return ;
}
var targetCell = e . target , nthChild = targetCell . cellIndex + 1 , table = targetCell . parentNode ;
while ( table && table . tagName . toLowerCase ( ) !== 'table' ) {
table = table . parentNode ;
}
var activeColumnClassName = 'jancss-active-col' ;
toArray ( table . querySelectorAll ( 'td:nth-child(' + nthChild + ')' ) ) . forEach ( function ( cell ) {
if ( e . type === 'mouseenter' ) {
addClass ( cell , activeColumnClassName ) ;
} else {
removeClass ( cell , activeColumnClassName ) ;
}
} ) ;
}
/* Check which tables are used for data instead of layout. */
toArray ( document . querySelectorAll ( 'table' ) ) . forEach ( function ( table ) {
var isTableForData = true ;
/* Are there any nested tables? */
if ( table . querySelector ( 'table' ) ) {
var isWikipediaInfobox = ( ' ' + table . className + ' ' ) . match ( /infobox/ ) ;
if ( ! isWikipediaInfobox ) {
console . log ( 'Readable++: TABLE contains other TABLEs, so probably for layout: ' , table ) ;
isTableForData = false ;
}
}
/* Are we in quirks mode and does this table takes up most of the page height? */
else if ( document . compatMode === 'BackCompat' && document . documentElement . scrollHeight > window . innerHeight && table . scrollHeight > 3 / 4 * document . documentElement . scrollHeight ) {
console . log ( 'Readable++: TABLE seems pretty high in this document in quirks mode, so probably for layout: ' , table ) ;
isTableForData = false ;
}
/* If the table has several rows, look whether they are consistent in their number of cells. */
else if ( table . rows . length > 3 ) {
var numCellsPerRow = [ ] ;
toArray ( table . rows ) . forEach ( function ( row ) {
if ( numCellsPerRow . indexOf ( row . cells . length ) === - 1 ) {
numCellsPerRow . push ( row . cells . length ) ;
}
} ) ;
/* Are we in quirks mode and does this table have at least three rows with a different number of cells? */
if ( document . compatMode === 'BackCompat' && numCellsPerRow . length >= 3 ) {
console . log ( 'Readable++: TABLE has a lot of differing cell counts in this document in quirks mode, so probably for layout: ' , table ) ;
isTableForData = false ;
}
/* Does this table only have rows with just a single column? */
else if ( numCellsPerRow . length === 1 && numCellsPerRow [ 0 ] === 1 ) {
console . log ( 'Readable++: TABLE has only rows of one cell each, so probably for layout: ' , table ) ;
isTableForData = false ;
}
}
if ( isTableForData ) {
addClass ( table , 'jancss-probably-for-data' ) ;
table . addEventListener ( 'mouseenter' , columnMouseHandler , true ) ;
table . addEventListener ( 'mouseleave' , columnMouseHandler , true ) ;
} else {
addClass ( table , 'jancss-probably-for-layout' ) ;
}
} ) ;
/* (Re-)add some syntax highlighters' CSS if necessary. Those styles are often defined in the main CSS, so the HREF test in toggleStyles() does not match. */
if ( document . querySelector ( '.prettyprint' ) ) {
prettyPrintStyleSheet = document . createElementNS ( 'http://www.w3.org/1999/xhtml' , 'style' ) ;
prettyPrintStyleSheet . textContent = '@import url(https://janmoesen.github.io/bookmarklets/css/prettify.css)' ;
document . head . appendChild ( prettyPrintStyleSheet ) ;
} else if ( document . querySelector ( '.syntaxhighlighter' ) ) {
prettyPrintStyleSheet = document . createElementNS ( 'http://www.w3.org/1999/xhtml' , 'style' ) ;
prettyPrintStyleSheet . textContent = '@import url(https://janmoesen.github.io/bookmarklets/css/syntaxhighlighter.css)' ;
document . head . appendChild ( prettyPrintStyleSheet ) ;
} else if ( document . querySelector ( '.highlight .c, .highlight .k, .highlight .m, .highlight .s, .highlight .w' ) ) {
prettyPrintStyleSheet = document . createElementNS ( 'http://www.w3.org/1999/xhtml' , 'style' ) ;
prettyPrintStyleSheet . textContent = '@import url(https://janmoesen.github.io/bookmarklets/css/pygments.css)' ;
document . head . appendChild ( prettyPrintStyleSheet ) ;
} else if ( document . querySelector ( 'code[class*="language-"] .token.punctuation' ) ) {
prettyPrintStyleSheet = document . createElementNS ( 'http://www.w3.org/1999/xhtml' , 'style' ) ;
prettyPrintStyleSheet . textContent = '@import url(https://janmoesen.github.io/bookmarklets/css/prism.css)' ;
document . head . appendChild ( prettyPrintStyleSheet ) ;
}
/* Add some classes to structure elements that have been used for layout. */
structureElementsForLayoutSelectors . forEach ( function ( selector ) {
var xPathResult = document . evaluate ( selector , document , null , XPathResult . ORDERED _NODE _SNAPSHOT _TYPE , null ) ;
for ( var i = 0 ; i < xPathResult . snapshotLength ; i ++ ) {
var elem = xPathResult . snapshotItem ( i ) ;
addClass ( elem , 'jancss-probably-layout' ) ;
}
} ) ;
/* Add some classes to layout elements that have been used for structure. */
layoutElementsForStructureSelectors . forEach ( function ( selector ) {
toArray ( document . querySelectorAll ( selector ) ) . forEach ( function ( elem ) {
if ( elem . tagName . toLowerCase ( ) !== 'b' ) {
elem = elem . previousElementSibling ;
}
addClass ( elem , 'jancss-probably-structure' ) ;
} ) ;
} ) ;
}
/* Toggle between our readable and the page's original style sheet(s). */
function toggleStyles ( ) {
ourStyleSheet . disabled = ! ourStyleSheet . disabled ;
if ( prettyPrintStyleSheet ) {
prettyPrintStyleSheet . disabled = ourStyleSheet . disabled ;
}
allStyleSheets . forEach ( function ( styleSheet , i ) {
if ( styleSheet . ownerNode !== ourStyleSheet && ! syntaxHighlightHrefRegex . test ( styleSheet . href ) ) {
/* Remember whether this style sheet was originally disabled or not. We can't store on the CSSStyleSheet object, so use our DOM node. */
if ( ourStyleSheet [ 'jancss-originally-disabled-' + i ] === undefined ) {
ourStyleSheet [ 'jancss-originally-disabled-' + i ] = styleSheet . disabled ;
}
if ( ourStyleSheet . disabled ) {
/* Restore this style sheet's original state. */
styleSheet . disabled = ourStyleSheet [ 'jancss-originally-disabled-' + i ] ;
} else {
/* Disable this style sheet when ours is enabled. */
styleSheet . disabled = true ;
try {
/* … unless it is a pretty-print style sheet. */
if ( styleSheet . cssRules [ 0 ] && styleSheet . cssRules [ 0 ] . type === styleSheet . cssRules [ 0 ] . IMPORT _RULE && styleSheet . cssRules [ 0 ] . href ) {
styleSheet . disabled = ! styleSheet . cssRules [ 0 ] . href . match ( /^https:\/\/janmoesen\.github\.io\// ) ;
}
} catch ( e ) {
}
}
}
} ) ;
/* Process all attributes for all elements. */
all . forEach ( function ( elem , i ) {
attrs . forEach ( function ( attr , j ) {
/* Parse the attribute definition. Attributes can be restricted to certain elements, e.g. "table@width". */
if ( ! ( matches = attr . match ( /([^@]+)@([^@]+)/ ) ) || ( elem . tagName && elem . tagName . toLowerCase ( ) == matches [ 1 ] ) ) {
attr = matches ? matches [ 2 ] : attr ;
var names = { enabled : attr , disabled : 'jancss-' + attr } ;
if ( elem . hasAttribute ( names . enabled ) ) {
elem . setAttribute ( names . disabled , elem . getAttribute ( names . enabled ) ) ;
elem . removeAttribute ( names . enabled ) ;
} else if ( elem . hasAttribute ( names . disabled ) ) {
elem . setAttribute ( names . enabled , elem . getAttribute ( names . disabled ) ) ;
elem . removeAttribute ( names . disabled ) ;
}
}
} ) ;
} ) ;
/* Restore the inline styles for certain code highlighters. */
var disabledStyleAttr = 'jancss-style' ;
toArray ( document . querySelectorAll ( '.wp_syntax [' + disabledStyleAttr + ']' ) ) . forEach ( function ( elem ) {
elem . setAttribute ( 'style' , elem . getAttribute ( disabledStyleAttr ) ) ;
elem . removeAttribute ( disabledStyleAttr ) ;
} ) ;
}
/* Find the first thing that looks like the start of the actual content so we can scroll to it after applying our CSS. */
function findContentElement ( ) {
/* When there seems to be a link to an in-page anchor (e.g. foo.html#mainContent), try to get the linked element. */
if ( location . hash ) {
var inPageAnchor , inPageAnchorSelectors = [
'a[name="' + location . hash . substring ( 1 ) + '"]' ,
location . hash . replace ( /\./g , '\\.' )
] ;
for ( var i = 0 ; i < inPageAnchorSelectors . length ; i ++ ) {
try {
if ( ( inPageAnchor = document . querySelector ( inPageAnchorSelectors [ i ] ) ) ) {
console . log ( 'Readable++: found in-page anchor based on location.hash: ' , inPageAnchor ) ;
return inPageAnchor ;
}
} catch ( e ) {
}
}
}
/* Check for headers whose text appears in the page title. */
var headerInPageTitle ;
var charactersToIgnore = / [ ^ A - Z a - z \ x A A \ x B 5 \ x B A \ x C 0 - \ x D 6 \ x D 8 - \ x F 6 \ x F 8 - \ u 0 2 C 1 \ u 0 2 C 6 - \ u 0 2 D 1 \ u 0 2 E 0 - \ u 0 2 E 4 \ u 0 2 E C \ u 0 2 E E \ u 0 3 7 0 - \ u 0 3 7 4 \ u 0 3 7 6 \ u 0 3 7 7 \ u 0 3 7 A - \ u 0 3 7 D \ u 0 3 7 F \ u 0 3 8 6 \ u 0 3 8 8 - \ u 0 3 8 A \ u 0 3 8 C \ u 0 3 8 E - \ u 0 3 A 1 \ u 0 3 A 3 - \ u 0 3 F 5 \ u 0 3 F 7 - \ u 0 4 8 1 \ u 0 4 8 A - \ u 0 5 2 F \ u 0 5 3 1 - \ u 0 5 5 6 \ u 0 5 5 9 \ u 0 5 6 1 - \ u 0 5 8 7 \ u 0 5 D 0 - \ u 0 5 E A \ u 0 5 F 0 - \ u 0 5 F 2 \ u 0 6 2 0 - \ u 0 6 4 A \ u 0 6 6 E \ u 0 6 6 F \ u 0 6 7 1 - \ u 0 6 D 3 \ u 0 6 D 5 \ u 0 6 E 5 \ u 0 6 E 6 \ u 0 6 E E \ u 0 6 E F \ u 0 6 F A - \ u 0 6 F C \ u 0 6 F F \ u 0 7 1 0 \ u 0 7 1 2 - \ u 0 7 2 F \ u 0 7 4 D - \ u 0 7 A 5 \ u 0 7 B 1 \ u 0 7 C A - \ u 0 7 E A \ u 0 7 F 4 \ u 0 7 F 5 \ u 0 7 F A \ u 0 8 0 0 - \ u 0 8 1 5 \ u 0 8 1 A \ u 0 8 2 4 \ u 0 8 2 8 \ u 0 8 4 0 - \ u 0 8 5 8 \ u 0 8 A 0 - \ u 0 8 B 4 \ u 0 9 0 4 - \ u 0 9 3 9 \ u 0 9 3 D \ u 0 9 5 0 \ u 0 9 5 8 - \ u 0 9 6 1 \ u 0 9 7 1 - \ u 0 9 8 0 \ u 0 9 8 5 - \ u 0 9 8 C \ u 0 9 8 F \ u 0 9 9 0 \ u 0 9 9 3 - \ u 0 9 A 8 \ u 0 9 A A - \ u 0 9 B 0 \ u 0 9 B 2 \ u 0 9 B 6 - \ u 0 9 B 9 \ u 0 9 B D \ u 0 9 C E \ u 0 9 D C \ u 0 9 D D \ u 0 9 D F - \ u 0 9 E 1 \ u 0 9 F 0 \ u 0 9 F 1 \ u 0 A 0 5 - \ u 0 A 0 A \ u 0 A 0 F \ u 0 A 1 0 \ u 0 A 1 3 - \ u 0 A 2 8 \ u 0 A 2 A - \ u 0 A 3 0 \ u 0 A 3 2 \ u 0 A 3 3 \ u 0 A 3 5 \ u 0 A 3 6 \ u 0 A 3 8 \ u 0 A 3 9 \ u 0 A 5 9 - \ u 0 A 5 C \ u 0 A 5 E \ u 0 A 7 2 - \ u 0 A 7 4 \ u 0 A 8 5 - \ u 0 A 8 D \ u 0 A 8 F - \ u 0 A 9 1 \ u 0 A 9 3 - \ u 0 A A 8 \ u 0 A A A - \ u 0 A B 0 \ u 0 A B 2 \ u 0 A B 3 \ u 0 A B 5 - \ u 0 A B 9 \ u 0 A B D \ u 0 A D 0 \ u 0 A E 0 \ u 0 A E 1 \ u 0 A F 9 \ u 0 B 0 5 - \ u 0 B 0 C \ u 0 B 0 F \ u 0 B 1 0 \ u 0 B 1 3 - \ u 0 B 2 8 \ u 0 B 2 A - \ u 0 B 3 0 \ u 0 B 3 2 \ u 0 B 3 3 \ u 0 B 3 5 - \ u 0 B 3 9 \ u 0 B 3 D \ u 0 B 5 C \ u 0 B 5 D \ u 0 B 5 F - \ u 0 B 6 1 \ u 0 B 7 1 \ u 0 B 8 3 \ u 0 B 8 5 - \ u 0 B 8 A \ u 0 B 8 E - \ u 0 B 9 0 \ u 0 B 9 2 - \ u 0 B 9 5 \ u 0 B 9 9 \ u 0 B 9 A \ u 0 B 9 C \ u 0 B 9 E \ u 0 B 9 F \ u 0 B A 3 \ u 0 B A 4 \ u 0 B A 8 - \ u 0 B A A \ u 0 B A E - \ u 0 B B 9 \ u 0 B D 0 \ u 0 C 0 5 - \ u 0 C 0 C \ u 0 C 0 E - \ u 0 C 1 0 \ u 0 C 1 2 - \ u 0 C 2 8 \ u 0 C 2 A - \ u 0 C 3 9 \ u 0 C 3 D \ u 0 C 5 8 - \ u 0 C 5 A \ u 0 C 6 0 \ u 0 C 6 1 \ u 0 C 8 5 - \ u 0 C 8 C \ u 0 C 8 E - \ u 0 C 9 0 \ u 0 C 9 2 - \ u 0 C A 8 \ u 0 C A A - \ u 0 C B 3 \ u 0 C B 5 - \ u 0 C B 9 \ u 0 C B D \ u 0 C D E \ u 0 C E 0 \ u 0 C E 1 \ u 0 C F 1 \ u 0 C F 2 \ u 0 D 0 5 - \ u 0 D 0 C \ u 0 D 0 E - \ u 0 D 1 0 \ u 0 D 1 2 - \ u 0 D 3 A \ u 0 D 3 D \ u 0 D 4 E \ u 0 D 5 F - \ u 0 D 6 1 \ u 0 D 7 A - \ u 0 D 7 F \ u 0 D 8 5 - \ u 0 D 9 6 \ u 0 D 9 A - \ u 0 D B 1 \ u 0 D B 3 - \ u 0 D B B \ u 0 D B D \ u 0 D C 0 - \ u 0 D C 6 \ u 0 E 0 1 - \ u 0 E 3 0 \ u 0 E 3 2 \ u 0 E 3 3 \ u 0 E 4 0 - \ u 0 E 4 6 \ u 0 E 8 1 \ u 0 E 8 2 \ u 0 E 8 4 \ u 0 E 8 7 \ u 0 E 8 8 \ u 0 E 8 A \ u 0 E 8 D \ u 0 E 9 4 - \ u 0 E 9 7 \ u 0 E 9 9 - \ u 0 E 9 F \ u 0 E A 1 - \ u 0 E A 3 \ u 0 E A 5 \ u 0 E A 7 \ u 0 E A A \ u 0 E A B \ u 0 E A D - \ u 0 E B 0 \ u 0 E B 2 \ u 0 E B 3 \ u 0 E B D \ u 0 E C 0 - \ u 0 E C 4 \ u 0 E C 6 \ u 0 E D C - \ u 0 E D F \ u 0 F 0 0 \ u 0 F 4 0 - \ u 0 F 4 7 \ u 0 F 4 9 - \ u 0 F 6 C \ u 0 F 8 8 - \ u 0 F 8 C \ u 1 0 0 0 - \ u 1 0 2 A \ u 1 0 3 F \ u 1 0 5 0 - \ u 1 0 5 5 \ u 1 0 5 A - \ u 1 0 5 D \ u 1 0 6 1 \ u 1 0 6 5 \ u 1 0 6 6 \ u 1 0 6 E - \ u 1 0 7 0 \ u 1 0 7 5 - \ u 1 0 8 1 \ u 1 0 8 E \ u 1 0 A 0 - \ u 1 0 C 5 \ u 1 0 C 7 \ u 1 0 C D \ u 1 0 D 0 - \ u 1 0 F A \ u 1 0 F C - \ u 1 2 4 8 \ u 1 2 4 A - \ u 1 2 4 D \ u 1 2 5 0 - \ u 1 2 5 6 \ u 1 2 5 8 \ u 1 2 5 A - \ u 1 2 5 D \ u 1 2 6 0 - \ u 1 2 8 8 \ u 1 2 8 A - \ u 1 2 8 D \ u 1 2 9 0 - \ u 1 2 B 0 \ u 1 2 B 2 - \ u 1 2 B 5 \ u 1 2 B 8 - \ u 1 2 B E \ u 1 2 C 0 \ u 1 2 C 2 - \ u 1 2 C 5 \ u 1 2 C 8 - \ u 1 2 D 6 \ u 1 2 D 8 - \ u 1 3 1 0 \ u 1 3 1 2 - \ u 1 3 1 5 \ u 1 3 1 8 - \ u 1 3 5 A \ u 1 3 8 0 - \ u 1 3 8 F \ u 1 3 A 0 - \ u 1 3 F 5 \ u 1 3 F 8 - \ u 1 3 F D \ u 1 4 0 1 - \ u 1 6 6 C \ u 1 6 6 F - \ u 1 6 7 F \ u 1 6 8 1 - \ u 1 6 9 A \ u 1 6 A 0 - \ u 1 6 E A \ u 1 6 F 1 - \ u 1 6 F 8 \ u 1 7 0 0 - \ u 1 7 0 C \ u 1 7 0 E - \ u 1 7 1 1 \ u 1 7 2 0 - \ u 1 7 3 1 \ u 1 7 4 0 - \ u 1 7 5 1 \ u 1 7 6 0 - \ u 1 7 6 C \ u 1 7 6 E - \ u 1 7 7 0 \ u 1 7 8 0 - \ u 1 7 B 3 \ u 1 7 D 7 \ u 1 7 D C \ u 1 8 2 0 - \ u 1 8 7 7 \ u 1 8 8 0 - \ u 1 8 A 8 \ u 1 8 A A \ u 1 8 B 0 - \ u 1 8 F 5 \ u 1 9 0 0 - \ u 1 9 1 E \ u 1 9 5 0 - \ u 1 9 6 D \ u 1 9 7 0 - \ u 1 9 7 4 \ u 1 9 8 0 - \ u 1 9 A B \ u 1 9 B 0 - \ u 1 9 C 9 \ u 1 A 0 0 - \ u 1 A 1 6 \ u 1 A 2 0 - \ u 1 A 5 4 \ u 1 A A 7 \ u 1 B 0 5 - \ u 1 B 3 3 \ u 1 B 4 5 - \ u 1 B 4 B \ u 1 B 8 3 - \ u 1 B A 0 \ u 1 B A E \ u 1 B A F \ u 1 B B A - \ u 1 B E 5 \ u 1 C 0 0 - \ u 1 C 2 3 \ u 1 C 4 D - \ u 1 C 4 F \ u 1 C 5 A - \ u 1 C 7 D \ u 1 C E 9 - \ u 1 C E C \ u 1 C E E - \ u 1 C F 1 \ u 1 C F 5 \ u 1 C F 6 \ u 1 D 0 0 - \ u 1 D B F \ u 1 E 0 0 - \ u 1 F 1 5 \ u 1 F 1 8 - \ u 1 F 1 D \ u 1 F 2 0 - \ u 1 F 4 5 \ u 1 F 4 8 - \ u 1 F 4 D \ u 1 F 5 0 - \ u 1 F 5 7 \ u 1 F 5 9 \ u 1 F 5 B \ u 1 F 5 D \ u 1 F 5 F - \ u 1 F 7 D \ u 1 F 8 0 - \ u 1 F B 4 \ u 1 F B 6 - \ u 1 F B C \ u 1 F B E \ u 1 F C 2 - \ u 1 F C 4 \ u 1 F C 6 - \ u 1 F C C \ u 1 F D 0 - \ u 1 F D 3 \ u 1 F D 6 - \ u 1 F D B \ u 1 F E 0 - \ u 1 F E C \ u 1 F F 2 - \ u 1 F F 4 \ u 1 F F 6 - \ u 1 F F C \ u 2 0 7 1 \ u 2 0 7 F \ u 2 0 9 0 - \ u 2 0 9 C \ u 2 1 0 2 \ u 2 1 0 7 \ u 2 1 0 A - \ u 2 1 1 3 \ u 2 1 1 5 \ u 2 1 1 9 - \ u 2 1 1 D \ u 2 1 2 4 \ u 2 1 2 6 \ u 2 1 2 8 \ u 2 1 2 A - \ u 2 1 2 D \ u 2 1 2 F - \ u 2 1 3 9 \ u 2 1 3 C - \ u 2 1 3 F \ u 2 1 4 5 - \ u 2 1 4 9 \ u 2 1 4 E \ u 2 1 8 3 \ u 2 1 8 4 \ u 2 C 0 0 - \ u 2 C 2 E \ u 2 C 3 0 - \ u 2 C 5 E \ u 2 C 6 0 - \ u 2 C E 4 \ u 2 C E B - \ u 2 C E E \ u 2 C F 2 \ u 2 C F 3 \ u 2 D 0 0 - \ u 2 D 2 5 \ u 2 D 2 7 \ u 2 D 2 D \ u 2 D 3 0 - \ u 2 D 6 7 \ u 2 D 6 F \ u 2 D 8 0 - \ u 2 D 9 6 \ u 2 D A 0 - \ u 2 D A 6 \ u 2 D A 8 - \ u 2 D A E \ u 2 D B 0 - \ u 2 D B 6 \ u 2 D B 8 - \ u 2 D B E \ u 2 D C 0 - \ u 2 D C 6 \ u 2 D C 8 - \ u 2 D C E \ u 2 D D 0 - \ u 2 D D 6 \ u 2 D D 8 - \ u 2 D D E \ u 2 E 2 F \ u 3 0 0 5 \ u 3 0 0 6 \ u 3 0 3 1 - \ u 3 0 3 5 \ u 3 0 3 B \ u 3 0 3 C \ u 3 0 4 1 - \ u 3 0 9 6 \ u 3 0 9 D - \ u 3 0 9 F \ u 3 0 A 1 - \ u 3 0 F A \ u 3 0 F C - \ u 3 0 F F \ u 3 1 0 5 - \ u 3 1 2 D \ u 3 1 3 1 - \ u 3 1 8 E \ u 3 1 A 0 - \ u 3 1 B A \ u 3 1 F 0 - \ u 3 1 F F \ u 3 4 0 0 - \ u 4 D B 5 \ u 4 E 0 0 - \ u 9 F D 5 \ u A 0 0 0 - \ u A 4 8 C \ u A 4 D 0 - \ u A 4 F D \ u A 5 0 0 - \ u A 6 0 C \ u A 6 1 0 - \ u A 6 1 F \ u A 6 2 A \ u A 6 2 B \ u A 6 4 0 - \ u A 6 6 E \ u A 6 7 F - \ u A 6 9 D \ u A 6 A 0 - \ u A 6 E 5 \ u A 7 1 7 - \ u A 7 1 F \ u A 7 2 2 - \ u A 7 8 8 \ u A 7 8 B - \ u A 7 A D \ u A 7 B 0 - \ u A 7 B 7 \ u A 7 F 7 - \ u A 8 0 1 \ u A 8 0 3 - \ u A 8 0 5 \ u A 8 0 7 - \ u A 8 0 A \ u A 8 0 C - \ u A 8 2 2 \ u A 8 4 0 - \ u A 8 7 3 \ u A 8 8 2 - \ u A 8 B 3 \ u A 8 F 2 - \ u A 8 F 7 \ u A 8 F B \ u A 8 F D \ u A 9 0 A - \ u A 9 2 5 \ u A 9 3 0 - \ u A 9 4 6 \ u A 9 6 0 - \ u A 9 7 C \ u A 9 8 4 - \ u A 9 B 2 \ u A 9 C F \ u A 9 E 0 - \ u A 9 E 4 \ u A 9 E 6 - \ u A 9 E F \ u A 9 F A - \ u A 9 F E \ u A A 0 0 - \ u A A 2 8 \ u A A 4 0 - \ u A A 4 2 \ u A A 4 4 - \ u A A 4 B \ u A A 6 0 - \ u A A 7 6 \ u A A 7 A \ u A A 7 E - \ u A A A F \ u A A B 1 \ u A A B 5 \ u A A B 6 \ u A A B 9 - \ u A A B D \ u A A C 0 \ u A A C 2 \ u A A D B - \ u A A D D \ u A A E 0 - \ u A A E A \ u A A F 2 - \ u A A F 4 \ u A B 0 1 - \ u A B 0 6 \ u A B 0 9 - \ u A B 0 E \ u A B 1 1 - \ u A B 1 6 \ u A B 2 0 - \ u A B 2 6 \ u A B 2 8 - \ u A B 2 E \ u A B 3 0 - \ u A B 5 A \ u A B 5 C - \ u A B 6 5 \ u A B 7 0 - \ u A B E 2 \ u A C 0 0 - \ u D 7 A 3 \ u D 7 B 0 - \ u D 7 C 6 \ u D 7 C B - \ u D 7 F B \ u F 9 0 0 - \ u F A 6 D \ u F A 7 0 - \ u F A D 9 \ u F B 0 0 - \ u F B 0 6 \ u F B 1 3 - \ u F B 1 7 \ u F B 1 D \ u F B 1 F - \ u F B 2 8 \ u F B 2 A - \ u F B 3 6 \ u F B 3 8 - \ u F B 3 C \ u F B 3 E \ u F B 4 0 \ u F B 4 1 \ u F B 4 3 \ u F B 4 4 \ u F B 4 6 -
function normalizeText ( str ) {
return str
. replace ( /\xAD/g , '' )
. replace ( charactersToIgnore , ' ' )
. trim ( ) ;
}
var metaTitleElement = document . querySelector ( 'meta[property="og:title"], meta[property="twitter:title"], meta[name="title"]' ) ;
var normalizedMetaTitle = metaTitleElement && normalizeText ( metaTitleElement . content ) ;
var normalizedPageTitle = normalizeText ( document . title ) ;
headerSelectors . forEach ( function ( selector ) {
toArray ( document . querySelectorAll ( selector ) ) . forEach ( function ( element ) {
var normalizedText = normalizeText ( element . textContent ) ;
/* Make sure the element has text. */
if ( ! normalizedText . length ) {
return ;
}
/* Make sure the element is visible. */
var boundingRect = element . getBoundingClientRect ( ) ;
if ( ! boundingRect . width || ! boundingRect . height ) {
return ;
}
/ * M a k e s u r e t h e e l e m e n t i s " a b o v e t h e f o l d " ( o r n e a r i t ) . S o m e s i t e s u s e
* bigger headings for a footer section than for the actual content , but in
* general , the real header should be visible within the first screenful . * /
if ( boundingRect . top + window . scrollY > window . innerHeight * 1.5 ) {
return ;
}
/* Make sure the title can contain the element's text. */
if (
normalizedPageTitle . length < normalizedText . length
&& ( ! metaTitleElement || normalizedMetaTitle . length < normalizedText . length )
) {
return ;
}
/* See if the element's text appears in the page title. */
var substringIndex = normalizedPageTitle . indexOf ( normalizedText ) ;
if ( substringIndex === - 1 && metaTitleElement ) {
substringIndex = normalizedMetaTitle . indexOf ( normalizedText ) ;
}
if ( substringIndex === - 1 ) {
return ;
}
/* Make sure the element is not contained in an ASIDE or a sidebar (e.g. in a list of articles). */
if ( typeof element . closest === 'function' && element . closest ( ancestorsForHeadersToIgnoreSelectors . join ( ', ' ) ) ) {
return ;
}
headerInPageTitle = element ;
} ) ;
} ) ;
if ( headerInPageTitle ) {
console . log ( 'Readable++: found suitable header element whose text appears in the page title: ' , headerInPageTitle ) ;
return headerInPageTitle ;
}
/* Fall back to common content element selectors. */
for ( var i = 0 ; i < contentSelectors . length ; i ++ ) {
try {
var element = document . querySelector ( contentSelectors [ i ] ) ;
/* Make sure the element was either an anchor or something "visible". */
if ( element && ( element . tagName . toLowerCase ( ) === 'a' || element . offsetWidth || element . offsetHeight ) ) {
console . log ( 'Readable++: found matching selector for content element: ' + contentSelectors [ i ] + '\nElement: ' , element ) ;
return element ;
}
}
catch ( e ) {
console . log ( 'Readable++: bad selector for content element: ' + contentSelectors [ i ] + '\nException: ' + e ) ;
}
}
}
/* Check if there is an element that we should scroll into view so we can immediately start reading. */
var contentElement , shouldScrollContentIntoView = false ;
var selection = document . getSelection && document . getSelection ( ) ;
if ( selection && selection . anchorNode && ( selection + '' ) . length ) {
/* If the user has created a selection, scroll the element containing that selection into view. (I often triple-click a paragraph to select it before reading.) */
shouldScrollContentIntoView = true ;
contentElement = selection . anchorNode ;
while ( contentElement . nodeType !== contentElement . ELEMENT _NODE && contentElement . parentNode ) {
contentElement = contentElement . parentNode ;
}
console . log ( 'Readable++: found selected element to scroll into view: ' , contentElement ) ;
} else if ( ourStyleSheet . disabled && ( contentElement = findContentElement ( ) ) ) {
/* When switching from the original style sheet to ours, scroll to the start of the content, unless the user had scrolled already. */
var tmpElement = contentElement , contentTop = 0 ;
do {
contentTop += tmpElement . offsetTop ;
} while ( ( tmpElement = tmpElement . offsetParent ) ) ;
shouldScrollContentIntoView = ! window . scrollY || Math . abs ( window . scrollY - contentTop ) < 20 ;
}
/* Finally, toggle the style sheets. */
toggleStyles ( ) ;
/* Scroll to the start of the content if we found it and have not scrolled yet. */
shouldScrollContentIntoView && contentElements . push ( contentElement ) ;
/* Recurse for frames and iframes. */
try {
toArray ( document . querySelectorAll ( 'frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]' ) ) . forEach ( function ( elem ) {
execute ( elem . contentDocument ) ;
} ) ;
} catch ( e ) {
/* Catch exceptions for out-of-domain access, but do not do anything with them. */
}
} ) ( document ) ;
/ * W h e n t h e r e a r e I F R A M E s e t c . w i t h t h e i r o w n m a i n c o n t e n t e l e m e n t ,
* scrolling them into view as soon as they are found would override the
* main document ’ s scroll position . By scrolling the nested elements into
* view * first * , and the main document ’ s main content element * last * ,
* things behave more as expected .
* /
var contentElement ;
while ( ( contentElement = contentElements . pop ( ) ) ) {
contentElement . scrollIntoView ( {
behavior : 'smooth' ,
block : 'start' ,
inline : 'start'
} ) ;
} ;
} ) ( ) ;