UNPKG

13.6 kBJavaScriptView Raw
1/**
2 * PDFObject v2.2.7
3 * https://github.com/pipwerks/PDFObject
4 * @license
5 * Copyright (c) 2008-2021 Philip Hutchison
6 * MIT-style license: http://pipwerks.mit-license.org/
7 * UMD module pattern from https://github.com/umdjs/umd/blob/master/templates/returnExports.js
8 */
9
10(function (root, factory) {
11 if (typeof define === "function" && define.amd) {
12 // AMD. Register as an anonymous module.
13 define([], factory);
14 } else if (typeof module === "object" && module.exports) {
15 // Node. Does not work with strict CommonJS, but
16 // only CommonJS-like environments that support module.exports,
17 // like Node.
18 module.exports = factory();
19 } else {
20 // Browser globals (root is window)
21 root.PDFObject = factory();
22 }
23}(this, function () {
24
25 "use strict";
26
27 //PDFObject is designed for client-side (browsers), not server-side (node)
28 //Will choke on undefined navigator and window vars when run on server
29 //Return boolean false and exit function when running server-side
30
31 if( typeof window === "undefined" ||
32 window.navigator === undefined ||
33 window.navigator.userAgent === undefined ||
34 window.navigator.mimeTypes === undefined){
35 return false;
36 }
37
38 let pdfobjectversion = "2.2.7";
39 let nav = window.navigator;
40 let ua = window.navigator.userAgent;
41
42 //Time to jump through hoops -- browser vendors do not make it easy to detect PDF support.
43
44 /*
45 IE11 still uses ActiveX for Adobe Reader, but IE 11 doesn't expose window.ActiveXObject the same way
46 previous versions of IE did. window.ActiveXObject will evaluate to false in IE 11, but "ActiveXObject"
47 in window evaluates to true.
48
49 MS Edge does not support ActiveX so this test will evaluate false
50 */
51 let isIE = ("ActiveXObject" in window);
52
53 /*
54 There is a coincidental correlation between implementation of window.promises and native PDF support in desktop browsers
55 We use this to assume if the browser supports promises it supports embedded PDFs
56 Is this fragile? Sort of. But browser vendors removed mimetype detection, so we're left to improvise
57 */
58 let isModernBrowser = (window.Promise !== undefined);
59
60 //Older browsers still expose the mimeType
61 let supportsPdfMimeType = (nav.mimeTypes["application/pdf"] !== undefined);
62
63 //Safari on iPadOS doesn't report as 'mobile' when requesting desktop site, yet still fails to embed PDFs
64 let isSafariIOSDesktopMode = ( nav.platform !== undefined &&
65 nav.platform === "MacIntel" &&
66 nav.maxTouchPoints !== undefined &&
67 nav.maxTouchPoints > 1 );
68
69 //Quick test for mobile devices.
70 let isMobileDevice = (isSafariIOSDesktopMode || /Mobi|Tablet|Android|iPad|iPhone/.test(ua));
71
72 //Safari desktop requires special handling
73 let isSafariDesktop = ( !isMobileDevice &&
74 nav.vendor !== undefined &&
75 /Apple/.test(nav.vendor) &&
76 /Safari/.test(ua) );
77
78 //Firefox started shipping PDF.js in Firefox 19. If this is Firefox 19 or greater, assume PDF.js is available
79 let isFirefoxWithPDFJS = (!isMobileDevice && /irefox/.test(ua) && ua.split("rv:").length > 1) ? (parseInt(ua.split("rv:")[1].split(".")[0], 10) > 18) : false;
80
81
82 /* ----------------------------------------------------
83 Supporting functions
84 ---------------------------------------------------- */
85
86 let createAXO = function (type){
87 var ax;
88 try {
89 ax = new ActiveXObject(type);
90 } catch (e) {
91 ax = null; //ensure ax remains null
92 }
93 return ax;
94 };
95
96 //If either ActiveX support for "AcroPDF.PDF" or "PDF.PdfCtrl" are found, return true
97 //Constructed as a method (not a prop) to avoid unneccesarry overhead -- will only be evaluated if needed
98 let supportsPdfActiveX = function (){ return !!(createAXO("AcroPDF.PDF") || createAXO("PDF.PdfCtrl")); };
99
100 //Determines whether PDF support is available
101 let supportsPDFs = (
102 //As of Sept 2020 no mobile browsers properly support PDF embeds
103 !isMobileDevice && (
104 //We're moving into the age of MIME-less browsers. They mostly all support PDF rendering without plugins.
105 isModernBrowser ||
106 //Modern versions of Firefox come bundled with PDFJS
107 isFirefoxWithPDFJS ||
108 //Browsers that still support the original MIME type check
109 supportsPdfMimeType ||
110 //Pity the poor souls still using IE
111 (isIE && supportsPdfActiveX())
112 )
113 );
114
115 //Create a fragment identifier for using PDF Open parameters when embedding PDF
116 let buildURLFragmentString = function(pdfParams){
117
118 let string = "";
119 let prop;
120
121 if(pdfParams){
122
123 for (prop in pdfParams) {
124 if (pdfParams.hasOwnProperty(prop)) {
125 string += encodeURIComponent(prop) + "=" + encodeURIComponent(pdfParams[prop]) + "&";
126 }
127 }
128
129 //The string will be empty if no PDF Params found
130 if(string){
131
132 string = "#" + string;
133
134 //Remove last ampersand
135 string = string.slice(0, string.length - 1);
136
137 }
138
139 }
140
141 return string;
142
143 };
144
145 let embedError = function (msg, suppressConsole){
146 if(!suppressConsole){
147 console.log("[PDFObject] " + msg);
148 }
149 return false;
150 };
151
152 let emptyNodeContents = function (node){
153 while(node.firstChild){
154 node.removeChild(node.firstChild);
155 }
156 };
157
158 let getTargetElement = function (targetSelector){
159
160 //Default to body for full-browser PDF
161 let targetNode = document.body;
162
163 //If a targetSelector is specified, check to see whether
164 //it's passing a selector, jQuery object, or an HTML element
165
166 if(typeof targetSelector === "string"){
167
168 //Is CSS selector
169 targetNode = document.querySelector(targetSelector);
170
171 } else if (window.jQuery !== undefined && targetSelector instanceof jQuery && targetSelector.length) {
172
173 //Is jQuery element. Extract HTML node
174 targetNode = targetSelector.get(0);
175
176 } else if (targetSelector.nodeType !== undefined && targetSelector.nodeType === 1){
177
178 //Is HTML element
179 targetNode = targetSelector;
180
181 }
182
183 return targetNode;
184
185 };
186
187 let generatePDFJSMarkup = function (targetNode, url, pdfOpenFragment, PDFJS_URL, id, title, omitInlineStyles){
188
189 //Ensure target element is empty first
190 emptyNodeContents(targetNode);
191
192 let fullURL = PDFJS_URL + "?file=" + encodeURIComponent(url) + pdfOpenFragment;
193 let div = document.createElement("div");
194 let iframe = document.createElement("iframe");
195
196 iframe.src = fullURL;
197 iframe.className = "pdfobject";
198 iframe.type = "application/pdf";
199 iframe.frameborder = "0";
200 iframe.allow = "fullscreen";
201 iframe.title = title;
202
203 if(id){
204 iframe.id = id;
205 }
206
207 if(!omitInlineStyles){
208 div.style.cssText = "position: absolute; top: 0; right: 0; bottom: 0; left: 0;";
209 iframe.style.cssText = "border: none; width: 100%; height: 100%;";
210 targetNode.style.position = "relative";
211 targetNode.style.overflow = "auto";
212 }
213
214 div.appendChild(iframe);
215 targetNode.appendChild(div);
216 targetNode.classList.add("pdfobject-container");
217
218 return targetNode.getElementsByTagName("iframe")[0];
219
220 };
221
222 let generatePDFObjectMarkup = function (embedType, targetNode, targetSelector, url, pdfOpenFragment, width, height, id, title, omitInlineStyles){
223
224 //Ensure target element is empty first
225 emptyNodeContents(targetNode);
226
227 let embed = document.createElement(embedType);
228 embed.src = url + pdfOpenFragment;
229 embed.className = "pdfobject";
230 embed.type = "application/pdf";
231 embed.title = title;
232
233 if(id){
234 embed.id = id;
235 }
236
237 if(embedType === "iframe"){
238 embed.allow = "fullscreen";
239 }
240
241 if(!omitInlineStyles){
242
243 let style = (embedType === "embed") ? "overflow: auto;" : "border: none;";
244
245 if(targetSelector && targetSelector !== document.body){
246 style += "width: " + width + "; height: " + height + ";";
247 } else {
248 style += "position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%;";
249 }
250
251 embed.style.cssText = style;
252
253 }
254
255 targetNode.classList.add("pdfobject-container");
256 targetNode.appendChild(embed);
257
258 return targetNode.getElementsByTagName(embedType)[0];
259
260 };
261
262 let embed = function(url, targetSelector, options){
263
264 //If targetSelector is not defined, convert to boolean
265 let selector = targetSelector || false;
266
267 //Ensure options object is not undefined -- enables easier error checking below
268 let opt = options || {};
269
270 //Get passed options, or set reasonable defaults
271 let id = (typeof opt.id === "string") ? opt.id : "";
272 let page = opt.page || false;
273 let pdfOpenParams = opt.pdfOpenParams || {};
274 let fallbackLink = opt.fallbackLink || true;
275 let width = opt.width || "100%";
276 let height = opt.height || "100%";
277 let title = opt.title || "Embedded PDF";
278 let assumptionMode = (typeof opt.assumptionMode === "boolean") ? opt.assumptionMode : true;
279 let forcePDFJS = (typeof opt.forcePDFJS === "boolean") ? opt.forcePDFJS : false;
280 let supportRedirect = (typeof opt.supportRedirect === "boolean") ? opt.supportRedirect : false;
281 let omitInlineStyles = (typeof opt.omitInlineStyles === "boolean") ? opt.omitInlineStyles : false;
282 let suppressConsole = (typeof opt.suppressConsole === "boolean") ? opt.suppressConsole : false;
283 let forceIframe = (typeof opt.forceIframe === "boolean") ? opt.forceIframe : false;
284 let PDFJS_URL = opt.PDFJS_URL || false;
285 let targetNode = getTargetElement(selector);
286 let fallbackHTML = "";
287 let pdfOpenFragment = "";
288 let fallbackHTML_default = "<p>This browser does not support inline PDFs. Please download the PDF to view it: <a href='[url]'>Download PDF</a></p>";
289
290 //Ensure URL is available. If not, exit now.
291 if(typeof url !== "string"){ return embedError("URL is not valid", suppressConsole); }
292
293 //If target element is specified but is not valid, exit without doing anything
294 if(!targetNode){ return embedError("Target element cannot be determined", suppressConsole); }
295
296 //page option overrides pdfOpenParams, if found
297 if(page){ pdfOpenParams.page = page; }
298
299 //Stringify optional Adobe params for opening document (as fragment identifier)
300 pdfOpenFragment = buildURLFragmentString(pdfOpenParams);
301
302
303 // --== Do the dance: Embed attempt #1 ==--
304
305 //If the forcePDFJS option is invoked, skip everything else and embed as directed
306 if(forcePDFJS && PDFJS_URL){
307 return generatePDFJSMarkup(targetNode, url, pdfOpenFragment, PDFJS_URL, id, title, omitInlineStyles);
308 }
309
310 // --== Embed attempt #2 ==--
311
312 //Embed PDF if traditional support is provided, or if this developer is willing to roll with assumption
313 //that modern desktop (not mobile) browsers natively support PDFs
314 if(supportsPDFs || (assumptionMode && !isMobileDevice)){
315
316 //Should we use <embed> or <iframe>? In most cases <embed>.
317 //Allow developer to force <iframe>, if desired
318 //There is an edge case where Safari does not respect 302 redirect requests for PDF files when using <embed> element.
319 //Redirect appears to work fine when using <iframe> instead of <embed> (Addresses issue #210)
320 //Forcing Safari desktop to use iframe due to freezing bug in macOS 11 (Big Sur)
321 let embedtype = (forceIframe || supportRedirect || isSafariDesktop) ? "iframe" : "embed";
322
323 return generatePDFObjectMarkup(embedtype, targetNode, targetSelector, url, pdfOpenFragment, width, height, id, title, omitInlineStyles);
324
325 }
326
327 // --== Embed attempt #3 ==--
328
329 //If everything else has failed and a PDFJS fallback is provided, try to use it
330 if(PDFJS_URL){
331 return generatePDFJSMarkup(targetNode, url, pdfOpenFragment, PDFJS_URL, id, title, omitInlineStyles);
332 }
333
334 // --== PDF embed not supported! Use fallback ==--
335
336 //Display the fallback link if available
337 if(fallbackLink){
338
339 fallbackHTML = (typeof fallbackLink === "string") ? fallbackLink : fallbackHTML_default;
340 targetNode.innerHTML = fallbackHTML.replace(/\[url\]/g, url);
341
342 }
343
344 return embedError("This browser does not support embedded PDFs", suppressConsole);
345
346 };
347
348 return {
349 embed: function (a,b,c){ return embed(a,b,c); },
350 pdfobjectversion: (function () { return pdfobjectversion; })(),
351 supportsPDFs: (function (){ return supportsPDFs; })()
352 };
353
354}));