|
@@ -0,0 +1,1840 @@
|
|
|
|
|
+/* Copyright 2017 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.
|
|
|
|
|
+ */
|
|
|
|
|
+/* eslint-disable no-extend-native */
|
|
|
|
|
+/* globals VBArray, PDFJS, global */
|
|
|
|
|
+
|
|
|
|
|
+// Skip compatibility checks for the extensions and if we already ran
|
|
|
|
|
+// this module.
|
|
|
|
|
+if ((typeof PDFJSDev === 'undefined' ||
|
|
|
|
|
+ !PDFJSDev.test('FIREFOX || MOZCENTRAL || CHROME')) &&
|
|
|
|
|
+ (typeof PDFJS === 'undefined' || !PDFJS.compatibilityChecked)) {
|
|
|
|
|
+
|
|
|
|
|
+var globalScope = (typeof window !== 'undefined') ? window :
|
|
|
|
|
+ (typeof global !== 'undefined') ? global :
|
|
|
|
|
+ (typeof self !== 'undefined') ? self : this;
|
|
|
|
|
+
|
|
|
|
|
+var userAgent = (typeof navigator !== 'undefined' && navigator.userAgent) || '';
|
|
|
|
|
+var isAndroid = /Android/.test(userAgent);
|
|
|
|
|
+var isAndroidPre3 = /Android\s[0-2][^\d]/.test(userAgent);
|
|
|
|
|
+var isAndroidPre5 = /Android\s[0-4][^\d]/.test(userAgent);
|
|
|
|
|
+var isChrome = userAgent.indexOf('Chrom') >= 0;
|
|
|
|
|
+var isChromeWithRangeBug = /Chrome\/(39|40)\./.test(userAgent);
|
|
|
|
|
+var isIOSChrome = userAgent.indexOf('CriOS') >= 0;
|
|
|
|
|
+var isIE = userAgent.indexOf('Trident') >= 0;
|
|
|
|
|
+var isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent);
|
|
|
|
|
+var isOpera = userAgent.indexOf('Opera') >= 0;
|
|
|
|
|
+var isSafari = /Safari\//.test(userAgent) &&
|
|
|
|
|
+ !/(Chrome\/|Android\s)/.test(userAgent);
|
|
|
|
|
+
|
|
|
|
|
+var hasDOM = typeof window === 'object' && typeof document === 'object';
|
|
|
|
|
+
|
|
|
|
|
+// 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') {
|
|
|
|
|
+ globalScope.PDFJS = {};
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+PDFJS.compatibilityChecked = true;
|
|
|
|
|
+
|
|
|
|
|
+// 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') {
|
|
|
|
|
+ globalScope.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 Uint32ArrayView(buffer, length) {
|
|
|
|
|
+ this.buffer = buffer;
|
|
|
|
|
+ this.byteLength = buffer.length;
|
|
|
|
|
+ this.length = length;
|
|
|
|
|
+ ensureUint32ArrayViewProps(this.length);
|
|
|
|
|
+ }
|
|
|
|
|
+ Uint32ArrayView.prototype = Object.create(null);
|
|
|
|
|
+
|
|
|
|
|
+ var uint32ArrayViewSetters = 0;
|
|
|
|
|
+ function createUint32ArrayProp(index) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ get() {
|
|
|
|
|
+ var buffer = this.buffer, offset = index << 2;
|
|
|
|
|
+ return (buffer[offset] | (buffer[offset + 1] << 8) |
|
|
|
|
|
+ (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)) >>> 0;
|
|
|
|
|
+ },
|
|
|
|
|
+ set(value) {
|
|
|
|
|
+ var buffer = this.buffer, offset = index << 2;
|
|
|
|
|
+ buffer[offset] = value & 255;
|
|
|
|
|
+ buffer[offset + 1] = (value >> 8) & 255;
|
|
|
|
|
+ buffer[offset + 2] = (value >> 16) & 255;
|
|
|
|
|
+ buffer[offset + 3] = (value >>> 24) & 255;
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function ensureUint32ArrayViewProps(length) {
|
|
|
|
|
+ while (uint32ArrayViewSetters < length) {
|
|
|
|
|
+ Object.defineProperty(Uint32ArrayView.prototype,
|
|
|
|
|
+ uint32ArrayViewSetters,
|
|
|
|
|
+ createUint32ArrayProp(uint32ArrayViewSetters));
|
|
|
|
|
+ uint32ArrayViewSetters++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ globalScope.Uint8Array = TypedArray;
|
|
|
|
|
+ globalScope.Int8Array = TypedArray;
|
|
|
|
|
+
|
|
|
|
|
+ // we don't need support for set, byteLength for 32-bit array
|
|
|
|
|
+ // so we can use the TypedArray as well
|
|
|
|
|
+ globalScope.Int32Array = TypedArray;
|
|
|
|
|
+ globalScope.Uint16Array = TypedArray;
|
|
|
|
|
+ globalScope.Float32Array = TypedArray;
|
|
|
|
|
+ globalScope.Float64Array = TypedArray;
|
|
|
|
|
+
|
|
|
|
|
+ globalScope.Uint32Array = function () {
|
|
|
|
|
+ if (arguments.length === 3) {
|
|
|
|
|
+ // Building view for buffer, offset, and length
|
|
|
|
|
+ if (arguments[1] !== 0) {
|
|
|
|
|
+ throw new Error('offset !== 0 is not supported');
|
|
|
|
|
+ }
|
|
|
|
|
+ return new Uint32ArrayView(arguments[0], arguments[2]);
|
|
|
|
|
+ }
|
|
|
|
|
+ return TypedArray.apply(this, arguments);
|
|
|
|
|
+ };
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// window.CanvasPixelArray.buffer/.byteLength
|
|
|
|
|
+// Support: IE9
|
|
|
|
|
+(function canvasPixelArrayBuffer() {
|
|
|
|
|
+ if (!hasDOM || !window.CanvasPixelArray) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ var cpaProto = window.CanvasPixelArray.prototype;
|
|
|
|
|
+ if ('buffer' in cpaProto) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // Trying to fake CanvasPixelArray as Uint8ClampedArray.
|
|
|
|
|
+ Object.defineProperty(cpaProto, 'buffer', {
|
|
|
|
|
+ get() {
|
|
|
|
|
+ return this;
|
|
|
|
|
+ },
|
|
|
|
|
+ enumerable: false,
|
|
|
|
|
+ configurable: true,
|
|
|
|
|
+ });
|
|
|
|
|
+ Object.defineProperty(cpaProto, 'byteLength', {
|
|
|
|
|
+ get() {
|
|
|
|
|
+ return this.length;
|
|
|
|
|
+ },
|
|
|
|
|
+ enumerable: false,
|
|
|
|
|
+ configurable: true,
|
|
|
|
|
+ });
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// URL = URL || webkitURL
|
|
|
|
|
+// Support: Safari<7, Android 4.2+
|
|
|
|
|
+(function normalizeURLObject() {
|
|
|
|
|
+ if (!globalScope.URL) {
|
|
|
|
|
+ globalScope.URL = globalScope.webkitURL;
|
|
|
|
|
+ }
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Object.defineProperty()?
|
|
|
|
|
+// Support: Android<4.0, Safari<5.1
|
|
|
|
|
+(function checkObjectDefinePropertyCompatibility() {
|
|
|
|
|
+ if (typeof Object.defineProperty !== 'undefined') {
|
|
|
|
|
+ var definePropertyPossible = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (hasDOM) {
|
|
|
|
|
+ // 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() {
|
|
|
|
|
+ if (typeof XMLHttpRequest === 'undefined') {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ 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;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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());
|
|
|
|
|
+ }
|
|
|
|
|
+ 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 globalScope) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var digits =
|
|
|
|
|
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
|
|
|
+
|
|
|
|
|
+ globalScope.btoa = function (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 globalScope) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // https://github.com/davidchambers/Base64.js
|
|
|
|
|
+ var digits =
|
|
|
|
|
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
|
|
|
+ globalScope.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() {
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ var div = document.createElement('div');
|
|
|
|
|
+ if ('dataset' in div) {
|
|
|
|
|
+ return; // dataset property exists
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Object.defineProperty(HTMLElement.prototype, 'dataset', {
|
|
|
|
|
+ get() {
|
|
|
|
|
+ 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() {
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var div = document.createElement('div');
|
|
|
|
|
+ if ('classList' in div) {
|
|
|
|
|
+ return; // classList property exists
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var classListPrototype = {
|
|
|
|
|
+ add(name) {
|
|
|
|
|
+ changeList(this.element, name, true, false);
|
|
|
|
|
+ },
|
|
|
|
|
+ contains(name) {
|
|
|
|
|
+ return changeList(this.element, name, false, false);
|
|
|
|
|
+ },
|
|
|
|
|
+ remove(name) {
|
|
|
|
|
+ changeList(this.element, name, false, true);
|
|
|
|
|
+ },
|
|
|
|
|
+ toggle(name) {
|
|
|
|
|
+ changeList(this.element, name, true, true);
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Object.defineProperty(HTMLElement.prototype, 'classList', {
|
|
|
|
|
+ get() {
|
|
|
|
|
+ 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,
|
|
|
|
|
+ });
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Checking if worker has console support. Forwarding all messages to the main
|
|
|
|
|
+// thread if console object is absent.
|
|
|
|
|
+(function checkWorkerConsoleCompatibility() {
|
|
|
|
|
+ if (typeof importScripts === 'undefined' || 'console' in globalScope) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var consoleTimer = {};
|
|
|
|
|
+
|
|
|
|
|
+ var workerConsole = {
|
|
|
|
|
+ log: function log() {
|
|
|
|
|
+ var args = Array.prototype.slice.call(arguments);
|
|
|
|
|
+ globalScope.postMessage({
|
|
|
|
|
+ targetName: 'main',
|
|
|
|
|
+ action: 'console_log',
|
|
|
|
|
+ data: args,
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ error: function error() {
|
|
|
|
|
+ var args = Array.prototype.slice.call(arguments);
|
|
|
|
|
+ globalScope.postMessage({
|
|
|
|
|
+ targetName: 'main',
|
|
|
|
|
+ action: 'console_error',
|
|
|
|
|
+ data: args,
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ time: function time(name) {
|
|
|
|
|
+ consoleTimer[name] = Date.now();
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ timeEnd: function timeEnd(name) {
|
|
|
|
|
+ var time = consoleTimer[name];
|
|
|
|
|
+ if (!time) {
|
|
|
|
|
+ throw new Error('Unknown timer name ' + name);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.log('Timer:', name, Date.now() - time);
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ globalScope.console = workerConsole;
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Check console compatibility
|
|
|
|
|
+// In older IE versions the console object is not available
|
|
|
|
|
+// unless console is open.
|
|
|
|
|
+// Support: IE<10
|
|
|
|
|
+(function checkConsoleCompatibility() {
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!('console' in window)) {
|
|
|
|
|
+ window.console = {
|
|
|
|
|
+ log() {},
|
|
|
|
|
+ error() {},
|
|
|
|
|
+ warn() {},
|
|
|
|
|
+ };
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ 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);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// 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 (isOpera) {
|
|
|
|
|
+ // use browser detection since we cannot feature-check this bug
|
|
|
|
|
+ document.addEventListener('click', ignoreIfTargetDisabled, true);
|
|
|
|
|
+ }
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Checks if possible to use URL.createObjectURL()
|
|
|
|
|
+// Support: IE, Chrome on iOS
|
|
|
|
|
+(function checkOnBlobSupport() {
|
|
|
|
|
+ // sometimes IE and Chrome on iOS loosing the data created with
|
|
|
|
|
+ // createObjectURL(), see #3977 and #8081
|
|
|
|
|
+ if (isIE || isIOSChrome) {
|
|
|
|
|
+ PDFJS.disableCreateObjectURL = true;
|
|
|
|
|
+ }
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Checks if navigator.language is supported
|
|
|
|
|
+(function checkNavigatorLanguage() {
|
|
|
|
|
+ if (typeof navigator === 'undefined') {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ('language' in navigator) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ PDFJS.locale = navigator.userLanguage || 'en-US';
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Support: Safari 6.0+, Android<3.0, Chrome 39/40, iOS
|
|
|
|
|
+(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.
|
|
|
|
|
+
|
|
|
|
|
+ // 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.
|
|
|
|
|
+
|
|
|
|
|
+ // Range requests are broken in Chrome 39 and 40, https://crbug.com/442318
|
|
|
|
|
+ if (isSafari || isAndroidPre3 || isChromeWithRangeBug || isIOS) {
|
|
|
|
|
+ PDFJS.disableRange = true;
|
|
|
|
|
+ PDFJS.disableStream = true;
|
|
|
|
|
+ }
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Check if the browser supports manipulation of the history.
|
|
|
|
|
+// Support: IE<10, Android<4.2
|
|
|
|
|
+(function checkHistoryManipulation() {
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 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 || isAndroidPre3) {
|
|
|
|
|
+ PDFJS.disableHistory = true;
|
|
|
|
|
+ }
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Support: IE<11, Chrome<21, Android<4.4, Safari<6
|
|
|
|
|
+(function checkSetPresenceInImageData() {
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 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 (isChrome) {
|
|
|
|
|
+ versionMatch = userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
|
|
|
|
|
+ // Chrome < 21 lacks the set function.
|
|
|
|
|
+ polyfill = versionMatch && parseInt(versionMatch[2]) < 21;
|
|
|
|
|
+ } else if (isAndroid) {
|
|
|
|
|
+ // 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 = isAndroidPre5;
|
|
|
|
|
+ } else if (isSafari) {
|
|
|
|
|
+ versionMatch = 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;
|
|
|
|
|
+ var createImageData = contextPrototype.createImageData;
|
|
|
|
|
+ contextPrototype.createImageData = function(w, h) {
|
|
|
|
|
+ var imageData = createImageData.call(this, w, h);
|
|
|
|
|
+ imageData.data.set = function(arr) {
|
|
|
|
|
+ for (var i = 0, ii = this.length; i < ii; i++) {
|
|
|
|
|
+ this[i] = arr[i];
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ return imageData;
|
|
|
|
|
+ };
|
|
|
|
|
+ // this closure will be kept referenced, so clear its vars
|
|
|
|
|
+ contextPrototype = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Support: IE<10, Android<4.0, iOS
|
|
|
|
|
+(function checkRequestAnimationFrame() {
|
|
|
|
|
+ function installFakeAnimationFrameFunctions() {
|
|
|
|
|
+ window.requestAnimationFrame = function (callback) {
|
|
|
|
|
+ return window.setTimeout(callback, 20);
|
|
|
|
|
+ };
|
|
|
|
|
+ window.cancelAnimationFrame = function (timeoutID) {
|
|
|
|
|
+ window.clearTimeout(timeoutID);
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isIOS) {
|
|
|
|
|
+ // requestAnimationFrame on iOS is broken, replacing with fake one.
|
|
|
|
|
+ installFakeAnimationFrameFunctions();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ('requestAnimationFrame' in window) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ window.requestAnimationFrame = window.mozRequestAnimationFrame ||
|
|
|
|
|
+ window.webkitRequestAnimationFrame;
|
|
|
|
|
+ if (window.requestAnimationFrame) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ installFakeAnimationFrameFunctions();
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Support: Android, iOS
|
|
|
|
|
+(function checkCanvasSizeLimitation() {
|
|
|
|
|
+ if (isIOS || isAndroid) {
|
|
|
|
|
+ // 5MP
|
|
|
|
|
+ PDFJS.maxCanvasPixels = 5242880;
|
|
|
|
|
+ }
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Disable fullscreen support for certain problematic configurations.
|
|
|
|
|
+// Support: IE11+ (when embedded).
|
|
|
|
|
+(function checkFullscreenSupport() {
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isIE && window.parent !== window) {
|
|
|
|
|
+ PDFJS.disableFullscreen = true;
|
|
|
|
|
+ }
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Provides document.currentScript support
|
|
|
|
|
+// Support: IE, Chrome<29.
|
|
|
|
|
+(function checkCurrentScript() {
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ('currentScript' in document) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Object.defineProperty(document, 'currentScript', {
|
|
|
|
|
+ get() {
|
|
|
|
|
+ var scripts = document.getElementsByTagName('script');
|
|
|
|
|
+ return scripts[scripts.length - 1];
|
|
|
|
|
+ },
|
|
|
|
|
+ enumerable: true,
|
|
|
|
|
+ configurable: true,
|
|
|
|
|
+ });
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Provides `input.type = 'type'` runtime failure protection.
|
|
|
|
|
+// Support: IE9,10.
|
|
|
|
|
+(function checkInputTypeNumberAssign() {
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ var el = document.createElement('input');
|
|
|
|
|
+ try {
|
|
|
|
|
+ el.type = 'number';
|
|
|
|
|
+ } catch (ex) {
|
|
|
|
|
+ var inputProto = el.constructor.prototype;
|
|
|
|
|
+ var typeProperty = Object.getOwnPropertyDescriptor(inputProto, 'type');
|
|
|
|
|
+ Object.defineProperty(inputProto, 'type', {
|
|
|
|
|
+ get() {
|
|
|
|
|
+ return typeProperty.get.call(this);
|
|
|
|
|
+ },
|
|
|
|
|
+ set(value) {
|
|
|
|
|
+ typeProperty.set.call(this, value === 'number' ? 'text' : value);
|
|
|
|
|
+ },
|
|
|
|
|
+ enumerable: true,
|
|
|
|
|
+ configurable: true,
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Provides correct document.readyState value for legacy browsers.
|
|
|
|
|
+// Support: IE9,10.
|
|
|
|
|
+(function checkDocumentReadyState() {
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!document.attachEvent) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ var documentProto = document.constructor.prototype;
|
|
|
|
|
+ var readyStateProto = Object.getOwnPropertyDescriptor(documentProto,
|
|
|
|
|
+ 'readyState');
|
|
|
|
|
+ Object.defineProperty(documentProto, 'readyState', {
|
|
|
|
|
+ get() {
|
|
|
|
|
+ var value = readyStateProto.get.call(this);
|
|
|
|
|
+ return value === 'interactive' ? 'loading' : value;
|
|
|
|
|
+ },
|
|
|
|
|
+ set(value) {
|
|
|
|
|
+ readyStateProto.set.call(this, value);
|
|
|
|
|
+ },
|
|
|
|
|
+ enumerable: true,
|
|
|
|
|
+ configurable: true,
|
|
|
|
|
+ });
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Provides support for ChildNode.remove in legacy browsers.
|
|
|
|
|
+// Support: IE.
|
|
|
|
|
+(function checkChildNodeRemove() {
|
|
|
|
|
+ if (!hasDOM) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof Element.prototype.remove !== 'undefined') {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Element.prototype.remove = function () {
|
|
|
|
|
+ if (this.parentNode) {
|
|
|
|
|
+ this.parentNode.removeChild(this);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Polyfill for Promises:
|
|
|
|
|
+ * The following promise implementation tries to generally implement the
|
|
|
|
|
+ * Promise/A+ spec. Some notable differences from other promise libraries are:
|
|
|
|
|
+ * - There currently isn't a separate deferred and promise object.
|
|
|
|
|
+ * - Unhandled rejections eventually show an error if they aren't handled.
|
|
|
|
|
+ *
|
|
|
|
|
+ * Based off of the work in:
|
|
|
|
|
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=810490
|
|
|
|
|
+ */
|
|
|
|
|
+(function checkPromise() {
|
|
|
|
|
+ if (globalScope.Promise) {
|
|
|
|
|
+ // Promises existing in the DOM/Worker, checking presence of all/resolve
|
|
|
|
|
+ if (typeof globalScope.Promise.all !== 'function') {
|
|
|
|
|
+ globalScope.Promise.all = function (iterable) {
|
|
|
|
|
+ var count = 0, results = [], resolve, reject;
|
|
|
|
|
+ var promise = new globalScope.Promise(function (resolve_, reject_) {
|
|
|
|
|
+ resolve = resolve_;
|
|
|
|
|
+ reject = reject_;
|
|
|
|
|
+ });
|
|
|
|
|
+ iterable.forEach(function (p, i) {
|
|
|
|
|
+ count++;
|
|
|
|
|
+ p.then(function (result) {
|
|
|
|
|
+ results[i] = result;
|
|
|
|
|
+ count--;
|
|
|
|
|
+ if (count === 0) {
|
|
|
|
|
+ resolve(results);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, reject);
|
|
|
|
|
+ });
|
|
|
|
|
+ if (count === 0) {
|
|
|
|
|
+ resolve(results);
|
|
|
|
|
+ }
|
|
|
|
|
+ return promise;
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof globalScope.Promise.resolve !== 'function') {
|
|
|
|
|
+ globalScope.Promise.resolve = function (value) {
|
|
|
|
|
+ return new globalScope.Promise(function (resolve) {
|
|
|
|
|
+ resolve(value);
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof globalScope.Promise.reject !== 'function') {
|
|
|
|
|
+ globalScope.Promise.reject = function (reason) {
|
|
|
|
|
+ return new globalScope.Promise(function (resolve, reject) {
|
|
|
|
|
+ reject(reason);
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof globalScope.Promise.prototype.catch !== 'function') {
|
|
|
|
|
+ globalScope.Promise.prototype.catch = function (onReject) {
|
|
|
|
|
+ return globalScope.Promise.prototype.then(undefined, onReject);
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var STATUS_PENDING = 0;
|
|
|
|
|
+ var STATUS_RESOLVED = 1;
|
|
|
|
|
+ var STATUS_REJECTED = 2;
|
|
|
|
|
+
|
|
|
|
|
+ // In an attempt to avoid silent exceptions, unhandled rejections are
|
|
|
|
|
+ // tracked and if they aren't handled in a certain amount of time an
|
|
|
|
|
+ // error is logged.
|
|
|
|
|
+ var REJECTION_TIMEOUT = 500;
|
|
|
|
|
+
|
|
|
|
|
+ var HandlerManager = {
|
|
|
|
|
+ handlers: [],
|
|
|
|
|
+ running: false,
|
|
|
|
|
+ unhandledRejections: [],
|
|
|
|
|
+ pendingRejectionCheck: false,
|
|
|
|
|
+
|
|
|
|
|
+ scheduleHandlers: function scheduleHandlers(promise) {
|
|
|
|
|
+ if (promise._status === STATUS_PENDING) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.handlers = this.handlers.concat(promise._handlers);
|
|
|
|
|
+ promise._handlers = [];
|
|
|
|
|
+
|
|
|
|
|
+ if (this.running) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.running = true;
|
|
|
|
|
+
|
|
|
|
|
+ setTimeout(this.runHandlers.bind(this), 0);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ runHandlers: function runHandlers() {
|
|
|
|
|
+ var RUN_TIMEOUT = 1; // ms
|
|
|
|
|
+ var timeoutAt = Date.now() + RUN_TIMEOUT;
|
|
|
|
|
+ while (this.handlers.length > 0) {
|
|
|
|
|
+ var handler = this.handlers.shift();
|
|
|
|
|
+
|
|
|
|
|
+ var nextStatus = handler.thisPromise._status;
|
|
|
|
|
+ var nextValue = handler.thisPromise._value;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (nextStatus === STATUS_RESOLVED) {
|
|
|
|
|
+ if (typeof handler.onResolve === 'function') {
|
|
|
|
|
+ nextValue = handler.onResolve(nextValue);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (typeof handler.onReject === 'function') {
|
|
|
|
|
+ nextValue = handler.onReject(nextValue);
|
|
|
|
|
+ nextStatus = STATUS_RESOLVED;
|
|
|
|
|
+
|
|
|
|
|
+ if (handler.thisPromise._unhandledRejection) {
|
|
|
|
|
+ this.removeUnhandeledRejection(handler.thisPromise);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (ex) {
|
|
|
|
|
+ nextStatus = STATUS_REJECTED;
|
|
|
|
|
+ nextValue = ex;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ handler.nextPromise._updateStatus(nextStatus, nextValue);
|
|
|
|
|
+ if (Date.now() >= timeoutAt) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (this.handlers.length > 0) {
|
|
|
|
|
+ setTimeout(this.runHandlers.bind(this), 0);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.running = false;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ addUnhandledRejection: function addUnhandledRejection(promise) {
|
|
|
|
|
+ this.unhandledRejections.push({
|
|
|
|
|
+ promise,
|
|
|
|
|
+ time: Date.now(),
|
|
|
|
|
+ });
|
|
|
|
|
+ this.scheduleRejectionCheck();
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ removeUnhandeledRejection: function removeUnhandeledRejection(promise) {
|
|
|
|
|
+ promise._unhandledRejection = false;
|
|
|
|
|
+ for (var i = 0; i < this.unhandledRejections.length; i++) {
|
|
|
|
|
+ if (this.unhandledRejections[i].promise === promise) {
|
|
|
|
|
+ this.unhandledRejections.splice(i);
|
|
|
|
|
+ i--;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ scheduleRejectionCheck: function scheduleRejectionCheck() {
|
|
|
|
|
+ if (this.pendingRejectionCheck) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.pendingRejectionCheck = true;
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.pendingRejectionCheck = false;
|
|
|
|
|
+ var now = Date.now();
|
|
|
|
|
+ for (var i = 0; i < this.unhandledRejections.length; i++) {
|
|
|
|
|
+ if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) {
|
|
|
|
|
+ var unhandled = this.unhandledRejections[i].promise._value;
|
|
|
|
|
+ var msg = 'Unhandled rejection: ' + unhandled;
|
|
|
|
|
+ if (unhandled.stack) {
|
|
|
|
|
+ msg += '\n' + unhandled.stack;
|
|
|
|
|
+ }
|
|
|
|
|
+ // Raising and catching the error, so debugger can break on it.
|
|
|
|
|
+ try {
|
|
|
|
|
+ throw new Error(msg);
|
|
|
|
|
+ } catch (_) {
|
|
|
|
|
+ console.warn(msg);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.unhandledRejections.splice(i);
|
|
|
|
|
+ i--;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.unhandledRejections.length) {
|
|
|
|
|
+ this.scheduleRejectionCheck();
|
|
|
|
|
+ }
|
|
|
|
|
+ }, REJECTION_TIMEOUT);
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ var Promise = function Promise(resolver) {
|
|
|
|
|
+ this._status = STATUS_PENDING;
|
|
|
|
|
+ this._handlers = [];
|
|
|
|
|
+ try {
|
|
|
|
|
+ resolver.call(this, this._resolve.bind(this), this._reject.bind(this));
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ this._reject(e);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Builds a promise that is resolved when all the passed in promises are
|
|
|
|
|
+ * resolved.
|
|
|
|
|
+ * @param {array} promises array of data and/or promises to wait for.
|
|
|
|
|
+ * @return {Promise} New dependent promise.
|
|
|
|
|
+ */
|
|
|
|
|
+ Promise.all = function Promise_all(promises) {
|
|
|
|
|
+ var resolveAll, rejectAll;
|
|
|
|
|
+ var deferred = new Promise(function (resolve, reject) {
|
|
|
|
|
+ resolveAll = resolve;
|
|
|
|
|
+ rejectAll = reject;
|
|
|
|
|
+ });
|
|
|
|
|
+ var unresolved = promises.length;
|
|
|
|
|
+ var results = [];
|
|
|
|
|
+ if (unresolved === 0) {
|
|
|
|
|
+ resolveAll(results);
|
|
|
|
|
+ return deferred;
|
|
|
|
|
+ }
|
|
|
|
|
+ function reject(reason) {
|
|
|
|
|
+ if (deferred._status === STATUS_REJECTED) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ results = [];
|
|
|
|
|
+ rejectAll(reason);
|
|
|
|
|
+ }
|
|
|
|
|
+ for (var i = 0, ii = promises.length; i < ii; ++i) {
|
|
|
|
|
+ var promise = promises[i];
|
|
|
|
|
+ var resolve = (function(i) {
|
|
|
|
|
+ return function(value) {
|
|
|
|
|
+ if (deferred._status === STATUS_REJECTED) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ results[i] = value;
|
|
|
|
|
+ unresolved--;
|
|
|
|
|
+ if (unresolved === 0) {
|
|
|
|
|
+ resolveAll(results);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ })(i);
|
|
|
|
|
+ if (Promise.isPromise(promise)) {
|
|
|
|
|
+ promise.then(resolve, reject);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ resolve(promise);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return deferred;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Checks if the value is likely a promise (has a 'then' function).
|
|
|
|
|
+ * @return {boolean} true if value is thenable
|
|
|
|
|
+ */
|
|
|
|
|
+ Promise.isPromise = function Promise_isPromise(value) {
|
|
|
|
|
+ return value && typeof value.then === 'function';
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates resolved promise
|
|
|
|
|
+ * @param value resolve value
|
|
|
|
|
+ * @returns {Promise}
|
|
|
|
|
+ */
|
|
|
|
|
+ Promise.resolve = function Promise_resolve(value) {
|
|
|
|
|
+ return new Promise(function (resolve) {
|
|
|
|
|
+ resolve(value);
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates rejected promise
|
|
|
|
|
+ * @param reason rejection value
|
|
|
|
|
+ * @returns {Promise}
|
|
|
|
|
+ */
|
|
|
|
|
+ Promise.reject = function Promise_reject(reason) {
|
|
|
|
|
+ return new Promise(function (resolve, reject) {
|
|
|
|
|
+ reject(reason);
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Promise.prototype = {
|
|
|
|
|
+ _status: null,
|
|
|
|
|
+ _value: null,
|
|
|
|
|
+ _handlers: null,
|
|
|
|
|
+ _unhandledRejection: null,
|
|
|
|
|
+
|
|
|
|
|
+ _updateStatus: function Promise__updateStatus(status, value) {
|
|
|
|
|
+ if (this._status === STATUS_RESOLVED ||
|
|
|
|
|
+ this._status === STATUS_REJECTED) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (status === STATUS_RESOLVED &&
|
|
|
|
|
+ Promise.isPromise(value)) {
|
|
|
|
|
+ value.then(this._updateStatus.bind(this, STATUS_RESOLVED),
|
|
|
|
|
+ this._updateStatus.bind(this, STATUS_REJECTED));
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this._status = status;
|
|
|
|
|
+ this._value = value;
|
|
|
|
|
+
|
|
|
|
|
+ if (status === STATUS_REJECTED && this._handlers.length === 0) {
|
|
|
|
|
+ this._unhandledRejection = true;
|
|
|
|
|
+ HandlerManager.addUnhandledRejection(this);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ HandlerManager.scheduleHandlers(this);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ _resolve: function Promise_resolve(value) {
|
|
|
|
|
+ this._updateStatus(STATUS_RESOLVED, value);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ _reject: function Promise_reject(reason) {
|
|
|
|
|
+ this._updateStatus(STATUS_REJECTED, reason);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ then: function Promise_then(onResolve, onReject) {
|
|
|
|
|
+ var nextPromise = new Promise(function (resolve, reject) {
|
|
|
|
|
+ this.resolve = resolve;
|
|
|
|
|
+ this.reject = reject;
|
|
|
|
|
+ });
|
|
|
|
|
+ this._handlers.push({
|
|
|
|
|
+ thisPromise: this,
|
|
|
|
|
+ onResolve,
|
|
|
|
|
+ onReject,
|
|
|
|
|
+ nextPromise,
|
|
|
|
|
+ });
|
|
|
|
|
+ HandlerManager.scheduleHandlers(this);
|
|
|
|
|
+ return nextPromise;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ catch: function Promise_catch(onReject) {
|
|
|
|
|
+ return this.then(undefined, onReject);
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ globalScope.Promise = Promise;
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+(function checkWeakMap() {
|
|
|
|
|
+ if (globalScope.WeakMap) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var id = 0;
|
|
|
|
|
+ function WeakMap() {
|
|
|
|
|
+ this.id = '$weakmap' + (id++);
|
|
|
|
|
+ }
|
|
|
|
|
+ WeakMap.prototype = {
|
|
|
|
|
+ has(obj) {
|
|
|
|
|
+ return !!Object.getOwnPropertyDescriptor(obj, this.id);
|
|
|
|
|
+ },
|
|
|
|
|
+ get(obj, defaultValue) {
|
|
|
|
|
+ return this.has(obj) ? obj[this.id] : defaultValue;
|
|
|
|
|
+ },
|
|
|
|
|
+ set(obj, value) {
|
|
|
|
|
+ Object.defineProperty(obj, this.id, {
|
|
|
|
|
+ value,
|
|
|
|
|
+ enumerable: false,
|
|
|
|
|
+ configurable: true,
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ delete(obj) {
|
|
|
|
|
+ delete obj[this.id];
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ globalScope.WeakMap = WeakMap;
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+// Polyfill from https://github.com/Polymer/URL
|
|
|
|
|
+/* Any copyright is dedicated to the Public Domain.
|
|
|
|
|
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
|
+(function checkURLConstructor() {
|
|
|
|
|
+ // feature detect for URL constructor
|
|
|
|
|
+ var hasWorkingUrl = false;
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (typeof URL === 'function' &&
|
|
|
|
|
+ typeof URL.prototype === 'object' &&
|
|
|
|
|
+ ('origin' in URL.prototype)) {
|
|
|
|
|
+ var u = new URL('b', 'http://a');
|
|
|
|
|
+ u.pathname = 'c%20d';
|
|
|
|
|
+ hasWorkingUrl = u.href === 'http://a/c%20d';
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) { }
|
|
|
|
|
+
|
|
|
|
|
+ if (hasWorkingUrl) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var relative = Object.create(null);
|
|
|
|
|
+ relative['ftp'] = 21;
|
|
|
|
|
+ relative['file'] = 0;
|
|
|
|
|
+ relative['gopher'] = 70;
|
|
|
|
|
+ relative['http'] = 80;
|
|
|
|
|
+ relative['https'] = 443;
|
|
|
|
|
+ relative['ws'] = 80;
|
|
|
|
|
+ relative['wss'] = 443;
|
|
|
|
|
+
|
|
|
|
|
+ var relativePathDotMapping = Object.create(null);
|
|
|
|
|
+ relativePathDotMapping['%2e'] = '.';
|
|
|
|
|
+ relativePathDotMapping['.%2e'] = '..';
|
|
|
|
|
+ relativePathDotMapping['%2e.'] = '..';
|
|
|
|
|
+ relativePathDotMapping['%2e%2e'] = '..';
|
|
|
|
|
+
|
|
|
|
|
+ function isRelativeScheme(scheme) {
|
|
|
|
|
+ return relative[scheme] !== undefined;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function invalid() {
|
|
|
|
|
+ clear.call(this);
|
|
|
|
|
+ this._isInvalid = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function IDNAToASCII(h) {
|
|
|
|
|
+ if (h === '') {
|
|
|
|
|
+ invalid.call(this);
|
|
|
|
|
+ }
|
|
|
|
|
+ // XXX
|
|
|
|
|
+ return h.toLowerCase();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function percentEscape(c) {
|
|
|
|
|
+ var unicode = c.charCodeAt(0);
|
|
|
|
|
+ if (unicode > 0x20 &&
|
|
|
|
|
+ unicode < 0x7F &&
|
|
|
|
|
+ // " # < > ? `
|
|
|
|
|
+ [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) === -1
|
|
|
|
|
+ ) {
|
|
|
|
|
+ return c;
|
|
|
|
|
+ }
|
|
|
|
|
+ return encodeURIComponent(c);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function percentEscapeQuery(c) {
|
|
|
|
|
+ // XXX This actually needs to encode c using encoding and then
|
|
|
|
|
+ // convert the bytes one-by-one.
|
|
|
|
|
+
|
|
|
|
|
+ var unicode = c.charCodeAt(0);
|
|
|
|
|
+ if (unicode > 0x20 &&
|
|
|
|
|
+ unicode < 0x7F &&
|
|
|
|
|
+ // " # < > ` (do not escape '?')
|
|
|
|
|
+ [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) === -1
|
|
|
|
|
+ ) {
|
|
|
|
|
+ return c;
|
|
|
|
|
+ }
|
|
|
|
|
+ return encodeURIComponent(c);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var EOF, ALPHA = /[a-zA-Z]/,
|
|
|
|
|
+ ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
|
|
|
|
|
+
|
|
|
|
|
+ function parse(input, stateOverride, base) {
|
|
|
|
|
+ function err(message) {
|
|
|
|
|
+ errors.push(message);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var state = stateOverride || 'scheme start',
|
|
|
|
|
+ cursor = 0,
|
|
|
|
|
+ buffer = '',
|
|
|
|
|
+ seenAt = false,
|
|
|
|
|
+ seenBracket = false,
|
|
|
|
|
+ errors = [];
|
|
|
|
|
+
|
|
|
|
|
+ loop: while ((input[cursor - 1] !== EOF || cursor === 0) &&
|
|
|
|
|
+ !this._isInvalid) {
|
|
|
|
|
+ var c = input[cursor];
|
|
|
|
|
+ switch (state) {
|
|
|
|
|
+ case 'scheme start':
|
|
|
|
|
+ if (c && ALPHA.test(c)) {
|
|
|
|
|
+ buffer += c.toLowerCase(); // ASCII-safe
|
|
|
|
|
+ state = 'scheme';
|
|
|
|
|
+ } else if (!stateOverride) {
|
|
|
|
|
+ buffer = '';
|
|
|
|
|
+ state = 'no scheme';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ err('Invalid scheme.');
|
|
|
|
|
+ break loop;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'scheme':
|
|
|
|
|
+ if (c && ALPHANUMERIC.test(c)) {
|
|
|
|
|
+ buffer += c.toLowerCase(); // ASCII-safe
|
|
|
|
|
+ } else if (c === ':') {
|
|
|
|
|
+ this._scheme = buffer;
|
|
|
|
|
+ buffer = '';
|
|
|
|
|
+ if (stateOverride) {
|
|
|
|
|
+ break loop;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isRelativeScheme(this._scheme)) {
|
|
|
|
|
+ this._isRelative = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this._scheme === 'file') {
|
|
|
|
|
+ state = 'relative';
|
|
|
|
|
+ } else if (this._isRelative && base &&
|
|
|
|
|
+ base._scheme === this._scheme) {
|
|
|
|
|
+ state = 'relative or authority';
|
|
|
|
|
+ } else if (this._isRelative) {
|
|
|
|
|
+ state = 'authority first slash';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ state = 'scheme data';
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (!stateOverride) {
|
|
|
|
|
+ buffer = '';
|
|
|
|
|
+ cursor = 0;
|
|
|
|
|
+ state = 'no scheme';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ } else if (c === EOF) {
|
|
|
|
|
+ break loop;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ err('Code point not allowed in scheme: ' + c);
|
|
|
|
|
+ break loop;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'scheme data':
|
|
|
|
|
+ if (c === '?') {
|
|
|
|
|
+ this._query = '?';
|
|
|
|
|
+ state = 'query';
|
|
|
|
|
+ } else if (c === '#') {
|
|
|
|
|
+ this._fragment = '#';
|
|
|
|
|
+ state = 'fragment';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // XXX error handling
|
|
|
|
|
+ if (c !== EOF && c !== '\t' && c !== '\n' && c !== '\r') {
|
|
|
|
|
+ this._schemeData += percentEscape(c);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'no scheme':
|
|
|
|
|
+ if (!base || !(isRelativeScheme(base._scheme))) {
|
|
|
|
|
+ err('Missing scheme.');
|
|
|
|
|
+ invalid.call(this);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ state = 'relative';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'relative or authority':
|
|
|
|
|
+ if (c === '/' && input[cursor + 1] === '/') {
|
|
|
|
|
+ state = 'authority ignore slashes';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ err('Expected /, got: ' + c);
|
|
|
|
|
+ state = 'relative';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'relative':
|
|
|
|
|
+ this._isRelative = true;
|
|
|
|
|
+ if (this._scheme !== 'file') {
|
|
|
|
|
+ this._scheme = base._scheme;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (c === EOF) {
|
|
|
|
|
+ this._host = base._host;
|
|
|
|
|
+ this._port = base._port;
|
|
|
|
|
+ this._path = base._path.slice();
|
|
|
|
|
+ this._query = base._query;
|
|
|
|
|
+ this._username = base._username;
|
|
|
|
|
+ this._password = base._password;
|
|
|
|
|
+ break loop;
|
|
|
|
|
+ } else if (c === '/' || c === '\\') {
|
|
|
|
|
+ if (c === '\\') {
|
|
|
|
|
+ err('\\ is an invalid code point.');
|
|
|
|
|
+ }
|
|
|
|
|
+ state = 'relative slash';
|
|
|
|
|
+ } else if (c === '?') {
|
|
|
|
|
+ this._host = base._host;
|
|
|
|
|
+ this._port = base._port;
|
|
|
|
|
+ this._path = base._path.slice();
|
|
|
|
|
+ this._query = '?';
|
|
|
|
|
+ this._username = base._username;
|
|
|
|
|
+ this._password = base._password;
|
|
|
|
|
+ state = 'query';
|
|
|
|
|
+ } else if (c === '#') {
|
|
|
|
|
+ this._host = base._host;
|
|
|
|
|
+ this._port = base._port;
|
|
|
|
|
+ this._path = base._path.slice();
|
|
|
|
|
+ this._query = base._query;
|
|
|
|
|
+ this._fragment = '#';
|
|
|
|
|
+ this._username = base._username;
|
|
|
|
|
+ this._password = base._password;
|
|
|
|
|
+ state = 'fragment';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ var nextC = input[cursor + 1];
|
|
|
|
|
+ var nextNextC = input[cursor + 2];
|
|
|
|
|
+ if (this._scheme !== 'file' || !ALPHA.test(c) ||
|
|
|
|
|
+ (nextC !== ':' && nextC !== '|') ||
|
|
|
|
|
+ (nextNextC !== EOF && nextNextC !== '/' && nextNextC !== '\\' &&
|
|
|
|
|
+ nextNextC !== '?' && nextNextC !== '#')) {
|
|
|
|
|
+ this._host = base._host;
|
|
|
|
|
+ this._port = base._port;
|
|
|
|
|
+ this._username = base._username;
|
|
|
|
|
+ this._password = base._password;
|
|
|
|
|
+ this._path = base._path.slice();
|
|
|
|
|
+ this._path.pop();
|
|
|
|
|
+ }
|
|
|
|
|
+ state = 'relative path';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'relative slash':
|
|
|
|
|
+ if (c === '/' || c === '\\') {
|
|
|
|
|
+ if (c === '\\') {
|
|
|
|
|
+ err('\\ is an invalid code point.');
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this._scheme === 'file') {
|
|
|
|
|
+ state = 'file host';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ state = 'authority ignore slashes';
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (this._scheme !== 'file') {
|
|
|
|
|
+ this._host = base._host;
|
|
|
|
|
+ this._port = base._port;
|
|
|
|
|
+ this._username = base._username;
|
|
|
|
|
+ this._password = base._password;
|
|
|
|
|
+ }
|
|
|
|
|
+ state = 'relative path';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'authority first slash':
|
|
|
|
|
+ if (c === '/') {
|
|
|
|
|
+ state = 'authority second slash';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ err('Expected \'/\', got: ' + c);
|
|
|
|
|
+ state = 'authority ignore slashes';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'authority second slash':
|
|
|
|
|
+ state = 'authority ignore slashes';
|
|
|
|
|
+ if (c !== '/') {
|
|
|
|
|
+ err('Expected \'/\', got: ' + c);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'authority ignore slashes':
|
|
|
|
|
+ if (c !== '/' && c !== '\\') {
|
|
|
|
|
+ state = 'authority';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ err('Expected authority, got: ' + c);
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'authority':
|
|
|
|
|
+ if (c === '@') {
|
|
|
|
|
+ if (seenAt) {
|
|
|
|
|
+ err('@ already seen.');
|
|
|
|
|
+ buffer += '%40';
|
|
|
|
|
+ }
|
|
|
|
|
+ seenAt = true;
|
|
|
|
|
+ for (var i = 0; i < buffer.length; i++) {
|
|
|
|
|
+ var cp = buffer[i];
|
|
|
|
|
+ if (cp === '\t' || cp === '\n' || cp === '\r') {
|
|
|
|
|
+ err('Invalid whitespace in authority.');
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ // XXX check URL code points
|
|
|
|
|
+ if (cp === ':' && this._password === null) {
|
|
|
|
|
+ this._password = '';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ var tempC = percentEscape(cp);
|
|
|
|
|
+ if (this._password !== null) {
|
|
|
|
|
+ this._password += tempC;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this._username += tempC;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ buffer = '';
|
|
|
|
|
+ } else if (c === EOF || c === '/' || c === '\\' ||
|
|
|
|
|
+ c === '?' || c === '#') {
|
|
|
|
|
+ cursor -= buffer.length;
|
|
|
|
|
+ buffer = '';
|
|
|
|
|
+ state = 'host';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ buffer += c;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'file host':
|
|
|
|
|
+ if (c === EOF || c === '/' || c === '\\' || c === '?' || c === '#') {
|
|
|
|
|
+ if (buffer.length === 2 && ALPHA.test(buffer[0]) &&
|
|
|
|
|
+ (buffer[1] === ':' || buffer[1] === '|')) {
|
|
|
|
|
+ state = 'relative path';
|
|
|
|
|
+ } else if (buffer.length === 0) {
|
|
|
|
|
+ state = 'relative path start';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this._host = IDNAToASCII.call(this, buffer);
|
|
|
|
|
+ buffer = '';
|
|
|
|
|
+ state = 'relative path start';
|
|
|
|
|
+ }
|
|
|
|
|
+ continue;
|
|
|
|
|
+ } else if (c === '\t' || c === '\n' || c === '\r') {
|
|
|
|
|
+ err('Invalid whitespace in file host.');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ buffer += c;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'host':
|
|
|
|
|
+ case 'hostname':
|
|
|
|
|
+ if (c === ':' && !seenBracket) {
|
|
|
|
|
+ // XXX host parsing
|
|
|
|
|
+ this._host = IDNAToASCII.call(this, buffer);
|
|
|
|
|
+ buffer = '';
|
|
|
|
|
+ state = 'port';
|
|
|
|
|
+ if (stateOverride === 'hostname') {
|
|
|
|
|
+ break loop;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (c === EOF || c === '/' ||
|
|
|
|
|
+ c === '\\' || c === '?' || c === '#') {
|
|
|
|
|
+ this._host = IDNAToASCII.call(this, buffer);
|
|
|
|
|
+ buffer = '';
|
|
|
|
|
+ state = 'relative path start';
|
|
|
|
|
+ if (stateOverride) {
|
|
|
|
|
+ break loop;
|
|
|
|
|
+ }
|
|
|
|
|
+ continue;
|
|
|
|
|
+ } else if (c !== '\t' && c !== '\n' && c !== '\r') {
|
|
|
|
|
+ if (c === '[') {
|
|
|
|
|
+ seenBracket = true;
|
|
|
|
|
+ } else if (c === ']') {
|
|
|
|
|
+ seenBracket = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ buffer += c;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ err('Invalid code point in host/hostname: ' + c);
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'port':
|
|
|
|
|
+ if (/[0-9]/.test(c)) {
|
|
|
|
|
+ buffer += c;
|
|
|
|
|
+ } else if (c === EOF || c === '/' || c === '\\' ||
|
|
|
|
|
+ c === '?' || c === '#' || stateOverride) {
|
|
|
|
|
+ if (buffer !== '') {
|
|
|
|
|
+ var temp = parseInt(buffer, 10);
|
|
|
|
|
+ if (temp !== relative[this._scheme]) {
|
|
|
|
|
+ this._port = temp + '';
|
|
|
|
|
+ }
|
|
|
|
|
+ buffer = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ if (stateOverride) {
|
|
|
|
|
+ break loop;
|
|
|
|
|
+ }
|
|
|
|
|
+ state = 'relative path start';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ } else if (c === '\t' || c === '\n' || c === '\r') {
|
|
|
|
|
+ err('Invalid code point in port: ' + c);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ invalid.call(this);
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'relative path start':
|
|
|
|
|
+ if (c === '\\') {
|
|
|
|
|
+ err('\'\\\' not allowed in path.');
|
|
|
|
|
+ }
|
|
|
|
|
+ state = 'relative path';
|
|
|
|
|
+ if (c !== '/' && c !== '\\') {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'relative path':
|
|
|
|
|
+ if (c === EOF || c === '/' || c === '\\' ||
|
|
|
|
|
+ (!stateOverride && (c === '?' || c === '#'))) {
|
|
|
|
|
+ if (c === '\\') {
|
|
|
|
|
+ err('\\ not allowed in relative path.');
|
|
|
|
|
+ }
|
|
|
|
|
+ var tmp;
|
|
|
|
|
+ if ((tmp = relativePathDotMapping[buffer.toLowerCase()])) {
|
|
|
|
|
+ buffer = tmp;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (buffer === '..') {
|
|
|
|
|
+ this._path.pop();
|
|
|
|
|
+ if (c !== '/' && c !== '\\') {
|
|
|
|
|
+ this._path.push('');
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (buffer === '.' && c !== '/' && c !== '\\') {
|
|
|
|
|
+ this._path.push('');
|
|
|
|
|
+ } else if (buffer !== '.') {
|
|
|
|
|
+ if (this._scheme === 'file' && this._path.length === 0 &&
|
|
|
|
|
+ buffer.length === 2 && ALPHA.test(buffer[0]) &&
|
|
|
|
|
+ buffer[1] === '|') {
|
|
|
|
|
+ buffer = buffer[0] + ':';
|
|
|
|
|
+ }
|
|
|
|
|
+ this._path.push(buffer);
|
|
|
|
|
+ }
|
|
|
|
|
+ buffer = '';
|
|
|
|
|
+ if (c === '?') {
|
|
|
|
|
+ this._query = '?';
|
|
|
|
|
+ state = 'query';
|
|
|
|
|
+ } else if (c === '#') {
|
|
|
|
|
+ this._fragment = '#';
|
|
|
|
|
+ state = 'fragment';
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (c !== '\t' && c !== '\n' && c !== '\r') {
|
|
|
|
|
+ buffer += percentEscape(c);
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'query':
|
|
|
|
|
+ if (!stateOverride && c === '#') {
|
|
|
|
|
+ this._fragment = '#';
|
|
|
|
|
+ state = 'fragment';
|
|
|
|
|
+ } else if (c !== EOF && c !== '\t' && c !== '\n' && c !== '\r') {
|
|
|
|
|
+ this._query += percentEscapeQuery(c);
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'fragment':
|
|
|
|
|
+ if (c !== EOF && c !== '\t' && c !== '\n' && c !== '\r') {
|
|
|
|
|
+ this._fragment += c;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ cursor++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function clear() {
|
|
|
|
|
+ this._scheme = '';
|
|
|
|
|
+ this._schemeData = '';
|
|
|
|
|
+ this._username = '';
|
|
|
|
|
+ this._password = null;
|
|
|
|
|
+ this._host = '';
|
|
|
|
|
+ this._port = '';
|
|
|
|
|
+ this._path = [];
|
|
|
|
|
+ this._query = '';
|
|
|
|
|
+ this._fragment = '';
|
|
|
|
|
+ this._isInvalid = false;
|
|
|
|
|
+ this._isRelative = false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Does not process domain names or IP addresses.
|
|
|
|
|
+ // Does not handle encoding for the query parameter.
|
|
|
|
|
+ function JURL(url, base /* , encoding */) {
|
|
|
|
|
+ if (base !== undefined && !(base instanceof JURL)) {
|
|
|
|
|
+ base = new JURL(String(base));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this._url = url;
|
|
|
|
|
+ clear.call(this);
|
|
|
|
|
+
|
|
|
|
|
+ var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, '');
|
|
|
|
|
+ // encoding = encoding || 'utf-8'
|
|
|
|
|
+
|
|
|
|
|
+ parse.call(this, input, null, base);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JURL.prototype = {
|
|
|
|
|
+ toString() {
|
|
|
|
|
+ return this.href;
|
|
|
|
|
+ },
|
|
|
|
|
+ get href() {
|
|
|
|
|
+ if (this._isInvalid) {
|
|
|
|
|
+ return this._url;
|
|
|
|
|
+ }
|
|
|
|
|
+ var authority = '';
|
|
|
|
|
+ if (this._username !== '' || this._password !== null) {
|
|
|
|
|
+ authority = this._username +
|
|
|
|
|
+ (this._password !== null ? ':' + this._password : '') + '@';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return this.protocol +
|
|
|
|
|
+ (this._isRelative ? '//' + authority + this.host : '') +
|
|
|
|
|
+ this.pathname + this._query + this._fragment;
|
|
|
|
|
+ },
|
|
|
|
|
+ set href(href) {
|
|
|
|
|
+ clear.call(this);
|
|
|
|
|
+ parse.call(this, href);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ get protocol() {
|
|
|
|
|
+ return this._scheme + ':';
|
|
|
|
|
+ },
|
|
|
|
|
+ set protocol(protocol) {
|
|
|
|
|
+ if (this._isInvalid) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ parse.call(this, protocol + ':', 'scheme start');
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ get host() {
|
|
|
|
|
+ return this._isInvalid ? '' : this._port ?
|
|
|
|
|
+ this._host + ':' + this._port : this._host;
|
|
|
|
|
+ },
|
|
|
|
|
+ set host(host) {
|
|
|
|
|
+ if (this._isInvalid || !this._isRelative) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ parse.call(this, host, 'host');
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ get hostname() {
|
|
|
|
|
+ return this._host;
|
|
|
|
|
+ },
|
|
|
|
|
+ set hostname(hostname) {
|
|
|
|
|
+ if (this._isInvalid || !this._isRelative) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ parse.call(this, hostname, 'hostname');
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ get port() {
|
|
|
|
|
+ return this._port;
|
|
|
|
|
+ },
|
|
|
|
|
+ set port(port) {
|
|
|
|
|
+ if (this._isInvalid || !this._isRelative) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ parse.call(this, port, 'port');
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ get pathname() {
|
|
|
|
|
+ return this._isInvalid ? '' : this._isRelative ?
|
|
|
|
|
+ '/' + this._path.join('/') : this._schemeData;
|
|
|
|
|
+ },
|
|
|
|
|
+ set pathname(pathname) {
|
|
|
|
|
+ if (this._isInvalid || !this._isRelative) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this._path = [];
|
|
|
|
|
+ parse.call(this, pathname, 'relative path start');
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ get search() {
|
|
|
|
|
+ return this._isInvalid || !this._query || this._query === '?' ?
|
|
|
|
|
+ '' : this._query;
|
|
|
|
|
+ },
|
|
|
|
|
+ set search(search) {
|
|
|
|
|
+ if (this._isInvalid || !this._isRelative) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this._query = '?';
|
|
|
|
|
+ if (search[0] === '?') {
|
|
|
|
|
+ search = search.slice(1);
|
|
|
|
|
+ }
|
|
|
|
|
+ parse.call(this, search, 'query');
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ get hash() {
|
|
|
|
|
+ return this._isInvalid || !this._fragment || this._fragment === '#' ?
|
|
|
|
|
+ '' : this._fragment;
|
|
|
|
|
+ },
|
|
|
|
|
+ set hash(hash) {
|
|
|
|
|
+ if (this._isInvalid) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this._fragment = '#';
|
|
|
|
|
+ if (hash[0] === '#') {
|
|
|
|
|
+ hash = hash.slice(1);
|
|
|
|
|
+ }
|
|
|
|
|
+ parse.call(this, hash, 'fragment');
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ get origin() {
|
|
|
|
|
+ var host;
|
|
|
|
|
+ if (this._isInvalid || !this._scheme) {
|
|
|
|
|
+ return '';
|
|
|
|
|
+ }
|
|
|
|
|
+ // javascript: Gecko returns String(""), WebKit/Blink String("null")
|
|
|
|
|
+ // Gecko throws error for "data://"
|
|
|
|
|
+ // data: Gecko returns "", Blink returns "data://", WebKit returns "null"
|
|
|
|
|
+ // Gecko returns String("") for file: mailto:
|
|
|
|
|
+ // WebKit/Blink returns String("SCHEME://") for file: mailto:
|
|
|
|
|
+ switch (this._scheme) {
|
|
|
|
|
+ case 'data':
|
|
|
|
|
+ case 'file':
|
|
|
|
|
+ case 'javascript':
|
|
|
|
|
+ case 'mailto':
|
|
|
|
|
+ return 'null';
|
|
|
|
|
+ }
|
|
|
|
|
+ host = this.host;
|
|
|
|
|
+ if (!host) {
|
|
|
|
|
+ return '';
|
|
|
|
|
+ }
|
|
|
|
|
+ return this._scheme + '://' + host;
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // Copy over the static methods
|
|
|
|
|
+ var OriginalURL = globalScope.URL;
|
|
|
|
|
+ if (OriginalURL) {
|
|
|
|
|
+ JURL.createObjectURL = function(blob) {
|
|
|
|
|
+ // IE extension allows a second optional options argument.
|
|
|
|
|
+ // http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx
|
|
|
|
|
+ return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
|
|
|
|
|
+ };
|
|
|
|
|
+ JURL.revokeObjectURL = function(url) {
|
|
|
|
|
+ OriginalURL.revokeObjectURL(url);
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ globalScope.URL = JURL;
|
|
|
|
|
+})();
|
|
|
|
|
+
|
|
|
|
|
+}
|