mirror of
https://github.com/Estom/notes.git
synced 2026-02-12 06:46:22 +08:00
重新整理JavaScript笔记
This commit is contained in:
22
JavaScript/Ajax/1 ajax.md
Normal file
22
JavaScript/Ajax/1 ajax.md
Normal file
@@ -0,0 +1,22 @@
|
||||
## 1 基础
|
||||
|
||||
### 简介
|
||||
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
|
||||
|
||||
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
|
||||
|
||||
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
|
||||
|
||||
AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
|
||||
|
||||
### 原理
|
||||
异步的JavaScript和XML。创建快速动态网页技术。在后台与服务器进行少量的数据交换,实现网页的异步更新。
|
||||
|
||||
* 使用JS创建XMLHTTPRequest对象,发送HTTPRequest内容。
|
||||
* 使用JS处理被返回的数据,更新页面内容。
|
||||
|
||||
|
||||
|
||||
### ajax的多种实现方式
|
||||
|
||||

|
||||
64
JavaScript/Ajax/2 xhr.md
Normal file
64
JavaScript/Ajax/2 xhr.md
Normal file
@@ -0,0 +1,64 @@
|
||||
## 1 实例
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script>
|
||||
function loadXMLDoc()
|
||||
{
|
||||
var xmlhttp;
|
||||
if (window.XMLHttpRequest)
|
||||
{
|
||||
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
|
||||
xmlhttp=new XMLHttpRequest();
|
||||
}
|
||||
else
|
||||
{
|
||||
// IE6, IE5 浏览器执行代码
|
||||
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
xmlhttp.onreadystatechange=function()
|
||||
{
|
||||
if (xmlhttp.readyState==4 && xmlhttp.status==200)
|
||||
{
|
||||
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
|
||||
}
|
||||
}
|
||||
xmlhttp.open("GET","/try/ajax/ajax_info.txt",true);
|
||||
xmlhttp.send();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="myDiv"><h2>使用 AJAX 修改该文本内容</h2></div>
|
||||
<button type="button" onclick="loadXMLDoc()">修改内容</button>
|
||||
|
||||
</body>
|
||||
```
|
||||
|
||||
## 2 说明
|
||||
|
||||
* 创建xmlhttp=new XMLHTTPRequest()对象。(浏览器的内建对象)
|
||||
* xmlhttp.open(method,url,async)打开http请求async=true,异步执行。async=false同步执行,JavaScript会等到服务器返回后才执行。
|
||||
* xmlhttp.send(string);发送http请求
|
||||
* xmlhttp.responseText;返回字符串形式
|
||||
* xmlhttp.responseXML;使用XML解析返回值
|
||||
|
||||
## 3 事件响应机制
|
||||
|
||||
* onreadystatechange:存储函数,当readyState属性改变时,调用该函数(也是回调函数。)
|
||||
* readyState,存储有状态。0:请求初始化,1已经建立连接,2请求已接收,3请求处理中,4请求已完成
|
||||
* status:200:OK,404:未找到页面。
|
||||
|
||||
```
|
||||
xmlhttp.onreadystatechange=function()
|
||||
{
|
||||
if (xmlhttp.readyState==4 && xmlhttp.status==200)
|
||||
{
|
||||
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
|
||||
}
|
||||
}
|
||||
```
|
||||
387
JavaScript/Ajax/3 axios.md
Normal file
387
JavaScript/Ajax/3 axios.md
Normal file
@@ -0,0 +1,387 @@
|
||||
## 1 是什么
|
||||
|
||||
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
|
||||
|
||||
参考文件:javaguide.cn/java/concurrent/aqs.html#aqs-介绍
|
||||
|
||||
## 2 怎么用
|
||||
|
||||
|
||||
### 安装
|
||||
使用 npm:
|
||||
```
|
||||
$ npm install axios
|
||||
```
|
||||
|
||||
使用 bower:
|
||||
```
|
||||
$ bower install axios
|
||||
```
|
||||
使用 cdn:
|
||||
```
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
```
|
||||
|
||||
### 执行 GET 请求
|
||||
```js
|
||||
// 为给定 ID 的 user 创建请求
|
||||
axios.get('/user?ID=12345')
|
||||
.then(function (response) {
|
||||
console.log(response);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
// 上面的请求也可以这样做
|
||||
axios.get('/user', {
|
||||
params: {
|
||||
ID: 12345
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
console.log(response);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
```
|
||||
|
||||
### 执行 POST 请求
|
||||
```js
|
||||
axios.post('/user', {
|
||||
firstName: 'Fred',
|
||||
lastName: 'Flintstone'
|
||||
})
|
||||
.then(function (response) {
|
||||
console.log(response);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
```
|
||||
|
||||
### 执行多个并发请求
|
||||
|
||||
```js
|
||||
function getUserAccount() {
|
||||
return axios.get('/user/12345');
|
||||
}
|
||||
|
||||
function getUserPermissions() {
|
||||
return axios.get('/user/12345/permissions');
|
||||
}
|
||||
|
||||
axios.all([getUserAccount(), getUserPermissions()])
|
||||
.then(axios.spread(function (acct, perms) {
|
||||
// 两个请求现在都执行完成
|
||||
}));
|
||||
```
|
||||
|
||||
### 添加配置
|
||||
```
|
||||
可以通过向 axios 传递相关配置来创建请求
|
||||
|
||||
axios(config)
|
||||
// 发送 POST 请求
|
||||
axios({
|
||||
method: 'post',
|
||||
url: '/user/12345',
|
||||
data: {
|
||||
firstName: 'Fred',
|
||||
lastName: 'Flintstone'
|
||||
}
|
||||
});
|
||||
// 获取远端图片
|
||||
axios({
|
||||
method:'get',
|
||||
url:'http://bit.ly/2mTM3nY',
|
||||
responseType:'stream'
|
||||
})
|
||||
.then(function(response) {
|
||||
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## 2 配置内容
|
||||
|
||||
### 请求配置
|
||||
|
||||
```js
|
||||
{
|
||||
// `url` 是用于请求的服务器 URL
|
||||
url: '/user',
|
||||
|
||||
// `method` 是创建请求时使用的方法
|
||||
method: 'get', // default
|
||||
|
||||
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
|
||||
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
|
||||
baseURL: 'https://some-domain.com/api/',
|
||||
|
||||
// `transformRequest` 允许在向服务器发送前,修改请求数据
|
||||
// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
|
||||
// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
|
||||
transformRequest: [function (data, headers) {
|
||||
// 对 data 进行任意转换处理
|
||||
return data;
|
||||
}],
|
||||
|
||||
// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
|
||||
transformResponse: [function (data) {
|
||||
// 对 data 进行任意转换处理
|
||||
return data;
|
||||
}],
|
||||
|
||||
// `headers` 是即将被发送的自定义请求头
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
|
||||
// `params` 是即将与请求一起发送的 URL 参数
|
||||
// 必须是一个无格式对象(plain object)或 URLSearchParams 对象
|
||||
params: {
|
||||
ID: 12345
|
||||
},
|
||||
|
||||
// `paramsSerializer` 是一个负责 `params` 序列化的函数
|
||||
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
|
||||
paramsSerializer: function(params) {
|
||||
return Qs.stringify(params, {arrayFormat: 'brackets'})
|
||||
},
|
||||
|
||||
// `data` 是作为请求主体被发送的数据
|
||||
// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
|
||||
// 在没有设置 `transformRequest` 时,必须是以下类型之一:
|
||||
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
|
||||
// - 浏览器专属:FormData, File, Blob
|
||||
// - Node 专属: Stream
|
||||
data: {
|
||||
firstName: 'Fred'
|
||||
},
|
||||
|
||||
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
|
||||
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
|
||||
timeout: 1000,
|
||||
|
||||
// `withCredentials` 表示跨域请求时是否需要使用凭证
|
||||
withCredentials: false, // default
|
||||
|
||||
// `adapter` 允许自定义处理请求,以使测试更轻松
|
||||
// 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
|
||||
adapter: function (config) {
|
||||
/* ... */
|
||||
},
|
||||
|
||||
// `auth` 表示应该使用 HTTP 基础验证,并提供凭据
|
||||
// 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
|
||||
auth: {
|
||||
username: 'janedoe',
|
||||
password: 's00pers3cret'
|
||||
},
|
||||
|
||||
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
|
||||
responseType: 'json', // default
|
||||
|
||||
// `responseEncoding` indicates encoding to use for decoding responses
|
||||
// Note: Ignored for `responseType` of 'stream' or client-side requests
|
||||
responseEncoding: 'utf8', // default
|
||||
|
||||
// `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
|
||||
xsrfCookieName: 'XSRF-TOKEN', // default
|
||||
|
||||
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
|
||||
xsrfHeaderName: 'X-XSRF-TOKEN', // default
|
||||
|
||||
// `onUploadProgress` 允许为上传处理进度事件
|
||||
onUploadProgress: function (progressEvent) {
|
||||
// Do whatever you want with the native progress event
|
||||
},
|
||||
|
||||
// `onDownloadProgress` 允许为下载处理进度事件
|
||||
onDownloadProgress: function (progressEvent) {
|
||||
// 对原生进度事件的处理
|
||||
},
|
||||
|
||||
// `maxContentLength` 定义允许的响应内容的最大尺寸
|
||||
maxContentLength: 2000,
|
||||
|
||||
// `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
|
||||
validateStatus: function (status) {
|
||||
return status >= 200 && status < 300; // default
|
||||
},
|
||||
|
||||
// `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
|
||||
// 如果设置为0,将不会 follow 任何重定向
|
||||
maxRedirects: 5, // default
|
||||
|
||||
// `socketPath` defines a UNIX Socket to be used in node.js.
|
||||
// e.g. '/var/run/docker.sock' to send requests to the docker daemon.
|
||||
// Only either `socketPath` or `proxy` can be specified.
|
||||
// If both are specified, `socketPath` is used.
|
||||
socketPath: null, // default
|
||||
|
||||
// `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
|
||||
// `keepAlive` 默认没有启用
|
||||
httpAgent: new http.Agent({ keepAlive: true }),
|
||||
httpsAgent: new https.Agent({ keepAlive: true }),
|
||||
|
||||
// 'proxy' 定义代理服务器的主机名称和端口
|
||||
// `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
|
||||
// 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
|
||||
proxy: {
|
||||
host: '127.0.0.1',
|
||||
port: 9000,
|
||||
auth: {
|
||||
username: 'mikeymike',
|
||||
password: 'rapunz3l'
|
||||
}
|
||||
},
|
||||
|
||||
// `cancelToken` 指定用于取消请求的 cancel token
|
||||
// (查看后面的 Cancellation 这节了解更多)
|
||||
cancelToken: new CancelToken(function (cancel) {
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 响应结构
|
||||
|
||||
|
||||
某个请求的响应包含以下信息
|
||||
```js
|
||||
|
||||
{
|
||||
// `data` 由服务器提供的响应
|
||||
data: {},
|
||||
|
||||
// `status` 来自服务器响应的 HTTP 状态码
|
||||
status: 200,
|
||||
|
||||
// `statusText` 来自服务器响应的 HTTP 状态信息
|
||||
statusText: 'OK',
|
||||
|
||||
// `headers` 服务器响应的头
|
||||
headers: {},
|
||||
|
||||
// `config` 是为请求提供的配置信息
|
||||
config: {},
|
||||
// 'request'
|
||||
// `request` is the request that generated this response
|
||||
// It is the last ClientRequest instance in node.js (in redirects)
|
||||
// and an XMLHttpRequest instance the browser
|
||||
request: {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
axios.get('/user/12345')
|
||||
.then(function(response) {
|
||||
console.log(response.data);
|
||||
console.log(response.status);
|
||||
console.log(response.statusText);
|
||||
console.log(response.headers);
|
||||
console.log(response.config);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## 3 特殊处理
|
||||
|
||||
### 全局的 axios 默认值
|
||||
```js
|
||||
axios.defaults.baseURL = 'https://api.example.com';
|
||||
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
|
||||
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
```
|
||||
|
||||
### 自定义实例默认值
|
||||
|
||||
```js
|
||||
// Set config defaults when creating the instance
|
||||
const instance = axios.create({
|
||||
baseURL: 'https://api.example.com'
|
||||
});
|
||||
|
||||
// Alter defaults after instance has been created
|
||||
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
|
||||
```
|
||||
### 配置的优先顺序
|
||||
|
||||
配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者。这里是一个例子:
|
||||
|
||||
```
|
||||
// 使用由库提供的配置的默认值来创建实例
|
||||
// 此时超时配置的默认值是 `0`
|
||||
var instance = axios.create();
|
||||
|
||||
// 覆写库的超时默认值
|
||||
// 现在,在超时前,所有请求都会等待 2.5 秒
|
||||
instance.defaults.timeout = 2500;
|
||||
|
||||
// 为已知需要花费很长时间的请求覆写超时设置
|
||||
instance.get('/longRequest', {
|
||||
timeout: 5000
|
||||
});
|
||||
```
|
||||
### 拦截器
|
||||
在请求或响应被 then 或 catch 处理前拦截它们。
|
||||
```js
|
||||
// 添加请求拦截器
|
||||
axios.interceptors.request.use(function (config) {
|
||||
// 在发送请求之前做些什么
|
||||
return config;
|
||||
}, function (error) {
|
||||
// 对请求错误做些什么
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// 添加响应拦截器
|
||||
axios.interceptors.response.use(function (response) {
|
||||
// 对响应数据做点什么
|
||||
return response;
|
||||
}, function (error) {
|
||||
// 对响应错误做点什么
|
||||
return Promise.reject(error);
|
||||
});
|
||||
```
|
||||
如果你想在稍后移除拦截器,可以这样:
|
||||
```
|
||||
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
|
||||
axios.interceptors.request.eject(myInterceptor);
|
||||
```
|
||||
可以为自定义 axios 实例添加拦截器
|
||||
```
|
||||
const instance = axios.create();
|
||||
instance.interceptors.request.use(function () {/*...*/});
|
||||
```
|
||||
### 错误处理
|
||||
```js
|
||||
axios.get('/user/12345')
|
||||
.catch(function (error) {
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
console.log(error.response.data);
|
||||
console.log(error.response.status);
|
||||
console.log(error.response.headers);
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
console.log(error.request);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log('Error', error.message);
|
||||
}
|
||||
console.log(error.config);
|
||||
});
|
||||
```
|
||||
可以使用 validateStatus 配置选项定义一个自定义 HTTP 状态码的错误范围。
|
||||
```js
|
||||
axios.get('/user/12345', {
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // Reject only if the status code is greater than or equal to 500
|
||||
}
|
||||
})
|
||||
```
|
||||
55
JavaScript/Ajax/4 fetch.md
Normal file
55
JavaScript/Ajax/4 fetch.md
Normal file
@@ -0,0 +1,55 @@
|
||||
## 1 简介
|
||||
|
||||
参考文件:https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
|
||||
|
||||
## 2 使用
|
||||
### 基本用法
|
||||
|
||||
```js
|
||||
// Example POST method implementation:
|
||||
async function postData(url = "", data = {}) {
|
||||
// Default options are marked with *
|
||||
const response = await fetch(url, {
|
||||
method: "POST", // *GET, POST, PUT, DELETE, etc.
|
||||
mode: "cors", // no-cors, *cors, same-origin
|
||||
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
|
||||
credentials: "same-origin", // include, *same-origin, omit
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
// 'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
redirect: "follow", // manual, *follow, error
|
||||
referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
|
||||
body: JSON.stringify(data), // body data type must match "Content-Type" header
|
||||
});
|
||||
return response.json(); // parses JSON response into native JavaScript objects
|
||||
}
|
||||
|
||||
postData("https://example.com/answer", { answer: 42 }).then((data) => {
|
||||
console.log(data); // JSON data parsed by `data.json()` call
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 上传文件
|
||||
|
||||
```js
|
||||
const formData = new FormData();
|
||||
const fileField = document.querySelector('input[type="file"]');
|
||||
|
||||
formData.append("username", "abc123");
|
||||
formData.append("avatar", fileField.files[0]);
|
||||
|
||||
fetch("https://example.com/profile/avatar", {
|
||||
method: "PUT",
|
||||
body: formData,
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((result) => {
|
||||
console.log("Success:", result);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
});
|
||||
```
|
||||
74
JavaScript/Ajax/5 jquery.md
Normal file
74
JavaScript/Ajax/5 jquery.md
Normal file
@@ -0,0 +1,74 @@
|
||||
## 1 load
|
||||
|
||||
### load方法
|
||||
jQuery load() 方法是简单但强大的 AJAX 方法。
|
||||
|
||||
load() 方法从服务器加载数据,并把返回的数据放入被选元素中
|
||||
|
||||
```
|
||||
$(selector).load(URL,data,callback);
|
||||
|
||||
$("#div1").load("demo_test.txt");
|
||||
```
|
||||
|
||||
### load回调参数
|
||||
|
||||
可选的 callback 参数规定当 load() 方法完成后所要允许的回调函数。回调函数可以设置不同的参数:
|
||||
|
||||
responseTxt - 包含调用成功时的结果内容
|
||||
statusTXT - 包含调用的状态
|
||||
xhr - 包含 XMLHttpRequest 对象
|
||||
|
||||
```
|
||||
$("button").click(function(){
|
||||
$("#div1").load("demo_test.txt",function(responseTxt,statusTxt,xhr){
|
||||
if(statusTxt=="success")
|
||||
alert("外部内容加载成功!");
|
||||
if(statusTxt=="error")
|
||||
alert("Error: "+xhr.status+": "+xhr.statusText);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 2 GET方法
|
||||
GET - 从指定的资源请求数据.GET 基本上用于从服务器获得(取回)数据。注释:GET 方法可能返回缓存数据。
|
||||
```
|
||||
$.get(URL,callback);
|
||||
|
||||
$("button").click(function(){
|
||||
$.get("demo_test.php",function(data,status){
|
||||
alert("数据: " + data + "\n状态: " + status);
|
||||
});
|
||||
});
|
||||
```
|
||||
## 3 POST方法
|
||||
|
||||
POST - 向指定的资源提交要处理的数据.POST 也可用于从服务器获取数据。不过,POST 方法不会缓存数据,并且常用于连同请求一起发送数据
|
||||
|
||||
```
|
||||
$.post(URL,data,callback);
|
||||
|
||||
$("button").click(function(){
|
||||
$.post("/try/ajax/demo_test_post.php",
|
||||
{
|
||||
name:"菜鸟教程",
|
||||
url:"http://www.runoob.com"
|
||||
},
|
||||
function(data,status){
|
||||
alert("数据: \n" + data + "\n状态: " + status);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## 2 GET JSON方法
|
||||
GET - 从指定的资源请求数据.GET 基本上用于从服务器获得(取回)数据。注释:GET 方法可能返回缓存数据。
|
||||
```
|
||||
$.getJSON(URL,callback);
|
||||
|
||||
$("button").click(function(){
|
||||
$.get("demo_test.php",function(data,status){
|
||||
alert("数据: " + data + "\n状态: " + status);
|
||||
});
|
||||
});
|
||||
```
|
||||
BIN
JavaScript/Ajax/image/1705132223204.png
Normal file
BIN
JavaScript/Ajax/image/1705132223204.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 135 KiB |
Reference in New Issue
Block a user