📜 ⬆️ ⬇️

Expressive JavaScript: Project: Experience Sharing Website

Content




At experience sharing meetings, people with common interests meet and make small informal presentations on their knowledge. At a meeting on the exchange of experience among farmers, anyone can tell about the cultivation of celery. At a meeting of programmers you can make a story about Node.js

Such meetings are a great way to expand your horizons, find out about new products in the region, or just chat with people with similar interests. In many cities there are meetings of fans of JavaScript. Usually their visit is free, and I found those I visited, friendly and hospitable.
')


In the final chapter of the project, we will arrange a website for servicing the speeches that are made at such meetings. Imagine a group of people who meet regularly in the office of one of the participants to talk about monocycles. The problem is that when the previous meeting organizer moved to another city, no one took his place. We need a system that allows participants to propose and discuss topics with each other, without the participation of the organizer.

Monocyclist meetings


As in the previous chapter, the code is written for Node.js and will not work in the browser. The full code is available here .

Design


The project has a server part written for Node.js, and a client part written for the browser. The server stores system data and transfers it to the client. It also provides HTML and JavaScript files that create the system on the client side.

The server has a list of topics for the next meeting, and the client shows them. Each topic has a speaker name, a title, a description, and a list of comments. The client allows you to suggest new topics (add them to the list), delete topics and comment on existing ones. When a user makes this change, the client makes an HTTP request to inform the server of this.



An application will be created to display the current topic suggestions and comments on them. When someone adds a new topic somewhere or leaves a comment, all people who open the page in the browser, changes should occur instantly. This is not an easy task, because the web server cannot open a connection to the client, and because there is no suitable way to find out which client is currently browsing this website.

A common solution to the problem is long queries (long polling), which served as one of the motivations for the development of Node.

Long queries


To instantly notify the client about the changes, we need a connection to the client. Browsers traditionally do not accept connection requests, and clients are still hidden behind devices that these connections would not accept, so there is no point in starting a connection from the server.

You can make the client open the connection and hold it so that the server can send information through it as needed.

But the HTTP request allows only simple information exchange - the client sends the request, the server returns a response, that's all. There is a technology called web sockets, which is supported by modern browsers, which allows you to open connections to exchange arbitrary data. But they are quite difficult to use.

In this chapter, we turn to relatively simple technology, long requests, when customers constantly request the server for new information via regular HTTP requests, and the server is just slow in responding when it has nothing to say.

As long as the client constantly keeps an open request, it will receive information from the server immediately. For example, if Alice has an application for sharing experience open in the browser, the browser will make a request for updates and will wait for a response. When Bob sends the “Extreme descent on a monocycle from the mountain” theme from his browser, the server will notice that Alice is waiting for updates and will send information on a new topic in response to her pending request. Alice's browser will receive the data and refresh the page by displaying a new topic.

To prevent connections from terminating by timeout (inactive connections are terminated after the time expires), the technology of long queries usually sets the maximum time for each request after which the server will respond in any case, even if it has nothing to inform, and then the client will launch a new request. Periodic updating of the request makes the technique more reliable, allowing clients to recover from temporary breaks or server problems.

A busy server that uses long requests can have thousands of requests open, and therefore TCP connections. Node is well suited for such a system, because it allows you to easily manage many connections without creating separate streams.

HTTP interface


Before we start making the server or client, let's think about their point of contact: the HTTP interface through which they interact.

The interface will be based on JSON, and, as in the file server in Chapter 20, we will profitably use HTTP methods. The interface is centered around the way / talks. Paths that do not begin with / talks will be used to upload static files - HTML and JavaScript, defining the client part.

A GET request to / talks returns a JSON document like this:

{"serverTime": 1405438911833, "talks": [{"title": "Unituning ", "presenter": "", "summary": "  ", "comment": []}]} 


The serverTime field is used to make long requests reliable. Let's return to it later.

Creating a new topic occurs through a PUT request to the URL of the form / talks / Unituning, where the part after the second slash is the name of the topic. The body of the PUT request must contain a JSON object, which describes the properties of the presenter and summary.

Since the subject headings may contain spaces and other characters that cannot be inserted into the URL, they must be encoded using the encodeURIComponent function when creating the URL.

 console.log("/talks/" + encodeURIComponent("How to Idle")); // → /talks/How%20to%20Idle 


A request to create a topic might look like

PUT / talks / How% 20to% 20Idle HTTP / 1.1
Content-Type: application / json
Content-Length: 92

{"Presenter": "Dasha",
"Summary": "Standing still on the monocycle"}

These URLs support GET requests to get a JSON representation of a topic and a DELETE to delete a topic.

Adding a comment occurs through a POST request to the URL of the form / talks / Unituning / comments, with a JSON object containing the author and message properties in the request body.

POST / talks / Unituning / comments HTTP / 1.1
Content-Type: application / json
Content-Length: 72

{"Author": "Alice",
"Message": "Will you talk about raising a cycle?"}

To support long requests, GET requests to / talks can include a parameter called changesSince, indicating that the client needs updates that have occurred after a given point in time. When updates appear, they are immediately returned. When there are none, the request is delayed until something happens, or until a specified period of time passes (we set 90 seconds).

Time is used in the format of the number of milliseconds since the beginning of 1970, in the same format that Date.now () returns. To make sure that the client receives all updates, and does not receive the same update twice, the client must transmit the time at which he last received information from the server. The server clock may not coincide with the client, and even if they coincided, the client would not know the exact time at which the server sent the response, because data transmission over the network takes time.

Therefore, in responses to GET requests to / talks, there is a serverTime property. It tells the client the exact time on the server’s clock when the transmitted data was created. The client simply saves the time and sends it along with the following request to make sure that he receives only those updates that he has not yet received.

GET / talks? ChangesSince = 1405438911833 HTTP / 1.1

(time passed)

HTTP / 1.1 200 OK
Content-Type: application / json
Content-Length: 95

{"ServerTime": 1405438913401,
"Talks": [{"title": "Unituning",
"Deleted": true}]}

When a topic changes, is created or commented, in response to the next request, full information about the topic is included. When a topic is deleted, only the name and property of the deleted are included. The client can add topics with headers that he has not yet seen to the page, update topics that he already shows, and delete topics that have been deleted.

The protocol described in this chapter does not control access. Everyone can comment, change and delete topics. Since the Internet is full of hooligans, placing such a system online without protection is likely to end badly.

A simple solution would be to place the system behind a reverse proxy — this is an HTTP server that accepts connections from outside the system and redirects them to HTTP servers that are running locally. Such a proxy can be configured to ask the user name and password, and you could arrange that only members of your group have the password.

Server


Let's start by writing the server part of the program. The code works on Node.js

Routing


To start the server will be used http.createServer. In the function that processes the new request, we must distinguish between the requests (determined by the method and the way) that we support. This can be done through a long if / else chain, but it can also be more beautiful.

A router is a component that helps distribute a request to a function that can process it. You can tell the router that PUT requests with a path that matches the regular schedule / ^ \ / talks \ / ([^ \ /] +) $ / (which is the same as / talks /, followed by the name of the topic) can be processed by a given function . In addition, it can help extract meaningful parts of the path, in our case - the name of the topic, enclosed in quotes, and pass them to the auxiliary function.

There are many good routing modules in NPM, but here we will write this ourselves to demonstrate how it works.

Here is the file router.js, which will be requested through require from the server module:

 var Router = module.exports = function() { this.routes = []; }; Router.prototype.add = function(method, url, handler) { this.routes.push({method: method, url: url, handler: handler}); }; Router.prototype.resolve = function(request, response) { var path = require("url").parse(request.url).pathname; return this.routes.some(function(route) { var match = route.url.exec(path); if (!match || route.method != request.method) return false; var urlParts = match.slice(1).map(decodeURIComponent); route.handler.apply(null, [request, response] .concat(urlParts)); return true; }); }; 


The module exports the Router constructor. The router object allows you to register new handlers with the add method, and distribute requests with the resolve method.

The latter returns a boolean value indicating whether a handler was found. The some method of the array of paths will try them in turn (in the order in which they were specified), and stop with returning true if the path is found.

Handler functions are called with request and response objects. When the regular URL checker returns groups, the strings representing them are passed to the handler as additional arguments. These lines need to be decoded from the URL-style% 20.

File delivery


When the type of request does not match any of the types that the router processes, the server should interpret it as a file request from a common directory. It would be possible to use the file server from Chapter 20 for issuing these files, but we do not need PUT and DELETE support, but we need additional functions such as caching support. Therefore, let's use a proven and tested NPM file server.

I chose ecstatic. This is not the only NPM server, but it works well and meets our requirements. The ecstatic module exports a function that can be called with a configuration object so that it issues a handler function. We use the root option to tell the server where to look for files. The handler takes the request and response parameters, and you can pass it directly to createServer to create a server that gives only files. But first we need to check the requests that we process especially - so we wrap it in another function.

 var http = require("http"); var Router = require("./router"); var ecstatic = require("ecstatic"); var fileServer = ecstatic({root: "./public"}); var router = new Router(); http.createServer(function(request, response) { if (!router.resolve(request, response)) fileServer(request, response); }).listen(8000);  respond  respondJSON    ,        . function respond(response, status, data, type) { response.writeHead(status, { "Content-Type": type || "text/plain" }); response.end(data); } function respondJSON(response, status, data) { respond(response, status, JSON.stringify(data), "application/json"); } 


Topics as resources


The server stores the proposed topics in the talks object, in which the names of the properties are the names of the topics. They will look like HTTP resources at / talks / [title], so we need to add to the router handlers that implement various methods that clients can use to work with them.

The handler for GET requests for one topic should find it and either return the data to JSON or generate a 404 error.

 var talks = Object.create(null); router.add("GET", /^\/talks\/([^\/]+)$/, function(request, response, title) { if (title in talks) respondJSON(response, 200, talks[title]); else respond(response, 404, "No talk '" + title + "' found"); });       talks. router.add("DELETE", /^\/talks\/([^\/]+)$/, function(request, response, title) { if (title in talks) { delete talks[title]; registerChange(title); } respond(response, 204, null); }); 


The registerChange function, which we will define later, notifies long requests for changes.

To make it easy to receive the content of request bodies encoded with JSON, we define the readStreamAsJSON function, which reads the entire contents of the stream, parses it according to JSON rules, and then makes a callback.

 function readStreamAsJSON(stream, callback) { var data = ""; stream.on("data", function(chunk) { data += chunk; }); stream.on("end", function() { var result, error; try { result = JSON.parse(data); } catch (e) { error = e; } callback(error, result); }); stream.on("error", function(error) { callback(error); }); } 


One of the handlers that needs to read responses in JSON is the PUT handler, which is used to create new topics. It must check if the data has the properties presenter and summary, which should be strings. Data coming from outside can always turn out to be garbage, and we don’t want our system to be broken due to a bad query.

If the data looks acceptable, the handler stores the object representing the new topic in the talks object, while possibly overwriting the existing topic with the same title, and then calls registerChange again.

 router.add("PUT", /^\/talks\/([^\/]+)$/, function(request, response, title) { readStreamAsJSON(request, function(error, talk) { if (error) { respond(response, 400, error.toString()); } else if (!talk || typeof talk.presenter != "string" || typeof talk.summary != "string") { respond(response, 400, "Bad talk data"); } else { talks[title] = {title: title, presenter: talk.presenter, summary: talk.summary, comments: []}; registerChange(title); respond(response, 204, null); } }); }); 


Adding a comment to a topic works in a similar way. We use readStreamAsJSON to get the content of the message, check the resulting data and save it as a comment if it is acceptable.

 router.add("POST", /^\/talks\/([^\/]+)\/comments$/, function(request, response, title) { readStreamAsJSON(request, function(error, comment) { if (error) { respond(response, 400, error.toString()); } else if (!comment || typeof comment.author != "string" || typeof comment.message != "string") { respond(response, 400, "Bad comment data"); } else if (title in talks) { talks[title].comments.push(comment); registerChange(title); respond(response, 204, null); } else { respond(response, 404, "No talk '" + title + "' found"); } }); }); 


Attempting to add a comment to a non-existent topic should return a 404 error.

Long query support


The most interesting aspect of the server is the part that supports long queries. When a GET request is sent to the address / talks, it can be a simple request for all topics, or an update request with the changeSince parameter.

There are many different situations in which we need to send a list of topics to the client, so we first define a helper function that attaches the serverTime field to such answers.

 function sendTalks(talks, response) { respondJSON(response, 200, { serverTime: Date.now(), talks: talks }); } 


The handler should look at all the request parameters in its URL in order to check if the changesSince parameter is set. If you give the parse function of the “url” module the second argument of the value true, it also parses the second part of the URL - query, part of the query. The returned object will have a query property, in which there will be another object with the names and values ​​of the parameters.

 router.add("GET", /^\/talks$/, function(request, response) { var query = require("url").parse(request.url, true).query; if (query.changesSince == null) { var list = []; for (var title in talks) list.push(talks[title]); sendTalks(list, response); } else { var since = Number(query.changesSince); if (isNaN(since)) { respond(response, 400, "Invalid parameter"); } else { var changed = getChangedTalks(since); if (changed.length > 0) sendTalks(changed, response); else waitForChanges(since, response); } } }); 


If there is no changesSince parameter, the handler simply builds a list of all the themes and returns it.

Otherwise, you first need to check the changeSince parameter to see if it is a number. The getChangedTalks function, which we will soon define, returns an array of modified topics from a certain specified time. If it returns an empty array, then the server has nothing to return to the client, so it saves the response object (with waitForChanges) to respond later.

 var waiting = []; function waitForChanges(since, response) { var waiter = {since: since, response: response}; waiting.push(waiter); setTimeout(function() { var found = waiting.indexOf(waiter); if (found > -1) { waiting.splice(found, 1); sendTalks([], response); } }, 90 * 1000); } 


The splice method is used to cut a piece of an array. It is given an index and the number of elements, and it changes the array, removing this number of elements after a given index. In this case, we delete one element - the object waiting for an answer, whose index we learned through indexOf. If you pass additional arguments in splice, their values ​​will be inserted into the array at the specified position, and will replace the removed elements.

When the response object is stored in the waiting array, a timeout is set. After 90 seconds, it checks to see if another request is waiting, and if so, it sends an empty response and removes it from the waiting array.

To find exactly those topics that have changed after a given time, we need to track the history of changes. Registering a change with registerChange will remember this change, along with the current time, in the changes array. When a change happens, it means that there is new data, so you can immediately respond to all pending requests.

 var changes = []; function registerChange(title) { changes.push({title: title, time: Date.now()}); waiting.forEach(function(waiter) { sendTalks(getChangedTalks(waiter.since), waiter.response); }); waiting = []; } 


Finally, getChangedTalks uses the changes array to build an array of changed topics, including objects with the deleted property for those that no longer exist. When building an array, getChangedTalks must make sure that the same topic is not included twice, since the topic could have changed several times from a given point in time.

 function getChangedTalks(since) { var found = []; function alreadySeen(title) { return found.some(function(f) {return f.title == title;}); } for (var i = changes.length - 1; i >= 0; i--) { var change = changes[i]; if (change.time <= since) break; else if (alreadySeen(change.title)) continue; else if (change.title in talks) found.push(talks[change.title]); else found.push({title: change.title, deleted: true}); } return found; } 


That's it with the server code. Running the written code will give you a server running on port 8000, which issues files from a public subdirectory and manages the interface on the / talks address.

Customer


The client side of the theme management website consists of three files: an HTML page, a style sheet, and a JavaScript file.

HTML


Servers according to the standard scheme in the case of a request for the path corresponding to the directory, give the file under the name index.html from this directory. The ecstatic file server module supports this convention. When a path is requested, the server looks for the ./public/index.html file (where ./public is the root directory) and returns it if it is there.

So, if you need to show the page, when the browser requests our server, it should be put in public / index.html. Here is the beginning of the index file:

 <!doctype html> <title> </title> <link rel="stylesheet" href="skillsharing.css"> <h1> </h1> <p> : <input type="text" id="name"></p> <div id="talks"></div> 


The title is determined and a style sheet is included, where styles are defined — among other things, a frame around topics. Then the title and the name field are added. The user must enter his name so that it is attached to his topics and comments.

Element
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
   ID “talks”    .   ,      . 

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?
ID “talks” . , .

.

<form id="newtalk"> <h3>Submit a talk</h3> : <input type="text" style="width: 40em" name="title"> <br> Summary: <input type="text" style="width: 40em" name="summary"> <button type="submit"> </button> </form>


“submit” , HTTP-, .

, display none, . , ?

<div id="template" style="display: none"> <div class="talk"> <h2>{{title}}</h2> <div>by <span class="name">{{presenter}}</span></div> <p>{{summary}}</p> <div class="comments"></div> <form> <input type="text" name="comment"> <button type="submit"> </button> <button type="button" class="del"> </button> </form> </div> <div class="comment"> <span class="name">{{author}}</span>: {{message}} </div> </div>

DOM JavaScript . elt 13, , HTML, - DOM-.

DOM- , , , , – .

, HTML , .

<script src="skillsharing_client.js"></script>


, , . HTTP-, XMLHttpRequest, .

function request(options, callback) { var req = new XMLHttpRequest(); req.open(options.method || "GET", options.pathname, true); req.addEventListener("load", function() { if (req.status < 400) callback(null, req.responseText); else callback(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(new Error("Network error")); }); req.send(options.body || null); }

, waitForChanges.

var lastServerTime = 0; request({pathname: "talks"}, function(error, response) { if (error) { reportError(error); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } });

lastServerTime , . , , . , serverTime, , lastServerTime.

, , . reportError, , .

function reportError(error) { if (error) alert(error.toString()); }

, , . , , . , .


, , . , , , . , , DOM .

displayTalks , . shownTalks, DOM, , .

var talkDiv = document.querySelector("#talks"); var shownTalks = Object.create(null); function displayTalks(talks) { talks.forEach(function(talk) { var shown = shownTalks[talk.title]; if (talk.deleted) { if (shown) { talkDiv.removeChild(shown); delete shownTalks[talk.title]; } } else { var node = drawTalk(talk); if (shown) talkDiv.replaceChild(node, shown); else talkDiv.appendChild(node); shownTalks[talk.title] = node; } }); }

DOM , HTML . instantiateTemplate, .

name – . , , , ID “template”. querySelector . “talk” “comment”.

function instantiateTemplate(name, values) { function instantiateText(text) { return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { return values[name]; }); } function instantiate(node) { if (node.nodeType == document.ELEMENT_NODE) { var copy = node.cloneNode(); for (var i = 0; i < node.childNodes.length; i++) copy.appendChild(instantiate(node.childNodes[i])); return copy; } else if (node.nodeType == document.TEXT_NODE) { return document.createTextNode( instantiateText(node.nodeValue)); } } var template = document.querySelector("#template ." + name); return instantiate(template); }

cloneNode, DOM, . , true. instantiate , .

instantiateTemplate , , . {{title}} “title”.

, drawTalk .

function drawTalk(talk) { var node = instantiateTemplate("talk", talk); var comments = node.querySelector(".comments"); talk.comments.forEach(function(comment) { comments.appendChild( instantiateTemplate("comment", comment)); }); node.querySelector("button.del").addEventListener( "click", deleteTalk.bind(null, talk.title)); var form = node.querySelector("form"); form.addEventListener("submit", function(event) { event.preventDefault(); addComment(talk.title, form.elements.comment.value); form.reset(); }); return node; }

“talk” . -, , “comment” "comments". , , , .


, drawTalk, deleteTalk addComment , . URL, , talkURL.

function talkURL(title) { return "talks/" + encodeURIComponent(title); }

deleteTalk DELETE .

function deleteTalk(title) { request({pathname: talkURL(title), method: "DELETE"}, reportError); }

JSON POST-.

function addComment(title, comment) { var comment = {author: nameField.value, message: comment}; request({pathname: talkURL(title) + "/comments", body: JSON.stringify(comment), method: "POST"}, reportError); }

nameField, author, , . localStorage, .

var nameField = document.querySelector("#name"); nameField.value = localStorage.getItem("name") || ""; nameField.addEventListener("change", function() { localStorage.setItem("name", nameField.value); }); “submit”. ( ), PUT- . var talkForm = document.querySelector("#newtalk"); talkForm.addEventListener("submit", function(event) { event.preventDefault(); request({pathname: talkURL(talkForm.elements.title.value), method: "PUT", body: JSON.stringify({ presenter: nameField.value, summary: talkForm.elements.summary.value })}, reportError); talkForm.reset(); });



, , , , , , . - , .

, displayTalks , , .

function waitForChanges() { request({pathname: "talks?changesSince=" + lastServerTime}, function(error, response) { if (error) { setTimeout(waitForChanges, 2500); console.error(error.stack); } else { response = JSON.parse(response); displayTalks(response.talks); lastServerTime = response.serverTime; waitForChanges(); } }); }

, , , , . , reportError, . ( ), 2.5 .

, , lastServerTime , , . , .

, localhost:8000, , , , .


, . , , Node.js .


. , .

, . , .


, DOM , . . - , , , , .

, , . , ?


, . , if, , .

, ("comment") . “talk”, , comments, , , .

:

<div class="comments"> <div class="comment" template-repeat="comments"> <span class="name">{{author}}</span>: {{message}} </div> </div>

: template-repeat, , , , , . ( values instantiateTemplate) , {{author}} comment, .

instantiateTemplate , , , , drawTalk.

, , true false?

?
- JavaScript, . - .

- JavaScript. , . .

, - JavaScript. , . , .

. . , , ?

Source: https://habr.com/ru/post/246331/


All Articles