1/* 2 * printThis v2.0.0 3 * @desc Printing plug-in for jQuery 4 * @author Jason Day 5 * @author Samuel Rouse 6 * 7 * Resources (based on): 8 * - jPrintArea: http://plugins.jquery.com/project/jPrintArea 9 * - jqPrint: https://github.com/permanenttourist/jquery.jqprint 10 * - Ben Nadal: http://www.bennadel.com/blog/1591-Ask-Ben-Print-Part-Of-A-Web-Page-With-jQuery.htm 11 * 12 * Licensed under the MIT licence: 13 * http://www.opensource.org/licenses/mit-license.php 14 * 15 * (c) Jason Day 2015-2022 16 * 17 * Usage: 18 * 19 * $("#mySelector").printThis({ 20 * debug: false, // show the iframe for debugging 21 * importCSS: true, // import parent page css 22 * importStyle: true, // import style tags 23 * printContainer: true, // grab outer container as well as the contents of the selector 24 * loadCSS: "path/to/my.css", // path to additional css file - use an array [] for multiple 25 * pageTitle: "", // add title to print page 26 * removeInline: false, // remove all inline styles from print elements 27 * removeInlineSelector: "body *", // custom selectors to filter inline styles. removeInline must be true 28 * printDelay: 1000, // variable print delay 29 * header: null, // prefix to html 30 * footer: null, // postfix to html 31 * base: false, // preserve the BASE tag, or accept a string for the URL 32 * formValues: true, // preserve input/form values 33 * canvas: true, // copy canvas elements 34 * doctypeString: '...', // enter a different doctype for older markup 35 * removeScripts: false, // remove script tags from print content 36 * copyTagClasses: true // copy classes from the html & body tag 37 * copyTagStyles: true, // copy styles from html & body tag (for CSS Variables) 38 * beforePrintEvent: null, // callback function for printEvent in iframe 39 * beforePrint: null, // function called before iframe is filled 40 * afterPrint: null // function called before iframe is removed 41 * }); 42 * 43 * Notes: 44 * - the loadCSS will load additional CSS (with or without @media print) into the iframe, adjusting layout 45 */ 46; 47(function($) { 48 49 function appendContent($el, content) { 50 if (!content) return; 51 52 // Simple test for a jQuery element 53 $el.append(content.jquery ? content.clone() : content); 54 } 55 56 function appendBody($body, $element, opt) { 57 // Clone for safety and convenience 58 // Calls clone(withDataAndEvents = true) to copy form values. 59 var $content = $element.clone(opt.formValues); 60 61 if (opt.formValues) { 62 // Copy original select and textarea values to their cloned counterpart 63 // Makes up for inability to clone select and textarea values with clone(true) 64 copyValues($element, $content, 'select, textarea'); 65 } 66 67 if (opt.removeScripts) { 68 $content.find('script').remove(); 69 } 70 71 if (opt.printContainer) { 72 // grab $.selector as container 73 $content.appendTo($body); 74 } else { 75 // otherwise just print interior elements of container 76 $content.each(function() { 77 $(this).children().appendTo($body) 78 }); 79 } 80 } 81 82 // Copies values from origin to clone for passed in elementSelector 83 function copyValues(origin, clone, elementSelector) { 84 var $originalElements = origin.find(elementSelector); 85 86 clone.find(elementSelector).each(function(index, item) { 87 $(item).val($originalElements.eq(index).val()); 88 }); 89 } 90 91 var opt; 92 $.fn.printThis = function(options) { 93 opt = $.extend({}, $.fn.printThis.defaults, options); 94 var $element = this instanceof jQuery ? this : $(this); 95 96 var strFrameName = "printThis-" + (new Date()).getTime(); 97 98 if (window.location.hostname !== document.domain && navigator.userAgent.match(/msie/i)) { 99 // Ugly IE hacks due to IE not inheriting document.domain from parent 100 // checks if document.domain is set by comparing the host name against document.domain 101 var iframeSrc = "javascript:document.write(\"<head><script>document.domain=\\\"" + document.domain + "\\\";</s" + "cript></head><body></body>\")"; 102 var printI = document.createElement('iframe'); 103 printI.name = "printIframe"; 104 printI.id = strFrameName; 105 printI.className = "MSIE"; 106 document.body.appendChild(printI); 107 printI.src = iframeSrc; 108 109 } else { 110 // other browsers inherit document.domain, and IE works if document.domain is not explicitly set 111 var $frame = $("<iframe id='" + strFrameName + "' name='printIframe' />"); 112 $frame.appendTo("body"); 113 } 114 115 var $iframe = $("#" + strFrameName); 116 117 // show frame if in debug mode 118 if (!opt.debug) $iframe.css({ 119 position: "absolute", 120 width: "0px", 121 height: "0px", 122 left: "-600px", 123 top: "-600px" 124 }); 125 126 // before print callback 127 if (typeof opt.beforePrint === "function") { 128 opt.beforePrint(); 129 } 130 131 // $iframe.ready() and $iframe.load were inconsistent between browsers 132 setTimeout(function() { 133 134 // Add doctype to fix the style difference between printing and render 135 function setDocType($iframe, doctype){ 136 var win, doc; 137 win = $iframe.get(0); 138 win = win.contentWindow || win.contentDocument || win; 139 doc = win.document || win.contentDocument || win; 140 doc.open(); 141 doc.write(doctype); 142 doc.close(); 143 } 144 145 if (opt.doctypeString){ 146 setDocType($iframe, opt.doctypeString); 147 } 148 149 var $doc = $iframe.contents(), 150 $head = $doc.find("head"), 151 $body = $doc.find("body"), 152 $base = $('base'), 153 baseURL; 154 155 // add base tag to ensure elements use the parent domain 156 if (opt.base === true && $base.length > 0) { 157 // take the base tag from the original page 158 baseURL = $base.attr('href'); 159 } else if (typeof opt.base === 'string') { 160 // An exact base string is provided 161 baseURL = opt.base; 162 } else { 163 // Use the page URL as the base 164 baseURL = document.location.protocol + '//' + document.location.host; 165 } 166 167 $head.append('<base href="' + baseURL + '">'); 168 169 // import page stylesheets 170 if (opt.importCSS) $("link[rel=stylesheet]").each(function() { 171 var href = $(this).attr("href"); 172 if (href) { 173 var media = $(this).attr("media") || "all"; 174 $head.append("<link type='text/css' rel='stylesheet' href='" + href + "' media='" + media + "'>"); 175 } 176 }); 177 178 // import style tags 179 if (opt.importStyle) $("style").each(function() { 180 $head.append(this.outerHTML); 181 }); 182 183 // add title of the page 184 if (opt.pageTitle) $head.append("<title>" + opt.pageTitle + "</title>"); 185 186 // import additional stylesheet(s) 187 if (opt.loadCSS) { 188 if ($.isArray(opt.loadCSS)) { 189 jQuery.each(opt.loadCSS, function(index, value) { 190 $head.append("<link type='text/css' rel='stylesheet' href='" + this + "'>"); 191 }); 192 } else { 193 $head.append("<link type='text/css' rel='stylesheet' href='" + opt.loadCSS + "'>"); 194 } 195 } 196 197 var pageHtml = $('html')[0]; 198 199 // CSS VAR in html tag when dynamic apply e.g. document.documentElement.style.setProperty("--foo", bar); 200 $doc.find('html').prop('style', pageHtml.style.cssText); 201 202 // copy 'root' tag classes 203 var tag = opt.copyTagClasses; 204 if (tag) { 205 tag = tag === true ? 'bh' : tag; 206 if (tag.indexOf('b') !== -1) { 207 $body.addClass($('body')[0].className); 208 } 209 if (tag.indexOf('h') !== -1) { 210 $doc.find('html').addClass(pageHtml.className); 211 } 212 } 213 214 // copy ':root' tag classes 215 tag = opt.copyTagStyles; 216 if (tag) { 217 tag = tag === true ? 'bh' : tag; 218 if (tag.indexOf('b') !== -1) { 219 $body.attr('style', $('body')[0].style.cssText); 220 } 221 if (tag.indexOf('h') !== -1) { 222 $doc.find('html').attr('style', pageHtml.style.cssText); 223 } 224 } 225 226 // print header 227 appendContent($body, opt.header); 228 229 if (opt.canvas) { 230 // add canvas data-ids for easy access after cloning. 231 var canvasId = 0; 232 // .addBack('canvas') adds the top-level element if it is a canvas. 233 $element.find('canvas').addBack('canvas').each(function(){ 234 $(this).attr('data-printthis', canvasId++); 235 }); 236 } 237 238 appendBody($body, $element, opt); 239 240 if (opt.canvas) { 241 // Re-draw new canvases by referencing the originals 242 $body.find('canvas').each(function(){ 243 var cid = $(this).data('printthis'), 244 $src = $('[data-printthis="' + cid + '"]'); 245 246 this.getContext('2d').drawImage($src[0], 0, 0); 247 248 // Remove the markup from the original 249 if ($.isFunction($.fn.removeAttr)) { 250 $src.removeAttr('data-printthis'); 251 } else { 252 $.each($src, function(i, el) { 253 el.removeAttribute('data-printthis'); 254 }); 255 } 256 }); 257 } 258 259 // remove inline styles 260 if (opt.removeInline) { 261 // Ensure there is a selector, even if it's been mistakenly removed 262 var selector = opt.removeInlineSelector || '*'; 263 // $.removeAttr available jQuery 1.7+ 264 if ($.isFunction($.removeAttr)) { 265 $body.find(selector).removeAttr("style"); 266 } else { 267 $body.find(selector).attr("style", ""); 268 } 269 } 270 271 // print "footer" 272 appendContent($body, opt.footer); 273 274 // attach event handler function to beforePrint event 275 function attachOnBeforePrintEvent($iframe, beforePrintHandler) { 276 var win = $iframe.get(0); 277 win = win.contentWindow || win.contentDocument || win; 278 279 if (typeof beforePrintHandler === "function") { 280 if ('matchMedia' in win) { 281 win.matchMedia('print').addListener(function(mql) { 282 if(mql.matches) beforePrintHandler(); 283 }); 284 } else { 285 win.onbeforeprint = beforePrintHandler; 286 } 287 } 288 } 289 attachOnBeforePrintEvent($iframe, opt.beforePrintEvent); 290 291 setTimeout(function() { 292 if ($iframe.hasClass("MSIE")) { 293 // check if the iframe was created with the ugly hack 294 // and perform another ugly hack out of neccessity 295 window.frames["printIframe"].focus(); 296 $head.append("<script> window.print(); </s" + "cript>"); 297 } else { 298 // proper method 299 if (document.queryCommandSupported("print")) { 300 $iframe[0].contentWindow.document.execCommand("print", false, null); 301 } else { 302 $iframe[0].contentWindow.focus(); 303 $iframe[0].contentWindow.print(); 304 } 305 } 306 307 // remove iframe after print 308 if (!opt.debug) { 309 setTimeout(function() { 310 $iframe.remove(); 311 312 }, 1000); 313 } 314 315 // after print callback 316 if (typeof opt.afterPrint === "function") { 317 opt.afterPrint(); 318 } 319 320 }, opt.printDelay); 321 322 }, 333); 323 324 }; 325 326 // defaults 327 $.fn.printThis.defaults = { 328 debug: false, // show the iframe for debugging 329 importCSS: true, // import parent page css 330 importStyle: true, // import style tags 331 printContainer: true, // print outer container/$.selector 332 loadCSS: "", // path to additional css file - use an array [] for multiple 333 pageTitle: "", // add title to print page 334 removeInline: false, // remove inline styles from print elements 335 removeInlineSelector: "*", // custom selectors to filter inline styles. removeInline must be true 336 printDelay: 1000, // variable print delay 337 header: null, // prefix to html 338 footer: null, // postfix to html 339 base: false, // preserve the BASE tag or accept a string for the URL 340 formValues: true, // preserve input/form values 341 canvas: true, // copy canvas content
342 doctypeString: '<!DOCTYPE html>', // enter a different doctype for older markup 343 removeScripts: false, // remove script tags from print content 344 copyTagClasses: true, // copy classes from the html & body tag 345 copyTagStyles: true, // copy styles from html & body tag (for CSS Variables) 346 beforePrintEvent: null, // callback function for printEvent in iframe 347 beforePrint: null, // function called before iframe is filled 348 afterPrint: null // function called before iframe is removed 349 }; 350})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.