| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- /**
- * polyfills.js — IE11 及老浏览器兼容垫片(ES5,离线内置,无外部依赖)
- * 提供: Promise / fetch / EventSource / padStart / startsWith / endsWith / includes
- * 注: 仅在浏览器缺少对应能力时才生效,现代浏览器加载本文件无任何副作用。
- */
- /* ---------------- String / Array 补齐 ---------------- */
- if (!String.prototype.padStart) {
- String.prototype.padStart = function (targetLength, padString) {
- targetLength = targetLength >> 0;
- if (this.length >= targetLength) return String(this);
- padString = padString === undefined ? " " : String(padString);
- var fill = "";
- while (fill.length < targetLength - this.length) fill += padString;
- return fill.slice(0, targetLength - this.length) + String(this);
- };
- }
- if (!String.prototype.startsWith) {
- String.prototype.startsWith = function (search, pos) {
- pos = pos || 0;
- return this.substr(pos, search.length) === String(search);
- };
- }
- if (!String.prototype.endsWith) {
- String.prototype.endsWith = function (search, endPos) {
- if (endPos === undefined || endPos > this.length) endPos = this.length;
- return this.substring(endPos - search.length, endPos) === String(search);
- };
- }
- if (!String.prototype.includes) {
- String.prototype.includes = function (search, start) {
- if (start === undefined) start = 0;
- return this.indexOf(String(search), start) !== -1;
- };
- }
- if (!Array.prototype.includes) {
- Array.prototype.includes = function (search) {
- for (var i = 0; i < this.length; i++) {
- if (this[i] === search) return true;
- }
- return false;
- };
- }
- if (!Object.assign) {
- Object.assign = function (target) {
- if (target === null || target === undefined) {
- throw new TypeError("Cannot convert undefined or null to object");
- }
- var to = Object(target);
- for (var i = 1; i < arguments.length; i++) {
- var src = arguments[i];
- if (src !== null && src !== undefined) {
- for (var k in src) {
- if (Object.prototype.hasOwnProperty.call(src, k)) to[k] = src[k];
- }
- }
- }
- return to;
- };
- }
- /* ---------------- Promise 补齐 (含 finally) ---------------- */
- if (typeof Promise !== "function") {
- function PromisePolyfill(executor) {
- var self = this;
- this._state = "pending";
- this._value = undefined;
- this._callbacks = [];
- function resolve(value) {
- settle(self, "fulfilled", value);
- }
- function reject(reason) {
- settle(self, "rejected", reason);
- }
- function settle(p, state, value) {
- if (p._state !== "pending") return;
- p._state = state;
- p._value = value;
- setTimeout(function () {
- flush(p);
- }, 0);
- }
- function flush(p) {
- var cbs = p._callbacks;
- p._callbacks = [];
- for (var i = 0; i < cbs.length; i++) handle(p, cbs[i]);
- }
- try {
- executor(resolve, reject);
- } catch (e) {
- reject(e);
- }
- }
- function handle(p, cb) {
- if (p._state === "pending") {
- p._callbacks.push(cb);
- return;
- }
- var state = p._state,
- value = p._value;
- var thenFn = state === "fulfilled" ? cb.onFulfilled : cb.onRejected;
- if (typeof thenFn !== "function") {
- (state === "fulfilled" ? cb.resolve : cb.reject)(value);
- return;
- }
- var called = false;
- try {
- var result = thenFn(value);
- if (
- result &&
- (typeof result === "object" || typeof result === "function") &&
- typeof result.then === "function"
- ) {
- result.then(cb.resolve, cb.reject);
- } else {
- cb.resolve(result);
- }
- } catch (e) {
- if (!called) cb.reject(e);
- called = true;
- }
- }
- function resolvePromise(p, x, resolve, reject) {
- var called = false;
- try {
- var then = x && x.then;
- if (
- x !== null &&
- (typeof x === "object" || typeof x === "function") &&
- typeof then === "function"
- ) {
- then.call(
- x,
- function (y) {
- if (called) return;
- called = true;
- resolvePromise(p, y, resolve, reject);
- },
- function (r) {
- if (called) return;
- called = true;
- reject(r);
- }
- );
- } else {
- resolve(x);
- }
- } catch (e) {
- if (!called) reject(e);
- }
- }
- PromisePolyfill.prototype.then = function (onFulfilled, onRejected) {
- var self = this;
- return new PromisePolyfill(function (resolve, reject) {
- handle(self, {
- onFulfilled: onFulfilled,
- onRejected: onRejected,
- resolve: resolve,
- reject: reject,
- });
- });
- };
- PromisePolyfill.prototype.catch = function (onRejected) {
- return this.then(null, onRejected);
- };
- PromisePolyfill.prototype.finally = function (fn) {
- var C = this.constructor;
- function wrap() {
- return C.resolve(fn && fn()).then(function () {
- return;
- });
- }
- return this.then(wrap, wrap);
- };
- PromisePolyfill.resolve = function (value) {
- if (value instanceof PromisePolyfill) return value;
- return new PromisePolyfill(function (resolve) {
- resolve(value);
- });
- };
- PromisePolyfill.reject = function (reason) {
- return new PromisePolyfill(function (resolve, reject) {
- reject(reason);
- });
- };
- PromisePolyfill.all = function (list) {
- return new PromisePolyfill(function (resolve, reject) {
- var results = [],
- remaining = list.length;
- if (!remaining) return resolve(results);
- list.forEach(function (item, i) {
- PromisePolyfill.resolve(item).then(function (v) {
- results[i] = v;
- if (--remaining === 0) resolve(results);
- }, reject);
- });
- });
- };
- window.Promise = PromisePolyfill;
- } else if (typeof Promise.prototype.finally !== "function") {
- Promise.prototype.finally = function (fn) {
- var C = this.constructor;
- function wrap() {
- return C.resolve(fn && fn()).then(function () {});
- }
- return this.then(wrap, wrap);
- };
- }
- /* ---------------- fetch 补齐 (XHR 实现) ---------------- */
- if (typeof window.fetch !== "function") {
- function FetchResponse(xhr) {
- this.ok = xhr.status >= 200 && xhr.status < 300;
- this.status = xhr.status;
- this.statusText = xhr.statusText;
- this.url = xhr.responseURL || "";
- this._raw = xhr;
- var raw = xhr;
- this.headers = {
- get: function (name) {
- var v = raw.getResponseHeader(name);
- return v === null ? null : String(v);
- },
- };
- }
- FetchResponse.prototype.text = function () {
- return Promise.resolve(this._raw.responseText);
- };
- FetchResponse.prototype.json = function () {
- var raw = this._raw;
- return Promise.resolve().then(function () {
- return JSON.parse(raw.responseText);
- });
- };
- FetchResponse.prototype.blob = function () {
- var raw = this._raw;
- return Promise.resolve().then(function () {
- var type = raw.getResponseHeader("Content-Type") || "application/octet-stream";
- return new Blob([raw.responseText], { type: String(type) });
- });
- };
- window.fetch = function (input, init) {
- init = init || {};
- var url = typeof input === "string" ? input : String(input.url || input);
- return new Promise(function (resolve, reject) {
- var xhr = new XMLHttpRequest();
- xhr.open(init.method || "GET", url, true);
- if (init.headers) {
- var h = init.headers;
- if (typeof h.forEach === "function") {
- h.forEach(function (v, k) {
- xhr.setRequestHeader(k, v);
- });
- } else {
- for (var k in h) {
- if (Object.prototype.hasOwnProperty.call(h, k)) {
- xhr.setRequestHeader(k, h[k]);
- }
- }
- }
- }
- xhr.timeout = init.timeout || 0;
- xhr.onload = function () {
- resolve(new FetchResponse(xhr));
- };
- xhr.onerror = function () {
- reject(new TypeError("Network request failed"));
- };
- xhr.ontimeout = function () {
- reject(new TypeError("Request timed out"));
- };
- xhr.send(init.body === undefined ? null : init.body);
- });
- };
- }
- /* ---------------- EventSource (SSE) 补齐 (XHR 流式实现) ---------------- */
- if (typeof window.EventSource !== "function") {
- var ES_CONNECTING = 0,
- ES_OPEN = 1,
- ES_CLOSED = 2;
- function EventSourcePolyfill(url) {
- var self = this;
- this.url = url;
- this.readyState = ES_CONNECTING;
- this.onopen = null;
- this.onmessage = null;
- this.onerror = null;
- this._xhr = null;
- this._processed = 0;
- this._buf = "";
- this._closed = false;
- this._connect();
- }
- EventSourcePolyfill.prototype._fire = function (type, data) {
- var handler = this["on" + type];
- if (typeof handler !== "function") return;
- var evt;
- try {
- evt = new MessageEvent(type, { data: data });
- } catch (e) {
- evt = document.createEvent("MessageEvent");
- evt.initMessageEvent(type, false, false, data, "", "", window, []);
- }
- handler.call(this, evt);
- };
- EventSourcePolyfill.prototype._connect = function () {
- var self = this;
- var xhr = new XMLHttpRequest();
- this._xhr = xhr;
- this._processed = 0;
- this._buf = "";
- xhr.open("GET", this.url, true);
- xhr.setRequestHeader("Accept", "text/event-stream");
- xhr.setRequestHeader("Cache-Control", "no-cache");
- if (xhr.overrideMimeType) xhr.overrideMimeType("text/plain; charset=x-user-defined");
- var opened = false;
- function parseChunk(text) {
- // 以空行分隔事件块
- var blocks = self._buf.split("\n\n");
- self._buf = blocks.pop();
- blocks.forEach(function (block) {
- var dataLines = [];
- block.split("\n").forEach(function (line) {
- if (line.indexOf("data:") === 0) {
- dataLines.push(line.charAt(5) === " " ? line.slice(6) : line.slice(5));
- }
- // event:/id:/retry: 当前业务未使用, 忽略
- });
- if (dataLines.length) {
- self._fire("message", dataLines.join("\n"));
- }
- });
- }
- xhr.onprogress = function () {
- if (!opened) {
- opened = true;
- self.readyState = ES_OPEN;
- self._fire("open", undefined);
- }
- var full = xhr.responseText;
- if (full.length > self._processed) {
- self._buf += full.slice(self._processed);
- self._processed = full.length;
- parseChunk();
- }
- };
- xhr.onload = xhr.onprogress;
- xhr.onerror = function () {
- if (self._closed) return;
- self.readyState = ES_CONNECTING;
- self._fire("error", undefined);
- // 简单重连策略: 关闭后由上层重新创建; 此处自动重连
- if (self._retryTimer) clearTimeout(self._retryTimer);
- self._retryTimer = setTimeout(function () {
- if (!self._closed && self.readyState !== ES_CLOSED) self._connect();
- }, 3000);
- };
- try {
- xhr.send(null);
- } catch (e) {
- setTimeout(function () {
- self._fire("error", undefined);
- }, 0);
- }
- };
- EventSourcePolyfill.prototype.close = function () {
- this._closed = true;
- this.readyState = ES_CLOSED;
- if (this._xhr) {
- try {
- this._xhr.abort();
- } catch (e) {}
- }
- if (this._retryTimer) {
- clearTimeout(this._retryTimer);
- this._retryTimer = null;
- }
- };
- window.EventSource = EventSourcePolyfill;
- }
- /* ---------------- 文件下载兜底 (IE11 不支持 a[download]) ---------------- */
- /**
- * 统一文件下载入口: 现代浏览器走 a[download], IE10/11 走 msSaveBlob
- * @param {Blob} blob
- * @param {string} filename
- */
- function downloadBlobFile(blob, filename) {
- if (window.navigator && window.navigator.msSaveOrOpenBlob) {
- window.navigator.msSaveOrOpenBlob(blob, filename);
- return;
- }
- var url = URL.createObjectURL(blob);
- var a = document.createElement("a");
- a.href = url;
- a.download = filename || "download";
- document.body.appendChild(a);
- a.click();
- setTimeout(function () {
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- }, 100);
- }
|