updates
This commit is contained in:
parent
94c83e9e50
commit
cc5213b09e
79 changed files with 1341 additions and 357 deletions
530
5-network/08-xmlhttprequest/article.md
Normal file
530
5-network/08-xmlhttprequest/article.md
Normal file
|
@ -0,0 +1,530 @@
|
|||
# XMLHttpRequest
|
||||
|
||||
`XMLHttpRequest` is a built-in browser object that allows to make HTTP requests in JavaScript.
|
||||
|
||||
Despite of having the word "XML" in its name, it can operate on any data, not only in XML format. We can upload/download files, track progress and much more.
|
||||
|
||||
Right now, there's another, more modern method `fetch`, that somewhat deprecates `XMLHttpRequest`.
|
||||
|
||||
In modern web-development `XMLHttpRequest` may be used for three reasons:
|
||||
|
||||
1. Historical reasons: we need to support existing scripts with `XMLHttpRequest`.
|
||||
2. We need to support old browsers, and don't want polyfills (e.g. to keep scripts tiny).
|
||||
3. We need something that `fetch` can't do yet, e.g. to track upload progress.
|
||||
|
||||
Does that sound familiar? If yes, then all right, go on with `XMLHttpRequest`. Otherwise, please head on to <info:fetch>.
|
||||
|
||||
## The basics
|
||||
|
||||
XMLHttpRequest has two modes of operation: synchronous and asynchronous.
|
||||
|
||||
Let's see the asynchronous first, as it's used in the majority of cases.
|
||||
|
||||
To do the request, we need 3 steps:
|
||||
|
||||
1. Create `XMLHttpRequest`:
|
||||
```js
|
||||
let xhr = new XMLHttpRequest(); // the constructor has no arguments
|
||||
```
|
||||
|
||||
2. Initialize it:
|
||||
```js
|
||||
xhr.open(method, URL, [async, user, password])
|
||||
```
|
||||
|
||||
This method is usually called right after `new XMLHttpRequest`. It specifies the main parameters of the request:
|
||||
|
||||
- `method` -- HTTP-method. Usually `"GET"` or `"POST"`.
|
||||
- `URL` -- the URL to request, a string, can be [URL](info:url) object.
|
||||
- `async` -- if explicitly set to `false`, then the request is synchronous, we'll cover that a bit later.
|
||||
- `user`, `password` -- login and password for basic HTTP auth (if required).
|
||||
|
||||
Please note that `open` call, contrary to its name, does not open the connection. It only configures the request, but the network activity only starts with the call of `send`.
|
||||
|
||||
3. Send it out.
|
||||
|
||||
```js
|
||||
xhr.send([body])
|
||||
```
|
||||
|
||||
This method opens the connection and sends the request to server. The optional `body` parameter contains the request body.
|
||||
|
||||
Some request methods like `GET` do not have a body. And some of them like `POST` use `body` to send the data to the server. We'll see examples later.
|
||||
|
||||
4. Listen to events for response.
|
||||
|
||||
These three are the most widely used:
|
||||
- `load` -- when the result is ready, that includes HTTP errors like 404.
|
||||
- `error` -- when the request couldn't be made, e.g. network down or invalid URL.
|
||||
- `progress` -- triggers periodically during the download, reports how much downloaded.
|
||||
|
||||
```js
|
||||
xhr.onload = function() {
|
||||
alert(`Loaded: ${xhr.status} ${xhr.response}`);
|
||||
};
|
||||
|
||||
xhr.onerror = function() { // only triggers if the request couldn't be made at all
|
||||
alert(`Network Error`);
|
||||
};
|
||||
|
||||
xhr.onprogress = function(event) { // triggers periodically
|
||||
// event.loaded - how many bytes downloaded
|
||||
// event.lengthComputable = true if the server sent Content-Length header
|
||||
// event.total - total number of bytes (if lengthComputable)
|
||||
alert(`Received ${event.loaded} of ${event.total}`);
|
||||
};
|
||||
```
|
||||
|
||||
Here's a full example. The code below loads the URL at `/article/xmlhttprequest/example/load` from the server and prints the progress:
|
||||
|
||||
```js run
|
||||
// 1. Create a new XMLHttpRequest object
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
// 2. Configure it: GET-request for the URL /article/.../load
|
||||
xhr.open('GET', '/article/xmlhttprequest/example/load');
|
||||
|
||||
// 3. Send the request over the network
|
||||
xhr.send();
|
||||
|
||||
// 4. This will be called after the response is received
|
||||
xhr.onload = function() {
|
||||
if (xhr.status != 200) { // analyze HTTP status of the response
|
||||
alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found
|
||||
} else { // show the result
|
||||
alert(`Done, got ${xhr.response.length} bytes`); // responseText is the server
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onprogress = function(event) {
|
||||
if (event.lengthComputable) {
|
||||
alert(`Received ${event.loaded} of ${event.total} bytes`);
|
||||
} else {
|
||||
alert(`Received ${event.loaded} bytes`); // no Content-Length
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
xhr.onerror = function() {
|
||||
alert("Request failed");
|
||||
};
|
||||
```
|
||||
|
||||
Once the server has responded, we can receive the result in the following properties of the request object:
|
||||
|
||||
`status`
|
||||
: HTTP status code (a number): `200`, `404`, `403` and so on, can be `0` in case of a non-HTTP failure.
|
||||
|
||||
`statusText`
|
||||
: HTTP status message (a string): usually `OK` for `200`, `Not Found` for `404`, `Forbidden` for `403` and so on.
|
||||
|
||||
`response` (old scripts may use `responseText`)
|
||||
: The server response.
|
||||
|
||||
We can also specify a timeout using the corresponding property:
|
||||
|
||||
```js
|
||||
xhr.timeout = 10000; // timeout in ms, 10 seconds
|
||||
```
|
||||
|
||||
If the request does not succeed within the given time, it gets canceled and `timeout` event triggers.
|
||||
|
||||
````smart header="URL search parameters"
|
||||
To pass URL parameters, like `?name=value`, and ensure the proper encoding, we can use [URL](info:url) object:
|
||||
|
||||
```js
|
||||
let url = new URL('https://google.com/search');
|
||||
url.searchParams.set('q', 'test me!');
|
||||
|
||||
// the parameter 'q' is encoded
|
||||
xhr.open('GET', url); // https://google.com/search?q=test+me%21
|
||||
```
|
||||
|
||||
````
|
||||
|
||||
## Response Type
|
||||
|
||||
We can use `xhr.responseType` property to set the response format:
|
||||
|
||||
- `""` (default) -- get as string,
|
||||
- `"text"` -- get as string,
|
||||
- `"arraybuffer"` -- get as `ArrayBuffer` (for binary data, see chapter <info:arraybuffer-binary-arrays>),
|
||||
- `"blob"` -- get as `Blob` (for binary data, see chapter <info:blob>),
|
||||
- `"document"` -- get as XML document (can use XPath and other XML methods),
|
||||
- `"json"` -- get as JSON (parsed automatically).
|
||||
|
||||
For example, let's get the response as JSON:
|
||||
|
||||
```js run
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open('GET', '/article/xmlhttprequest/example/json');
|
||||
|
||||
*!*
|
||||
xhr.responseType = 'json';
|
||||
*/!*
|
||||
|
||||
xhr.send();
|
||||
|
||||
// the response is {"message": "Hello, world!"}
|
||||
xhr.onload = function() {
|
||||
let responseObj = xhr.response;
|
||||
alert(responseObj.message); // Hello, world!
|
||||
};
|
||||
```
|
||||
|
||||
```smart
|
||||
In the old scripts you may also find `xhr.responseText` and even `xhr.responseXML` properties.
|
||||
|
||||
They exist for historical reasons, to get either a string or XML document. Nowadays, we should set the format in `xhr.responseType` and get `xhr.response` as demonstrated above.
|
||||
```
|
||||
|
||||
## Ready states
|
||||
|
||||
`XMLHttpRequest` changes between states as it progresses. The current state is accessible as `xhr.readyState`.
|
||||
|
||||
All states, as in [the specification](https://xhr.spec.whatwg.org/#states):
|
||||
|
||||
```js
|
||||
UNSENT = 0; // initial state
|
||||
OPENED = 1; // open called
|
||||
HEADERS_RECEIVED = 2; // response headers received
|
||||
LOADING = 3; // response is loading (a data packed is received)
|
||||
DONE = 4; // request complete
|
||||
```
|
||||
|
||||
An `XMLHttpRequest` object travels them in the order `0` -> `1` -> `2` -> `3` -> ... -> `3` -> `4`. State `3` repeats every time a data packet is received over the network.
|
||||
|
||||
We can track them using `readystatechange` event:
|
||||
|
||||
```js
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 3) {
|
||||
// loading
|
||||
}
|
||||
if (xhr.readyState == 4) {
|
||||
// request finished
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
You can find `readystatechange` listeners in really old code, it's there for historical reasons, as there was a time when there were no `load` and other events.
|
||||
|
||||
Nowadays, `load/error/progress` handlers deprecate it.
|
||||
|
||||
## Aborting request
|
||||
|
||||
We can terminate the request at any time. The call to `xhr.abort()` does that:
|
||||
|
||||
```js
|
||||
xhr.abort(); // terminate the request
|
||||
```
|
||||
|
||||
That triggers `abort` event.
|
||||
|
||||
That
|
||||
Also, `x and `xhr.status` become `0` in that case.
|
||||
|
||||
|
||||
## Synchronous requests
|
||||
|
||||
If in the `open` method the third parameter `async` is set to `false`, the request is made synchronously.
|
||||
|
||||
In other words, JavaScript execution pauses at `send()` and resumes when the response is received. Somewhat like `alert` or `prompt` commands.
|
||||
|
||||
Here's the rewritten example, the 3rd parameter of `open` is `false`:
|
||||
|
||||
```js
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open('GET', '/article/xmlhttprequest/hello.txt', *!*false*/!*);
|
||||
|
||||
try {
|
||||
xhr.send();
|
||||
if (xhr.status != 200) {
|
||||
alert(`Error ${xhr.status}: ${xhr.statusText}`);
|
||||
} else {
|
||||
alert(xhr.response);
|
||||
}
|
||||
} catch(err) { // instead of onerror
|
||||
alert("Request failed");
|
||||
};
|
||||
```
|
||||
|
||||
It might look good, but synchronous calls are used rarely, because they block in-page JavaScript till the loading is complete. In some browsers it becomes impossible to scroll. If a synchronous call takes too much time, the browser may suggest to close the "hanging" webpage.
|
||||
|
||||
Many advanced capabilities of `XMLHttpRequest`, like requesting from another domain or specifying a timeout, are unavailable for synchronous requests. Also, as you can see, no progress indication.
|
||||
|
||||
Because of all that, synchronous requests are used very sparingly, almost never. We won't talk about them any more.
|
||||
|
||||
## HTTP-headers
|
||||
|
||||
`XMLHttpRequest` allows both to send custom headers and read headers from the response.
|
||||
|
||||
There are 3 methods for HTTP-headers:
|
||||
|
||||
`setRequestHeader(name, value)`
|
||||
: Sets the request header with the given `name` and `value`.
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
```
|
||||
|
||||
```warn header="Headers limitations"
|
||||
Several headers are managed exclusively by the browser, e.g. `Referer` and `Host`.
|
||||
The full list is [in the specification](http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader-method).
|
||||
|
||||
`XMLHttpRequest` is not allowed to change them, for the sake of user safety and correctness of the request.
|
||||
```
|
||||
|
||||
````warn header="Can't remove a header"
|
||||
Another peculiarity of `XMLHttpRequest` is that one can't undo `setRequestHeader`.
|
||||
|
||||
Once the header is set, it's set. Additional calls add information to the header, don't overwrite it.
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
xhr.setRequestHeader('X-Auth', '123');
|
||||
xhr.setRequestHeader('X-Auth', '456');
|
||||
|
||||
// the header will be:
|
||||
// X-Auth: 123, 456
|
||||
```
|
||||
````
|
||||
|
||||
`getResponseHeader(name)`
|
||||
: Gets the response header with the given `name` (except `Set-Cookie` and `Set-Cookie2`).
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
xhr.getResponseHeader('Content-Type')
|
||||
```
|
||||
|
||||
`getAllResponseHeaders()`
|
||||
: Returns all response headers, except `Set-Cookie` and `Set-Cookie2`.
|
||||
|
||||
Headers are returned as a single line, e.g.:
|
||||
|
||||
```
|
||||
Cache-Control: max-age=31536000
|
||||
Content-Length: 4260
|
||||
Content-Type: image/png
|
||||
Date: Sat, 08 Sep 2012 16:53:16 GMT
|
||||
```
|
||||
|
||||
The line break between headers is always `"\r\n"` (doesn't depend on OS), so we can easily split it into individual headers. The separator between the name and the value is always a colon followed by a space `": "`. That's fixed in the specification.
|
||||
|
||||
So, if we want to get an object with name/value pairs, we need to throw in a bit JS.
|
||||
|
||||
Like this (assuming that if two headers have the same name, then the latter one overwrites the former one):
|
||||
|
||||
```js
|
||||
let headers = xhr
|
||||
.getAllResponseHeaders()
|
||||
.split('\r\n')
|
||||
.reduce((result, current) => {
|
||||
let [name, value] = current.split(': ');
|
||||
result[name] = value;
|
||||
return result;
|
||||
}, {});
|
||||
```
|
||||
|
||||
## POST, FormData
|
||||
|
||||
To make a POST request, we can use the built-in [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object.
|
||||
|
||||
The syntax:
|
||||
|
||||
```js
|
||||
let formData = new FormData([form]); // creates an object, optionally fill from <form>
|
||||
formData.append(name, value); // appends a field
|
||||
```
|
||||
|
||||
We create it, optionally from a form, `append` more fields if needed, and then:
|
||||
|
||||
1. `xhr.open('POST', ...)` – use `POST` method.
|
||||
2. `xhr.send(formData)` to submit the form to the server.
|
||||
|
||||
For instance:
|
||||
|
||||
```html run
|
||||
<form name="person">
|
||||
<input name="name" value="John">
|
||||
<input name="surname" value="Smith">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
// pre-fill FormData from the form
|
||||
let formData = new FormData(document.forms.person);
|
||||
|
||||
// add one more field
|
||||
formData.append("middle", "Lee");
|
||||
|
||||
// send it out
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "/article/xmlhttprequest/post/user");
|
||||
xhr.send(formData);
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
The form is sent with `multipart/form-data` encoding.
|
||||
|
||||
Or, if we like JSON more, then `JSON.stringify` and send as a string.
|
||||
|
||||
Just don't forget to set the header `Content-Type: application/json`, many server-side frameworks automatically decode JSON with it:
|
||||
|
||||
```js
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
let json = JSON.stringify({
|
||||
name: "John",
|
||||
surname: "Smith"
|
||||
});
|
||||
|
||||
xhr.open("POST", '/submit')
|
||||
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
|
||||
|
||||
xhr.send(json);
|
||||
```
|
||||
|
||||
The `.send(body)` method is pretty omnivore. It can send almost everything, including `Blob` and `BufferSource` objects.
|
||||
|
||||
## Upload progress
|
||||
|
||||
The `progress` event only works on the downloading stage.
|
||||
|
||||
That is: if we `POST` something, `XMLHttpRequest` first uploads our data (the request body), then downloads the response.
|
||||
|
||||
If we're uploading something big, then we're surely more interested in tracking the upload progress. But `xhr.onprogress` doesn't help here.
|
||||
|
||||
There's another object `xhr.upload`, without methods, exclusively for upload events.
|
||||
|
||||
The event list is similar to `xhr` events, but `xhr.upload` triggers them on uploading:
|
||||
|
||||
- `loadstart` -- upload started.
|
||||
- `progress` -- triggers periodically during the upload.
|
||||
- `abort` -- upload aborted.
|
||||
- `error` -- non-HTTP error.
|
||||
- `load` -- upload finished successfully.
|
||||
- `timeout` -- upload timed out (if `timeout` property is set).
|
||||
- `loadend` -- upload finished with either success or error.
|
||||
|
||||
Example of handlers:
|
||||
|
||||
```js
|
||||
xhr.upload.onprogress = function(event) {
|
||||
alert(`Uploaded ${event.loaded} of ${event.total} bytes`);
|
||||
};
|
||||
|
||||
xhr.upload.onload = function() {
|
||||
alert(`Upload finished successfully.`);
|
||||
};
|
||||
|
||||
xhr.upload.onerror = function() {
|
||||
alert(`Error during the upload: ${xhr.status}`);
|
||||
};
|
||||
```
|
||||
|
||||
Here's a real-life example: file upload with progress indication:
|
||||
|
||||
```html run
|
||||
<input type="file" onchange="upload(this.files[0])">
|
||||
|
||||
<script>
|
||||
function upload(file) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
// track upload progress
|
||||
*!*
|
||||
xhr.upload.onprogress = function(event) {
|
||||
console.log(`Uploaded ${event.loaded} of ${event.total}`);
|
||||
};
|
||||
*/!*
|
||||
|
||||
// track completion: both successful or not
|
||||
xhr.onloadend = function() {
|
||||
if (xhr.status == 200) {
|
||||
console.log("success");
|
||||
} else {
|
||||
console.log("error " + this.status);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open("POST", "/article/xmlhttprequest/post/upload");
|
||||
xhr.send(file);
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Cross-origin requests
|
||||
|
||||
`XMLHttpRequest` can make cross-domain requests, using the same CORS policy as [fetch](info:fetch-crossorigin).
|
||||
|
||||
Just like `fetch`, it doesn't send cookies and HTTP-authorization to another origin by default. To enable them, set `xhr.withCredentials` to `true`:
|
||||
|
||||
```js
|
||||
let xhr = new XMLHttpRequest();
|
||||
*!*
|
||||
xhr.withCredentials = true;
|
||||
*/!*
|
||||
|
||||
xhr.open('POST', 'http://anywhere.com/request');
|
||||
...
|
||||
```
|
||||
|
||||
See the chapter <info:fetch-crossorigin> for details about cross-origin headers.
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
Typical code of the GET-request with `XMLHttpRequest`:
|
||||
|
||||
```js
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open('GET', '/my/url');
|
||||
|
||||
xhr.send(); s
|
||||
|
||||
xhr.onload = function() {
|
||||
if (xhr.status != 200) { // HTTP error?
|
||||
// handle error
|
||||
alert( 'Error: ' + xhr.status);
|
||||
return;
|
||||
}
|
||||
|
||||
// get the response from xhr.response
|
||||
};
|
||||
|
||||
xhr.onprogress = function(event) {
|
||||
// report progress
|
||||
alert(`Loaded ${event.loaded} of ${event.total}`);
|
||||
};
|
||||
|
||||
xhr.onerror = function() {
|
||||
// handle non-HTTP error (e.g. network down)
|
||||
};
|
||||
```
|
||||
|
||||
There are actually more events, the [modern specification](http://www.w3.org/TR/XMLHttpRequest/#events) lists them (in the lifecycle order):
|
||||
|
||||
- `loadstart` -- the request has started.
|
||||
- `progress` -- a data packet of the response has arrived, the whole response body at the moment is in `responseText`.
|
||||
- `abort` -- the request was canceled by the call `xhr.abort()`.
|
||||
- `error` -- connection error has occurred, e.g. wrong domain name. Doesn't happen for HTTP-errors like 404.
|
||||
- `load` -- the request has finished successfully.
|
||||
- `timeout` -- the request was canceled due to timeout (only happens if it was set).
|
||||
- `loadend` -- triggers after `load`, `error`, `timeout` or `abort`.
|
||||
|
||||
The `error`, `abort`, `timeout`, and `load` events are mutually exclusive.
|
||||
|
||||
The most used events are load completion (`load`), load failure (`error`), or we can use a single `loadend` handler and check event and response to see what happened.
|
||||
|
||||
We've already seen another event: `readystatechange`. Historically, it appeared long ago, before the specification settled. Nowadays, there's no need to use it, we can replace it with newer events, but it can often be found in older scripts.
|
||||
|
||||
If we need to track uploading specifically, then we should listen to same events on `xhr.upload` object.
|
30
5-network/08-xmlhttprequest/example.view/index.html
Normal file
30
5-network/08-xmlhttprequest/example.view/index.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE HTML>
|
||||
<script>
|
||||
function run() {
|
||||
|
||||
let xhr = new XMLHttpRequest();
|
||||
write(`readyState=${xhr.readyState}`);
|
||||
|
||||
xhr.open('GET', 'digits');
|
||||
write(`readyState=${xhr.readyState}`);
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
write(`readyState=${xhr.readyState}, responseText.length=${xhr.responseText.length}`);
|
||||
};
|
||||
|
||||
xhr.onprogress = function() {
|
||||
write(`readyState=${xhr.readyState}, responseText.length=${xhr.responseText.length}`);
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function write(text) {
|
||||
let li = log.appendChild(document.createElement('li'));
|
||||
li.innerHTML = text;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick="run()">Load digits</button>
|
||||
|
||||
<ul id="log"></ul>
|
52
5-network/08-xmlhttprequest/example.view/server.js
Normal file
52
5-network/08-xmlhttprequest/example.view/server.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
let http = require('http');
|
||||
let url = require('url');
|
||||
let querystring = require('querystring');
|
||||
let static = require('node-static');
|
||||
let file = new static.Server('.');
|
||||
|
||||
function accept(req, res) {
|
||||
|
||||
if (req.url == '/load') {
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/plain',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Length': 90000
|
||||
});
|
||||
|
||||
let i = 0;
|
||||
|
||||
let timer = setInterval(write, 1000);
|
||||
write();
|
||||
|
||||
function write() {
|
||||
res.write(String(i).repeat(10000));
|
||||
i++;
|
||||
if (i == 9) {
|
||||
clearInterval(timer);
|
||||
res.end();
|
||||
}
|
||||
|
||||
}
|
||||
} else if (req.url == '/json') {
|
||||
res.writeHead(200, {
|
||||
// 'Content-Type': 'application/json;charset=utf-8',
|
||||
'Cache-Control': 'no-cache'
|
||||
});
|
||||
|
||||
res.write(JSON.stringify({message: "Hello, world!"}));
|
||||
res.end();
|
||||
} else {
|
||||
file.serve(req, res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----- запуск accept как сервера из консоли или как модуля ------
|
||||
|
||||
if (!module.parent) {
|
||||
http.createServer(accept).listen(8080);
|
||||
} else {
|
||||
exports.accept = accept;
|
||||
}
|
1
5-network/08-xmlhttprequest/hello.txt
Normal file
1
5-network/08-xmlhttprequest/hello.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Hello from the server!
|
42
5-network/08-xmlhttprequest/phones-async.view/index.html
Normal file
42
5-network/08-xmlhttprequest/phones-async.view/index.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<button onclick="loadPhones()" id="button">Load phones.json!</button>
|
||||
|
||||
<script>
|
||||
function loadPhones() {
|
||||
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open('GET', 'phones.json');
|
||||
|
||||
|
||||
xhr.send();
|
||||
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState != 4) return;
|
||||
|
||||
button.innerHTML = 'Complete!';
|
||||
|
||||
if (xhr.status != 200) {
|
||||
// handle error
|
||||
alert(xhr.status + ': ' + xhr.statusText);
|
||||
} else {
|
||||
// show result
|
||||
alert(xhr.responseText);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
button.innerHTML = 'Loading...';
|
||||
button.disabled = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
155
5-network/08-xmlhttprequest/phones-async.view/phones.json
Normal file
155
5-network/08-xmlhttprequest/phones-async.view/phones.json
Normal file
|
@ -0,0 +1,155 @@
|
|||
[
|
||||
{
|
||||
"age": 0,
|
||||
"id": "motorola-xoom-with-wi-fi",
|
||||
"imageUrl": "img/phones/motorola-xoom-with-wi-fi.0.jpg",
|
||||
"name": "Motorola XOOM\u2122 with Wi-Fi",
|
||||
"snippet": "The Next, Next Generation\r\n\r\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb)."
|
||||
},
|
||||
{
|
||||
"age": 1,
|
||||
"id": "motorola-xoom",
|
||||
"imageUrl": "img/phones/motorola-xoom.0.jpg",
|
||||
"name": "MOTOROLA XOOM\u2122",
|
||||
"snippet": "The Next, Next Generation\n\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb)."
|
||||
},
|
||||
{
|
||||
"age": 2,
|
||||
"carrier": "AT&T",
|
||||
"id": "motorola-atrix-4g",
|
||||
"imageUrl": "img/phones/motorola-atrix-4g.0.jpg",
|
||||
"name": "MOTOROLA ATRIX\u2122 4G",
|
||||
"snippet": "MOTOROLA ATRIX 4G the world's most powerful smartphone."
|
||||
},
|
||||
{
|
||||
"age": 3,
|
||||
"id": "dell-streak-7",
|
||||
"imageUrl": "img/phones/dell-streak-7.0.jpg",
|
||||
"name": "Dell Streak 7",
|
||||
"snippet": "Introducing Dell\u2122 Streak 7. Share photos, videos and movies together. It\u2019s small enough to carry around, big enough to gather around."
|
||||
},
|
||||
{
|
||||
"age": 4,
|
||||
"carrier": "Cellular South",
|
||||
"id": "samsung-gem",
|
||||
"imageUrl": "img/phones/samsung-gem.0.jpg",
|
||||
"name": "Samsung Gem\u2122",
|
||||
"snippet": "The Samsung Gem\u2122 brings you everything that you would expect and more from a touch display smart phone \u2013 more apps, more features and a more affordable price."
|
||||
},
|
||||
{
|
||||
"age": 5,
|
||||
"carrier": "Dell",
|
||||
"id": "dell-venue",
|
||||
"imageUrl": "img/phones/dell-venue.0.jpg",
|
||||
"name": "Dell Venue",
|
||||
"snippet": "The Dell Venue; Your Personal Express Lane to Everything"
|
||||
},
|
||||
{
|
||||
"age": 6,
|
||||
"carrier": "Best Buy",
|
||||
"id": "nexus-s",
|
||||
"imageUrl": "img/phones/nexus-s.0.jpg",
|
||||
"name": "Nexus S",
|
||||
"snippet": "Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet."
|
||||
},
|
||||
{
|
||||
"age": 7,
|
||||
"carrier": "Cellular South",
|
||||
"id": "lg-axis",
|
||||
"imageUrl": "img/phones/lg-axis.0.jpg",
|
||||
"name": "LG Axis",
|
||||
"snippet": "Android Powered, Google Maps Navigation, 5 Customizable Home Screens"
|
||||
},
|
||||
{
|
||||
"age": 8,
|
||||
"id": "samsung-galaxy-tab",
|
||||
"imageUrl": "img/phones/samsung-galaxy-tab.0.jpg",
|
||||
"name": "Samsung Galaxy Tab\u2122",
|
||||
"snippet": "Feel Free to Tab\u2122. The Samsung Galaxy Tab\u2122 brings you an ultra-mobile entertainment experience through its 7\u201d display, high-power processor and Adobe\u00ae Flash\u00ae Player compatibility."
|
||||
},
|
||||
{
|
||||
"age": 9,
|
||||
"carrier": "Cellular South",
|
||||
"id": "samsung-showcase-a-galaxy-s-phone",
|
||||
"imageUrl": "img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg",
|
||||
"name": "Samsung Showcase\u2122 a Galaxy S\u2122 phone",
|
||||
"snippet": "The Samsung Showcase\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance, even outdoors"
|
||||
},
|
||||
{
|
||||
"age": 10,
|
||||
"carrier": "Verizon",
|
||||
"id": "droid-2-global-by-motorola",
|
||||
"imageUrl": "img/phones/droid-2-global-by-motorola.0.jpg",
|
||||
"name": "DROID\u2122 2 Global by Motorola",
|
||||
"snippet": "The first smartphone with a 1.2 GHz processor and global capabilities."
|
||||
},
|
||||
{
|
||||
"age": 11,
|
||||
"carrier": "Verizon",
|
||||
"id": "droid-pro-by-motorola",
|
||||
"imageUrl": "img/phones/droid-pro-by-motorola.0.jpg",
|
||||
"name": "DROID\u2122 Pro by Motorola",
|
||||
"snippet": "The next generation of DOES."
|
||||
},
|
||||
{
|
||||
"age": 12,
|
||||
"carrier": "AT&T",
|
||||
"id": "motorola-bravo-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-bravo-with-motoblur.0.jpg",
|
||||
"name": "MOTOROLA BRAVO\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "An experience to cheer about."
|
||||
},
|
||||
{
|
||||
"age": 13,
|
||||
"carrier": "T-Mobile",
|
||||
"id": "motorola-defy-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg",
|
||||
"name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "Are you ready for everything life throws your way?"
|
||||
},
|
||||
{
|
||||
"age": 14,
|
||||
"carrier": "T-Mobile",
|
||||
"id": "t-mobile-mytouch-4g",
|
||||
"imageUrl": "img/phones/t-mobile-mytouch-4g.0.jpg",
|
||||
"name": "T-Mobile myTouch 4G",
|
||||
"snippet": "The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi."
|
||||
},
|
||||
{
|
||||
"age": 15,
|
||||
"carrier": "US Cellular",
|
||||
"id": "samsung-mesmerize-a-galaxy-s-phone",
|
||||
"imageUrl": "img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg",
|
||||
"name": "Samsung Mesmerize\u2122 a Galaxy S\u2122 phone",
|
||||
"snippet": "The Samsung Mesmerize\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance,even outdoors"
|
||||
},
|
||||
{
|
||||
"age": 16,
|
||||
"carrier": "Sprint",
|
||||
"id": "sanyo-zio",
|
||||
"imageUrl": "img/phones/sanyo-zio.0.jpg",
|
||||
"name": "SANYO ZIO",
|
||||
"snippet": "The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value."
|
||||
},
|
||||
{
|
||||
"age": 17,
|
||||
"id": "samsung-transform",
|
||||
"imageUrl": "img/phones/samsung-transform.0.jpg",
|
||||
"name": "Samsung Transform\u2122",
|
||||
"snippet": "The Samsung Transform\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \u201cSprint ID Service Pack\u201d."
|
||||
},
|
||||
{
|
||||
"age": 18,
|
||||
"id": "t-mobile-g2",
|
||||
"imageUrl": "img/phones/t-mobile-g2.0.jpg",
|
||||
"name": "T-Mobile G2",
|
||||
"snippet": "The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible."
|
||||
},
|
||||
{
|
||||
"age": 19,
|
||||
"id": "motorola-charm-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-charm-with-motoblur.0.jpg",
|
||||
"name": "Motorola CHARM\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "Motorola CHARM fits easily in your pocket or palm. Includes MOTOBLUR service."
|
||||
}
|
||||
]
|
30
5-network/08-xmlhttprequest/phones-async.view/server.js
Normal file
30
5-network/08-xmlhttprequest/phones-async.view/server.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
var http = require('http');
|
||||
var url = require('url');
|
||||
var querystring = require('querystring');
|
||||
var static = require('node-static');
|
||||
var file = new static.Server('.', {
|
||||
cache: 0
|
||||
});
|
||||
|
||||
|
||||
function accept(req, res) {
|
||||
|
||||
if (req.url == '/phones.json') {
|
||||
// искусственная задержка для наглядности
|
||||
setTimeout(function() {
|
||||
file.serve(req, res);
|
||||
}, 2000);
|
||||
} else {
|
||||
file.serve(req, res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ------ запустить сервер -------
|
||||
|
||||
if (!module.parent) {
|
||||
http.createServer(accept).listen(8080);
|
||||
} else {
|
||||
exports.accept = accept;
|
||||
}
|
155
5-network/08-xmlhttprequest/phones.json
Normal file
155
5-network/08-xmlhttprequest/phones.json
Normal file
|
@ -0,0 +1,155 @@
|
|||
[
|
||||
{
|
||||
"age": 0,
|
||||
"id": "motorola-xoom-with-wi-fi",
|
||||
"imageUrl": "img/phones/motorola-xoom-with-wi-fi.0.jpg",
|
||||
"name": "Motorola XOOM\u2122 with Wi-Fi",
|
||||
"snippet": "The Next, Next Generation\r\n\r\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb)."
|
||||
},
|
||||
{
|
||||
"age": 1,
|
||||
"id": "motorola-xoom",
|
||||
"imageUrl": "img/phones/motorola-xoom.0.jpg",
|
||||
"name": "MOTOROLA XOOM\u2122",
|
||||
"snippet": "The Next, Next Generation\n\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb)."
|
||||
},
|
||||
{
|
||||
"age": 2,
|
||||
"carrier": "AT&T",
|
||||
"id": "motorola-atrix-4g",
|
||||
"imageUrl": "img/phones/motorola-atrix-4g.0.jpg",
|
||||
"name": "MOTOROLA ATRIX\u2122 4G",
|
||||
"snippet": "MOTOROLA ATRIX 4G the world's most powerful smartphone."
|
||||
},
|
||||
{
|
||||
"age": 3,
|
||||
"id": "dell-streak-7",
|
||||
"imageUrl": "img/phones/dell-streak-7.0.jpg",
|
||||
"name": "Dell Streak 7",
|
||||
"snippet": "Introducing Dell\u2122 Streak 7. Share photos, videos and movies together. It\u2019s small enough to carry around, big enough to gather around."
|
||||
},
|
||||
{
|
||||
"age": 4,
|
||||
"carrier": "Cellular South",
|
||||
"id": "samsung-gem",
|
||||
"imageUrl": "img/phones/samsung-gem.0.jpg",
|
||||
"name": "Samsung Gem\u2122",
|
||||
"snippet": "The Samsung Gem\u2122 brings you everything that you would expect and more from a touch display smart phone \u2013 more apps, more features and a more affordable price."
|
||||
},
|
||||
{
|
||||
"age": 5,
|
||||
"carrier": "Dell",
|
||||
"id": "dell-venue",
|
||||
"imageUrl": "img/phones/dell-venue.0.jpg",
|
||||
"name": "Dell Venue",
|
||||
"snippet": "The Dell Venue; Your Personal Express Lane to Everything"
|
||||
},
|
||||
{
|
||||
"age": 6,
|
||||
"carrier": "Best Buy",
|
||||
"id": "nexus-s",
|
||||
"imageUrl": "img/phones/nexus-s.0.jpg",
|
||||
"name": "Nexus S",
|
||||
"snippet": "Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet."
|
||||
},
|
||||
{
|
||||
"age": 7,
|
||||
"carrier": "Cellular South",
|
||||
"id": "lg-axis",
|
||||
"imageUrl": "img/phones/lg-axis.0.jpg",
|
||||
"name": "LG Axis",
|
||||
"snippet": "Android Powered, Google Maps Navigation, 5 Customizable Home Screens"
|
||||
},
|
||||
{
|
||||
"age": 8,
|
||||
"id": "samsung-galaxy-tab",
|
||||
"imageUrl": "img/phones/samsung-galaxy-tab.0.jpg",
|
||||
"name": "Samsung Galaxy Tab\u2122",
|
||||
"snippet": "Feel Free to Tab\u2122. The Samsung Galaxy Tab\u2122 brings you an ultra-mobile entertainment experience through its 7\u201d display, high-power processor and Adobe\u00ae Flash\u00ae Player compatibility."
|
||||
},
|
||||
{
|
||||
"age": 9,
|
||||
"carrier": "Cellular South",
|
||||
"id": "samsung-showcase-a-galaxy-s-phone",
|
||||
"imageUrl": "img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg",
|
||||
"name": "Samsung Showcase\u2122 a Galaxy S\u2122 phone",
|
||||
"snippet": "The Samsung Showcase\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance, even outdoors"
|
||||
},
|
||||
{
|
||||
"age": 10,
|
||||
"carrier": "Verizon",
|
||||
"id": "droid-2-global-by-motorola",
|
||||
"imageUrl": "img/phones/droid-2-global-by-motorola.0.jpg",
|
||||
"name": "DROID\u2122 2 Global by Motorola",
|
||||
"snippet": "The first smartphone with a 1.2 GHz processor and global capabilities."
|
||||
},
|
||||
{
|
||||
"age": 11,
|
||||
"carrier": "Verizon",
|
||||
"id": "droid-pro-by-motorola",
|
||||
"imageUrl": "img/phones/droid-pro-by-motorola.0.jpg",
|
||||
"name": "DROID\u2122 Pro by Motorola",
|
||||
"snippet": "The next generation of DOES."
|
||||
},
|
||||
{
|
||||
"age": 12,
|
||||
"carrier": "AT&T",
|
||||
"id": "motorola-bravo-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-bravo-with-motoblur.0.jpg",
|
||||
"name": "MOTOROLA BRAVO\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "An experience to cheer about."
|
||||
},
|
||||
{
|
||||
"age": 13,
|
||||
"carrier": "T-Mobile",
|
||||
"id": "motorola-defy-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg",
|
||||
"name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "Are you ready for everything life throws your way?"
|
||||
},
|
||||
{
|
||||
"age": 14,
|
||||
"carrier": "T-Mobile",
|
||||
"id": "t-mobile-mytouch-4g",
|
||||
"imageUrl": "img/phones/t-mobile-mytouch-4g.0.jpg",
|
||||
"name": "T-Mobile myTouch 4G",
|
||||
"snippet": "The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi."
|
||||
},
|
||||
{
|
||||
"age": 15,
|
||||
"carrier": "US Cellular",
|
||||
"id": "samsung-mesmerize-a-galaxy-s-phone",
|
||||
"imageUrl": "img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg",
|
||||
"name": "Samsung Mesmerize\u2122 a Galaxy S\u2122 phone",
|
||||
"snippet": "The Samsung Mesmerize\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance,even outdoors"
|
||||
},
|
||||
{
|
||||
"age": 16,
|
||||
"carrier": "Sprint",
|
||||
"id": "sanyo-zio",
|
||||
"imageUrl": "img/phones/sanyo-zio.0.jpg",
|
||||
"name": "SANYO ZIO",
|
||||
"snippet": "The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value."
|
||||
},
|
||||
{
|
||||
"age": 17,
|
||||
"id": "samsung-transform",
|
||||
"imageUrl": "img/phones/samsung-transform.0.jpg",
|
||||
"name": "Samsung Transform\u2122",
|
||||
"snippet": "The Samsung Transform\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \u201cSprint ID Service Pack\u201d."
|
||||
},
|
||||
{
|
||||
"age": 18,
|
||||
"id": "t-mobile-g2",
|
||||
"imageUrl": "img/phones/t-mobile-g2.0.jpg",
|
||||
"name": "T-Mobile G2",
|
||||
"snippet": "The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible."
|
||||
},
|
||||
{
|
||||
"age": 19,
|
||||
"id": "motorola-charm-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-charm-with-motoblur.0.jpg",
|
||||
"name": "Motorola CHARM\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "Motorola CHARM fits easily in your pocket or palm. Includes MOTOBLUR service."
|
||||
}
|
||||
]
|
28
5-network/08-xmlhttprequest/phones.view/index.html
Normal file
28
5-network/08-xmlhttprequest/phones.view/index.html
Normal file
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<button onclick="loadPhones()">Load phones.json!</button>
|
||||
|
||||
<script>
|
||||
function loadPhones() {
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open('GET', 'phones.json', false);
|
||||
xhr.send();
|
||||
|
||||
if (xhr.status != 200) {
|
||||
// handle error
|
||||
alert('Error ' + xhr.status + ': ' + xhr.statusText);
|
||||
} else {
|
||||
// show result
|
||||
alert(xhr.responseText);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
155
5-network/08-xmlhttprequest/phones.view/phones.json
Normal file
155
5-network/08-xmlhttprequest/phones.view/phones.json
Normal file
|
@ -0,0 +1,155 @@
|
|||
[
|
||||
{
|
||||
"age": 0,
|
||||
"id": "motorola-xoom-with-wi-fi",
|
||||
"imageUrl": "img/phones/motorola-xoom-with-wi-fi.0.jpg",
|
||||
"name": "Motorola XOOM\u2122 with Wi-Fi",
|
||||
"snippet": "The Next, Next Generation\r\n\r\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb)."
|
||||
},
|
||||
{
|
||||
"age": 1,
|
||||
"id": "motorola-xoom",
|
||||
"imageUrl": "img/phones/motorola-xoom.0.jpg",
|
||||
"name": "MOTOROLA XOOM\u2122",
|
||||
"snippet": "The Next, Next Generation\n\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb)."
|
||||
},
|
||||
{
|
||||
"age": 2,
|
||||
"carrier": "AT&T",
|
||||
"id": "motorola-atrix-4g",
|
||||
"imageUrl": "img/phones/motorola-atrix-4g.0.jpg",
|
||||
"name": "MOTOROLA ATRIX\u2122 4G",
|
||||
"snippet": "MOTOROLA ATRIX 4G the world's most powerful smartphone."
|
||||
},
|
||||
{
|
||||
"age": 3,
|
||||
"id": "dell-streak-7",
|
||||
"imageUrl": "img/phones/dell-streak-7.0.jpg",
|
||||
"name": "Dell Streak 7",
|
||||
"snippet": "Introducing Dell\u2122 Streak 7. Share photos, videos and movies together. It\u2019s small enough to carry around, big enough to gather around."
|
||||
},
|
||||
{
|
||||
"age": 4,
|
||||
"carrier": "Cellular South",
|
||||
"id": "samsung-gem",
|
||||
"imageUrl": "img/phones/samsung-gem.0.jpg",
|
||||
"name": "Samsung Gem\u2122",
|
||||
"snippet": "The Samsung Gem\u2122 brings you everything that you would expect and more from a touch display smart phone \u2013 more apps, more features and a more affordable price."
|
||||
},
|
||||
{
|
||||
"age": 5,
|
||||
"carrier": "Dell",
|
||||
"id": "dell-venue",
|
||||
"imageUrl": "img/phones/dell-venue.0.jpg",
|
||||
"name": "Dell Venue",
|
||||
"snippet": "The Dell Venue; Your Personal Express Lane to Everything"
|
||||
},
|
||||
{
|
||||
"age": 6,
|
||||
"carrier": "Best Buy",
|
||||
"id": "nexus-s",
|
||||
"imageUrl": "img/phones/nexus-s.0.jpg",
|
||||
"name": "Nexus S",
|
||||
"snippet": "Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet."
|
||||
},
|
||||
{
|
||||
"age": 7,
|
||||
"carrier": "Cellular South",
|
||||
"id": "lg-axis",
|
||||
"imageUrl": "img/phones/lg-axis.0.jpg",
|
||||
"name": "LG Axis",
|
||||
"snippet": "Android Powered, Google Maps Navigation, 5 Customizable Home Screens"
|
||||
},
|
||||
{
|
||||
"age": 8,
|
||||
"id": "samsung-galaxy-tab",
|
||||
"imageUrl": "img/phones/samsung-galaxy-tab.0.jpg",
|
||||
"name": "Samsung Galaxy Tab\u2122",
|
||||
"snippet": "Feel Free to Tab\u2122. The Samsung Galaxy Tab\u2122 brings you an ultra-mobile entertainment experience through its 7\u201d display, high-power processor and Adobe\u00ae Flash\u00ae Player compatibility."
|
||||
},
|
||||
{
|
||||
"age": 9,
|
||||
"carrier": "Cellular South",
|
||||
"id": "samsung-showcase-a-galaxy-s-phone",
|
||||
"imageUrl": "img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg",
|
||||
"name": "Samsung Showcase\u2122 a Galaxy S\u2122 phone",
|
||||
"snippet": "The Samsung Showcase\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance, even outdoors"
|
||||
},
|
||||
{
|
||||
"age": 10,
|
||||
"carrier": "Verizon",
|
||||
"id": "droid-2-global-by-motorola",
|
||||
"imageUrl": "img/phones/droid-2-global-by-motorola.0.jpg",
|
||||
"name": "DROID\u2122 2 Global by Motorola",
|
||||
"snippet": "The first smartphone with a 1.2 GHz processor and global capabilities."
|
||||
},
|
||||
{
|
||||
"age": 11,
|
||||
"carrier": "Verizon",
|
||||
"id": "droid-pro-by-motorola",
|
||||
"imageUrl": "img/phones/droid-pro-by-motorola.0.jpg",
|
||||
"name": "DROID\u2122 Pro by Motorola",
|
||||
"snippet": "The next generation of DOES."
|
||||
},
|
||||
{
|
||||
"age": 12,
|
||||
"carrier": "AT&T",
|
||||
"id": "motorola-bravo-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-bravo-with-motoblur.0.jpg",
|
||||
"name": "MOTOROLA BRAVO\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "An experience to cheer about."
|
||||
},
|
||||
{
|
||||
"age": 13,
|
||||
"carrier": "T-Mobile",
|
||||
"id": "motorola-defy-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg",
|
||||
"name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "Are you ready for everything life throws your way?"
|
||||
},
|
||||
{
|
||||
"age": 14,
|
||||
"carrier": "T-Mobile",
|
||||
"id": "t-mobile-mytouch-4g",
|
||||
"imageUrl": "img/phones/t-mobile-mytouch-4g.0.jpg",
|
||||
"name": "T-Mobile myTouch 4G",
|
||||
"snippet": "The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi."
|
||||
},
|
||||
{
|
||||
"age": 15,
|
||||
"carrier": "US Cellular",
|
||||
"id": "samsung-mesmerize-a-galaxy-s-phone",
|
||||
"imageUrl": "img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg",
|
||||
"name": "Samsung Mesmerize\u2122 a Galaxy S\u2122 phone",
|
||||
"snippet": "The Samsung Mesmerize\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance,even outdoors"
|
||||
},
|
||||
{
|
||||
"age": 16,
|
||||
"carrier": "Sprint",
|
||||
"id": "sanyo-zio",
|
||||
"imageUrl": "img/phones/sanyo-zio.0.jpg",
|
||||
"name": "SANYO ZIO",
|
||||
"snippet": "The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value."
|
||||
},
|
||||
{
|
||||
"age": 17,
|
||||
"id": "samsung-transform",
|
||||
"imageUrl": "img/phones/samsung-transform.0.jpg",
|
||||
"name": "Samsung Transform\u2122",
|
||||
"snippet": "The Samsung Transform\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \u201cSprint ID Service Pack\u201d."
|
||||
},
|
||||
{
|
||||
"age": 18,
|
||||
"id": "t-mobile-g2",
|
||||
"imageUrl": "img/phones/t-mobile-g2.0.jpg",
|
||||
"name": "T-Mobile G2",
|
||||
"snippet": "The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible."
|
||||
},
|
||||
{
|
||||
"age": 19,
|
||||
"id": "motorola-charm-with-motoblur",
|
||||
"imageUrl": "img/phones/motorola-charm-with-motoblur.0.jpg",
|
||||
"name": "Motorola CHARM\u2122 with MOTOBLUR\u2122",
|
||||
"snippet": "Motorola CHARM fits easily in your pocket or palm. Includes MOTOBLUR service."
|
||||
}
|
||||
]
|
30
5-network/08-xmlhttprequest/phones.view/server.js
Normal file
30
5-network/08-xmlhttprequest/phones.view/server.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
let http = require('http');
|
||||
let url = require('url');
|
||||
let querystring = require('querystring');
|
||||
let static = require('node-static');
|
||||
let file = new static.Server('.', {
|
||||
cache: 0
|
||||
});
|
||||
|
||||
|
||||
function accept(req, res) {
|
||||
|
||||
if (req.url == '/phones.json') {
|
||||
// stall a bit to let "loading" message show up
|
||||
setTimeout(function() {
|
||||
file.serve(req, res);
|
||||
}, 2000);
|
||||
} else {
|
||||
file.serve(req, res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ------ запустить сервер -------
|
||||
|
||||
if (!module.parent) {
|
||||
http.createServer(accept).listen(8080);
|
||||
} else {
|
||||
exports.accept = accept;
|
||||
}
|
36
5-network/08-xmlhttprequest/post.view/index.html
Normal file
36
5-network/08-xmlhttprequest/post.view/index.html
Normal file
|
@ -0,0 +1,36 @@
|
|||
<!doctype html>
|
||||
<script>
|
||||
(async () {
|
||||
|
||||
const response = await fetch('long.txt');
|
||||
const reader = response.body.getReader();
|
||||
|
||||
const contentLength = +response.headers.get('Content-Length');
|
||||
let receivedLength = 0;
|
||||
let chunks = [];
|
||||
while(true) {
|
||||
const chunk = await reader.read();
|
||||
|
||||
if (chunk.done) {
|
||||
console.log("done!");
|
||||
break;
|
||||
}
|
||||
|
||||
chunks.push(chunk.value);
|
||||
receivedLength += chunk.value.length;
|
||||
console.log(`${receivedLength}/${contentLength} received`)
|
||||
}
|
||||
|
||||
|
||||
let chunksMerged = new Uint8Array(receivedLength);
|
||||
let length = 0;
|
||||
for(let chunk of chunks) {
|
||||
chunksMerged.set(chunk, length);
|
||||
length += chunk.length;
|
||||
}
|
||||
|
||||
let result = new TextDecoder("utf-8").decode(chunksMerged);
|
||||
console.log(result);
|
||||
})();
|
||||
|
||||
</script>
|
58
5-network/08-xmlhttprequest/post.view/server.js
Normal file
58
5-network/08-xmlhttprequest/post.view/server.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
let http = require('http');
|
||||
let url = require('url');
|
||||
let querystring = require('querystring');
|
||||
let static = require('node-static');
|
||||
let file = new static.Server('.', {
|
||||
cache: 0
|
||||
});
|
||||
|
||||
|
||||
function accept(req, res) {
|
||||
|
||||
if (req.method == 'POST') {
|
||||
let chunks = [];
|
||||
let length = 0;
|
||||
|
||||
req.on('data', function (data) {
|
||||
chunks.push(data);
|
||||
length += data.length;
|
||||
|
||||
// More than 10mb, kill the connection!
|
||||
if (length > 1e8) {
|
||||
req.connection.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
req.on('end', function() {
|
||||
// let post = JSON.parse(chunks.join(''));
|
||||
|
||||
if (req.url == '/user') {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ message: 'User saved' }));
|
||||
} else if (req.url == '/image') {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ message: "Image saved", imageSize: length }));
|
||||
} else if (req.url == '/upload') {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ message: "Upload complete", size: length }));
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end("Not found");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
file.serve(req, res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ------ запустить сервер -------
|
||||
|
||||
if (!module.parent) {
|
||||
http.createServer(accept).listen(8080);
|
||||
} else {
|
||||
exports.accept = accept;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue