/* -*- mode: java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* copyright 2012 mozilla foundation * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ /* globals vbarray, pdfjs */ 'use strict'; // initializing pdfjs global object here, it case if we need to change/disable // some pdf.js features, e.g. range requests if (typeof pdfjs === 'undefined') { (typeof window !== 'undefined' ? window : this).pdfjs = {}; } // checking if the typed arrays are supported // support: ios<6.0 (subarray), ie<10, android<4.0 (function checktypedarraycompatibility() { if (typeof uint8array !== 'undefined') { // support: ios<6.0 if (typeof uint8array.prototype.subarray === 'undefined') { uint8array.prototype.subarray = function subarray(start, end) { return new uint8array(this.slice(start, end)); }; float32array.prototype.subarray = function subarray(start, end) { return new float32array(this.slice(start, end)); }; } // support: android<4.1 if (typeof float64array === 'undefined') { window.float64array = float32array; } return; } function subarray(start, end) { return new typedarray(this.slice(start, end)); } function setarrayoffset(array, offset) { if (arguments.length < 2) { offset = 0; } for (var i = 0, n = array.length; i < n; ++i, ++offset) { this[offset] = array[i] & 0xff; } } function typedarray(arg1) { var result, i, n; if (typeof arg1 === 'number') { result = []; for (i = 0; i < arg1; ++i) { result[i] = 0; } } else if ('slice' in arg1) { result = arg1.slice(0); } else { result = []; for (i = 0, n = arg1.length; i < n; ++i) { result[i] = arg1[i]; } } result.subarray = subarray; result.buffer = result; result.bytelength = result.length; result.set = setarrayoffset; if (typeof arg1 === 'object' && arg1.buffer) { result.buffer = arg1.buffer; } return result; } window.uint8array = typedarray; window.int8array = typedarray; // we don't need support for set, bytelength for 32-bit array // so we can use the typedarray as well window.uint32array = typedarray; window.int32array = typedarray; window.uint16array = typedarray; window.float32array = typedarray; window.float64array = typedarray; })(); // url = url || webkiturl // support: safari<7, android 4.2+ (function normalizeurlobject() { if (!window.url) { window.url = window.webkiturl; } })(); // object.defineproperty()? // support: android<4.0, safari<5.1 (function checkobjectdefinepropertycompatibility() { if (typeof object.defineproperty !== 'undefined') { var definepropertypossible = true; try { // some browsers (e.g. safari) cannot use defineproperty() on dom objects // and thus the native version is not sufficient object.defineproperty(new image(), 'id', { value: 'test' }); // ... another test for android gb browser for non-dom objects var test = function test() {}; test.prototype = { get id() { } }; object.defineproperty(new test(), 'id', { value: '', configurable: true, enumerable: true, writable: false }); } catch (e) { definepropertypossible = false; } if (definepropertypossible) { return; } } object.defineproperty = function objectdefineproperty(obj, name, def) { delete obj[name]; if ('get' in def) { obj.__definegetter__(name, def['get']); } if ('set' in def) { obj.__definesetter__(name, def['set']); } if ('value' in def) { obj.__definesetter__(name, function objectdefinepropertysetter(value) { this.__definegetter__(name, function objectdefinepropertygetter() { return value; }); return value; }); obj[name] = def.value; } }; })(); // no xmlhttprequest#response? // support: ie<11, android <4.0 (function checkxmlhttprequestresponsecompatibility() { var xhrprototype = xmlhttprequest.prototype; var xhr = new xmlhttprequest(); if (!('overridemimetype' in xhr)) { // ie10 might have response, but not overridemimetype // support: ie10 object.defineproperty(xhrprototype, 'overridemimetype', { value: function xmlhttprequestoverridemimetype(mimetype) {} }); } if ('responsetype' in xhr) { return; } // the worker will be using xhr, so we can save time and disable worker. pdfjs.disableworker = true; object.defineproperty(xhrprototype, 'responsetype', { get: function xmlhttprequestgetresponsetype() { return this._responsetype || 'text'; }, set: function xmlhttprequestsetresponsetype(value) { if (value === 'text' || value === 'arraybuffer') { this._responsetype = value; if (value === 'arraybuffer' && typeof this.overridemimetype === 'function') { this.overridemimetype('text/plain; charset=x-user-defined'); } } } }); // support: ie9 if (typeof vbarray !== 'undefined') { object.defineproperty(xhrprototype, 'response', { get: function xmlhttprequestresponseget() { if (this.responsetype === 'arraybuffer') { return new uint8array(new vbarray(this.responsebody).toarray()); } else { return this.responsetext; } } }); return; } object.defineproperty(xhrprototype, 'response', { get: function xmlhttprequestresponseget() { if (this.responsetype !== 'arraybuffer') { return this.responsetext; } var text = this.responsetext; var i, n = text.length; var result = new uint8array(n); for (i = 0; i < n; ++i) { result[i] = text.charcodeat(i) & 0xff; } return result.buffer; } }); })(); // window.btoa (base64 encode function) ? // support: ie<10 (function checkwindowbtoacompatibility() { if ('btoa' in window) { return; } var digits = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789+/='; window.btoa = function windowbtoa(chars) { var buffer = ''; var i, n; for (i = 0, n = chars.length; i < n; i += 3) { var b1 = chars.charcodeat(i) & 0xff; var b2 = chars.charcodeat(i + 1) & 0xff; var b3 = chars.charcodeat(i + 2) & 0xff; var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4); var d3 = i + 1 < n ? ((b2 & 0xf) << 2) | (b3 >> 6) : 64; var d4 = i + 2 < n ? (b3 & 0x3f) : 64; buffer += (digits.charat(d1) + digits.charat(d2) + digits.charat(d3) + digits.charat(d4)); } return buffer; }; })(); // window.atob (base64 encode function)? // support: ie<10 (function checkwindowatobcompatibility() { if ('atob' in window) { return; } // https://github.com/davidchambers/base64.js var digits = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789+/='; window.atob = function (input) { input = input.replace(/=+$/, ''); if (input.length % 4 === 1) { throw new error('bad atob input'); } for ( // initialize result and counters var bc = 0, bs, buffer, idx = 0, output = ''; // get next character buffer = input.charat(idx++); // character found in table? // initialize bit storage and add its ascii value ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, // and if not first of each 4 characters, // convert the first 8 bits to one ascii character bc++ % 4) ? output += string.fromcharcode(255 & bs >> (-2 * bc & 6)) : 0 ) { // try to find character in table (0-63, not found => -1) buffer = digits.indexof(buffer); } return output; }; })(); // function.prototype.bind? // support: android<4.0, ios<6.0 (function checkfunctionprototypebindcompatibility() { if (typeof function.prototype.bind !== 'undefined') { return; } function.prototype.bind = function functionprototypebind(obj) { var fn = this, headargs = array.prototype.slice.call(arguments, 1); var bound = function functionprototypebindbound() { var args = headargs.concat(array.prototype.slice.call(arguments)); return fn.apply(obj, args); }; return bound; }; })(); // htmlelement dataset property // support: ie<11, safari<5.1, android<4.0 (function checkdatasetproperty() { var div = document.createelement('div'); if ('dataset' in div) { return; // dataset property exists } object.defineproperty(htmlelement.prototype, 'dataset', { get: function() { if (this._dataset) { return this._dataset; } var dataset = {}; for (var j = 0, jj = this.attributes.length; j < jj; j++) { var attribute = this.attributes[j]; if (attribute.name.substring(0, 5) !== 'data-') { continue; } var key = attribute.name.substring(5).replace(/\-([a-z])/g, function(all, ch) { return ch.touppercase(); }); dataset[key] = attribute.value; } object.defineproperty(this, '_dataset', { value: dataset, writable: false, enumerable: false }); return dataset; }, enumerable: true }); })(); // htmlelement classlist property // support: ie<10, android<4.0, ios<5.0 (function checkclasslistproperty() { var div = document.createelement('div'); if ('classlist' in div) { return; // classlist property exists } function changelist(element, itemname, add, remove) { var s = element.classname || ''; var list = s.split(/\s+/g); if (list[0] === '') { list.shift(); } var index = list.indexof(itemname); if (index < 0 && add) { list.push(itemname); } if (index >= 0 && remove) { list.splice(index, 1); } element.classname = list.join(' '); return (index >= 0); } var classlistprototype = { add: function(name) { changelist(this.element, name, true, false); }, contains: function(name) { return changelist(this.element, name, false, false); }, remove: function(name) { changelist(this.element, name, false, true); }, toggle: function(name) { changelist(this.element, name, true, true); } }; object.defineproperty(htmlelement.prototype, 'classlist', { get: function() { if (this._classlist) { return this._classlist; } var classlist = object.create(classlistprototype, { element: { value: this, writable: false, enumerable: true } }); object.defineproperty(this, '_classlist', { value: classlist, writable: false, enumerable: false }); return classlist; }, enumerable: true }); })(); // check console compatibility // in older ie versions the console object is not available // unless console is open. // support: ie<10 (function checkconsolecompatibility() { if (!('console' in window)) { window.console = { log: function() {}, error: function() {}, warn: function() {} }; } else if (!('bind' in console.log)) { // native functions in ie9 might not have bind console.log = (function(fn) { return function(msg) { return fn(msg); }; })(console.log); console.error = (function(fn) { return function(msg) { return fn(msg); }; })(console.error); console.warn = (function(fn) { return function(msg) { return fn(msg); }; })(console.warn); } })(); // check onclick compatibility in opera // support: opera<15 (function checkonclickcompatibility() { // workaround for reported opera bug dsk-354448: // onclick fires on disabled buttons with opaque content function ignoreiftargetdisabled(event) { if (isdisabled(event.target)) { event.stoppropagation(); } } function isdisabled(node) { return node.disabled || (node.parentnode && isdisabled(node.parentnode)); } if (navigator.useragent.indexof('opera') !== -1) { // use browser detection since we cannot feature-check this bug document.addeventlistener('click', ignoreiftargetdisabled, true); } })(); // checks if possible to use url.createobjecturl() // support: ie (function checkonblobsupport() { // sometimes ie loosing the data created with createobjecturl(), see #3977 if (navigator.useragent.indexof('trident') >= 0) { pdfjs.disablecreateobjecturl = true; } })(); // checks if navigator.language is supported (function checknavigatorlanguage() { if ('language' in navigator) { return; } pdfjs.locale = navigator.userlanguage || 'en-us'; })(); (function checkrangerequests() { // safari has issues with cached range requests see: // https://github.com/mozilla/pdf.js/issues/3260 // last tested with version 6.0.4. // support: safari 6.0+ var issafari = object.prototype.tostring.call( window.htmlelement).indexof('constructor') > 0; // older versions of android (pre 3.0) has issues with range requests, see: // https://github.com/mozilla/pdf.js/issues/3381. // make sure that we only match webkit-based android browsers, // since firefox/fennec works as expected. // support: android<3.0 var regex = /android\s[0-2][^\d]/; var isoldandroid = regex.test(navigator.useragent); if (issafari || isoldandroid) { pdfjs.disablerange = true; pdfjs.disablestream = true; } })(); // check if the browser supports manipulation of the history. // support: ie<10, android<4.2 (function checkhistorymanipulation() { // android 2.x has so buggy pushstate support that it was removed in // android 3.0 and restored as late as in android 4.2. // support: android 2.x if (!history.pushstate || navigator.useragent.indexof('android 2.') >= 0) { pdfjs.disablehistory = true; } })(); // support: ie<11, chrome<21, android<4.4, safari<6 (function checksetpresenceinimagedata() { // ie < 11 will use window.canvaspixelarray which lacks set function. if (window.canvaspixelarray) { if (typeof window.canvaspixelarray.prototype.set !== 'function') { window.canvaspixelarray.prototype.set = function(arr) { for (var i = 0, ii = this.length; i < ii; i++) { this[i] = arr[i]; } }; } } else { // old chrome and android use an inaccessible canvaspixelarray prototype. // because we cannot feature detect it, we rely on user agent parsing. var polyfill = false, versionmatch; if (navigator.useragent.indexof('chrom') >= 0) { versionmatch = navigator.useragent.match(/chrom(e|ium)\/([0-9]+)\./); // chrome < 21 lacks the set function. polyfill = versionmatch && parseint(versionmatch[2]) < 21; } else if (navigator.useragent.indexof('android') >= 0) { // android < 4.4 lacks the set function. // android >= 4.4 will contain chrome in the user agent, // thus pass the chrome check above and not reach this block. polyfill = /android\s[0-4][^\d]/g.test(navigator.useragent); } else if (navigator.useragent.indexof('safari') >= 0) { versionmatch = navigator.useragent. match(/version\/([0-9]+)\.([0-9]+)\.([0-9]+) safari\//); // safari < 6 lacks the set function. polyfill = versionmatch && parseint(versionmatch[1]) < 6; } if (polyfill) { var contextprototype = window.canvasrenderingcontext2d.prototype; contextprototype._createimagedata = contextprototype.createimagedata; contextprototype.createimagedata = function(w, h) { var imagedata = this._createimagedata(w, h); imagedata.data.set = function(arr) { for (var i = 0, ii = this.length; i < ii; i++) { this[i] = arr[i]; } }; return imagedata; }; } } })(); // support: ie<10, android<4.0, ios (function checkrequestanimationframe() { function fakerequestanimationframe(callback) { window.settimeout(callback, 20); } var isios = /(ipad|iphone|ipod)/g.test(navigator.useragent); if (isios) { // requestanimationframe on ios is broken, replacing with fake one. window.requestanimationframe = fakerequestanimationframe; return; } if ('requestanimationframe' in window) { return; } window.requestanimationframe = window.mozrequestanimationframe || window.webkitrequestanimationframe || fakerequestanimationframe; })(); (function checkcanvassizelimitation() { var isios = /(ipad|iphone|ipod)/g.test(navigator.useragent); var isandroid = /android/g.test(navigator.useragent); if (isios || isandroid) { // 5mp pdfjs.maxcanvaspixels = 5242880; } })(); // disable fullscreen support for certain problematic configurations. // support: ie11+ (when embedded). (function checkfullscreensupport() { var isembeddedie = (navigator.useragent.indexof('trident') >= 0 && window.parent !== window); if (isembeddedie) { pdfjs.disablefullscreen = true; } })();