diff --git a/src/apps/viewer/index.ts b/src/apps/viewer/index.ts index 42d787011f29d8d4d7d9d96d28e76750db078b90..da6fb6504bc321868704f98f7699163b0f4d157c 100644 --- a/src/apps/viewer/index.ts +++ b/src/apps/viewer/index.ts @@ -5,6 +5,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ +import '../../mol-util/polyfill'; import { createPlugin, DefaultPluginSpec } from '../../mol-plugin'; import './index.html' import './favicon.ico' diff --git a/src/mol-util/polyfill.ts b/src/mol-util/polyfill.ts new file mode 100644 index 0000000000000000000000000000000000000000..85b2831888894850342755c33344879e80817141 --- /dev/null +++ b/src/mol-util/polyfill.ts @@ -0,0 +1,595 @@ +/** + * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +if (typeof window !== 'undefined') { + (function () { + // Console-polyfill. MIT license. + // https://github.com/paulmillr/console-polyfill + // Make it safe to do console.log() always. + + (window as any).console = window.console || {} + let con = window.console + let prop, method + let empty = {} + let dummy = function () {} + let properties = 'memory'.split(',') + let methods = ( + 'assert,clear,count,debug,dir,dirxml,error,exception,group,' + + 'groupCollapsed,groupEnd,info,log,markTimeline,profile,profiles,profileEnd,' + + 'show,table,time,timeEnd,timeline,timelineEnd,timeStamp,trace,warn' + ).split(',') + + while ((prop = properties.pop())) if (!(con as any)[prop]) (con as any)[prop] = empty + while ((method = methods.pop())) if (!(con as any)[method]) (con as any)[method] = dummy + })() +} + +if (typeof window.HTMLCanvasElement !== 'undefined' && !window.HTMLCanvasElement.prototype.toBlob) { + // http://code.google.com/p/chromium/issues/detail?id=67587#57 + Object.defineProperty(window.HTMLCanvasElement.prototype, 'toBlob', { + + value: function (callback: any, type: any, quality: any) { + let bin = window.atob(this.toDataURL(type, quality).split(',')[ 1 ]) + let len = bin.length + let len32 = len >> 2 + let a8 = new Uint8Array(len) + let a32 = new Uint32Array(a8.buffer, 0, len32) + + let j = 0 + for (let i = 0; i < len32; i++) { + a32[i] = ( + bin.charCodeAt(j++) | + bin.charCodeAt(j++) << 8 | + bin.charCodeAt(j++) << 16 | + bin.charCodeAt(j++) << 24 + ) + } + + let tailLength = len & 3 + + while (tailLength--) { + a8[ j ] = bin.charCodeAt(j++) + } + + callback(new window.Blob([ a8 ], { 'type': type || 'image/png' })) + } + + }) +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt +Math.cbrt = Math.cbrt || function (x) { + let y = Math.pow(Math.abs(x), 1 / 3) + return x < 0 ? -y : y +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign +if (!Math.sign) { + Math.sign = function (x) { + // If x is NaN, the result is NaN. + // If x is -0, the result is -0. + // If x is +0, the result is +0. + // If x is negative and not -0, the result is -1. + // If x is positive and not +0, the result is +1. + x = +x // convert to a number + if (x === 0 || isNaN(x)) { + return Number(x) + } + return x > 0 ? 1 : -1 + } +} + +if (!Number.isInteger) { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger + Number.isInteger = function isInteger (nVal) { + return typeof nVal === 'number' && isFinite(nVal) && nVal > -9007199254740992 && nVal < 9007199254740992 && Math.floor(nVal) === nVal + } +} + +if (!Number.isNaN) { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN + Number.isNaN = function isNaN (value) { + return value !== value // eslint-disable-line no-self-compare + } +} + +if (!Object.assign) { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign + Object.defineProperty(Object, 'assign', { + + enumerable: false, + configurable: true, + writable: true, + + value: function (target: any/* , firstSource */) { + 'use strict' + if (target === undefined || target === null) { throw new TypeError('Cannot convert first argument to object') } + + let to = Object(target) + + let hasPendingException = false + let pendingException + + for (let i = 1; i < arguments.length; i++) { + let nextSource = arguments[i] + if (nextSource === undefined || nextSource === null) { continue } + + let keysArray = Object.keys(Object(nextSource)) + for (let nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { + let nextKey = keysArray[nextIndex] + try { + let desc = Object.getOwnPropertyDescriptor(nextSource, nextKey) + if (desc !== undefined && desc.enumerable) { to[nextKey] = nextSource[nextKey] } + } catch (e) { + if (!hasPendingException) { + hasPendingException = true + pendingException = e + } + } + } + + if (hasPendingException) { throw pendingException } + } + + return to + } + + }) +} + +if (!String.prototype.startsWith) { + /* ! https://mths.be/startswith v0.2.0 by @mathias */ + + (function () { + 'use strict' // needed to support `apply`/`call` with `undefined`/`null` + let defineProperty = (function () { + // IE 8 only supports `Object.defineProperty` on DOM elements + let result + try { + let object = {} + let $defineProperty = Object.defineProperty + result = $defineProperty(object, object as any, object) && $defineProperty + } catch (error) {} // eslint-disable-line no-empty + return result + }()) + let toString = {}.toString + let startsWith = function (this: any, search: any) { + if (this === null) { + throw TypeError() + } + let string = String(this) + if (search && toString.call(search) === '[object RegExp]') { + throw TypeError() + } + let stringLength = string.length + let searchString = String(search) + let searchLength = searchString.length + let position = arguments.length > 1 ? arguments[1] : undefined + // `ToInteger` + let pos = position ? Number(position) : 0 + if (Number.isNaN(pos)) { + pos = 0 + } + let start = Math.min(Math.max(pos, 0), stringLength) + // Avoid the `indexOf` call if no match is possible + if (searchLength + start > stringLength) { + return false + } + let index = -1 + while (++index < searchLength) { + if (string.charCodeAt(start + index) !== searchString.charCodeAt(index)) { + return false + } + } + return true + } + if (defineProperty) { + defineProperty(String.prototype, 'startsWith', { + 'value': startsWith, + 'configurable': true, + 'writable': true + }) + } else { + // eslint-disable-next-line no-extend-native + String.prototype.startsWith = startsWith + } + }()) +} + +if (!String.prototype.endsWith) { + // eslint-disable-next-line no-extend-native + String.prototype.endsWith = function (searchString, position) { + let subjectString = this.toString() + if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { + position = subjectString.length + } + position -= searchString.length + let lastIndex = subjectString.indexOf(searchString, position) + return lastIndex !== -1 && lastIndex === position + } +} + +if (!String.prototype.repeat) { + // eslint-disable-next-line no-extend-native + String.prototype.repeat = function (count) { + 'use strict' + if (this === null) { + throw new TypeError('can\'t convert ' + this + ' to object') + } + let str = '' + this + count = +count + if (Number.isNaN(count)) { + count = 0 + } + if (count < 0) { + throw new RangeError('repeat count must be non-negative') + } + if (count === Infinity) { + throw new RangeError('repeat count must be less than infinity') + } + count = Math.floor(count) + if (str.length === 0 || count === 0) { + return '' + } + // Ensuring count is a 31-bit integer allows us to heavily optimize the + // main part. But anyway, most current (August 2014) browsers can't handle + // strings 1 << 28 chars or longer, so: + if (str.length * count >= 1 << 28) { + throw new RangeError('repeat count must not overflow maximum string size') + } + let rpt = '' + for (;;) { + if ((count & 1) === 1) { + rpt += str + } + count >>>= 1 + if (count === 0) { + break + } + str += str + } + // Could we try: + // return Array(count + 1).join(this); + return rpt + } +} + +if (!String.prototype.includes) { + // eslint-disable-next-line no-extend-native + String.prototype.includes = function (search, start) { + 'use strict' + if (typeof start !== 'number') { + start = 0 + } + + if (start + search.length > this.length) { + return false + } else { + return this.indexOf(search, start) !== -1 + } + } +} + +if (!Array.prototype.includes) { + // eslint-disable-next-line no-extend-native + Array.prototype.includes = function (searchElement /* , fromIndex */) { + 'use strict' + if (this == null) { + throw new TypeError('Array.prototype.includes called on null or undefined') + } + + let O = Object(this) + let len = parseInt(O.length, 10) || 0 + if (len === 0) { + return false + } + let n = parseInt(arguments[1], 10) || 0 + let k + if (n >= 0) { + k = n + } else { + k = len + n + if (k < 0) { k = 0 } + } + let currentElement + while (k < len) { + currentElement = O[k] + if (searchElement === currentElement || + (Number.isNaN(searchElement) && Number.isNaN(currentElement)) + ) { + return true + } + k++ + } + return false + } +} + +// Production steps of ECMA-262, Edition 6, 22.1.2.1 +// Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from +if (!Array.from) { + Array.from = (function () { + let toStr = Object.prototype.toString + let isCallable = function (fn: any) { + return typeof fn === 'function' || toStr.call(fn) === '[object Function]' + } + let toInteger = function (value: any) { + let number = Number(value) + if (isNaN(number)) { return 0 } + if (number === 0 || !isFinite(number)) { return number } + return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)) + } + let maxSafeInteger = Math.pow(2, 53) - 1 + let toLength = function (value: any) { + let len = toInteger(value) + return Math.min(Math.max(len, 0), maxSafeInteger) + } + + // The length property of the from method is 1. + return function from (this: any, arrayLike: any/* , mapFn, thisArg */) { + // 1. Let C be the this value. + let C = this + + // 2. Let items be ToObject(arrayLike). + let items = Object(arrayLike) + + // 3. ReturnIfAbrupt(items). + if (arrayLike == null) { + throw new TypeError('Array.from requires an array-like object - not null or undefined') + } + + // 4. If mapfn is undefined, then let mapping be false. + let mapFn = arguments.length > 1 ? arguments[1] : void undefined + let T + if (typeof mapFn !== 'undefined') { + // 5. else + // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. + if (!isCallable(mapFn)) { + throw new TypeError('Array.from: when provided, the second argument must be a function') + } + + // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (arguments.length > 2) { + T = arguments[2] + } + } + + // 10. Let lenValue be Get(items, "length"). + // 11. Let len be ToLength(lenValue). + let len = toLength(items.length) + + // 13. If IsConstructor(C) is true, then + // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len. + // 14. a. Else, Let A be ArrayCreate(len). + let A = isCallable(C) ? Object(new C(len)) : new Array(len) + + // 16. Let k be 0. + let k = 0 + // 17. Repeat, while k < len… (also steps a - h) + let kValue + while (k < len) { + kValue = items[k] + if (mapFn) { + A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k) + } else { + A[k] = kValue + } + k += 1 + } + // 18. Let putStatus be Put(A, "length", len, true). + A.length = len + // 20. Return A. + return A + } + }()) +} + +if (typeof window !== 'undefined') { + (function () { + // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating + + // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel + + // MIT license + + let lastTime = 0 + let vendors = [ 'ms', 'moz', 'webkit', 'o' ] + + for (let x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = ( + (window as any)[ vendors[ x ] + 'RequestAnimationFrame' ] + ) + + window.cancelAnimationFrame = ( + (window as any)[ vendors[ x ] + 'CancelAnimationFrame' ] || + (window as any)[ vendors[ x ] + 'CancelRequestAnimationFrame' ] + ) + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function (callback/* , element */) { + let currTime = new Date().getTime() + let timeToCall = Math.max(0, 16 - (currTime - lastTime)) + + let id = window.setTimeout(function () { + let time = currTime + timeToCall + callback(time) + }, timeToCall) + + lastTime = currTime + timeToCall + + return id + } + } + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function (id) { + clearTimeout(id) + } + } + }()) +} + +if (Function.prototype.name === undefined && Object.defineProperty !== undefined) { + // Missing in IE9-11. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name + + // eslint-disable-next-line no-extend-native + Object.defineProperty(Function.prototype, 'name', { + + get: function () { + return this.toString().match(/^\s*function\s*(\S*)\s*\(/)[ 1 ] + } + + }) +} + +if (typeof window !== 'undefined') { + if (window.performance === undefined) { + /* global self */ + (window as any).performance = {} + } + + if (window.performance.now === undefined) { + (function () { + let start = Date.now() + + window.performance.now = function () { + return Date.now() - start + } + })() + } +} + +if (Object.defineProperty !== undefined) { + // Missing in IE < 13 + // MIT license + // Copyright (c) 2016 Financial Times + // https://github.com/Financial-Times/polyfill-service + if (Number.MAX_SAFE_INTEGER === undefined) { + Object.defineProperty(Number, 'MAX_SAFE_INTEGER', { + enumerable: false, + configurable: false, + writable: false, + value: Math.pow(2, 53) - 1 + }) + } + if (Number.MIN_SAFE_INTEGER === undefined) { + Object.defineProperty(Number, 'MIN_SAFE_INTEGER', { + enumerable: false, + configurable: false, + writable: false, + value: -(Math.pow(2, 53) - 1) + }) + } +} + +if (!Object.entries) { + Object.entries = function(obj: any){ + let ownProps = Object.keys( obj ), + i = ownProps.length, + resArray = new Array(i); // preallocate the Array + while (i--) + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + + return resArray; + }; +} + +// from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find +// https://tc39.github.io/ecma262/#sec-array.prototype.find +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, 'find', { + value: function(predicate: any) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw TypeError('"this" is null or not defined'); + } + + let o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + let len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + let thisArg = arguments[1]; + + // 5. Let k be 0. + let k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). + // d. If testResult is true, return kValue. + let kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } + // e. Increase k by 1. + k++; + } + + // 7. Return undefined. + return undefined; + }, + configurable: true, + writable: true + }); +} + +// from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill +if (!Array.prototype.fill) { + Object.defineProperty(Array.prototype, 'fill', { + value: function(value: any) { + + // Steps 1-2. + if (this == null) { + throw new TypeError('this is null or not defined'); + } + + let O = Object(this); + + // Steps 3-5. + let len = O.length >>> 0; + + // Steps 6-7. + let start = arguments[1]; + let relativeStart = start >> 0; + + // Step 8. + let k = relativeStart < 0 ? + Math.max(len + relativeStart, 0) : + Math.min(relativeStart, len); + + // Steps 9-10. + let end = arguments[2]; + let relativeEnd = end === undefined ? + len : end >> 0; + + // Step 11. + let finalValue = relativeEnd < 0 ? + Math.max(len + relativeEnd, 0) : + Math.min(relativeEnd, len); + + // Step 12. + while (k < finalValue) { + O[k] = value; + k++; + } + + // Step 13. + return O; + } + }); +} \ No newline at end of file