UNPKG

13.1 kBJavaScriptView Raw
1/**
2 * PDFObject v2.2.4
3 * https://github.com/pipwerks/PDFObject
4 * @license
5 * Copyright (c) 2008-2020 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.3";
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)) ? (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 //Modern versions of Firefox come bundled with PDFJS
105 isFirefoxWithPDFJS ||
106 //Browsers that still support the original MIME type check
107 supportsPdfMimeType ||
108 //Pity the poor souls still using IE
109 (isIE && supportsPdfActiveX())
110 )
111 );
112
113 //Create a fragment identifier for using PDF Open parameters when embedding PDF
114 let buildURLFragmentString = function(pdfParams){
115
116 let string = "";
117 let prop;
118
119 if(pdfParams){
120
121 for (prop in pdfParams) {
122 if (pdfParams.hasOwnProperty(prop)) {
123 string += encodeURIComponent(prop) + "=" + encodeURIComponent(pdfParams[prop]) + "&";
124 }
125 }
126
127 //The string will be empty if no PDF Params found
128 if(string){
129
130 string = "#" + string;
131
132 //Remove last ampersand
133 string = string.slice(0, string.length - 1);
134
135 }
136
137 }
138
139 return string;
140
141 };
142
143 let embedError = function (msg, suppressConsole){
144 if(!suppressConsole){
145 console.log("[PDFObject] " + msg);
146 }
147 return false;
148 };
149
150 let emptyNodeContents = function (node){
151 while(node.firstChild){
152 node.removeChild(node.firstChild);
153 }
154 };
155
156 let getTargetElement = function (targetSelector){
157
158 //Default to body for full-browser PDF
159 let targetNode = document.body;
160
161 //If a targetSelector is specified, check to see whether
162 //it's passing a selector, jQuery object, or an HTML element
163
164 if(typeof targetSelector === "string"){
165
166 //Is CSS selector
167 targetNode = document.querySelector(targetSelector);
168
169 } else if (window.jQuery !== undefined && targetSelector instanceof jQuery && targetSelector.length) {
170
171 //Is jQuery element. Extract HTML node
172 targetNode = targetSelector.get(0);
173
174 } else if (targetSelector.nodeType !== undefined && targetSelector.nodeType === 1){
175
176 //Is HTML element
177 targetNode = targetSelector;
178
179 }
180
181 return targetNode;
182
183 };
184
185 let generatePDFJSMarkup = function (targetNode, url, pdfOpenFragment, PDFJS_URL, id, omitInlineStyles){
186
187 //Ensure target element is empty first
188 emptyNodeContents(targetNode);
189
190 let fullURL = PDFJS_URL + "?file=" + encodeURIComponent(url) + pdfOpenFragment;
191 let div = document.createElement("div");
192 let iframe = document.createElement("iframe");
193
194 iframe.src = fullURL;
195 iframe.className = "pdfobject";
196 iframe.type = "application/pdf";
197 iframe.frameborder = "0";
198
199 if(id){
200 iframe.id = id;
201 }
202
203 if(!omitInlineStyles){
204 div.style.cssText = "position: absolute; top: 0; right: 0; bottom: 0; left: 0;";
205 iframe.style.cssText = "border: none; width: 100%; height: 100%;";
206 targetNode.style.position = "relative";
207 targetNode.style.overflow = "auto";
208 }
209
210 div.appendChild(iframe);
211 targetNode.appendChild(div);
212 targetNode.classList.add("pdfobject-container");
213
214 return targetNode.getElementsByTagName("iframe")[0];
215
216 };
217
218 let generatePDFObjectMarkup = function (embedType, targetNode, targetSelector, url, pdfOpenFragment, width, height, id, omitInlineStyles){
219
220 //Ensure target element is empty first
221 emptyNodeContents(targetNode);
222
223 let embed = document.createElement(embedType);
224 embed.src = url + pdfOpenFragment;
225 embed.className = "pdfobject";
226 embed.type = "application/pdf";
227
228 if(id){
229 embed.id = id;
230 }
231
232 if(!omitInlineStyles){
233
234 let style = (embedType === "embed") ? "overflow: auto;" : "border: none;";
235
236 if(targetSelector && targetSelector !== document.body){
237 style += "width: " + width + "; height: " + height + ";";
238 } else {
239 style += "position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%;";
240 }
241
242 embed.style.cssText = style;
243
244 }
245
246 targetNode.classList.add("pdfobject-container");
247 targetNode.appendChild(embed);
248
249 return targetNode.getElementsByTagName(embedType)[0];
250
251 };
252
253 let embed = function(url, targetSelector, options){
254
255 //If targetSelector is not defined, convert to boolean
256 let selector = targetSelector || false;
257
258 //Ensure options object is not undefined -- enables easier error checking below
259 let opt = options || {};
260
261 //Get passed options, or set reasonable defaults
262 let id = (typeof opt.id === "string") ? opt.id : "";
263 let page = opt.page || false;
264 let pdfOpenParams = opt.pdfOpenParams || {};
265 let fallbackLink = opt.fallbackLink || true;
266 let width = opt.width || "100%";
267 let height = opt.height || "100%";
268 let assumptionMode = (typeof opt.assumptionMode === "boolean") ? opt.assumptionMode : true;
269 let forcePDFJS = (typeof opt.forcePDFJS === "boolean") ? opt.forcePDFJS : false;
270 let supportRedirect = (typeof opt.supportRedirect === "boolean") ? opt.supportRedirect : false;
271 let omitInlineStyles = (typeof opt.omitInlineStyles === "boolean") ? opt.omitInlineStyles : false;
272 let suppressConsole = (typeof opt.suppressConsole === "boolean") ? opt.suppressConsole : false;
273 let forceIframe = (typeof opt.forceIframe === "boolean") ? opt.forceIframe : false;
274 let PDFJS_URL = opt.PDFJS_URL || false;
275 let targetNode = getTargetElement(selector);
276 let fallbackHTML = "";
277 let pdfOpenFragment = "";
278 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>";
279
280 //Ensure URL is available. If not, exit now.
281 if(typeof url !== "string"){ return embedError("URL is not valid", suppressConsole); }
282
283 //If target element is specified but is not valid, exit without doing anything
284 if(!targetNode){ return embedError("Target element cannot be determined", suppressConsole); }
285
286 //page option overrides pdfOpenParams, if found
287 if(page){ pdfOpenParams.page = page; }
288
289 //Stringify optional Adobe params for opening document (as fragment identifier)
290 pdfOpenFragment = buildURLFragmentString(pdfOpenParams);
291
292
293 // --== Do the dance: Embed attempt #1 ==--
294
295 //If the forcePDFJS option is invoked, skip everything else and embed as directed
296 if(forcePDFJS && PDFJS_URL){
297 return generatePDFJSMarkup(targetNode, url, pdfOpenFragment, PDFJS_URL, id, omitInlineStyles);
298 }
299
300 // --== Embed attempt #2 ==--
301
302 //Embed PDF if traditional support is provided, or if this developer is willing to roll with assumption
303 //that modern desktop (not mobile) browsers natively support PDFs
304 if(supportsPDFs || (assumptionMode && isModernBrowser && !isMobileDevice)){
305
306 //Should we use <embed> or <iframe>? In most cases <embed>.
307 //Allow developer to force <iframe>, if desired
308 //There is an edge case where Safari does not respect 302 redirect requests for PDF files when using <embed> element.
309 //Redirect appears to work fine when using <iframe> instead of <embed> (Addresses issue #210)
310 let embedtype = (forceIframe || (supportRedirect && isSafariDesktop)) ? "iframe" : "embed";
311
312 return generatePDFObjectMarkup(embedtype, targetNode, targetSelector, url, pdfOpenFragment, width, height, id, omitInlineStyles);
313
314 }
315
316 // --== Embed attempt #3 ==--
317
318 //If everything else has failed and a PDFJS fallback is provided, try to use it
319 if(PDFJS_URL){
320 return generatePDFJSMarkup(targetNode, url, pdfOpenFragment, PDFJS_URL, id, omitInlineStyles);
321 }
322
323 // --== PDF embed not supported! Use fallback ==--
324
325 //Display the fallback link if available
326 if(fallbackLink){
327
328 fallbackHTML = (typeof fallbackLink === "string") ? fallbackLink : fallbackHTML_default;
329 targetNode.innerHTML = fallbackHTML.replace(/\[url\]/g, url);
330
331 }
332
333 return embedError("This browser does not support embedded PDFs", suppressConsole);
334
335 };
336
337 return {
338 embed: function (a,b,c){ return embed(a,b,c); },
339 pdfobjectversion: (function () { return pdfobjectversion; })(),
340 supportsPDFs: (function (){ return supportsPDFs; })()
341 };
342
343}));