diff options
Diffstat (limited to 'src/printliminator.js')
-rw-r--r-- | src/printliminator.js | 921 |
1 files changed, 735 insertions, 186 deletions
diff --git a/src/printliminator.js b/src/printliminator.js index 91403fb..5d7f201 100644 --- a/src/printliminator.js +++ b/src/printliminator.js @@ -1,199 +1,748 @@ -/* Printliminator */ -function csstricksPrintliminator( jQ ) { - // remove conflicts with other javascript libraries - var $ = jQ.noConflict(), - history = [], - flags = {}, - // if local, load local sprite image - sprite = ( window.location.origin === 'file://' ? '' : '//css-tricks.github.io/The-Printliminator/' ) + - 'printliminator.png', - // programmically added stylesheets - styles = '' + - '._print_controls { position: fixed; top: 25px; right: 25px; width: 162px; height: 182px; z-index: 10000;' + - '-moz-user-select: none; -webkit-user-select: none; -ms-user-select: none;' + - 'background: url(' + sprite + ') no-repeat; }' + - '._print_controls_close { position: absolute; top: -20px; right: -20px; width: 33px; height: 33px;' + - 'background: url(' + sprite + ') -222px -3px no-repeat; }' + - '._print_controls_close:hover { background-position: -222px -39px; }' + - '._print_controls_remove_graphics, ._print_controls_print, ._print_controls_undo, ._print_controls_stylize {' + - 'position: absolute; height: 74px; width: 74px;' + - 'background: url(' + sprite + ') no-repeat; }' + - '._print_controls_remove_graphics { top: 6px; left: 6px; background-position: 0px -182px; }' + - '._print_controls_remove_graphics:hover { background-position: 0 -256px; }' + - '._print_controls_remove_graphics.active { background-position: 0 -330px; }' + - '._print_controls_print { top: 83px; left: 83px; background-position: -74px -182px; }' + - '._print_controls_print:hover { background-position: -74px -256px; }' + - '._print_controls_print.active { background-position: -74px -330px; }' + - '._print_controls_undo { top: 83px; left: 6px; background-position: -148px -182px; }' + - '._print_controls_undo:hover { background-position: -148px -256px; }' + - '._print_controls_undo.active { background-position: -148px -330px; }' + - '._print_controls_stylize { top: 6px; left: 83px; background-position: -222px -182px; }' + - '._print_controls_stylize:hover { background-position: -222px -256px; }' + - '._print_controls_stylize.active { background-position: -222px -330px; }' + - '._print_removed { display: none !important; }' + - '._printliminator_highlight { outline: 3px solid red; }' + - '@media print{ ._print_controls { display: none; } }', - - printstylesheet = '@media print, screen {' + - 'body { margin:0; padding:0; line-height: 1.4; word-spacing: 1.1pt; letter-spacing: 0.2pt;' + - 'font-family: Garamond, "Times New Roman", serif; color: #000; background: none; font-size: 12pt; }' + - 'h1, h2, h3, h4, h5, h6 { font-family: Helvetica, Arial, sans-serif; }' + - 'h1 { font-size: 19pt; }' + - 'h2 { font-size: 17pt; }' + - 'h3 { font-size: 15pt; }' + - 'h4, h5, h6 { font-size: 12pt; }' + - 'code { font: 10pt Courier, monospace; }' + - 'blockquote { margin: 1.3em; padding: 1em; font-size: 10pt; }' + - 'hr { background-color: #ccc; }' + - 'img { float: left; margin: 1em 1.5em 1.5em 0; }' + - 'a img { border: none; }' + - 'table { margin: 1px; text-align: left; border-collapse: collapse; }' + - 'th { border: 1px solid #333; font-weight: bold; }' + - 'td { border: 1px solid #333; }' + - 'th, td { padding: 4px 10px; }' + - 'tfoot { font-style: italic; }' + - 'caption { background: #fff; margin-bottom: 20px; text-align: left; }' + - 'thead {display: table-header-group; }' + - 'tr { page-break-inside: avoid; }' + - '} @media screen { body { padding: 20px; } }'; - - $( '<style id="_print_controls_styles">' ) - .text( styles ) - .appendTo( 'head' ); - - $( 'body *:not(._print_controls, ._print_controls *)' ) - .live( 'click', function( e ) { - e.preventDefault(); - var $done, - $this = $( this ); - if ( e.altKey ) { - $done = $( 'body *' ) - .not( '._print_controls, ._print_controls *, style' ) - .not( $this.parents().andSelf() ) - .not( $this.find( '*' ) ) - .addClass( '_print_removed' ); +/* Printliminator v3.1.0 */ +/* jshint expr:false */ +/* global csstricksPrintliminatorVars */ +;( function() { +'use strict'; +var pl = { + + version : '3.1.0', + + css : { + hilite : '_printliminator_highlight', + fullWidth : '_printliminator_full_width', + hidden : '_printliminator_hidden', + stylized : '_printliminator_stylized', // class name added to body + noSelection: '_printliminator_noSelection', // class on body while dragging + + // printliminator + stylesheet : '_print_controls_styles', // stylesheet ID + controls : '_print_controls', + icon : '_print_controls_icon', + wrap : '_print_controls_wrap', + noGraphics : '_print_controls_remove_graphics', + stylize : '_print_controls_stylize', + print : '_print_controls_print', + close : '_print_controls_close', + undo : '_print_controls_undo', + drag : '_print_controls_icon_drag', + keyboard : '_print_controls_keyboard' + }, + + 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 + noGraphicsSelectors : 'img, iframe:not(._print_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 : 630, + keyboardClosed : 220, + + // dragging parameters stored here + drag : { + el : null, + pos : [ 0, 0 ], + elm : [ 0, 0 ] + }, + + init : function() { + + var body = document.querySelector( 'body' ); + + // need a global variable to store history & flags + if ( typeof window.csstricksPrintliminatorVars === 'undefined' ) { + // use object separate from pl, otherwise these values get lost + // upon javascript injection a second time (after uses presses Esc) + window.csstricksPrintliminatorVars = { + history : [], + // flags to prevent multiple applications of same action + 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 ); + + }, + + addStyles : function(){ + var el, + body = document.querySelector( 'body' ), + prefix = 'body.' + pl.css.stylized + ' ', + impt = '!important;', + + // programmically added stylesheets + styles = '' + + // hide printliminator controls from print + '@media print{ .' + pl.css.wrap + '{ display: none; } }' + + + // print stylesheet + '@media print, screen {' + + prefix + '{ margin: 0; padding: 0; line-height: 1.4;' + + 'word-spacing: 1.1pt; letter-spacing: 0.2pt; font-size: 12pt;' + + 'font-family: Garamond, "Times New Roman", serif; color: #000; background: none; }' + + prefix + 'h1,' + prefix + 'h2,' + prefix + 'h3,' + + prefix + 'h4,' + prefix +'h5,' + prefix +'h6' + + '{ font-family: Helvetica, Arial, sans-serif; }' + + prefix + 'h1 { font-size: 19pt; }' + + prefix + 'h2 { font-size: 17pt; }' + + prefix + 'h3 { font-size: 15pt; }' + + prefix + 'h4, ' + prefix +'h5,' + prefix + 'h6 { font-size: 12pt; }' + + prefix + 'code { font: 10pt Courier, monospace; }' + + prefix + 'blockquote { margin: 1.3em; padding: 1em; font-size: 10pt; }' + + prefix + 'hr { background-color: #ccc; }' + + prefix + 'img { float: left; margin: 1em 1.5em 1.5em 0; }' + + prefix + 'a img { border: none; }' + + prefix + 'table { margin: 1px; text-align:left; border-collapse: collapse; }' + + prefix + 'th { border: 1px solid #333; font-weight: bold; }' + + prefix + 'td { border: 1px solid #333; }' + + prefix + 'th, ' + prefix +' td { padding: 4px 10px; }' + + prefix + 'tfoot { font-style: italic; }' + + prefix + 'caption { background: #fff; margin-bottom: 20px; text-align:left; }' + + prefix + 'thead { display: table-header-group; }' + + prefix + 'tr { page-break-inside: avoid; }' + + + // elements hidden by Printliminator + '.' + pl.css.hidden + ' { display: none' + impt + '}' + + // elements set to full width/no margins + '.' + pl.css.fullWidth + ' { width: 100%' + impt + ' min-width: 100%' + impt + ' max-width: 100%' + impt + + 'margin: 0' + impt + '}' + + + '} @media screen {' + + prefix + '{ padding: 20px; }' + + + // printliminator controls + '.' + pl.css.wrap + '{ width: 375px' + impt + ' height: ' + pl.keyboardClosed + 'px; position: fixed' + impt + + 'top: 0; right: 0; z-index: 999999' + impt + ' border: #000 1px solid' + impt + '}' + + '.' + pl.css.drag + '{ width: 28px' + impt + 'height: 20px' + impt + 'position: absolute' + impt + + ' top: 0' + impt + ' left: 0' + impt + 'cursor: move' + impt + '}' + + '.' + pl.css.wrap + ' iframe { width: 375px' + impt + ' height: ' + pl.keyboardClosed + 'px; border: 0' + impt + + 'overflow-x: hidden' + impt + ' margin: 0' + impt + ' padding: 0' + impt + '}' + + + // prevent selection + 'body.' + pl.css.noSelection + ',.' + pl.css.hilite + ',.' + pl.css.wrap + ',.' + pl.css.drag + ',.' + pl.css.wrap + ' iframe {' + + '-webkit-user-select: none' + impt + '-moz-user-select: none' + impt + ' -ms-user-select: none' + impt + + ' user-select: none' + impt + '}' + + + // box highlighting + '.' + pl.css.hilite + '{ outline: 3px solid red' + impt + 'cursor: default' + impt + '}' + + '.' + pl.css.hilite + '.' + pl.css.fullWidth + '{ outline-color: blue' + impt + '}'; + + // add print stylesheet + el = document.createElement( 'style' ); + el.id = pl.css.stylesheet; + el.innerHTML = styles; + document.querySelector( 'head' ).appendChild( el ); + + }, + + // create popup + addControls : function(){ + var frame, + body = document.querySelector( 'body' ), + el = document.createElement( 'div' ), + + controls = pl.css.controls, + prefix = '.' + controls, + button = '_print_controls_button', + icon = pl.css.icon, + + sprite = 'data:image/png;base64,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'; + + body.appendChild( el ); + pl.addClass( el, pl.css.wrap ); + pl.addClass( el, controls ); + + el.innerHTML = '<iframe class="' + pl.css.controls + '"></iframe>' + + '<div class="' + pl.css.controls + ' ' + pl.css.drag + '"></div>'; + + frame = el.querySelector( 'iframe.' + pl.css.controls ).contentWindow.document.body; + frame.innerHTML = '<div class="' + controls + '_header">' + + '<div class="' + pl.css.close + ' ' + controls + '_right">CLOSE <span class="' + icon + ' ' + icon + '_close"></span></div>' + + '<div><span class="' + icon + ' ' + pl.css.drag + '"></span> DRAG</div>' + + '</div>' + + '<div class="' + controls + '_top">' + + '<h1>The <strong>Printliminator</strong></h1>' + + '<h3>Click highlighted boxes to delete</h3>' + + '</div>' + + '<div class="' + controls + '_footer">' + + '<h3>Other Useful Superpowers</h3>' + + '<ul>' + + '<li class="' + button + ' ' + pl.css.undo + '"><span class="' + icon + ' ' + icon + '_undo"></span>Undo<br>Last</li>' + + '<li class="' + button + ' ' + pl.css.stylize + '"><span class="' + icon + ' ' + icon + '_stylize"></span>Add Print<br>Styles</li>' + + '<li class="' + button + ' ' + pl.css.noGraphics + '"><span class="' + icon + ' ' + icon + '_nographics"></span>Remove<br>Graphics</li>' + + '<li class="' + button + ' ' + pl.css.print + '"><span class="' + icon + ' ' + icon + '_print"></span>Send to<br>print</li>' + + '<li class="' + button + ' ' + pl.css.keyboard + '"><span class="' + icon + ' ' + icon + '_keys"></span>Keyboard<br>Commands</li>' + + '</ul>' + + '<table id="' + pl.css.keyboard + '" style="display:none">' + + '<thead>' + + '<tr><th class="key">Key</th><th>Command</th></tr>' + + '</thead>' + + '<tbody>' + + '<tr><td><kbd>PageUp</kbd> <span class="lower">or</span> <kbd class="bold" title="Up Arrow">↑</kbd></td><td>Find wrapper of highlighted box</td></tr>' + + '<tr><td><kbd>PageDown</kbd> <span class="lower">or</span> <kbd class="bold" title="Down Arrow">↓</kbd></td><td>Find content of highlighted box</td></tr>' + + '<tr><td><kbd class="bold" title="Right Arrow">→</kbd></td><td>Find next box inside same wrapper</td></tr>' + + '<tr><td><kbd class="bold" title="Left Arrow">←</kbd></td><td>Find previous box inside same wrapper</td></tr>' + + '<tr><td><kbd>Enter</kbd></td><td>Remove the highlighted box</td></tr>' + + '<tr><td><kbd>Backspace</kbd></td><td>Undo last action</td></tr>' + + '<tr><td><kbd title="Numpad Plus">Numpad <span class="bold">+</span></kbd></td><td>Increase font-size by 1</td></tr>' + + '<tr><td><kbd title="Numpad Minus">NumPad <span class="bold">-</span></kbd></td><td>Decrease font-size by 1</td></tr>' + + '<tr><td><kbd title="Numpad Asterisk (Multiply)">NumPad <span class="bold asterisk">*</span></kbd></td><td>Reset font-size</td></tr>' + + '<tr><td><kbd>Alt</kbd> + <span class="' + icon + ' ' + icon + '_left_click" title="left-click on mouse"></span></td>' + + '<td>Remove everything but highlighted box</td></tr>' + + '<tr><td><kbd>Shift</kbd> + <span class="' + icon + ' ' + icon + '_left_click" title="left-click on mouse"></span></td>' + + '<td>Set box width to 100% & margins to zero (highlight turns blue)</td></tr>' + + '<tr><td><kbd title="Print Screen">PrtScn</kbd></td><td>Print Page</td></tr>' + + '<tr><td><kbd title="Escape">Esc</kbd></td><td>Disable Printliminator, but save undo history</td></tr>' + + '</tbody></table></div>' + + '<style>' + + 'html { box-sizing: border-box; height: 100%; } *, *:before, *:after { box-sizing: inherit; }' + + 'html,body { background: #eee; width: 375px; min-height: ' + pl.keyboardClosed + 'px;' + + 'font-size: 14px; margin: 0; padding: 0; cursor: default;' + + 'font-family: Univers, Calibri, "Myriad Pro", Myriad, Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif;' + + '-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }' + + prefix + '_top { background: #fff; }' + + 'h1, h3 { margin: 0 0 0 10px; font-weight: normal;' + + 'text-transform: uppercase; }' + + 'h1 { font-size: 32px; }' + + 'h3 { font-size: 19px; font-weight: bold; }' + + prefix + '_top h3 { color: red; }' + + '.' + pl.css.icon + '{ display: inline-block; background: url(' + sprite + ') no-repeat;' + + 'width: 25px; height: 25px; vertical-align: middle; }' + + '.' + pl.css.icon + '.' + pl.css.drag + ' { background-position: 0 0; }' + + '.' + pl.css.icon + '_print { background-position: -25px 0; }' + + '.' + pl.css.icon + '_keys { background-position: -50px 0; }' + + '.' + pl.css.icon + '_close { background-position: -75px 0; width: 40px; cursor: pointer; }' + + '.' + pl.css.icon + '_undo { background-position: 0 -25px; }' + + '.' + pl.css.icon + '_nographics { background-position: -25px -25px; }' + + '.' + pl.css.icon + '_left_click { background-position: -50px -25px; }' + + '.' + pl.css.icon + '_stylize { background-position: -75px -25px; width: 35px; }' + + prefix + '_header,' + prefix + '_button { background: #000; color: #fff;}' + + prefix + '_header,' + prefix + '_header > div { height: 21px; }' + + prefix + '_header > div,' + prefix + '_button { display: inline-block; }' + + prefix + '_right { float: right; margin-right: 6px; }' + + prefix + '_footer ul { margin: 2px; text-align: center; padding: 4px;' + + 'list-style-type: none;}' + + prefix + '_button { padding: 4px 8px 4px 4px; line-height: 18px; min-width: 90px; text-align: left;' + + 'white-space: nowrap; margin: 2px; cursor: pointer; display: inline-block; }' + + prefix + '_button:hover,' + prefix + '_button.active { background-color: #333; }' + + prefix + '_button span { float: left; margin: 5px 5px 5px 0; text-align: left; }' + + prefix + '_keyboard { width: 110px; }' + + '.key { width: 30%; }' + + 'table { margin: 0 4px; }' + + 'kbd { background: #fff; border: #000 1px solid; border-radius: 3px;' + + 'padding: 1px 3px; }' + + 'td { border-top: 1px solid #aaa; }' + + '</style>'; + + 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 ); + + }, + + bodyClick : function( event ) { + event.preventDefault(); + event.stopImmediatePropagation(); + + if ( event.target.nodeName !== 'BODY' && !pl.hasClass( event.target, pl.css.controls ) ) { + var done, sel, + hilite = document.querySelector( '.' + pl.css.hilite ); + + // show opposite (Alt + click) + if ( event[ pl.keys.opposite ] ) { + done = pl.getOpposite( hilite ); + sel = done.length; + if ( !sel ) { + // nothing left to remove + return false; + } } else { - $done = $this; + // hide clicked element + done = [ hilite ]; } - $done.addClass( '_print_removed' ); - history.push( $done ); - }) - .live( 'mouseover', function() { - $(this).addClass( '_printliminator_highlight' ); - }) - .live( 'mouseout', function() { - $(this).removeClass( '_printliminator_highlight' ); - }); - - var $controls = $( '<div class="_print_controls">' ) - .appendTo( 'body' ); - - // fix IE6, which doesn't support position: fixed - if ( $controls.css( 'position' ) !== 'fixed' ) { - $controls.css( 'position', 'absolute' ); - } - // Remove Graphics - $( '<div class="_print_controls_remove_graphics">' ) - .click( function() { - if ( !flags.removeGraphics ) { - var indx, $el, bkgd, - bkgds = [], - $done = $( 'img, iframe, object, embed, input[type=image], ins' ), - $item = $( 'body *:not(._print_controls, ._print_controls *)' ), - len = $item.length; - for ( indx = 0; indx < len; indx++ ) { - $el = $item.eq( indx ); - bkgd = $el.css( 'background-image' ); - if ( bkgd !== 'none' ) { - bkgds.push( [ $el, bkgd ] ); - $el.css( 'background-image', 'none' ); + pl.hide( done ); + csstricksPrintliminatorVars.history.push( done ); + + // remove any text selection + pl.clearSelection(); + + } + }, + + bodyMouseover : function( event ) { + if ( !pl.hasClass( event.target, pl.css.controls ) ) { + pl.addClass( event.target, pl.css.hilite ); + } + }, + + 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 ); + } + }, + + bodyKeyUp : function( event ) { + event.preventDefault(); + // PrntScrn only works on keyup + if ( event.which === pl.keys.print ) { + pl.print(); + } + }, + + bodyKeyDown : function( event ) { + event.preventDefault(); + var n, suffix, elm, els, isBody, + body = document.querySelectorAll( 'body' )[ 0 ], + el = document.querySelectorAll( '.' + pl.css.hilite )[ 0 ], + 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 + if ( !isBody && el.parentNode ) { + pl.removeClass( el, highlight ); + pl.addClass( el.parentNode, highlight ); } - } - $done.addClass( '_print_removed' ); - flags.removeGraphics = true; - - history.push( function() { - flags.removeGraphics = false; - $done.removeClass( '_print_removed' ); - var $el, - len = bkgds.length; - for ( indx = 0; indx < len; indx++ ) { - $el = bkgds[ indx ][ 0 ]; - $el.css( 'background-image', bkgds[ indx ][ 1 ] ); + break; + + case pl.keys.child1 : // pageDown + case pl.keys.child2 : // down arrow + els = Array.prototype.filter.call( el.children, pl.filterElements ); + if ( els.length ) { + pl.removeClass( el, highlight ); + pl.addClass( els[0], 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 ); + csstricksPrintliminatorVars.history.push( el ); } - }); + break; + } - }) - .appendTo( $controls ); - - // Print Stylize - $( '<div class="_print_controls_stylize">' ) - .click( function() { - window.print(); - }) - .appendTo( $controls ); - - // Print - $( '<div class="_print_controls_print">' ) - .click( function() { - if ( !flags.stylize ) { - var links = $( 'link[rel="stylesheet"], style:not(#_print_controls_styles)' ).remove(), - // cache and remove inline styles - inline = $( 'body *:not(._print_controls, ._print_controls > *, ._print_removed)' ).map( function() { - var $this = $( this ), - style = $this.attr( 'style' ); - $this.attr( 'style', '' ); - return { - el: this, + } else { + el = event.target; + pl.addClass( el, highlight ); + } + + 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; + + } + }, + + // drag code adapted from http://jsfiddle.net/tovic/Xcb8d/light/ + dragInit : function() { + var drag = pl.drag; + 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.toggleSelection(); + }, + + stopSelection : function() { + return false; + }, + + 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(); + } + } + }, + + toggleSelection : function( disable ) { + var body = document.querySelector( '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(); + }, + + removeGraphics : function() { + if ( !csstricksPrintliminatorVars.flags.removeGraphics ) { + var indx, bkgd, + bkgds = [], + body = document.querySelector( 'body' ), + done = body.querySelectorAll( pl.noGraphicsSelectors ), + 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 ); + csstricksPrintliminatorVars.flags.removeGraphics = true; + + csstricksPrintliminatorVars.history.push( function() { + csstricksPrintliminatorVars.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 ( !csstricksPrintliminatorVars.flags.stylize ) { + var indx, + inline = [], + body = document.querySelector( 'body' ), + links = document.querySelectorAll( 'link[rel="stylesheet"], style' ), + 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 - }; - }), - print = $( '<style id="_print_controls_printstylesheet">' ) - .text( printstylesheet ) - .appendTo( 'head' ); - flags.stylize = true; - - history.push( function() { - flags.stylize = false; - print.remove(); - links.appendTo( 'head' ); - inline.each( function() { - $( this.el ).attr( 'style', this.style ); }); - }); - } - }) - .appendTo( $controls ); + } + }); + + pl.addClass( body, pl.css.stylized ); + pl.removeHighlight(); + csstricksPrintliminatorVars.flags.stylize = true; + + csstricksPrintliminatorVars.history.push( function() { + csstricksPrintliminatorVars.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 ); + } + }); + } + }, - // Close - $( '<div class="_print_controls_close">' ) - .click( function() { - $( '._print_controls' ).remove(); - }) - .appendTo( $controls ); + print : function() { + pl.removeHighlight(); + window.print(); + }, // Undo - $( '<div class="_print_controls_undo">' ) - .click( function() { - var last = history.pop(); - if ( last ) { - if ( typeof last !== 'function' ) { - last.removeClass( '_print_removed' ); - } else { - last.call(); + undo : function() { + var last = csstricksPrintliminatorVars.history.pop(); + if ( last ) { + pl.removeHighlight(); + if ( typeof last !== 'function' ) { + pl.show( last ); + } else { + last.call(); + } + } + }, + + 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'; + pl[ makeVisible ? 'addClass' : 'removeClass' ]( button, 'active' ); + 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 ) + 'px'; + }, + + abort : function() { + var body = document.querySelector( 'body' ); + pl.removeHighlight(); + 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 ) ); + + }, + + 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; + }, + + // 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; + }, + + 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 ); } - }) - .appendTo( $controls ); - - // active state - $( '._print_controls_remove_graphics, ._print_controls_print, ._print_controls_undo, ._print_controls_stylize' ) - .bind( 'mousedown', function() { - $( this ).addClass( 'active' ); - }) - .bind( 'mouseleave mouseup', function() { - $( this ).removeClass( 'active' ); - }); - -} + } + }, + + addClass : function( el, name ) { + if ( el.classList ) { + el.classList.add( name ); + } else if ( !pl.hasClass( el, name ) ) { + el.className += ' ' + name; + } + }, + + removeClass : function( el, name ) { + if ( el.classList ) { + el.classList.remove( name ); + } else { + el.className = el.className.replace( new RegExp( '\\b' + name + '\\b', 'g' ), '' ); + } + }, + + hasClass : function( el, name ) { + return el.classList ? + el.classList.contains( name ) : + new RegExp( '\\b' + name + '\\b' ).test( el.className ); + }, + + 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 ); + } + } + +}; + +window.csstricksPrintliminator = function() { + pl.init(); +}; + +})(); |