Skip to content

Commit 4a9738c

Browse files
committed
v1
1 parent dab2b7b commit 4a9738c

14 files changed

Lines changed: 2418 additions & 10 deletions

assets/js/phoenix/ajax.ts

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import {
2+
global,
3+
XHR_STATES
4+
} from "./constants"
5+
6+
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
7+
type Headers = Record<string, string>
8+
type RequestBody = string | null
9+
type AjaxCallback = (response: any) => void
10+
type TimeoutCallback = () => void
11+
12+
interface XDomainRequest {
13+
timeout: number
14+
open(method: string, url: string): void
15+
send(body?: string | null): void
16+
onload: (() => void) | null
17+
ontimeout: (() => void) | null
18+
onprogress: (() => void) | null
19+
responseText: string
20+
}
21+
22+
interface XMLHttpRequestLike {
23+
open(method: string, url: string, async?: boolean): void
24+
send(body?: string | null): void
25+
setRequestHeader(name: string, value: string): void
26+
timeout: number
27+
readyState: number
28+
responseText: string
29+
onreadystatechange: (() => void) | null
30+
onerror: (() => void) | null
31+
ontimeout: (() => void) | null
32+
}
33+
34+
export default class Ajax {
35+
static request(
36+
method: HttpMethod,
37+
endPoint: string,
38+
headers: Headers,
39+
body: RequestBody,
40+
timeout: number,
41+
ontimeout: TimeoutCallback | null,
42+
callback: AjaxCallback | null
43+
): XMLHttpRequestLike | XDomainRequest | AbortController {
44+
if (global.XDomainRequest) {
45+
let req = new global.XDomainRequest() // IE8, IE9
46+
return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)
47+
} else if (global.XMLHttpRequest) {
48+
let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari
49+
return this.xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback)
50+
} else if (global.fetch && global.AbortController) {
51+
// Fetch with AbortController for modern browsers
52+
return this.fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback)
53+
} else {
54+
throw new Error("No suitable XMLHttpRequest implementation found")
55+
}
56+
}
57+
58+
static fetchRequest(
59+
method: HttpMethod,
60+
endPoint: string,
61+
headers: Headers,
62+
body: RequestBody,
63+
timeout: number,
64+
ontimeout: TimeoutCallback | null,
65+
callback: AjaxCallback | null
66+
): AbortController {
67+
let options: RequestInit = {
68+
method,
69+
headers,
70+
body,
71+
}
72+
let controller = new AbortController()
73+
if (timeout) {
74+
const _timeoutId = setTimeout(() => controller.abort(), timeout)
75+
options.signal = controller.signal
76+
}
77+
global.fetch(endPoint, options)
78+
.then(response => response.text())
79+
.then(data => this.parseJSON(data))
80+
.then(data => callback && callback(data))
81+
.catch(err => {
82+
if (err.name === "AbortError" && ontimeout) {
83+
ontimeout()
84+
} else {
85+
callback && callback(null)
86+
}
87+
})
88+
return controller
89+
}
90+
91+
static xdomainRequest(
92+
req: XDomainRequest,
93+
method: HttpMethod,
94+
endPoint: string,
95+
body: RequestBody,
96+
timeout: number,
97+
ontimeout: TimeoutCallback | null,
98+
callback: AjaxCallback | null
99+
): XDomainRequest {
100+
req.timeout = timeout
101+
req.open(method, endPoint)
102+
req.onload = () => {
103+
let response = this.parseJSON(req.responseText)
104+
callback && callback(response)
105+
}
106+
if (ontimeout) { req.ontimeout = ontimeout }
107+
108+
// Work around bug in IE9 that requires an attached onprogress handler
109+
req.onprogress = () => { }
110+
111+
req.send(body)
112+
return req
113+
}
114+
115+
static xhrRequest(
116+
req: XMLHttpRequestLike,
117+
method: HttpMethod,
118+
endPoint: string,
119+
headers: Headers,
120+
body: RequestBody,
121+
timeout: number,
122+
ontimeout: TimeoutCallback | null,
123+
callback: AjaxCallback | null
124+
): XMLHttpRequestLike {
125+
req.open(method, endPoint, true)
126+
req.timeout = timeout
127+
for (let [key, value] of Object.entries(headers)) {
128+
req.setRequestHeader(key, value)
129+
}
130+
req.onerror = () => callback && callback(null)
131+
req.onreadystatechange = () => {
132+
if (req.readyState === XHR_STATES.complete && callback) {
133+
let response = this.parseJSON(req.responseText)
134+
callback(response)
135+
}
136+
}
137+
if (ontimeout) { req.ontimeout = ontimeout }
138+
139+
req.send(body)
140+
return req
141+
}
142+
143+
static parseJSON(resp: string | null | undefined): any {
144+
if (!resp || resp === "") { return null }
145+
146+
try {
147+
return JSON.parse(resp)
148+
} catch {
149+
console && console.log("failed to parse JSON response", resp)
150+
return null
151+
}
152+
}
153+
154+
static serialize(obj: Record<string, any>, parentKey?: string): string {
155+
let queryStr: string[] = []
156+
for (var key in obj) {
157+
if (!Object.prototype.hasOwnProperty.call(obj, key)) { continue }
158+
let paramKey = parentKey ? `${parentKey}[${key}]` : key
159+
let paramVal = obj[key]
160+
if (typeof paramVal === "object") {
161+
queryStr.push(this.serialize(paramVal, paramKey))
162+
} else {
163+
queryStr.push(encodeURIComponent(paramKey) + "=" + encodeURIComponent(paramVal))
164+
}
165+
}
166+
return queryStr.join("&")
167+
}
168+
169+
static appendParams(url: string, params: Record<string, any>): string {
170+
if (Object.keys(params).length === 0) { return url }
171+
172+
let prefix = url.match(/\?/) ? "&" : "?"
173+
return `${url}${prefix}${this.serialize(params)}`
174+
}
175+
}

0 commit comments

Comments
 (0)