Drop ViewerJS
@ -1,577 +0,0 @@
|
|||||||
/* -*- 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);
|
|
||||||
|
|
||||||
// Range requests are broken in Chrome 39 and 40, https://crbug.com/442318
|
|
||||||
var isChromeWithRangeBug = /Chrome\/(39|40)\./.test(navigator.userAgent);
|
|
||||||
|
|
||||||
if (isSafari || isOldAndroid || isChromeWithRangeBug) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,27 +0,0 @@
|
|||||||
/* This is just a sample file with CSS rules. You should write your own @font-face declarations
|
|
||||||
* to add support for your desired fonts.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Novecentowide Book';
|
|
||||||
src: url("/ViewerJS/fonts/Novecentowide-Bold-webfont.eot");
|
|
||||||
src: url("/ViewerJS/fonts/Novecentowide-Bold-webfont.eot?#iefix") format("embedded-opentype"),
|
|
||||||
url("/ViewerJS/fonts/Novecentowide-Bold-webfont.woff") format("woff"),
|
|
||||||
url("/fonts/Novecentowide-Bold-webfont.ttf") format("truetype"),
|
|
||||||
url("/fonts/Novecentowide-Bold-webfont.svg#NovecentowideBookBold") format("svg");
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'exotica';
|
|
||||||
src: url('/ViewerJS/fonts/Exotica-webfont.eot');
|
|
||||||
src: url('/ViewerJS/fonts/Exotica-webfont.eot?#iefix') format('embedded-opentype'),
|
|
||||||
url('/ViewerJS/fonts/Exotica-webfont.woff') format('woff'),
|
|
||||||
url('/ViewerJS/fonts/Exotica-webfont.ttf') format('truetype'),
|
|
||||||
url('/ViewerJS/fonts/Exotica-webfont.svg#exoticamedium') format('svg');
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 512 B |
Before Width: | Height: | Size: 491 B |
Before Width: | Height: | Size: 237 B |
Before Width: | Height: | Size: 353 B |
Before Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 143 B |
8052
ViewerJS/pdf.js
39353
ViewerJS/pdf.worker.js
vendored
@ -1 +0,0 @@
|
|||||||
var /**@const{!string}*/pdfjs_version = "v1.1.114";
|
|
@ -1,419 +0,0 @@
|
|||||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* 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 CustomStyle, PDFJS */
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var MAX_TEXT_DIVS_TO_RENDER = 100000;
|
|
||||||
|
|
||||||
var NonWhitespaceRegexp = /\S/;
|
|
||||||
|
|
||||||
function isAllWhitespace(str) {
|
|
||||||
return !NonWhitespaceRegexp.test(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} TextLayerBuilderOptions
|
|
||||||
* @property {HTMLDivElement} textLayerDiv - The text layer container.
|
|
||||||
* @property {number} pageIndex - The page index.
|
|
||||||
* @property {PageViewport} viewport - The viewport of the text layer.
|
|
||||||
* @property {PDFFindController} findController
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TextLayerBuilder provides text-selection functionality for the PDF.
|
|
||||||
* It does this by creating overlay divs over the PDF text. These divs
|
|
||||||
* contain text that matches the PDF text they are overlaying. This object
|
|
||||||
* also provides a way to highlight text that is being searched for.
|
|
||||||
* @class
|
|
||||||
*/
|
|
||||||
var TextLayerBuilder = (function TextLayerBuilderClosure() {
|
|
||||||
function TextLayerBuilder(options) {
|
|
||||||
this.textLayerDiv = options.textLayerDiv;
|
|
||||||
this.renderingDone = false;
|
|
||||||
this.divContentDone = false;
|
|
||||||
this.pageIdx = options.pageIndex;
|
|
||||||
this.pageNumber = this.pageIdx + 1;
|
|
||||||
this.matches = [];
|
|
||||||
this.viewport = options.viewport;
|
|
||||||
this.textDivs = [];
|
|
||||||
this.findController = options.findController || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextLayerBuilder.prototype = {
|
|
||||||
_finishRendering: function TextLayerBuilder_finishRendering() {
|
|
||||||
this.renderingDone = true;
|
|
||||||
|
|
||||||
var event = document.createEvent('CustomEvent');
|
|
||||||
event.initCustomEvent('textlayerrendered', true, true, {
|
|
||||||
pageNumber: this.pageNumber
|
|
||||||
});
|
|
||||||
this.textLayerDiv.dispatchEvent(event);
|
|
||||||
},
|
|
||||||
|
|
||||||
renderLayer: function TextLayerBuilder_renderLayer() {
|
|
||||||
var textLayerFrag = document.createDocumentFragment();
|
|
||||||
var textDivs = this.textDivs;
|
|
||||||
var textDivsLength = textDivs.length;
|
|
||||||
var canvas = document.createElement('canvas');
|
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
// No point in rendering many divs as it would make the browser
|
|
||||||
// unusable even after the divs are rendered.
|
|
||||||
if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {
|
|
||||||
this._finishRendering();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastFontSize;
|
|
||||||
var lastFontFamily;
|
|
||||||
for (var i = 0; i < textDivsLength; i++) {
|
|
||||||
var textDiv = textDivs[i];
|
|
||||||
if (textDiv.dataset.isWhitespace !== undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var fontSize = textDiv.style.fontSize;
|
|
||||||
var fontFamily = textDiv.style.fontFamily;
|
|
||||||
|
|
||||||
// Only build font string and set to context if different from last.
|
|
||||||
if (fontSize !== lastFontSize || fontFamily !== lastFontFamily) {
|
|
||||||
ctx.font = fontSize + ' ' + fontFamily;
|
|
||||||
lastFontSize = fontSize;
|
|
||||||
lastFontFamily = fontFamily;
|
|
||||||
}
|
|
||||||
|
|
||||||
var width = ctx.measureText(textDiv.textContent).width;
|
|
||||||
if (width > 0) {
|
|
||||||
textLayerFrag.appendChild(textDiv);
|
|
||||||
var transform;
|
|
||||||
if (textDiv.dataset.canvasWidth !== undefined) {
|
|
||||||
// Dataset values come of type string.
|
|
||||||
var textScale = textDiv.dataset.canvasWidth / width;
|
|
||||||
transform = 'scaleX(' + textScale + ')';
|
|
||||||
} else {
|
|
||||||
transform = '';
|
|
||||||
}
|
|
||||||
var rotation = textDiv.dataset.angle;
|
|
||||||
if (rotation) {
|
|
||||||
transform = 'rotate(' + rotation + 'deg) ' + transform;
|
|
||||||
}
|
|
||||||
if (transform) {
|
|
||||||
CustomStyle.setProp('transform' , textDiv, transform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.textLayerDiv.appendChild(textLayerFrag);
|
|
||||||
this._finishRendering();
|
|
||||||
this.updateMatches();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the text layer.
|
|
||||||
* @param {number} timeout (optional) if specified, the rendering waits
|
|
||||||
* for specified amount of ms.
|
|
||||||
*/
|
|
||||||
render: function TextLayerBuilder_render(timeout) {
|
|
||||||
if (!this.divContentDone || this.renderingDone) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.renderTimer) {
|
|
||||||
clearTimeout(this.renderTimer);
|
|
||||||
this.renderTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!timeout) { // Render right away
|
|
||||||
this.renderLayer();
|
|
||||||
} else { // Schedule
|
|
||||||
var self = this;
|
|
||||||
this.renderTimer = setTimeout(function() {
|
|
||||||
self.renderLayer();
|
|
||||||
self.renderTimer = null;
|
|
||||||
}, timeout);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
appendText: function TextLayerBuilder_appendText(geom, styles) {
|
|
||||||
var style = styles[geom.fontName];
|
|
||||||
var textDiv = document.createElement('div');
|
|
||||||
this.textDivs.push(textDiv);
|
|
||||||
if (isAllWhitespace(geom.str)) {
|
|
||||||
textDiv.dataset.isWhitespace = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform);
|
|
||||||
var angle = Math.atan2(tx[1], tx[0]);
|
|
||||||
if (style.vertical) {
|
|
||||||
angle += Math.PI / 2;
|
|
||||||
}
|
|
||||||
var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3]));
|
|
||||||
var fontAscent = fontHeight;
|
|
||||||
if (style.ascent) {
|
|
||||||
fontAscent = style.ascent * fontAscent;
|
|
||||||
} else if (style.descent) {
|
|
||||||
fontAscent = (1 + style.descent) * fontAscent;
|
|
||||||
}
|
|
||||||
|
|
||||||
var left;
|
|
||||||
var top;
|
|
||||||
if (angle === 0) {
|
|
||||||
left = tx[4];
|
|
||||||
top = tx[5] - fontAscent;
|
|
||||||
} else {
|
|
||||||
left = tx[4] + (fontAscent * Math.sin(angle));
|
|
||||||
top = tx[5] - (fontAscent * Math.cos(angle));
|
|
||||||
}
|
|
||||||
textDiv.style.left = left + 'px';
|
|
||||||
textDiv.style.top = top + 'px';
|
|
||||||
textDiv.style.fontSize = fontHeight + 'px';
|
|
||||||
textDiv.style.fontFamily = style.fontFamily;
|
|
||||||
|
|
||||||
textDiv.textContent = geom.str;
|
|
||||||
// |fontName| is only used by the Font Inspector. This test will succeed
|
|
||||||
// when e.g. the Font Inspector is off but the Stepper is on, but it's
|
|
||||||
// not worth the effort to do a more accurate test.
|
|
||||||
if (PDFJS.pdfBug) {
|
|
||||||
textDiv.dataset.fontName = geom.fontName;
|
|
||||||
}
|
|
||||||
// Storing into dataset will convert number into string.
|
|
||||||
if (angle !== 0) {
|
|
||||||
textDiv.dataset.angle = angle * (180 / Math.PI);
|
|
||||||
}
|
|
||||||
// We don't bother scaling single-char text divs, because it has very
|
|
||||||
// little effect on text highlighting. This makes scrolling on docs with
|
|
||||||
// lots of such divs a lot faster.
|
|
||||||
if (textDiv.textContent.length > 1) {
|
|
||||||
if (style.vertical) {
|
|
||||||
textDiv.dataset.canvasWidth = geom.height * this.viewport.scale;
|
|
||||||
} else {
|
|
||||||
textDiv.dataset.canvasWidth = geom.width * this.viewport.scale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setTextContent: function TextLayerBuilder_setTextContent(textContent) {
|
|
||||||
this.textContent = textContent;
|
|
||||||
|
|
||||||
var textItems = textContent.items;
|
|
||||||
for (var i = 0, len = textItems.length; i < len; i++) {
|
|
||||||
this.appendText(textItems[i], textContent.styles);
|
|
||||||
}
|
|
||||||
this.divContentDone = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
convertMatches: function TextLayerBuilder_convertMatches(matches) {
|
|
||||||
var i = 0;
|
|
||||||
var iIndex = 0;
|
|
||||||
var bidiTexts = this.textContent.items;
|
|
||||||
var end = bidiTexts.length - 1;
|
|
||||||
var queryLen = (this.findController === null ?
|
|
||||||
0 : this.findController.state.query.length);
|
|
||||||
var ret = [];
|
|
||||||
|
|
||||||
for (var m = 0, len = matches.length; m < len; m++) {
|
|
||||||
// Calculate the start position.
|
|
||||||
var matchIdx = matches[m];
|
|
||||||
|
|
||||||
// Loop over the divIdxs.
|
|
||||||
while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
|
|
||||||
iIndex += bidiTexts[i].str.length;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i === bidiTexts.length) {
|
|
||||||
console.error('Could not find a matching mapping');
|
|
||||||
}
|
|
||||||
|
|
||||||
var match = {
|
|
||||||
begin: {
|
|
||||||
divIdx: i,
|
|
||||||
offset: matchIdx - iIndex
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calculate the end position.
|
|
||||||
matchIdx += queryLen;
|
|
||||||
|
|
||||||
// Somewhat the same array as above, but use > instead of >= to get
|
|
||||||
// the end position right.
|
|
||||||
while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
|
|
||||||
iIndex += bidiTexts[i].str.length;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
match.end = {
|
|
||||||
divIdx: i,
|
|
||||||
offset: matchIdx - iIndex
|
|
||||||
};
|
|
||||||
ret.push(match);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
},
|
|
||||||
|
|
||||||
renderMatches: function TextLayerBuilder_renderMatches(matches) {
|
|
||||||
// Early exit if there is nothing to render.
|
|
||||||
if (matches.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bidiTexts = this.textContent.items;
|
|
||||||
var textDivs = this.textDivs;
|
|
||||||
var prevEnd = null;
|
|
||||||
var pageIdx = this.pageIdx;
|
|
||||||
var isSelectedPage = (this.findController === null ?
|
|
||||||
false : (pageIdx === this.findController.selected.pageIdx));
|
|
||||||
var selectedMatchIdx = (this.findController === null ?
|
|
||||||
-1 : this.findController.selected.matchIdx);
|
|
||||||
var highlightAll = (this.findController === null ?
|
|
||||||
false : this.findController.state.highlightAll);
|
|
||||||
var infinity = {
|
|
||||||
divIdx: -1,
|
|
||||||
offset: undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
function beginText(begin, className) {
|
|
||||||
var divIdx = begin.divIdx;
|
|
||||||
textDivs[divIdx].textContent = '';
|
|
||||||
appendTextToDiv(divIdx, 0, begin.offset, className);
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
|
|
||||||
var div = textDivs[divIdx];
|
|
||||||
var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
|
|
||||||
var node = document.createTextNode(content);
|
|
||||||
if (className) {
|
|
||||||
var span = document.createElement('span');
|
|
||||||
span.className = className;
|
|
||||||
span.appendChild(node);
|
|
||||||
div.appendChild(span);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
div.appendChild(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
var i0 = selectedMatchIdx, i1 = i0 + 1;
|
|
||||||
if (highlightAll) {
|
|
||||||
i0 = 0;
|
|
||||||
i1 = matches.length;
|
|
||||||
} else if (!isSelectedPage) {
|
|
||||||
// Not highlighting all and this isn't the selected page, so do nothing.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = i0; i < i1; i++) {
|
|
||||||
var match = matches[i];
|
|
||||||
var begin = match.begin;
|
|
||||||
var end = match.end;
|
|
||||||
var isSelected = (isSelectedPage && i === selectedMatchIdx);
|
|
||||||
var highlightSuffix = (isSelected ? ' selected' : '');
|
|
||||||
|
|
||||||
if (this.findController) {
|
|
||||||
this.findController.updateMatchPosition(pageIdx, i, textDivs,
|
|
||||||
begin.divIdx, end.divIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match inside new div.
|
|
||||||
if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
|
|
||||||
// If there was a previous div, then add the text at the end.
|
|
||||||
if (prevEnd !== null) {
|
|
||||||
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
|
|
||||||
}
|
|
||||||
// Clear the divs and set the content until the starting point.
|
|
||||||
beginText(begin);
|
|
||||||
} else {
|
|
||||||
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (begin.divIdx === end.divIdx) {
|
|
||||||
appendTextToDiv(begin.divIdx, begin.offset, end.offset,
|
|
||||||
'highlight' + highlightSuffix);
|
|
||||||
} else {
|
|
||||||
appendTextToDiv(begin.divIdx, begin.offset, infinity.offset,
|
|
||||||
'highlight begin' + highlightSuffix);
|
|
||||||
for (var n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
|
|
||||||
textDivs[n0].className = 'highlight middle' + highlightSuffix;
|
|
||||||
}
|
|
||||||
beginText(end, 'highlight end' + highlightSuffix);
|
|
||||||
}
|
|
||||||
prevEnd = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevEnd) {
|
|
||||||
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateMatches: function TextLayerBuilder_updateMatches() {
|
|
||||||
// Only show matches when all rendering is done.
|
|
||||||
if (!this.renderingDone) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear all matches.
|
|
||||||
var matches = this.matches;
|
|
||||||
var textDivs = this.textDivs;
|
|
||||||
var bidiTexts = this.textContent.items;
|
|
||||||
var clearedUntilDivIdx = -1;
|
|
||||||
|
|
||||||
// Clear all current matches.
|
|
||||||
for (var i = 0, len = matches.length; i < len; i++) {
|
|
||||||
var match = matches[i];
|
|
||||||
var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
|
|
||||||
for (var n = begin, end = match.end.divIdx; n <= end; n++) {
|
|
||||||
var div = textDivs[n];
|
|
||||||
div.textContent = bidiTexts[n].str;
|
|
||||||
div.className = '';
|
|
||||||
}
|
|
||||||
clearedUntilDivIdx = match.end.divIdx + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.findController === null || !this.findController.active) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the matches on the page controller into the match format
|
|
||||||
// used for the textLayer.
|
|
||||||
this.matches = this.convertMatches(this.findController === null ?
|
|
||||||
[] : (this.findController.pageMatches[this.pageIdx] || []));
|
|
||||||
this.renderMatches(this.matches);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return TextLayerBuilder;
|
|
||||||
})();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @constructor
|
|
||||||
* @implements IPDFTextLayerFactory
|
|
||||||
*/
|
|
||||||
function DefaultTextLayerFactory() {}
|
|
||||||
DefaultTextLayerFactory.prototype = {
|
|
||||||
/**
|
|
||||||
* @param {HTMLDivElement} textLayerDiv
|
|
||||||
* @param {number} pageIndex
|
|
||||||
* @param {PageViewport} viewport
|
|
||||||
* @returns {TextLayerBuilder}
|
|
||||||
*/
|
|
||||||
createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) {
|
|
||||||
return new TextLayerBuilder({
|
|
||||||
textLayerDiv: textLayerDiv,
|
|
||||||
pageIndex: pageIndex,
|
|
||||||
viewport: viewport
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,394 +0,0 @@
|
|||||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var CSS_UNITS = 96.0 / 72.0;
|
|
||||||
var DEFAULT_SCALE = 'auto';
|
|
||||||
var UNKNOWN_SCALE = 0;
|
|
||||||
var MAX_AUTO_SCALE = 1.25;
|
|
||||||
var SCROLLBAR_PADDING = 40;
|
|
||||||
var VERTICAL_PADDING = 5;
|
|
||||||
|
|
||||||
// optimised CSS custom property getter/setter
|
|
||||||
var CustomStyle = (function CustomStyleClosure() {
|
|
||||||
|
|
||||||
// As noted on: http://www.zachstronaut.com/posts/2009/02/17/
|
|
||||||
// animate-css-transforms-firefox-webkit.html
|
|
||||||
// in some versions of IE9 it is critical that ms appear in this list
|
|
||||||
// before Moz
|
|
||||||
var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
|
|
||||||
var _cache = {};
|
|
||||||
|
|
||||||
function CustomStyle() {}
|
|
||||||
|
|
||||||
CustomStyle.getProp = function get(propName, element) {
|
|
||||||
// check cache only when no element is given
|
|
||||||
if (arguments.length === 1 && typeof _cache[propName] === 'string') {
|
|
||||||
return _cache[propName];
|
|
||||||
}
|
|
||||||
|
|
||||||
element = element || document.documentElement;
|
|
||||||
var style = element.style, prefixed, uPropName;
|
|
||||||
|
|
||||||
// test standard property first
|
|
||||||
if (typeof style[propName] === 'string') {
|
|
||||||
return (_cache[propName] = propName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// capitalize
|
|
||||||
uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
|
|
||||||
|
|
||||||
// test vendor specific properties
|
|
||||||
for (var i = 0, l = prefixes.length; i < l; i++) {
|
|
||||||
prefixed = prefixes[i] + uPropName;
|
|
||||||
if (typeof style[prefixed] === 'string') {
|
|
||||||
return (_cache[propName] = prefixed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if all fails then set to undefined
|
|
||||||
return (_cache[propName] = 'undefined');
|
|
||||||
};
|
|
||||||
|
|
||||||
CustomStyle.setProp = function set(propName, element, str) {
|
|
||||||
var prop = this.getProp(propName);
|
|
||||||
if (prop !== 'undefined') {
|
|
||||||
element.style[prop] = str;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return CustomStyle;
|
|
||||||
})();
|
|
||||||
|
|
||||||
function getFileName(url) {
|
|
||||||
var anchor = url.indexOf('#');
|
|
||||||
var query = url.indexOf('?');
|
|
||||||
var end = Math.min(
|
|
||||||
anchor > 0 ? anchor : url.length,
|
|
||||||
query > 0 ? query : url.length);
|
|
||||||
return url.substring(url.lastIndexOf('/', end) + 1, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns scale factor for the canvas. It makes sense for the HiDPI displays.
|
|
||||||
* @return {Object} The object with horizontal (sx) and vertical (sy)
|
|
||||||
scales. The scaled property is set to false if scaling is
|
|
||||||
not required, true otherwise.
|
|
||||||
*/
|
|
||||||
function getOutputScale(ctx) {
|
|
||||||
var devicePixelRatio = window.devicePixelRatio || 1;
|
|
||||||
var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
|
|
||||||
ctx.mozBackingStorePixelRatio ||
|
|
||||||
ctx.msBackingStorePixelRatio ||
|
|
||||||
ctx.oBackingStorePixelRatio ||
|
|
||||||
ctx.backingStorePixelRatio || 1;
|
|
||||||
var pixelRatio = devicePixelRatio / backingStoreRatio;
|
|
||||||
return {
|
|
||||||
sx: pixelRatio,
|
|
||||||
sy: pixelRatio,
|
|
||||||
scaled: pixelRatio !== 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scrolls specified element into view of its parent.
|
|
||||||
* element {Object} The element to be visible.
|
|
||||||
* spot {Object} An object with optional top and left properties,
|
|
||||||
* specifying the offset from the top left edge.
|
|
||||||
*/
|
|
||||||
function scrollIntoView(element, spot) {
|
|
||||||
// Assuming offsetParent is available (it's not available when viewer is in
|
|
||||||
// hidden iframe or object). We have to scroll: if the offsetParent is not set
|
|
||||||
// producing the error. See also animationStartedClosure.
|
|
||||||
var parent = element.offsetParent;
|
|
||||||
var offsetY = element.offsetTop + element.clientTop;
|
|
||||||
var offsetX = element.offsetLeft + element.clientLeft;
|
|
||||||
if (!parent) {
|
|
||||||
console.error('offsetParent is not set -- cannot scroll');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while (parent.clientHeight === parent.scrollHeight) {
|
|
||||||
if (parent.dataset._scaleY) {
|
|
||||||
offsetY /= parent.dataset._scaleY;
|
|
||||||
offsetX /= parent.dataset._scaleX;
|
|
||||||
}
|
|
||||||
offsetY += parent.offsetTop;
|
|
||||||
offsetX += parent.offsetLeft;
|
|
||||||
parent = parent.offsetParent;
|
|
||||||
if (!parent) {
|
|
||||||
return; // no need to scroll
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (spot) {
|
|
||||||
if (spot.top !== undefined) {
|
|
||||||
offsetY += spot.top;
|
|
||||||
}
|
|
||||||
if (spot.left !== undefined) {
|
|
||||||
offsetX += spot.left;
|
|
||||||
parent.scrollLeft = offsetX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parent.scrollTop = offsetY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to start monitoring the scroll event and converting them into
|
|
||||||
* PDF.js friendly one: with scroll debounce and scroll direction.
|
|
||||||
*/
|
|
||||||
function watchScroll(viewAreaElement, callback) {
|
|
||||||
var debounceScroll = function debounceScroll(evt) {
|
|
||||||
if (rAF) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// schedule an invocation of scroll for next animation frame.
|
|
||||||
rAF = window.requestAnimationFrame(function viewAreaElementScrolled() {
|
|
||||||
rAF = null;
|
|
||||||
|
|
||||||
var currentY = viewAreaElement.scrollTop;
|
|
||||||
var lastY = state.lastY;
|
|
||||||
if (currentY !== lastY) {
|
|
||||||
state.down = currentY > lastY;
|
|
||||||
}
|
|
||||||
state.lastY = currentY;
|
|
||||||
callback(state);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var state = {
|
|
||||||
down: true,
|
|
||||||
lastY: viewAreaElement.scrollTop,
|
|
||||||
_eventHandler: debounceScroll
|
|
||||||
};
|
|
||||||
|
|
||||||
var rAF = null;
|
|
||||||
viewAreaElement.addEventListener('scroll', debounceScroll, true);
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use binary search to find the index of the first item in a given array which
|
|
||||||
* passes a given condition. The items are expected to be sorted in the sense
|
|
||||||
* that if the condition is true for one item in the array, then it is also true
|
|
||||||
* for all following items.
|
|
||||||
*
|
|
||||||
* @returns {Number} Index of the first array element to pass the test,
|
|
||||||
* or |items.length| if no such element exists.
|
|
||||||
*/
|
|
||||||
function binarySearchFirstItem(items, condition) {
|
|
||||||
var minIndex = 0;
|
|
||||||
var maxIndex = items.length - 1;
|
|
||||||
|
|
||||||
if (items.length === 0 || !condition(items[maxIndex])) {
|
|
||||||
return items.length;
|
|
||||||
}
|
|
||||||
if (condition(items[minIndex])) {
|
|
||||||
return minIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (minIndex < maxIndex) {
|
|
||||||
var currentIndex = (minIndex + maxIndex) >> 1;
|
|
||||||
var currentItem = items[currentIndex];
|
|
||||||
if (condition(currentItem)) {
|
|
||||||
maxIndex = currentIndex;
|
|
||||||
} else {
|
|
||||||
minIndex = currentIndex + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return minIndex; /* === maxIndex */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic helper to find out what elements are visible within a scroll pane.
|
|
||||||
*/
|
|
||||||
function getVisibleElements(scrollEl, views, sortByVisibility) {
|
|
||||||
var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight;
|
|
||||||
var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth;
|
|
||||||
|
|
||||||
function isElementBottomBelowViewTop(view) {
|
|
||||||
var element = view.div;
|
|
||||||
var elementBottom =
|
|
||||||
element.offsetTop + element.clientTop + element.clientHeight;
|
|
||||||
return elementBottom > top;
|
|
||||||
}
|
|
||||||
|
|
||||||
var visible = [], view, element;
|
|
||||||
var currentHeight, viewHeight, hiddenHeight, percentHeight;
|
|
||||||
var currentWidth, viewWidth;
|
|
||||||
var firstVisibleElementInd = (views.length === 0) ? 0 :
|
|
||||||
binarySearchFirstItem(views, isElementBottomBelowViewTop);
|
|
||||||
|
|
||||||
for (var i = firstVisibleElementInd, ii = views.length; i < ii; i++) {
|
|
||||||
view = views[i];
|
|
||||||
element = view.div;
|
|
||||||
currentHeight = element.offsetTop + element.clientTop;
|
|
||||||
viewHeight = element.clientHeight;
|
|
||||||
|
|
||||||
if (currentHeight > bottom) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentWidth = element.offsetLeft + element.clientLeft;
|
|
||||||
viewWidth = element.clientWidth;
|
|
||||||
if (currentWidth + viewWidth < left || currentWidth > right) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
hiddenHeight = Math.max(0, top - currentHeight) +
|
|
||||||
Math.max(0, currentHeight + viewHeight - bottom);
|
|
||||||
percentHeight = ((viewHeight - hiddenHeight) * 100 / viewHeight) | 0;
|
|
||||||
|
|
||||||
visible.push({
|
|
||||||
id: view.id,
|
|
||||||
x: currentWidth,
|
|
||||||
y: currentHeight,
|
|
||||||
view: view,
|
|
||||||
percent: percentHeight
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var first = visible[0];
|
|
||||||
var last = visible[visible.length - 1];
|
|
||||||
|
|
||||||
if (sortByVisibility) {
|
|
||||||
visible.sort(function(a, b) {
|
|
||||||
var pc = a.percent - b.percent;
|
|
||||||
if (Math.abs(pc) > 0.001) {
|
|
||||||
return -pc;
|
|
||||||
}
|
|
||||||
return a.id - b.id; // ensure stability
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {first: first, last: last, views: visible};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event handler to suppress context menu.
|
|
||||||
*/
|
|
||||||
function noContextMenuHandler(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the filename or guessed filename from the url (see issue 3455).
|
|
||||||
* url {String} The original PDF location.
|
|
||||||
* @return {String} Guessed PDF file name.
|
|
||||||
*/
|
|
||||||
function getPDFFileNameFromURL(url) {
|
|
||||||
var reURI = /^(?:([^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
|
|
||||||
// SCHEME HOST 1.PATH 2.QUERY 3.REF
|
|
||||||
// Pattern to get last matching NAME.pdf
|
|
||||||
var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
|
|
||||||
var splitURI = reURI.exec(url);
|
|
||||||
var suggestedFilename = reFilename.exec(splitURI[1]) ||
|
|
||||||
reFilename.exec(splitURI[2]) ||
|
|
||||||
reFilename.exec(splitURI[3]);
|
|
||||||
if (suggestedFilename) {
|
|
||||||
suggestedFilename = suggestedFilename[0];
|
|
||||||
if (suggestedFilename.indexOf('%') !== -1) {
|
|
||||||
// URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
|
|
||||||
try {
|
|
||||||
suggestedFilename =
|
|
||||||
reFilename.exec(decodeURIComponent(suggestedFilename))[0];
|
|
||||||
} catch(e) { // Possible (extremely rare) errors:
|
|
||||||
// URIError "Malformed URI", e.g. for "%AA.pdf"
|
|
||||||
// TypeError "null has no properties", e.g. for "%2F.pdf"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return suggestedFilename || 'document.pdf';
|
|
||||||
}
|
|
||||||
|
|
||||||
var ProgressBar = (function ProgressBarClosure() {
|
|
||||||
|
|
||||||
function clamp(v, min, max) {
|
|
||||||
return Math.min(Math.max(v, min), max);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ProgressBar(id, opts) {
|
|
||||||
this.visible = true;
|
|
||||||
|
|
||||||
// Fetch the sub-elements for later.
|
|
||||||
this.div = document.querySelector(id + ' .progress');
|
|
||||||
|
|
||||||
// Get the loading bar element, so it can be resized to fit the viewer.
|
|
||||||
this.bar = this.div.parentNode;
|
|
||||||
|
|
||||||
// Get options, with sensible defaults.
|
|
||||||
this.height = opts.height || 100;
|
|
||||||
this.width = opts.width || 100;
|
|
||||||
this.units = opts.units || '%';
|
|
||||||
|
|
||||||
// Initialize heights.
|
|
||||||
this.div.style.height = this.height + this.units;
|
|
||||||
this.percent = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgressBar.prototype = {
|
|
||||||
|
|
||||||
updateBar: function ProgressBar_updateBar() {
|
|
||||||
if (this._indeterminate) {
|
|
||||||
this.div.classList.add('indeterminate');
|
|
||||||
this.div.style.width = this.width + this.units;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.div.classList.remove('indeterminate');
|
|
||||||
var progressSize = this.width * this._percent / 100;
|
|
||||||
this.div.style.width = progressSize + this.units;
|
|
||||||
},
|
|
||||||
|
|
||||||
get percent() {
|
|
||||||
return this._percent;
|
|
||||||
},
|
|
||||||
|
|
||||||
set percent(val) {
|
|
||||||
this._indeterminate = isNaN(val);
|
|
||||||
this._percent = clamp(val, 0, 100);
|
|
||||||
this.updateBar();
|
|
||||||
},
|
|
||||||
|
|
||||||
setWidth: function ProgressBar_setWidth(viewer) {
|
|
||||||
if (viewer) {
|
|
||||||
var container = viewer.parentNode;
|
|
||||||
var scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
|
|
||||||
if (scrollbarWidth > 0) {
|
|
||||||
this.bar.setAttribute('style', 'width: calc(100% - ' +
|
|
||||||
scrollbarWidth + 'px);');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
hide: function ProgressBar_hide() {
|
|
||||||
if (!this.visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.visible = false;
|
|
||||||
this.bar.classList.add('hidden');
|
|
||||||
document.body.classList.remove('loadingInProgress');
|
|
||||||
},
|
|
||||||
|
|
||||||
show: function ProgressBar_show() {
|
|
||||||
if (this.visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.visible = true;
|
|
||||||
document.body.classList.add('loadingInProgress');
|
|
||||||
this.bar.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return ProgressBar;
|
|
||||||
})();
|
|
@ -874,15 +874,7 @@ export class filemanagerAPP extends EgwApp
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
let url;
|
egw.open({path: path, type: data.data.mime, download_url: data.data.download_url}, 'file','view',null,'_browser');
|
||||||
// Build ViewerJS url
|
|
||||||
if (data.data.mime.match(/application\/vnd\.oasis\.opendocument/) &&
|
|
||||||
egw.preference('document_doubleclick_action', 'filemanager') == 'collabeditor')
|
|
||||||
{
|
|
||||||
url = '/ViewerJS/#..' + data.data.download_url;
|
|
||||||
}
|
|
||||||
|
|
||||||
egw.open({path: path, type: data.data.mime, download_url: url}, 'file','view',null,'_browser');
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|