You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

303 lines
12 KiB

8 years ago
  1. /*
  2. * printThis v1.10.0
  3. * @desc Printing plug-in for jQuery
  4. * @author Jason Day
  5. *
  6. * Resources (based on) :
  7. * jPrintArea: http://plugins.jquery.com/project/jPrintArea
  8. * jqPrint: https://github.com/permanenttourist/jquery.jqprint
  9. * Ben Nadal: http://www.bennadel.com/blog/1591-Ask-Ben-Print-Part-Of-A-Web-Page-With-jQuery.htm
  10. *
  11. * Licensed under the MIT licence:
  12. * http://www.opensource.org/licenses/mit-license.php
  13. *
  14. * (c) Jason Day 2015
  15. *
  16. * Usage:
  17. *
  18. * $("#mySelector").printThis({
  19. * debug: false, // show the iframe for debugging
  20. * importCSS: true, // import page CSS
  21. * importStyle: false, // import style tags
  22. * printContainer: true, // grab outer container as well as the contents of the selector
  23. * loadCSS: "path/to/my.css", // path to additional css file - us an array [] for multiple
  24. * pageTitle: "", // add title to print page
  25. * removeInline: false, // remove all inline styles from print elements
  26. * printDelay: 333, // variable print delay
  27. * header: null, // prefix to html
  28. * footer: null, // postfix to html
  29. * base: false, // preserve the BASE tag, or accept a string for the URL
  30. * formValues: true, // preserve input/form values
  31. * canvas: false, // copy canvas elements (experimental)
  32. * doctypeString: '...', // enter a different doctype for older markup
  33. * removeScripts: false // remove script tags from print content
  34. * });
  35. *
  36. * Notes:
  37. * - the loadCSS will load additional css (with or without @media print) into the iframe, adjusting layout
  38. */
  39. ;
  40. (function($) {
  41. function appendContent($el, content) {
  42. if (!content) return;
  43. // Simple test for a jQuery element
  44. $el.append(content.jquery ? content.clone() : content);
  45. }
  46. function appendBody($body, $element, opt) {
  47. // Clone for safety and convenience
  48. var $content = $element.clone();
  49. if (opt.removeScripts) {
  50. $content.find('script').remove();
  51. }
  52. if (opt.printContainer) {
  53. // grab $.selector as container
  54. $body.append($("<div/>").html($content).html());
  55. } else {
  56. // otherwise just print interior elements of container
  57. $content.each(function() {
  58. $body.append($(this).html());
  59. });
  60. }
  61. }
  62. var opt;
  63. $.fn.printThis = function(options) {
  64. opt = $.extend({}, $.fn.printThis.defaults, options);
  65. var $element = this instanceof jQuery ? this : $(this);
  66. var strFrameName = "printThis-" + (new Date()).getTime();
  67. if (window.location.hostname !== document.domain && navigator.userAgent.match(/msie/i)) {
  68. // Ugly IE hacks due to IE not inheriting document.domain from parent
  69. // checks if document.domain is set by comparing the host name against document.domain
  70. var iframeSrc = "javascript:document.write(\"<head><script>document.domain=\\\"" + document.domain + "\\\";</s" + "cript></head><body></body>\")";
  71. var printI = document.createElement('iframe');
  72. printI.name = "printIframe";
  73. printI.id = strFrameName;
  74. printI.className = "MSIE";
  75. document.body.appendChild(printI);
  76. printI.src = iframeSrc;
  77. } else {
  78. // other browsers inherit document.domain, and IE works if document.domain is not explicitly set
  79. var $frame = $("<iframe id='" + strFrameName + "' name='printIframe' />");
  80. $frame.appendTo("body");
  81. }
  82. var $iframe = $("#" + strFrameName);
  83. // show frame if in debug mode
  84. if (!opt.debug) $iframe.css({
  85. position: "absolute",
  86. width: "0px",
  87. height: "0px",
  88. left: "-600px",
  89. top: "-600px"
  90. });
  91. // $iframe.ready() and $iframe.load were inconsistent between browsers
  92. setTimeout(function() {
  93. // Add doctype to fix the style difference between printing and render
  94. function setDocType($iframe, doctype){
  95. var win, doc;
  96. win = $iframe.get(0);
  97. win = win.contentWindow || win.contentDocument || win;
  98. doc = win.document || win.contentDocument || win;
  99. doc.open();
  100. doc.write(doctype);
  101. doc.close();
  102. }
  103. if (opt.doctypeString){
  104. setDocType($iframe, opt.doctypeString);
  105. }
  106. var $doc = $iframe.contents(),
  107. $head = $doc.find("head"),
  108. $body = $doc.find("body"),
  109. $base = $('base'),
  110. baseURL;
  111. // add base tag to ensure elements use the parent domain
  112. if (opt.base === true && $base.length > 0) {
  113. // take the base tag from the original page
  114. baseURL = $base.attr('href');
  115. } else if (typeof opt.base === 'string') {
  116. // An exact base string is provided
  117. baseURL = opt.base;
  118. } else {
  119. // Use the page URL as the base
  120. baseURL = document.location.protocol + '//' + document.location.host;
  121. }
  122. $head.append('<base href="' + baseURL + '">');
  123. // import page stylesheets
  124. if (opt.importCSS) $("link[rel=stylesheet]").each(function() {
  125. var href = $(this).attr("href");
  126. if (href) {
  127. var media = $(this).attr("media") || "all";
  128. $head.append("<link type='text/css' rel='stylesheet' href='" + href + "' media='" + media + "'>");
  129. }
  130. });
  131. // import style tags
  132. if (opt.importStyle) $("style").each(function() {
  133. $(this).clone().appendTo($head);
  134. });
  135. // add title of the page
  136. if (opt.pageTitle) $head.append("<title>" + opt.pageTitle + "</title>");
  137. // import additional stylesheet(s)
  138. if (opt.loadCSS) {
  139. if ($.isArray(opt.loadCSS)) {
  140. jQuery.each(opt.loadCSS, function(index, value) {
  141. $head.append("<link type='text/css' rel='stylesheet' href='" + this + "'>");
  142. });
  143. } else {
  144. $head.append("<link type='text/css' rel='stylesheet' href='" + opt.loadCSS + "'>");
  145. }
  146. }
  147. // print header
  148. appendContent($body, opt.header);
  149. if (opt.canvas) {
  150. // add canvas data-ids for easy access after cloning.
  151. var canvasId = 0;
  152. $element.find('canvas').each(function(){
  153. $(this).attr('data-printthis', canvasId++);
  154. });
  155. }
  156. appendBody($body, $element, opt);
  157. if (opt.canvas) {
  158. // Re-draw new canvases by referencing the originals
  159. $body.find('canvas').each(function(){
  160. var cid = $(this).data('printthis'),
  161. $src = $('[data-printthis="' + cid + '"]');
  162. this.getContext('2d').drawImage($src[0], 0, 0);
  163. // Remove the markup from the original
  164. $src.removeData('printthis');
  165. });
  166. }
  167. // capture form/field values
  168. if (opt.formValues) {
  169. // loop through inputs
  170. var $input = $element.find('input');
  171. if ($input.length) {
  172. $input.each(function() {
  173. var $this = $(this),
  174. $name = $(this).attr('name'),
  175. $checker = $this.is(':checkbox') || $this.is(':radio'),
  176. $iframeInput = $doc.find('input[name="' + $name + '"]'),
  177. $value = $this.val();
  178. // order matters here
  179. if (!$checker) {
  180. $iframeInput.val($value);
  181. } else if ($this.is(':checked')) {
  182. if ($this.is(':checkbox')) {
  183. $iframeInput.attr('checked', 'checked');
  184. } else if ($this.is(':radio')) {
  185. $doc.find('input[name="' + $name + '"][value="' + $value + '"]').attr('checked', 'checked');
  186. }
  187. }
  188. });
  189. }
  190. // loop through selects
  191. var $select = $element.find('select');
  192. if ($select.length) {
  193. $select.each(function() {
  194. var $this = $(this),
  195. $name = $(this).attr('name'),
  196. $value = $this.val();
  197. $doc.find('select[name="' + $name + '"]').val($value);
  198. });
  199. }
  200. // loop through textareas
  201. var $textarea = $element.find('textarea');
  202. if ($textarea.length) {
  203. $textarea.each(function() {
  204. var $this = $(this),
  205. $name = $(this).attr('name'),
  206. $value = $this.val();
  207. $doc.find('textarea[name="' + $name + '"]').val($value);
  208. });
  209. }
  210. } // end capture form/field values
  211. // remove inline styles
  212. if (opt.removeInline) {
  213. // $.removeAttr available jQuery 1.7+
  214. if ($.isFunction($.removeAttr)) {
  215. $doc.find("body *").removeAttr("style");
  216. } else {
  217. $doc.find("body *").attr("style", "");
  218. }
  219. }
  220. // print "footer"
  221. appendContent($body, opt.footer);
  222. setTimeout(function() {
  223. if ($iframe.hasClass("MSIE")) {
  224. // check if the iframe was created with the ugly hack
  225. // and perform another ugly hack out of neccessity
  226. window.frames["printIframe"].focus();
  227. $head.append("<script> window.print(); </s" + "cript>");
  228. } else {
  229. // proper method
  230. if (document.queryCommandSupported("print")) {
  231. $iframe[0].contentWindow.document.execCommand("print", false, null);
  232. } else {
  233. $iframe[0].contentWindow.focus();
  234. $iframe[0].contentWindow.print();
  235. }
  236. }
  237. // remove iframe after print
  238. if (!opt.debug) {
  239. setTimeout(function() {
  240. $iframe.remove();
  241. }, 1000);
  242. }
  243. }, opt.printDelay);
  244. }, 333);
  245. };
  246. // defaults
  247. $.fn.printThis.defaults = {
  248. debug: false, // show the iframe for debugging
  249. importCSS: true, // import parent page css
  250. importStyle: false, // import style tags
  251. printContainer: true, // print outer container/$.selector
  252. loadCSS: "", // load an additional css file - load multiple stylesheets with an array []
  253. pageTitle: "", // add title to print page
  254. removeInline: false, // remove all inline styles
  255. printDelay: 333, // variable print delay
  256. header: null, // prefix to html
  257. footer: null, // postfix to html
  258. formValues: true, // preserve input/form values
  259. canvas: false, // copy canvas content (experimental)
  260. base: false, // preserve the BASE tag, or accept a string for the URL
  261. doctypeString: '<!DOCTYPE html>', // html doctype
  262. removeScripts: false // remove script tags before appending
  263. };
  264. })(jQuery);