From 2dccc2a0dff7abdf23a4317dff991a029ac9cca1 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Thu, 12 Dec 2024 21:01:32 -0500 Subject: [PATCH] chore: localize fetch-jsonp --- .../package-lock.json | 59 ++++++------ webpack-bs5-demo-with-locales/package.json | 1 - .../src/examples/jsonp.ts | 92 +++++++++++++++++++ .../src/examples/slickgrid/example3.ts | 8 +- webpack-bs5-demo/package-lock.json | 60 ++++++------ webpack-bs5-demo/package.json | 1 - webpack-bs5-demo/src/examples/jsonp.ts | 92 +++++++++++++++++++ .../src/examples/slickgrid/example3.ts | 10 +- 8 files changed, 248 insertions(+), 75 deletions(-) create mode 100644 webpack-bs5-demo-with-locales/src/examples/jsonp.ts create mode 100644 webpack-bs5-demo/src/examples/jsonp.ts diff --git a/webpack-bs5-demo-with-locales/package-lock.json b/webpack-bs5-demo-with-locales/package-lock.json index 5a0ade67..5c9081e3 100644 --- a/webpack-bs5-demo-with-locales/package-lock.json +++ b/webpack-bs5-demo-with-locales/package-lock.json @@ -24,7 +24,6 @@ "aurelia-slickgrid": "^8.10.2", "bootstrap": "^5.3.3", "dompurify": "^3.2.2", - "fetch-jsonp": "^1.3.0", "rxjs": "^7.8.1", "tslib": "^2.8.1" }, @@ -4053,9 +4052,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "license": "MIT", "dependencies": { @@ -4078,7 +4077,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -4093,6 +4092,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/debug": { @@ -4192,11 +4195,6 @@ "node": ">=0.8.0" } }, - "node_modules/fetch-jsonp": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fetch-jsonp/-/fetch-jsonp-1.3.0.tgz", - "integrity": "sha512-hxCYGvmANEmpkHpeWY8Kawfa5Z1t2csTpIClIDG/0S92eALWHRU1RnGaj86Tf5Cc0QF+afSa4SQ4pFB2rFM5QA==" - }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -6090,9 +6088,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -6100,6 +6098,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6718,10 +6717,11 @@ "dev": true }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", - "dev": true + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true, + "license": "MIT" }, "node_modules/path-type": { "version": "5.0.0", @@ -12170,9 +12170,9 @@ } }, "express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "requires": { "accepts": "~1.3.8", @@ -12194,7 +12194,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -12297,11 +12297,6 @@ "websocket-driver": ">=0.5.1" } }, - "fetch-jsonp": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fetch-jsonp/-/fetch-jsonp-1.3.0.tgz", - "integrity": "sha512-hxCYGvmANEmpkHpeWY8Kawfa5Z1t2csTpIClIDG/0S92eALWHRU1RnGaj86Tf5Cc0QF+afSa4SQ4pFB2rFM5QA==" - }, "fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -13623,9 +13618,9 @@ } }, "nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true }, "natural-compare": { @@ -14095,9 +14090,9 @@ "dev": true }, "path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true }, "path-type": { diff --git a/webpack-bs5-demo-with-locales/package.json b/webpack-bs5-demo-with-locales/package.json index 0106c909..e7357223 100644 --- a/webpack-bs5-demo-with-locales/package.json +++ b/webpack-bs5-demo-with-locales/package.json @@ -37,7 +37,6 @@ "aurelia-slickgrid": "^8.10.2", "bootstrap": "^5.3.3", "dompurify": "^3.2.2", - "fetch-jsonp": "^1.3.0", "rxjs": "^7.8.1", "tslib": "^2.8.1" }, diff --git a/webpack-bs5-demo-with-locales/src/examples/jsonp.ts b/webpack-bs5-demo-with-locales/src/examples/jsonp.ts new file mode 100644 index 00000000..74a0f523 --- /dev/null +++ b/webpack-bs5-demo-with-locales/src/examples/jsonp.ts @@ -0,0 +1,92 @@ +/* + * copied and rewritten as ESM (just a simple rewrite as ESM to avoid loading a CJS package) + * https://github.com/camsong/fetch-jsonp/blob/master/src/fetch-jsonp.js + */ + +interface JsonpOptions { + timeout: number; + jsonpCallback: string; + jsonpCallbackFunction: string; + charset: string; + nonce: string; + referrerPolicy: string; + crossorigin: boolean; +} + +const defaultOptions = { + timeout: 5000, + jsonpCallback: 'callback', + jsonpCallbackFunction: null, +}; +const generateCallbackFunction = () => `jsonp_${Date.now()}_${Math.ceil(Math.random() * 100000)}`; +const clearFunction = (functionName: string) => delete (window as any)[functionName]; +const removeScript = (scriptId: string) => { + const script = document.getElementById(scriptId); + if (script) { + document.getElementsByTagName('head')[0].removeChild(script); + } +}; + +function fetchJsonp( + _url: string, + options: Partial = {} +): Promise<{ ok: boolean; json: () => Promise }> { + // to avoid param reassign + let url = _url; + const timeout = options.timeout || defaultOptions.timeout; + const jsonpCallback = options.jsonpCallback || defaultOptions.jsonpCallback; + let timeoutId: any; + + return new Promise((resolve, reject) => { + const callbackFunction = options.jsonpCallbackFunction || generateCallbackFunction(); + const scriptId = `${jsonpCallback}_${callbackFunction}`; + + (window as any)[callbackFunction] = (response: T) => { + // keep consistent with fetch API + resolve({ ok: true, json: () => Promise.resolve(response) }); + if (timeoutId) { clearTimeout(timeoutId); } + removeScript(scriptId); + clearFunction(callbackFunction); + }; + + // Check if the user set their own params, and if not add a ? to start a list of params + url += url.indexOf('?') === -1 ? '?' : '&'; + + const jsonpScript = document.createElement('script'); + jsonpScript.setAttribute('src', `${url}${jsonpCallback}=${callbackFunction}`); + if (options.charset) { + jsonpScript.setAttribute('charset', options.charset); + } + if (options.nonce) { + jsonpScript.setAttribute('nonce', options.nonce); + } + if (options.referrerPolicy) { + jsonpScript.setAttribute('referrerPolicy', options.referrerPolicy); + } + if (options.crossorigin) { + jsonpScript.setAttribute('crossorigin', 'true'); + } + jsonpScript.id = scriptId; + document.getElementsByTagName('head')[0].appendChild(jsonpScript); + + timeoutId = setTimeout(() => { + reject(new Error(`JSONP request to ${_url} timed out`)); + + clearFunction(callbackFunction); + removeScript(scriptId); + (window as any)[callbackFunction] = () => { + clearFunction(callbackFunction); + }; + }, timeout); + + // Caught if got 404/500 + jsonpScript.onerror = () => { + reject(new Error(`JSONP request to ${_url} failed`)); + clearFunction(callbackFunction); + removeScript(scriptId); + if (timeoutId) { clearTimeout(timeoutId); } + }; + }); +} + +export default fetchJsonp; diff --git a/webpack-bs5-demo-with-locales/src/examples/slickgrid/example3.ts b/webpack-bs5-demo-with-locales/src/examples/slickgrid/example3.ts index 0eaca688..df296fbc 100644 --- a/webpack-bs5-demo-with-locales/src/examples/slickgrid/example3.ts +++ b/webpack-bs5-demo-with-locales/src/examples/slickgrid/example3.ts @@ -1,7 +1,5 @@ import { IHttpClient } from '@aurelia/fetch-client'; import { newInstanceOf, resolve } from '@aurelia/kernel'; -import fetchJsonp from 'fetch-jsonp'; - import { type AureliaGridInstance, type AutocompleterOption, @@ -19,8 +17,10 @@ import { SortComparers, type VanillaCalendarOption, } from 'aurelia-slickgrid'; + import { CustomInputEditor } from './custom-inputEditor'; import { CustomInputFilter } from './custom-inputFilter'; +import fetchJsonp from '../jsonp'; const NB_ITEMS = 100; const URL_SAMPLE_COLLECTION_DATA = 'assets/data/collection_100_numbers.json'; @@ -278,7 +278,7 @@ export class Example3 { // this.http.get(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`).subscribe(data => updateCallback(data)); /** with JSONP AJAX will work locally but not on the GitHub demo because of CORS */ - fetchJsonp(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`) + fetchJsonp(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`, { crossorigin: true }) .then((response: { json: () => Promise }) => response.json()) .then((json: any[]) => updateCallback(json)) .catch((ex) => console.log('invalid JSONP response', ex)); @@ -297,7 +297,7 @@ export class Example3 { filterOptions: { minLength: 3, fetch: (searchText: string, updateCallback: (items: false | any[]) => void) => { - fetchJsonp(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`) + fetchJsonp(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`, { crossorigin: true }) .then((response: { json: () => Promise }) => response.json()) .then((json: any[]) => updateCallback(json)) .catch((ex: any) => console.log('invalid JSONP response', ex)); diff --git a/webpack-bs5-demo/package-lock.json b/webpack-bs5-demo/package-lock.json index a9cd4fd5..6fe3a8ec 100644 --- a/webpack-bs5-demo/package-lock.json +++ b/webpack-bs5-demo/package-lock.json @@ -25,7 +25,6 @@ "aurelia-slickgrid": "^8.10.2", "bootstrap": "^5.3.3", "dompurify": "^3.2.2", - "fetch-jsonp": "^1.3.0", "i18next-fetch-backend": "^6.0.0", "rxjs": "^7.8.1", "tslib": "^2.8.1" @@ -4065,10 +4064,11 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -4089,7 +4089,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -4104,6 +4104,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/debug": { @@ -4203,11 +4207,6 @@ "node": ">=0.8.0" } }, - "node_modules/fetch-jsonp": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fetch-jsonp/-/fetch-jsonp-1.3.0.tgz", - "integrity": "sha512-hxCYGvmANEmpkHpeWY8Kawfa5Z1t2csTpIClIDG/0S92eALWHRU1RnGaj86Tf5Cc0QF+afSa4SQ4pFB2rFM5QA==" - }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -6119,9 +6118,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -6129,6 +6128,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6747,10 +6747,11 @@ "dev": true }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", - "dev": true + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true, + "license": "MIT" }, "node_modules/path-type": { "version": "5.0.0", @@ -12208,9 +12209,9 @@ } }, "express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "requires": { "accepts": "~1.3.8", @@ -12232,7 +12233,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -12335,11 +12336,6 @@ "websocket-driver": ">=0.5.1" } }, - "fetch-jsonp": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fetch-jsonp/-/fetch-jsonp-1.3.0.tgz", - "integrity": "sha512-hxCYGvmANEmpkHpeWY8Kawfa5Z1t2csTpIClIDG/0S92eALWHRU1RnGaj86Tf5Cc0QF+afSa4SQ4pFB2rFM5QA==" - }, "fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -13675,9 +13671,9 @@ } }, "nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true }, "natural-compare": { @@ -14147,9 +14143,9 @@ "dev": true }, "path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true }, "path-type": { diff --git a/webpack-bs5-demo/package.json b/webpack-bs5-demo/package.json index d2c2bd22..81d144a7 100644 --- a/webpack-bs5-demo/package.json +++ b/webpack-bs5-demo/package.json @@ -41,7 +41,6 @@ "aurelia-slickgrid": "^8.10.2", "bootstrap": "^5.3.3", "dompurify": "^3.2.2", - "fetch-jsonp": "^1.3.0", "i18next-fetch-backend": "^6.0.0", "rxjs": "^7.8.1", "tslib": "^2.8.1" diff --git a/webpack-bs5-demo/src/examples/jsonp.ts b/webpack-bs5-demo/src/examples/jsonp.ts new file mode 100644 index 00000000..74a0f523 --- /dev/null +++ b/webpack-bs5-demo/src/examples/jsonp.ts @@ -0,0 +1,92 @@ +/* + * copied and rewritten as ESM (just a simple rewrite as ESM to avoid loading a CJS package) + * https://github.com/camsong/fetch-jsonp/blob/master/src/fetch-jsonp.js + */ + +interface JsonpOptions { + timeout: number; + jsonpCallback: string; + jsonpCallbackFunction: string; + charset: string; + nonce: string; + referrerPolicy: string; + crossorigin: boolean; +} + +const defaultOptions = { + timeout: 5000, + jsonpCallback: 'callback', + jsonpCallbackFunction: null, +}; +const generateCallbackFunction = () => `jsonp_${Date.now()}_${Math.ceil(Math.random() * 100000)}`; +const clearFunction = (functionName: string) => delete (window as any)[functionName]; +const removeScript = (scriptId: string) => { + const script = document.getElementById(scriptId); + if (script) { + document.getElementsByTagName('head')[0].removeChild(script); + } +}; + +function fetchJsonp( + _url: string, + options: Partial = {} +): Promise<{ ok: boolean; json: () => Promise }> { + // to avoid param reassign + let url = _url; + const timeout = options.timeout || defaultOptions.timeout; + const jsonpCallback = options.jsonpCallback || defaultOptions.jsonpCallback; + let timeoutId: any; + + return new Promise((resolve, reject) => { + const callbackFunction = options.jsonpCallbackFunction || generateCallbackFunction(); + const scriptId = `${jsonpCallback}_${callbackFunction}`; + + (window as any)[callbackFunction] = (response: T) => { + // keep consistent with fetch API + resolve({ ok: true, json: () => Promise.resolve(response) }); + if (timeoutId) { clearTimeout(timeoutId); } + removeScript(scriptId); + clearFunction(callbackFunction); + }; + + // Check if the user set their own params, and if not add a ? to start a list of params + url += url.indexOf('?') === -1 ? '?' : '&'; + + const jsonpScript = document.createElement('script'); + jsonpScript.setAttribute('src', `${url}${jsonpCallback}=${callbackFunction}`); + if (options.charset) { + jsonpScript.setAttribute('charset', options.charset); + } + if (options.nonce) { + jsonpScript.setAttribute('nonce', options.nonce); + } + if (options.referrerPolicy) { + jsonpScript.setAttribute('referrerPolicy', options.referrerPolicy); + } + if (options.crossorigin) { + jsonpScript.setAttribute('crossorigin', 'true'); + } + jsonpScript.id = scriptId; + document.getElementsByTagName('head')[0].appendChild(jsonpScript); + + timeoutId = setTimeout(() => { + reject(new Error(`JSONP request to ${_url} timed out`)); + + clearFunction(callbackFunction); + removeScript(scriptId); + (window as any)[callbackFunction] = () => { + clearFunction(callbackFunction); + }; + }, timeout); + + // Caught if got 404/500 + jsonpScript.onerror = () => { + reject(new Error(`JSONP request to ${_url} failed`)); + clearFunction(callbackFunction); + removeScript(scriptId); + if (timeoutId) { clearTimeout(timeoutId); } + }; + }); +} + +export default fetchJsonp; diff --git a/webpack-bs5-demo/src/examples/slickgrid/example3.ts b/webpack-bs5-demo/src/examples/slickgrid/example3.ts index ea42335b..2b2d215b 100644 --- a/webpack-bs5-demo/src/examples/slickgrid/example3.ts +++ b/webpack-bs5-demo/src/examples/slickgrid/example3.ts @@ -1,8 +1,6 @@ import { IHttpClient } from '@aurelia/fetch-client'; import { newInstanceOf, resolve } from '@aurelia/kernel'; import { I18N } from '@aurelia/i18n'; -import fetchJsonp from 'fetch-jsonp'; - import { type AureliaGridInstance, type AutocompleterOption, @@ -20,8 +18,10 @@ import { SortComparers, type VanillaCalendarOption, } from 'aurelia-slickgrid'; + import { CustomInputEditor } from './custom-inputEditor'; import { CustomInputFilter } from './custom-inputFilter'; +import fetchJsonp from '../jsonp'; const NB_ITEMS = 100; const URL_SAMPLE_COLLECTION_DATA = 'assets/data/collection_100_numbers.json'; @@ -278,8 +278,8 @@ export class Example3 { /** with Aurelia Http, note this demo won't work because of CORS */ // this.http.get(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`).subscribe(data => updateCallback(data)); - /** with JSONP AJAX will work locally but not on the GitHub demo because of CORS */ - fetchJsonp(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`) + /** with JSONP will work locally but not on the GitHub demo because of CORS */ + fetchJsonp(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`, { crossorigin: true }) .then((response: { json: () => Promise }) => response.json()) .then((json: any[]) => updateCallback(json)) .catch((ex) => console.log('invalid JSONP response', ex)); @@ -298,7 +298,7 @@ export class Example3 { filterOptions: { minLength: 3, fetch: (searchText: string, updateCallback: (items: false | any[]) => void) => { - fetchJsonp(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`) + fetchJsonp(`http://gd.geobytes.com/AutoCompleteCity?q=${searchText}`, { crossorigin: true }) .then((response: { json: () => Promise }) => response.json()) .then((json: any[]) => updateCallback(json)) .catch((ex: any) => console.log('invalid JSONP response', ex));