/* Printliminator v4.0.4 * https://github.com/CSS-Tricks/The-Printliminator */ /*jshint expr:false */ /*globals thePrintliminatorVars */ ;( function() { 'use strict'; var pl = window.thePrintliminator = { version : '4.0.4', // preprocess is used to echo in settings from options.json css : { hilite : '_printliminator_highlight', fullWidth : '_printliminator_full_width', hidden : '_printliminator_hidden', // class name added to body when print styles applied (used in printliminator.css) stylized : '_printliminator_stylized', messages : '_printliminator_messages', noSelection: '_printliminator_no_selection', // class on body while dragging // exposed in main document stylesheet : '_printliminator_styles', // stylesheet ID wrap : '_printliminator_wrap', controls : '_printliminator_controls', drag : '_printliminator_drag_icon', dragActive : '_printliminator_drag_active', // inside bookmarklet iframe icon : 'icon', noGraphics : 'no_graphics', stylize : 'stylize', print : 'print', close : 'close', undo : 'undo', busy : 'busy', keyboard : 'keyboard', toggle : 'toggle' }, keys : { parent1 : 33, // pageUp parent2 : 38, // up arrow child1 : 34, // pageDown child2 : 40, // down arrow nextSib : 39, // right arrow prevSib : 37, // left arrow hide : 13, // enter undo : 8, // backspace fontUp : 107, // Numpad + fontDown : 109, // Numpad - fontReset : 106, // Numpad * print : 44, // PrtScn (keyup only) abort : 27, // Esc // use event key below opposite : 'altKey', // alt + click fullWidth : 'shiftKey' // shift + click }, // elements hidden when "remove graphics" is selected noGraphics : 'img, iframe:not(._printliminator_controls), object, embed, audio, video, input[type=image], svg', // elements to ignore while traversing ignoredElm : /^(br|meta|style|link|script)$/i, // iframe height with keyboard open/closed keyboardOpen : 615, keyboardClosed : 220, // Bookmarklet popup - dragging parameters stored here drag : { el : null, pos : [ 0, 0 ], elm : [ 0, 0 ] }, init : function() { var el, body = document.body; // need a global variable to store history & flags if ( typeof window.thePrintliminatorVars === 'undefined' ) { // use object separate from pl, otherwise these values get lost // upon javascript injection a second time (after uses presses Esc) window.thePrintliminatorVars = { init : true, history : [], messageCache : [], flags : {} }; pl.addStyles(); } pl.addControls(); // highlighting elements & keyboard binding pl.addEvent( body, 'click', pl.bodyClick ); pl.addEvent( body, 'mouseover', pl.bodyMouseover ); pl.addEvent( body, 'mouseout', pl.removeHighlight ); pl.addEvent( document, 'keyup', pl.bodyKeyUp ); pl.addEvent( document, 'keydown', pl.bodyKeyDown ); // drag pl.addEvent( document, 'mouseup', pl.docMouseUp ); pl.addEvent( document, 'mousemove', pl.docMouseMove ); }, // delegated event click bodyClick : function( event ) { event.preventDefault(); event.stopImmediatePropagation(); if ( event.target.nodeName !== 'BODY' && !pl.hasClass( event.target, pl.css.messages ) ) { var done, sel, opposite = false, msg = pl.messages, hilite = document.body.querySelector( '.' + pl.css.hilite ); // Make 100% width & zero margins (set by css) // Shift + click if ( event[ pl.keys.fullWidth ] ) { if ( !pl.hasClass( hilite, pl.css.fullWidth ) ) { pl.addClass( hilite, pl.css.fullWidth ); thePrintliminatorVars.history.push( function() { pl.removeClass( hilite, pl.css.fullWidth ); }); } } else { // show opposite // Alt + click if ( event[ pl.keys.opposite ] ) { done = pl.getOpposite( hilite ); sel = done.length; if ( sel ) { opposite = true; } else { // nothing left to remove return; } } else { // hide clicked element done = [ hilite ]; } pl.hide( done ); thePrintliminatorVars.history.push( done ); if ( opposite ) { // messages will get hidden if alt+click used // this is easier than trying to detect it pl.removeClass( document.querySelector( 'ul.' + pl.css.messages ), pl.css.hidden ); } } // remove any text selection pl.clearSelection(); } }, bodyMouseover : function( event ) { if ( !pl.hasClass( event.target, pl.css.controls ) ) { pl.addClass( event.target, pl.css.hilite ); } // make sure main window has focus window.focus(); }, bodyKeyUp : function( event ) { event.preventDefault(); switch ( event.which ) { // PrntScrn only works on keyup case pl.keys.print: pl.print(); break; } }, bodyKeyDown : function( event ) { event.preventDefault(); var n, suffix, elm, els, isBody, body = document.body, msg = pl.messages, el = body.querySelector( '.' + pl.css.hilite ), hidden = pl.css.hidden, highlight = pl.css.hilite; if ( el ) { isBody = el.nodeName === 'BODY'; switch ( event.which ) { case pl.keys.parent1 : // pageUp case pl.keys.parent2 : // up arrow els = el.parentNode; if ( !isBody && els ) { pl.removeClass( el, highlight ); pl.addClass( els, highlight ); } break; case pl.keys.child1 : // pageDown case pl.keys.child2 : // down arrow elm = pl.getFirstChild( el ); if ( elm ) { pl.removeClass( el, highlight ); // first visible child element pl.addClass( elm, highlight ); } break; case pl.keys.nextSib : // right arrow (siblings) elm = pl.getNext( el ); if ( !isBody && elm ) { pl.removeClass( el, highlight ); pl.addClass( elm, highlight ); } break; case pl.keys.prevSib : // left arrow (siblings) elm = pl.getPrev( el ); if ( !isBody && elm ) { pl.removeClass( el, highlight ); pl.addClass( elm, highlight ); } break; case pl.keys.hide : // enter if ( !isBody ) { pl.addClass( el, hidden ); pl.addClass( el.parentNode, highlight ); thePrintliminatorVars.history.push( el ); } break; } } n = window.getComputedStyle( body, null ).getPropertyValue( 'font-size' ); suffix = n.match( /[a-z]+/i )[0]; switch ( event.which ) { case pl.keys.fontUp : // Numpad + = Increase font size body.style.fontSize = ( parseFloat( n ) + 1 ) + suffix; break; case pl.keys.fontDown : // Numpad - = Decrease font size body.style.fontSize = ( parseFloat( n ) - 1 ) + suffix; break; case pl.keys.fontReset : // Numpad * = reset font-size body.style.fontSize = ''; break; case pl.keys.undo : // backspace pl.undo(); break; case pl.keys.abort : // Esc pl.abort(); break; } }, filterElements : function( elm ) { return elm && // element node elm.nodeType === 1 && // not an ignored element !pl.ignoredElm.test( elm.nodeName ) && // not controls !pl.hasClass( elm, pl.css.controls ) && // not hidden !( pl.hasClass( elm, pl.css.hidden ) || elm.style.display === 'none' ); }, getOpposite : function( el ) { var sibs, done = []; // method: start from highlighted element // get siblings & hide them; then go to parent, get siblings & hide them... // rinse & repeat until we hit the body element while ( el.nodeName !== 'BODY' ) { sibs = pl.getSiblings( el ); done = done.concat( sibs ); el = el.parentNode; } return done; }, getFirstChild : function( el ) { var children = Array.prototype.filter.call( el.children, pl.filterElements ); return children.length ? children[0] : null; }, // modified from // https://plainjs.com/javascript/traversing/get-siblings-of-an-element-40/ getSiblings : function ( el ) { var siblings = [], sibling = el.parentNode.firstChild; for ( ; sibling; sibling = sibling.nextSibling ) { if ( sibling !== el && pl.filterElements( sibling ) ) { siblings.push( sibling ); } } return siblings; }, // modified from // https://plainjs.com/javascript/traversing/get-siblings-of-an-element-40/ getNext : function ( el ) { while ( el = el.nextSibling ) { // jshint ignore:line if ( el && pl.filterElements( el ) ) { return el; } } return null; }, // modified from // https://plainjs.com/javascript/traversing/get-siblings-of-an-element-40/ getPrev : function( el ) { while ( el = el.previousSibling ) { // jshint ignore:line if ( el && pl.filterElements( el ) ) { return el; } } return null; }, removeHighlight : function() { // remove all highlight class names, just in case var indx, // include body as it might also get the highlight class hilite = document.querySelectorAll( '.' + pl.css.hilite ), len = hilite.length; for ( indx = 0; indx < len; indx++ ) { pl.removeClass( hilite[ indx ], pl.css.hilite ); } }, removeGraphics : function( event, body ) { if ( !thePrintliminatorVars.flags.removeGraphics ) { // for testing body = body || document.body; var indx, bkgd, bkgds = [], done = body.querySelectorAll( pl.noGraphics ), items = body.querySelectorAll( '*:not(.' + pl.css.controls + ')' ), len = items.length; for ( indx = 0; indx < len; indx++ ) { bkgd = window.getComputedStyle( items[ indx ] ).getPropertyValue( 'background-image' ); if ( bkgd && bkgd !== 'none' ) { bkgds.push( [ items[ indx ], bkgd ] ); items[ indx ].style.backgroundImage = 'none'; } } pl.removeHighlight(); pl.hide( done ); thePrintliminatorVars.flags.removeGraphics = true; thePrintliminatorVars.history.push( function() { thePrintliminatorVars.flags.removeGraphics = false; pl.show( done ); len = bkgds.length; for ( indx = 0; indx < len; indx++ ) { bkgds[ indx ][ 0 ].style.backgroundImage = bkgds[ indx ][ 1 ]; } }); } }, // Add print style stylize : function() { if ( !thePrintliminatorVars.flags.stylize ) { var indx, inline = [], body = document.body, links = document.querySelectorAll( 'link[rel="stylesheet"]' ), visibleElms = document.querySelectorAll( 'body *:not(.' + pl.css.hidden + '):not(.' + pl.css.controls + ')' ), len = links.length; for ( indx = 0; indx < len; indx++ ) { if ( links[ indx ].id !== pl.css.stylesheet ) { links[ indx ].disabled = true; } } // cache and remove inline styles Array.prototype.filter.call( visibleElms, function( elm ) { var style = elm.getAttribute( 'style' ); if ( style !== null ) { elm.removeAttribute( 'style' ); inline.push({ el: elm, style: style }); } }); pl.addClass( body, pl.css.stylized ); pl.removeHighlight(); thePrintliminatorVars.flags.stylize = true; thePrintliminatorVars.history.push( function() { thePrintliminatorVars.flags.stylize = false; pl.removeClass( body, pl.css.stylized ); var indx, len = links.length; for ( indx = 0; indx < len; indx++ ) { links[ indx ].disabled = false; } len = inline.length; for ( indx = 0; indx < len; indx++ ) { inline[ indx ].el.setAttribute( 'style', inline[ indx ].style ); } }); } }, print : function() { var frame = document.body.querySelector( 'iframe.' + pl.css.controls ).contentWindow.document; pl.addClass( frame.querySelector( 'li.' + pl.css.print ), pl.css.busy ); pl.removeHighlight(); // use setTimeout to allow class to render setTimeout( function() { window.print(); pl.busy( function() { pl.removeClass( frame.querySelector( 'li.' + pl.css.print ), pl.css.busy ); }); }, 10); }, busy : function( callback ) { if ( document.readyState !== 'complete' ) { var loopy = function( i ) { setTimeout(function () { // ready state is delayed when a file on the page is not found if ( document.readyState === 'complete' || i === 1 ) { callback(); i = 0; } if ( --i > 0 ) { loopy(i); } }, 1000); }; // repeat 20 times (20 seconds), then just remove the busy class loopy( 20 ); } else { callback(); } }, // Undo undo : function() { var last = thePrintliminatorVars.history.pop(); if ( last ) { pl.removeHighlight(); if ( typeof last !== 'function' ) { pl.show( last ); } else { last.call(); } } }, abort : function() { var body = document.body; pl.removeHighlight(); pl.removeClass( body, pl.css.enabled ); pl.removeEvent( body, 'click', pl.bodyClick ); pl.removeEvent( body, 'mouseover', pl.bodyMouseover ); pl.removeEvent( body, 'mouseout', pl.removeHighlight ); pl.removeEvent( document, 'keyup', pl.bodyKeyUp ); pl.removeEvent( document, 'keydown', pl.bodyKeyDown ); // drag pl.removeEvent( document, 'mouseup', pl.docMouseUp ); pl.removeEvent( document, 'mousemove', pl.docMouseMove ); body.removeChild( document.querySelector( '.' + pl.css.wrap ) ); }, addStyles : function() { var el, body = document.body, // programmically added stylesheets styles = '@media print, screen { body._printliminator_stylized { margin: 0 !important; padding: 0 !important; line-height: 1.4 !important; word-spacing: 1.1pt !important; letter-spacing: 0.2pt !important; font-family: Garamond, "Times New Roman", serif !important; color: #000 !important; background: none !important; font-size: 12pt !important; /*Headings */ /* Images */ /* Table */ } body._printliminator_stylized h1, body._printliminator_stylized h2, body._printliminator_stylized h3, body._printliminator_stylized h4, body._printliminator_stylized h5, body._printliminator_stylized h6 { font-family: Helvetica, Arial, sans-serif !important; } body._printliminator_stylized h1 { font-size: 19pt !important; } body._printliminator_stylized h2 { font-size: 17pt !important; } body._printliminator_stylized h3 { font-size: 15pt !important; } body._printliminator_stylized h4, body._printliminator_stylized h5, body._printliminator_stylized h6 { font-size: 12pt !important; } body._printliminator_stylized code { font: 10pt Courier, monospace !important; } body._printliminator_stylized blockquote { margin: 1.3em !important; padding: 1em !important; font-size: 10pt !important; } body._printliminator_stylized hr { background-color: #ccc !important; } body._printliminator_stylized img { float: left !important; margin: 1em 1.5em 1.5em 0 !important; } body._printliminator_stylized a img { border: none !important; } body._printliminator_stylized table { margin: 1px !important; text-align: left !important; border-collapse: collapse !important; } body._printliminator_stylized th { border: 1px solid #333 !important; font-weight: bold !important; } body._printliminator_stylized td { border: 1px solid #333 !important; } body._printliminator_stylized th, body._printliminator_stylized td { padding: 4px 10px !important; } body._printliminator_stylized tfoot { font-style: italic !important; } body._printliminator_stylized caption { background: #fff !important; margin-bottom: 20px !important; text-align: left !important; } body._printliminator_stylized thead { display: table-header-group !important; } body._printliminator_stylized tr { page-break-inside: avoid !important; } ._printliminator_hidden { display: none !important; } ._printliminator_full_width { width: 100% !important; min-width: 100% !important; max-width: 100% !important; margin: 0 !important; } } @media print { ._printliminator_wrap { display: none !important; } } @media screen { body._printliminator_stylized { padding: 20px !important; } ._printliminator_highlight { outline: 3px solid red !important; cursor: default !important; } ._printliminator_highlight._printliminator_full_width { outline-color: blue !important; } ._printliminator_wrap { width: 450px !important; height: 220px; position: fixed !important; top: 20px; right: 20px; z-index: 999999 !important; box-shadow: 0 0 80px black !important; } ._printliminator_wrap iframe { width: 450px !important; height: 220px; border: 0 !important; overflow-x: hidden !important; margin: 0 !important; padding: 0 !important; } ._printliminator_drag_icon { width: 28px !important; height: 20px !important; position: absolute !important; top: 0 !important; left: 0 !important; cursor: move !important; } ._printliminator_drag_icon._printliminator_drag_active { width: 120px !important; height: 100px !important; top: -40px !important; left: -40px !important; } body._printliminator_no_selection, ._printliminator_highlight, ._printliminator_wrap, ._printliminator_drag_icon, ._printliminator_wrap iframe { -webkit-user-select: none !important; -moz-user-select: none !important; -ms-user-select: none !important; user-select: none !important; } } '; // add print stylesheet el = document.createElement( 'style' ); el.id = pl.css.stylesheet; el.innerHTML = styles; document.querySelector( 'head' ).appendChild( el ); }, // create popup (bookmarklet) addControls : function() { var frame, body = document.body, el = document.createElement( 'div' ), controls = pl.css.controls; body.appendChild( el ); pl.addClass( el, pl.css.wrap ); pl.addClass( el, controls ); el.innerHTML = '' + '
'; frame = el.querySelector( 'iframe.' + controls ).contentWindow.document; // Firefox needs script to open, write, then close... innerHTML doesn't work. frame.open(); frame.write('
CLOSE
DRAG

Just click stuff on page to remove. Alt-click to remove opposite.

'); frame.close(); pl.addEvent( frame.querySelector( '.' + pl.css.noGraphics ), 'click', pl.removeGraphics ); pl.addEvent( frame.querySelector( '.' + pl.css.print ), 'click', pl.print ); pl.addEvent( frame.querySelector( '.' + pl.css.undo ), 'click', pl.undo ); pl.addEvent( frame.querySelector( '.' + pl.css.stylize ), 'click', pl.stylize ); pl.addEvent( frame.querySelector( '.' + pl.css.close ), 'click', pl.abort ); pl.addEvent( frame.querySelector( '.' + pl.css.keyboard ), 'click', pl.keyboard ); // can't drag from within the iframe - the mouse coordinates would be within it pl.addEvent( document.querySelector( '.' + pl.css.drag ), 'mousedown', pl.dragInit ); // include mouseup inside frame to stop the drag pl.addEvent( frame, 'mouseup', pl.docMouseUp ); }, keyboard : function() { var wrap = document.querySelector( '.' + pl.css.wrap ), iframe = wrap.querySelector( 'iframe.' + pl.css.controls ), ibody = iframe.contentWindow.document.body, kb = ibody.querySelector( '#' + pl.css.keyboard ), button = ibody.querySelector( '.' + pl.css.keyboard ), disply = kb.style.display, makeVisible = disply === 'none'; button.innerHTML = makeVisible ? 'Hide Keyboard Commands' : 'View Keyboard Commands'; kb.style.display = makeVisible ? '' : 'none'; wrap.style.height = ( makeVisible ? pl.keyboardOpen : pl.keyboardClosed ) + 5 + 'px'; // iframe needs to be a tiny bit taller than the body inside iframe.style.height = ( makeVisible ? pl.keyboardOpen : pl.keyboardClosed ) + 5 + 'px'; ibody.style.height = ( makeVisible ? pl.keyboardOpen : pl.keyboardClosed ) + 20 + 'px'; }, // drag code adapted from http://jsfiddle.net/tovic/Xcb8d/light/ dragInit : function() { var drag = pl.drag; pl.addClass( document.querySelector( '.' + pl.css.drag ), pl.css.dragActive ); drag.el = document.querySelector( '.' + pl.css.wrap ); drag.elm[0] = drag.pos[0] - drag.el.offsetLeft; drag.elm[1] = drag.pos[1] - drag.el.offsetTop; // prevent selecting content while dragging pl.toggleSelection( true ); }, docMouseMove : function( event ) { var drag = pl.drag; drag.pos[0] = document.all ? window.event.clientX : event.pageX; drag.pos[1] = document.all ? window.event.clientY : event.pageY; if ( pl.drag.el !== null ) { drag.el.style.left = ( drag.pos[0] - drag.elm[0] ) + 'px'; drag.el.style.top = ( drag.pos[1] - drag.elm[1] ) + 'px'; } }, docMouseUp : function() { pl.drag.el = null; pl.removeClass( document.querySelector( '.' + pl.css.drag ), pl.css.dragActive ); pl.toggleSelection(); }, stopSelection : function() { return false; }, toggleSelection : function( disable ) { var body = document.body; if ( disable ) { // save current "unselectable" value pl.savedUnsel = body.getAttribute( 'unselectable' ); body.setAttribute( 'unselectable', 'on' ); pl.addClass( body, pl.css.noSelection ); pl.addEvent( body, 'onselectstart', pl.stopSelection ); } else { if ( pl.savedUnsel ) { body.setAttribute( 'unselectable', pl.savedUnsel ); } pl.removeClass( body, pl.css.noSelection ); pl.removeEvent( body, 'onselectstart', pl.stopSelection ); } // clear any selections pl.clearSelection(); }, clearSelection : function() { // remove text selection - http://stackoverflow.com/a/3171348/145346 var sel = window.getSelection ? window.getSelection() : document.selection; if ( sel ) { if ( sel.removeAllRanges ) { sel.removeAllRanges(); } else if ( sel.empty ) { sel.empty(); } } }, hide : function ( els ) { if ( els ) { var indx, len = els.length; // single elements have undefined length if ( typeof len !== 'undefined' ) { for ( indx = 0; indx < len; indx++ ) { pl.addClass( els[ indx ], pl.css.hidden ); } } else { pl.addClass( els, pl.css.hidden ); } } }, show : function ( els ) { if ( els ) { var indx, len = els.length; if ( typeof len !== 'undefined' ) { for ( indx = 0; indx < len; indx++ ) { pl.removeClass( els[ indx ], pl.css.hidden ); } } else { pl.removeClass( els, pl.css.hidden ); } } }, addClass : function( el, name ) { if ( el ) { if ( el.classList ) { el.classList.add( name ); } else if ( !pl.hasClass( el, name ) ) { el.className += ' ' + name; } } }, removeClass : function( el, name ) { if ( el ) { if ( el.classList ) { el.classList.remove( name ); } else { el.className = el.className.replace( new RegExp( '\\b' + name + '\\b', 'g' ), '' ); } } }, hasClass : function( el, name ) { if ( el ) { return el.classList ? el.classList.contains( name ) : new RegExp( '\\b' + name + '\\b' ).test( el.className ); } return false; }, addEvent : function( el, type, handler ) { if ( el.attachEvent ) { el.attachEvent( 'on' + type, handler ); } else { el.addEventListener( type, handler ); } }, removeEvent : function( el, type, handler ) { if ( el.detachEvent ) { el.detachEvent( 'on' + type, handler ); } else { el.removeEventListener( type, handler ); } } }; })();