Path: blob/master/source/_static/ViewerJS/compatibility.js
1237 views
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */1/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */2/* Copyright 2012 Mozilla Foundation3*4* Licensed under the Apache License, Version 2.0 (the "License");5* you may not use this file except in compliance with the License.6* You may obtain a copy of the License at7*8* http://www.apache.org/licenses/LICENSE-2.09*10* Unless required by applicable law or agreed to in writing, software11* distributed under the License is distributed on an "AS IS" BASIS,12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13* See the License for the specific language governing permissions and14* limitations under the License.15*/16/* globals VBArray, PDFJS */1718'use strict';1920// Initializing PDFJS global object here, it case if we need to change/disable21// some PDF.js features, e.g. range requests22if (typeof PDFJS === 'undefined') {23(typeof window !== 'undefined' ? window : this).PDFJS = {};24}2526// Checking if the typed arrays are supported27// Support: iOS<6.0 (subarray), IE<10, Android<4.028(function checkTypedArrayCompatibility() {29if (typeof Uint8Array !== 'undefined') {30// Support: iOS<6.031if (typeof Uint8Array.prototype.subarray === 'undefined') {32Uint8Array.prototype.subarray = function subarray(start, end) {33return new Uint8Array(this.slice(start, end));34};35Float32Array.prototype.subarray = function subarray(start, end) {36return new Float32Array(this.slice(start, end));37};38}3940// Support: Android<4.141if (typeof Float64Array === 'undefined') {42window.Float64Array = Float32Array;43}44return;45}4647function subarray(start, end) {48return new TypedArray(this.slice(start, end));49}5051function setArrayOffset(array, offset) {52if (arguments.length < 2) {53offset = 0;54}55for (var i = 0, n = array.length; i < n; ++i, ++offset) {56this[offset] = array[i] & 0xFF;57}58}5960function TypedArray(arg1) {61var result, i, n;62if (typeof arg1 === 'number') {63result = [];64for (i = 0; i < arg1; ++i) {65result[i] = 0;66}67} else if ('slice' in arg1) {68result = arg1.slice(0);69} else {70result = [];71for (i = 0, n = arg1.length; i < n; ++i) {72result[i] = arg1[i];73}74}7576result.subarray = subarray;77result.buffer = result;78result.byteLength = result.length;79result.set = setArrayOffset;8081if (typeof arg1 === 'object' && arg1.buffer) {82result.buffer = arg1.buffer;83}84return result;85}8687window.Uint8Array = TypedArray;88window.Int8Array = TypedArray;8990// we don't need support for set, byteLength for 32-bit array91// so we can use the TypedArray as well92window.Uint32Array = TypedArray;93window.Int32Array = TypedArray;94window.Uint16Array = TypedArray;95window.Float32Array = TypedArray;96window.Float64Array = TypedArray;97})();9899// URL = URL || webkitURL100// Support: Safari<7, Android 4.2+101(function normalizeURLObject() {102if (!window.URL) {103window.URL = window.webkitURL;104}105})();106107// Object.defineProperty()?108// Support: Android<4.0, Safari<5.1109(function checkObjectDefinePropertyCompatibility() {110if (typeof Object.defineProperty !== 'undefined') {111var definePropertyPossible = true;112try {113// some browsers (e.g. safari) cannot use defineProperty() on DOM objects114// and thus the native version is not sufficient115Object.defineProperty(new Image(), 'id', { value: 'test' });116// ... another test for android gb browser for non-DOM objects117var Test = function Test() {};118Test.prototype = { get id() { } };119Object.defineProperty(new Test(), 'id',120{ value: '', configurable: true, enumerable: true, writable: false });121} catch (e) {122definePropertyPossible = false;123}124if (definePropertyPossible) {125return;126}127}128129Object.defineProperty = function objectDefineProperty(obj, name, def) {130delete obj[name];131if ('get' in def) {132obj.__defineGetter__(name, def['get']);133}134if ('set' in def) {135obj.__defineSetter__(name, def['set']);136}137if ('value' in def) {138obj.__defineSetter__(name, function objectDefinePropertySetter(value) {139this.__defineGetter__(name, function objectDefinePropertyGetter() {140return value;141});142return value;143});144obj[name] = def.value;145}146};147})();148149150// No XMLHttpRequest#response?151// Support: IE<11, Android <4.0152(function checkXMLHttpRequestResponseCompatibility() {153var xhrPrototype = XMLHttpRequest.prototype;154var xhr = new XMLHttpRequest();155if (!('overrideMimeType' in xhr)) {156// IE10 might have response, but not overrideMimeType157// Support: IE10158Object.defineProperty(xhrPrototype, 'overrideMimeType', {159value: function xmlHttpRequestOverrideMimeType(mimeType) {}160});161}162if ('responseType' in xhr) {163return;164}165166// The worker will be using XHR, so we can save time and disable worker.167PDFJS.disableWorker = true;168169Object.defineProperty(xhrPrototype, 'responseType', {170get: function xmlHttpRequestGetResponseType() {171return this._responseType || 'text';172},173set: function xmlHttpRequestSetResponseType(value) {174if (value === 'text' || value === 'arraybuffer') {175this._responseType = value;176if (value === 'arraybuffer' &&177typeof this.overrideMimeType === 'function') {178this.overrideMimeType('text/plain; charset=x-user-defined');179}180}181}182});183184// Support: IE9185if (typeof VBArray !== 'undefined') {186Object.defineProperty(xhrPrototype, 'response', {187get: function xmlHttpRequestResponseGet() {188if (this.responseType === 'arraybuffer') {189return new Uint8Array(new VBArray(this.responseBody).toArray());190} else {191return this.responseText;192}193}194});195return;196}197198Object.defineProperty(xhrPrototype, 'response', {199get: function xmlHttpRequestResponseGet() {200if (this.responseType !== 'arraybuffer') {201return this.responseText;202}203var text = this.responseText;204var i, n = text.length;205var result = new Uint8Array(n);206for (i = 0; i < n; ++i) {207result[i] = text.charCodeAt(i) & 0xFF;208}209return result.buffer;210}211});212})();213214// window.btoa (base64 encode function) ?215// Support: IE<10216(function checkWindowBtoaCompatibility() {217if ('btoa' in window) {218return;219}220221var digits =222'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';223224window.btoa = function windowBtoa(chars) {225var buffer = '';226var i, n;227for (i = 0, n = chars.length; i < n; i += 3) {228var b1 = chars.charCodeAt(i) & 0xFF;229var b2 = chars.charCodeAt(i + 1) & 0xFF;230var b3 = chars.charCodeAt(i + 2) & 0xFF;231var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);232var d3 = i + 1 < n ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;233var d4 = i + 2 < n ? (b3 & 0x3F) : 64;234buffer += (digits.charAt(d1) + digits.charAt(d2) +235digits.charAt(d3) + digits.charAt(d4));236}237return buffer;238};239})();240241// window.atob (base64 encode function)?242// Support: IE<10243(function checkWindowAtobCompatibility() {244if ('atob' in window) {245return;246}247248// https://github.com/davidchambers/Base64.js249var digits =250'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';251window.atob = function (input) {252input = input.replace(/=+$/, '');253if (input.length % 4 === 1) {254throw new Error('bad atob input');255}256for (257// initialize result and counters258var bc = 0, bs, buffer, idx = 0, output = '';259// get next character260buffer = input.charAt(idx++);261// character found in table?262// initialize bit storage and add its ascii value263~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,264// and if not first of each 4 characters,265// convert the first 8 bits to one ascii character266bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0267) {268// try to find character in table (0-63, not found => -1)269buffer = digits.indexOf(buffer);270}271return output;272};273})();274275// Function.prototype.bind?276// Support: Android<4.0, iOS<6.0277(function checkFunctionPrototypeBindCompatibility() {278if (typeof Function.prototype.bind !== 'undefined') {279return;280}281282Function.prototype.bind = function functionPrototypeBind(obj) {283var fn = this, headArgs = Array.prototype.slice.call(arguments, 1);284var bound = function functionPrototypeBindBound() {285var args = headArgs.concat(Array.prototype.slice.call(arguments));286return fn.apply(obj, args);287};288return bound;289};290})();291292// HTMLElement dataset property293// Support: IE<11, Safari<5.1, Android<4.0294(function checkDatasetProperty() {295var div = document.createElement('div');296if ('dataset' in div) {297return; // dataset property exists298}299300Object.defineProperty(HTMLElement.prototype, 'dataset', {301get: function() {302if (this._dataset) {303return this._dataset;304}305306var dataset = {};307for (var j = 0, jj = this.attributes.length; j < jj; j++) {308var attribute = this.attributes[j];309if (attribute.name.substring(0, 5) !== 'data-') {310continue;311}312var key = attribute.name.substring(5).replace(/\-([a-z])/g,313function(all, ch) {314return ch.toUpperCase();315});316dataset[key] = attribute.value;317}318319Object.defineProperty(this, '_dataset', {320value: dataset,321writable: false,322enumerable: false323});324return dataset;325},326enumerable: true327});328})();329330// HTMLElement classList property331// Support: IE<10, Android<4.0, iOS<5.0332(function checkClassListProperty() {333var div = document.createElement('div');334if ('classList' in div) {335return; // classList property exists336}337338function changeList(element, itemName, add, remove) {339var s = element.className || '';340var list = s.split(/\s+/g);341if (list[0] === '') {342list.shift();343}344var index = list.indexOf(itemName);345if (index < 0 && add) {346list.push(itemName);347}348if (index >= 0 && remove) {349list.splice(index, 1);350}351element.className = list.join(' ');352return (index >= 0);353}354355var classListPrototype = {356add: function(name) {357changeList(this.element, name, true, false);358},359contains: function(name) {360return changeList(this.element, name, false, false);361},362remove: function(name) {363changeList(this.element, name, false, true);364},365toggle: function(name) {366changeList(this.element, name, true, true);367}368};369370Object.defineProperty(HTMLElement.prototype, 'classList', {371get: function() {372if (this._classList) {373return this._classList;374}375376var classList = Object.create(classListPrototype, {377element: {378value: this,379writable: false,380enumerable: true381}382});383Object.defineProperty(this, '_classList', {384value: classList,385writable: false,386enumerable: false387});388return classList;389},390enumerable: true391});392})();393394// Check console compatibility395// In older IE versions the console object is not available396// unless console is open.397// Support: IE<10398(function checkConsoleCompatibility() {399if (!('console' in window)) {400window.console = {401log: function() {},402error: function() {},403warn: function() {}404};405} else if (!('bind' in console.log)) {406// native functions in IE9 might not have bind407console.log = (function(fn) {408return function(msg) { return fn(msg); };409})(console.log);410console.error = (function(fn) {411return function(msg) { return fn(msg); };412})(console.error);413console.warn = (function(fn) {414return function(msg) { return fn(msg); };415})(console.warn);416}417})();418419// Check onclick compatibility in Opera420// Support: Opera<15421(function checkOnClickCompatibility() {422// workaround for reported Opera bug DSK-354448:423// onclick fires on disabled buttons with opaque content424function ignoreIfTargetDisabled(event) {425if (isDisabled(event.target)) {426event.stopPropagation();427}428}429function isDisabled(node) {430return node.disabled || (node.parentNode && isDisabled(node.parentNode));431}432if (navigator.userAgent.indexOf('Opera') !== -1) {433// use browser detection since we cannot feature-check this bug434document.addEventListener('click', ignoreIfTargetDisabled, true);435}436})();437438// Checks if possible to use URL.createObjectURL()439// Support: IE440(function checkOnBlobSupport() {441// sometimes IE loosing the data created with createObjectURL(), see #3977442if (navigator.userAgent.indexOf('Trident') >= 0) {443PDFJS.disableCreateObjectURL = true;444}445})();446447// Checks if navigator.language is supported448(function checkNavigatorLanguage() {449if ('language' in navigator) {450return;451}452PDFJS.locale = navigator.userLanguage || 'en-US';453})();454455(function checkRangeRequests() {456// Safari has issues with cached range requests see:457// https://github.com/mozilla/pdf.js/issues/3260458// Last tested with version 6.0.4.459// Support: Safari 6.0+460var isSafari = Object.prototype.toString.call(461window.HTMLElement).indexOf('Constructor') > 0;462463// Older versions of Android (pre 3.0) has issues with range requests, see:464// https://github.com/mozilla/pdf.js/issues/3381.465// Make sure that we only match webkit-based Android browsers,466// since Firefox/Fennec works as expected.467// Support: Android<3.0468var regex = /Android\s[0-2][^\d]/;469var isOldAndroid = regex.test(navigator.userAgent);470471// Range requests are broken in Chrome 39 and 40, https://crbug.com/442318472var isChromeWithRangeBug = /Chrome\/(39|40)\./.test(navigator.userAgent);473474if (isSafari || isOldAndroid || isChromeWithRangeBug) {475PDFJS.disableRange = true;476PDFJS.disableStream = true;477}478})();479480// Check if the browser supports manipulation of the history.481// Support: IE<10, Android<4.2482(function checkHistoryManipulation() {483// Android 2.x has so buggy pushState support that it was removed in484// Android 3.0 and restored as late as in Android 4.2.485// Support: Android 2.x486if (!history.pushState || navigator.userAgent.indexOf('Android 2.') >= 0) {487PDFJS.disableHistory = true;488}489})();490491// Support: IE<11, Chrome<21, Android<4.4, Safari<6492(function checkSetPresenceInImageData() {493// IE < 11 will use window.CanvasPixelArray which lacks set function.494if (window.CanvasPixelArray) {495if (typeof window.CanvasPixelArray.prototype.set !== 'function') {496window.CanvasPixelArray.prototype.set = function(arr) {497for (var i = 0, ii = this.length; i < ii; i++) {498this[i] = arr[i];499}500};501}502} else {503// Old Chrome and Android use an inaccessible CanvasPixelArray prototype.504// Because we cannot feature detect it, we rely on user agent parsing.505var polyfill = false, versionMatch;506if (navigator.userAgent.indexOf('Chrom') >= 0) {507versionMatch = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);508// Chrome < 21 lacks the set function.509polyfill = versionMatch && parseInt(versionMatch[2]) < 21;510} else if (navigator.userAgent.indexOf('Android') >= 0) {511// Android < 4.4 lacks the set function.512// Android >= 4.4 will contain Chrome in the user agent,513// thus pass the Chrome check above and not reach this block.514polyfill = /Android\s[0-4][^\d]/g.test(navigator.userAgent);515} else if (navigator.userAgent.indexOf('Safari') >= 0) {516versionMatch = navigator.userAgent.517match(/Version\/([0-9]+)\.([0-9]+)\.([0-9]+) Safari\//);518// Safari < 6 lacks the set function.519polyfill = versionMatch && parseInt(versionMatch[1]) < 6;520}521522if (polyfill) {523var contextPrototype = window.CanvasRenderingContext2D.prototype;524contextPrototype._createImageData = contextPrototype.createImageData;525contextPrototype.createImageData = function(w, h) {526var imageData = this._createImageData(w, h);527imageData.data.set = function(arr) {528for (var i = 0, ii = this.length; i < ii; i++) {529this[i] = arr[i];530}531};532return imageData;533};534}535}536})();537538// Support: IE<10, Android<4.0, iOS539(function checkRequestAnimationFrame() {540function fakeRequestAnimationFrame(callback) {541window.setTimeout(callback, 20);542}543544var isIOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);545if (isIOS) {546// requestAnimationFrame on iOS is broken, replacing with fake one.547window.requestAnimationFrame = fakeRequestAnimationFrame;548return;549}550if ('requestAnimationFrame' in window) {551return;552}553window.requestAnimationFrame =554window.mozRequestAnimationFrame ||555window.webkitRequestAnimationFrame ||556fakeRequestAnimationFrame;557})();558559(function checkCanvasSizeLimitation() {560var isIOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);561var isAndroid = /Android/g.test(navigator.userAgent);562if (isIOS || isAndroid) {563// 5MP564PDFJS.maxCanvasPixels = 5242880;565}566})();567568// Disable fullscreen support for certain problematic configurations.569// Support: IE11+ (when embedded).570(function checkFullscreenSupport() {571var isEmbeddedIE = (navigator.userAgent.indexOf('Trident') >= 0 &&572window.parent !== window);573if (isEmbeddedIE) {574PDFJS.disableFullscreen = true;575}576})();577578579