PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.21
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.21
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
fluent-cart / assets / public / lib / printThis-2.0.0.min.js

printThis-2.0.0.min.js in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.3.21, at assets/public/lib/printThis-2.0.0.min.js

400 lines 14.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*
2 * printThis v2.0.0 - Vanilla JavaScript Version
3 * @desc Printing utility for vanilla JavaScript (converted from jQuery plugin)
4 * @author Jason Day (original jQuery version)
5 * @author Samuel Rouse (original jQuery version)
6 * @author Converted to vanilla JS
7 *
8 * Resources (based on):
9 * - jPrintArea: http://plugins.jquery.com/project/jPrintArea
10 * - jqPrint: https://github.com/permanenttourist/jquery.jqprint
11 * - Ben Nadel: http://www.bennadel.com/blog/1591-Ask-Ben-Print-Part-Of-A-Web-Page-With-jQuery.htm
12 *
13 * Licensed under the MIT licence:
14 * http://www.opensource.org/licenses/mit-license.php
15 *
16 * (c) Jason Day 2015-2022
17 *
18 * Usage:
19 *
20 * printThis(document.querySelector("#mySelector"), {
21 * debug: false, // show the iframe for debugging
22 * importCSS: true, // import parent page css
23 * importStyle: true, // import style tags
24 * printContainer: true, // grab outer container as well as the contents of the selector
25 * loadCSS: "path/to/my.css", // path to additional css file - use an array [] for multiple
26 * pageTitle: "", // add title to print page
27 * removeInline: false, // remove all inline styles from print elements
28 * removeInlineSelector: "body *", // custom selectors to filter inline styles. removeInline must be true
29 * printDelay: 1000, // variable print delay
30 * header: null, // prefix to html
31 * footer: null, // postfix to html
32 * base: false, // preserve the BASE tag, or accept a string for the URL
33 * formValues: true, // preserve input/form values
34 * canvas: true, // copy canvas elements
35 * doctypeString: '<!DOCTYPE html>', // enter a different doctype for older markup
36 * removeScripts: false, // remove script tags from print content
37 * copyTagClasses: true, // copy classes from the html & body tag
38 * copyTagStyles: true, // copy styles from html & body tag (for CSS Variables)
39 * beforePrintEvent: null, // callback function for printEvent in iframe
40 * beforePrint: null, // function called before iframe is filled
41 * afterPrint: null // function called before iframe is removed
42 * });
43 *
44 * Notes:
45 * - the loadCSS will load additional CSS (with or without @media print) into the iframe, adjusting layout
46 */
47
48 (function(window) {
49 'use strict';
50
51 // Default options
52 const defaults = {
53 debug: false,
54 importCSS: true,
55 importStyle: true,
56 printContainer: true,
57 loadCSS: "",
58 pageTitle: "",
59 removeInline: false,
60 removeInlineSelector: "*",
61 printDelay: 1000,
62 header: null,
63 footer: null,
64 base: false,
65 formValues: true,
66 canvas: true,
67 doctypeString: '<!DOCTYPE html>',
68 removeScripts: false,
69 copyTagClasses: true,
70 copyTagStyles: true,
71 beforePrintEvent: null,
72 beforePrint: null,
73 afterPrint: null
74 };
75
76 // Utility functions
77 function extend(target, ...sources) {
78 sources.forEach(source => {
79 if (source) {
80 Object.keys(source).forEach(key => {
81 target[key] = source[key];
82 });
83 }
84 });
85 return target;
86 }
87
88 function isArray(obj) {
89 return Array.isArray(obj);
90 }
91
92 function appendContent(container, content) {
93 if (!content) return;
94
95 if (typeof content === 'string') {
96 container.insertAdjacentHTML('beforeend', content);
97 } else if (content.nodeType) {
98 container.appendChild(content.cloneNode(true));
99 }
100 }
101
102 function cloneElement(element, deep = true) {
103 return element.cloneNode(deep);
104 }
105
106 function copyValues(origin, clone, elementSelector) {
107 const originalElements = origin.querySelectorAll(elementSelector);
108 const clonedElements = clone.querySelectorAll(elementSelector);
109
110 clonedElements.forEach((item, index) => {
111 if (originalElements[index]) {
112 item.value = originalElements[index].value;
113 }
114 });
115 }
116
117 function appendBody(body, element, opt) {
118 // Clone the element
119 let content = cloneElement(element, true);
120
121 if (opt.formValues) {
122 // Copy original select and textarea values to their cloned counterpart
123 copyValues(element, content, 'select, textarea');
124 }
125
126 if (opt.removeScripts) {
127 const scripts = content.querySelectorAll('script');
128 scripts.forEach(script => script.remove());
129 }
130
131 if (opt.printContainer) {
132 // Grab element as container
133 body.appendChild(content);
134 } else {
135 // Otherwise just print interior elements of container
136 while (content.firstChild) {
137 body.appendChild(content.firstChild);
138 }
139 }
140 }
141
142 function setDocType(iframe, doctype) {
143 const win = iframe.contentWindow || iframe.contentDocument || iframe;
144 const doc = win.document || win.contentDocument || win;
145 doc.open();
146 doc.write(doctype);
147 doc.close();
148 }
149
150 function attachOnBeforePrintEvent(iframe, beforePrintHandler) {
151 const win = iframe.contentWindow || iframe.contentDocument || iframe;
152
153 if (typeof beforePrintHandler === "function") {
154 if ('matchMedia' in win) {
155 win.matchMedia('print').addListener(function(mql) {
156 if (mql.matches) beforePrintHandler();
157 });
158 } else {
159 win.onbeforeprint = beforePrintHandler;
160 }
161 }
162 }
163
164 // Main printThis function
165 function printThis(element, options = {}) {
166 const opt = extend({}, defaults, options);
167
168 // Handle multiple elements or single element
169 const elements = element.nodeType ? [element] : Array.from(element);
170
171 elements.forEach(el => {
172 processPrint(el, opt);
173 });
174 }
175
176 function processPrint(element, opt) {
177 const strFrameName = "printThis-" + (new Date()).getTime();
178 let iframe;
179
180 if (window.location.hostname !== document.domain && navigator.userAgent.match(/msie/i)) {
181 // Ugly IE hacks due to IE not inheriting document.domain from parent
182 const iframeSrc = "javascript:document.write(\"<head><script>document.domain=\\\"" + document.domain + "\\\";</s" + "cript></head><body></body>\")";
183 iframe = document.createElement('iframe');
184 iframe.name = "printIframe";
185 iframe.id = strFrameName;
186 iframe.className = "MSIE";
187 document.body.appendChild(iframe);
188 iframe.src = iframeSrc;
189 } else {
190 // Other browsers inherit document.domain
191 iframe = document.createElement('iframe');
192 iframe.id = strFrameName;
193 iframe.name = 'printIframe';
194 document.body.appendChild(iframe);
195 }
196
197 // Show frame if in debug mode
198 if (!opt.debug) {
199 iframe.style.position = "absolute";
200 iframe.style.width = "0px";
201 iframe.style.height = "0px";
202 iframe.style.left = "-600px";
203 iframe.style.top = "-600px";
204 }
205
206 // Before print callback
207 if (typeof opt.beforePrint === "function") {
208 opt.beforePrint();
209 }
210
211 setTimeout(() => {
212 // Add doctype to fix the style difference between printing and render
213 if (opt.doctypeString) {
214 setDocType(iframe, opt.doctypeString);
215 }
216
217 const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
218 const head = iframeDoc.querySelector("head") || iframeDoc.createElement("head");
219 const body = iframeDoc.querySelector("body") || iframeDoc.createElement("body");
220
221 if (!iframeDoc.querySelector("head")) {
222 iframeDoc.documentElement.appendChild(head);
223 }
224 if (!iframeDoc.querySelector("body")) {
225 iframeDoc.documentElement.appendChild(body);
226 }
227
228 const baseElements = document.querySelectorAll('base');
229 let baseURL;
230
231 // Add base tag to ensure elements use the parent domain
232 if (opt.base === true && baseElements.length > 0) {
233 baseURL = baseElements[0].getAttribute('href');
234 } else if (typeof opt.base === 'string') {
235 baseURL = opt.base;
236 } else {
237 baseURL = document.location.protocol + '//' + document.location.host;
238 }
239
240 head.insertAdjacentHTML('beforeend', '<base href="' + baseURL + '">');
241
242 // Import page stylesheets
243 if (opt.importCSS) {
244 const links = document.querySelectorAll("link[rel=stylesheet]");
245 links.forEach(link => {
246 const href = link.getAttribute("href");
247 if (href) {
248 const media = link.getAttribute("media") || "all";
249 head.insertAdjacentHTML('beforeend',
250 "<link type='text/css' rel='stylesheet' href='" + href + "' media='" + media + "'>");
251 }
252 });
253 }
254
255 // Import style tags
256 if (opt.importStyle) {
257 const styles = document.querySelectorAll("style");
258 styles.forEach(style => {
259 head.appendChild(style.cloneNode(true));
260 });
261 }
262
263 // Add title of the page
264 if (opt.pageTitle) {
265 head.insertAdjacentHTML('beforeend', "<title>" + opt.pageTitle + "</title>");
266 }
267
268 // Import additional stylesheet(s)
269 if (opt.loadCSS) {
270 if (isArray(opt.loadCSS)) {
271 opt.loadCSS.forEach(css => {
272 head.insertAdjacentHTML('beforeend', "<link type='text/css' rel='stylesheet' href='" + css + "'>");
273 });
274 } else {
275 head.insertAdjacentHTML('beforeend', "<link type='text/css' rel='stylesheet' href='" + opt.loadCSS + "'>");
276 }
277 }
278
279 const pageHtml = document.documentElement;
280 const htmlElement = iframeDoc.documentElement;
281
282 // CSS VAR in html tag when dynamic apply
283 htmlElement.style.cssText = pageHtml.style.cssText;
284
285 // Copy 'root' tag classes
286 let tag = opt.copyTagClasses;
287 if (tag) {
288 tag = tag === true ? 'bh' : tag;
289 if (tag.indexOf('b') !== -1) {
290 body.className = document.body.className;
291 }
292 if (tag.indexOf('h') !== -1) {
293 htmlElement.className = pageHtml.className;
294 }
295 }
296
297 // Copy ':root' tag styles
298 tag = opt.copyTagStyles;
299 if (tag) {
300 tag = tag === true ? 'bh' : tag;
301 if (tag.indexOf('b') !== -1) {
302 body.style.cssText = document.body.style.cssText;
303 }
304 if (tag.indexOf('h') !== -1) {
305 htmlElement.style.cssText = pageHtml.style.cssText;
306 }
307 }
308
309 // Print header
310 appendContent(body, opt.header);
311
312 if (opt.canvas) {
313 // Add canvas data-ids for easy access after cloning
314 let canvasId = 0;
315 const canvases = element.querySelectorAll('canvas');
316
317 // Check if the element itself is a canvas
318 if (element.tagName === 'CANVAS') {
319 element.setAttribute('data-printthis', canvasId++);
320 }
321
322 canvases.forEach(canvas => {
323 canvas.setAttribute('data-printthis', canvasId++);
324 });
325 }
326
327 appendBody(body, element, opt);
328
329 if (opt.canvas) {
330 // Re-draw new canvases by referencing the originals
331 const iframeCanvases = body.querySelectorAll('canvas');
332 iframeCanvases.forEach(canvas => {
333 const cid = canvas.getAttribute('data-printthis');
334 const srcCanvas = document.querySelector('[data-printthis="' + cid + '"]');
335
336 if (srcCanvas) {
337 const ctx = canvas.getContext('2d');
338 ctx.drawImage(srcCanvas, 0, 0);
339
340 // Remove the markup from the original
341 srcCanvas.removeAttribute('data-printthis');
342 }
343 });
344 }
345
346 // Remove inline styles
347 if (opt.removeInline) {
348 const selector = opt.removeInlineSelector || '*';
349 const elements = body.querySelectorAll(selector);
350 elements.forEach(el => {
351 el.removeAttribute("style");
352 });
353 }
354
355 // Print footer
356 appendContent(body, opt.footer);
357
358 // Attach event handler function to beforePrint event
359 attachOnBeforePrintEvent(iframe, opt.beforePrintEvent);
360
361 setTimeout(() => {
362 if (iframe.className === "MSIE") {
363 // IE hack
364 window.frames["printIframe"].focus();
365 head.insertAdjacentHTML('beforeend', "<script>window.print();</script>");
366 } else {
367 // Proper method
368 if (document.queryCommandSupported && document.queryCommandSupported("print")) {
369 iframe.contentWindow.document.execCommand("print", false, null);
370 } else {
371 iframe.contentWindow.focus();
372 iframe.contentWindow.print();
373 }
374 }
375
376 // Remove iframe after print
377 if (!opt.debug) {
378 setTimeout(() => {
379 iframe.remove();
380 }, 1000);
381 }
382
383 // After print callback
384 if (typeof opt.afterPrint === "function") {
385 opt.afterPrint();
386 }
387
388 }, opt.printDelay);
389
390 }, 333);
391 }
392
393 // Expose printThis to global scope
394 if (typeof module !== 'undefined' && module.exports) {
395 module.exports = printThis;
396 } else {
397 window.printThis = printThis;
398 }
399
400 })(typeof window !== 'undefined' ? window : this);