表現力豊かなJavaScript:プロジェクト:経験共有Webサイト

内容







経験を交換するための会議では、共通の関心を持つ人々が集まり、彼らの知識をテーマに小さな非公式のプレゼンテーションを行います。 ピアツーピア交換会議では、誰かがセロリの成長について話すことができます。 プログラマーの会議で、Node.jsについて話すことができます



このような会議は、視野を広げたり、地域の最新情報を調べたり、同様の関心を持つ人々とチャットしたりするのに最適な方法です。 多くの都市でJavaScriptの愛好家とのミーティングがあります。 通常、彼らの訪問は無料であり、私は訪問し、フレンドリーで歓迎してくれる人を見つけました。







プロジェクトの最後の章では、そのような会議で行われるスピーチを提供するウェブサイトをセットアップします。 参加者の1人のオフィスで定期的に集まり、一輪車について話す人々のグループを想像してください。 問題は、前の会議の主催者が別の都市に移動したときに、誰も彼の代わりにならなかったことです。 主催者の参加なしに、参加者が互いにトピックを提案し、議論できるシステムが必要です。



一輪車会議



前の章と同様に、コードはNode.js向けに記述されており、ブラウザーでは機能しません。 完全なコードはこちらから入手できます



設計



このプロジェクトには、Node.js用に記述されたサーバー部分とブラウザー用に記述されたクライアント部分があります。 サーバーはシステムデータを保存し、クライアントに渡します。 また、クライアント側でシステムを作成するHTMLおよびJavaScriptファイルも提供します。



サーバーには次の会議のトピックのリストがあり、クライアントはそれらを表示します。 各トピックには、スピーカーの名前、タイトル、説明、コメントのリストがあります。 クライアントを使用すると、新しいトピックを提案(リストに追加)したり、トピックを削除したり、既存のトピックにコメントしたりできます。 ユーザーがこの変更を行うと、クライアントはこれをサーバーに報告するHTTP要求を作成します。







現在のトピックの提案とそれらに対するコメントを表示するアプリケーションが作成されます。 誰かが新しいトピックを追加したり、コメントを残したりすると、ブラウザーでページを開くすべてのユーザーに対して、即座に変更が行われます。 これは、Webサーバーがクライアントとの接続を開くことができず、現在このWebサイトを表示しているクライアントを見つける良い方法がないため、簡単なタスクではありません。



この問題に対する一般的に受け入れられている解決策は、長いポーリングであり、これはNodeを開発する動機の1つとして役立ちました。



長いクエリ



変更を即座にクライアントに通知するには、クライアントとの接続が必要です。 ブラウザは従来、接続要求を受け入れず、クライアントは依然としてこれらの接続を受け入れないデバイスの背後に隠されているため、サーバーから接続を開始しても意味がありません。



クライアントが接続を開いて保持し、サーバーが必要に応じて情報を送信できるようにすることができます。



ただし、HTTPリクエストでは、情報の単純な交換しか許可されていません。クライアントがリクエストを送信し、サーバーが応答を返すだけです。 Webソケットと呼ばれるテクノロジーがあり、これは最新のブラウザーでサポートされており、任意のデータの交換のために接続を開くことができます。 しかし、それらの使用は非常に困難です。



この章では、クライアントが通常のHTTPリクエストを介して常に新しい情報をサーバーに要求し、サーバーが何も伝える必要がない場合に応答を残している、比較的単純な技術である長いリクエストについて説明します。



クライアントは常にオープンリクエストを保持しますが、すぐにサーバーから情報を受け取ります。 たとえば、Aliceがブラウザーでエクスペリエンスを共有するためのアプリケーションを持っている場合、ブラウザーは更新を要求し、応答を待ちます。 ボブがブラウザから「山から一輪車に乗って極端な降下」というテーマを送信すると、サーバーはアリスが更新を待っていることに気付き、彼女の待機中の要求に応じて新しいトピックに関する情報を送信します。 アリスのブラウザはデータを受信して​​ページを更新し、新しいトピックを表示します。



接続がタイムアウトするのを防ぐため(時間の経過後に非アクティブな接続が切断される)、通常、長い要求テクノロジは各要求の最大時間を設定します。その後、報告するものがなくても、サーバーは応答し、クライアントは新しい要求を開始します。 要求を定期的に更新することにより、この手法の信頼性が高まり、クライアントがサーバー上の一時的な中断や問題から回復できるようになります。



長いクエリを使用するビジーなサーバーでは、数千の要求が開かれ、その結果、TCP接続が発生する可能性があります。 Nodeは、個別のスレッドを作成せずに多くの接続を簡単に管理できるため、このようなシステムに適しています。



HTTPインターフェース



サーバーまたはクライアントの作成を開始する前に、それらの共通点、つまりそれらが対話するHTTPインターフェイスについて考えます。



インターフェイスはJSONに基づいており、第20章のファイルサーバーのように、HTTPメソッドを有益に使用します。 インターフェイスは、/ talksパスを中心にしています。 / talksで始まらないパスは、静的ファイル(クライアント側を定義するHTMLおよびJavaScript)のレンダリングに使用されます。



/ talksへのGETリクエストは、次のようなJSONドキュメントを返します。



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







serverTimeフィールドは、長いクエリの信頼性のために使用されます。 後で彼に戻ってきます。



新しいトピックの作成は、フォーム/ talks / UnituningのURLへのPUTリクエストを介して行われます。2番目のスラッシュの後の部分はトピックの名前です。 PUTリクエストの本文には、プレゼンタープロパティとサマリープロパティを記述するJSONオブジェクトが含まれている必要があります。



トピックヘッダーには、URLに挿入できないスペースやその他の文字を含めることができるため、URLを作成する場合、encodeURIComponent関数を使用してエンコードする必要があります。



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







トピック作成リクエストは次のようになります。



PUT / talks / How%20to%20Idle HTTP / 1.1

コンテンツタイプ:アプリケーション/ json

コンテンツの長さ:92



{「プレゼンター」:「ダシャ」、

「要約」:「一輪車では動かない」}



これらのURLは、トピックのJSON表現を取得するGETリクエストと、トピックを削除するDELETEをサポートしています。



コメントの追加は、POSTリクエストを通じて、フォーム/ talks / Unituning / commentsのURLへのリクエストと、リクエスト本文に作成者とメッセージプロパティを含むJSONオブジェクトを使用して行われます。



POST /トーク/ Unituning /コメントHTTP / 1.1

コンテンツタイプ:アプリケーション/ json

コンテンツの長さ:72



{「著者」:「アリス」、

「メッセージ」:「サイクルを上げることについて話してくれますか?」}



長いリクエストをサポートするために、GETリクエストへの/ talksには、changesSinceと呼ばれるパラメーターを含めることができます。これは、クライアントが特定の時点以降に発生する更新を必要とすることを示します。 アップデートが表示されると、すぐに戻ります。 存在しない場合、何かが発生するまで、または指定された期間が経過するまで(90秒を設定します)、要求は遅延されます。



Date.now()が返す形式と同じ形式で、1970年の初めからミリ秒数の形式で時間が使用されています。 クライアントがすべての更新を受信し、同じ更新を2回受信しないようにするために、クライアントはサーバーから最後に情報を受信した時刻を転送する必要があります。 サーバーのクロックはクライアントと一致しない場合があり、一致した場合でも、ネットワークを介したデータ転送には時間がかかるため、クライアントはサーバーが応答を送信した正確な時間を知ることができません。



したがって、GET要求への応答/ talksには、serverTimeプロパティが存在します。 送信されたデータが作成されたときのサーバークロックの正確な時刻をクライアントに伝えます。 クライアントは単に時間を節約し、次のリクエストと一緒に渡して、まだ受け取っていない更新のみを受け取るようにします。



GET / talks?ChangesSince = 1405438911833 HTTP / 1.1



(時間が経ちました)



HTTP / 1.1 200 OK

コンテンツタイプ:アプリケーション/ json

コンテンツの長さ:95



{「ServerTime」:1405438913401、

「トーク」:[{"タイトル": "ユニット化"、

「削除済み」:true}]}



トピックが変更、作成、またはコメントされると、次の要求に応じて、トピックに関する完全な情報が含まれます。 トピックが削除されると、削除された名前とプロパティのみが含まれます。 クライアントは、まだページに表示されていない見出しを持つトピックを追加したり、既に表示しているトピックを更新したり、削除されたトピックを削除したりできます。



この章で説明するプロトコルは、アクセスを制御しません。 誰でもトピックをコメント、変更、削除できます。 インターネットはフーリガンでいっぱいなので、そのようなシステムを保護せずにオンラインにすると、ひどく終了する可能性があります。



簡単な解決策は、システムをリバースプロキシの背後に配置することです。これは、システムの外部からの接続を受け入れ、ローカルで動作するHTTPサーバーにリダイレクトするHTTPサーバーです。 このようなプロキシは、ユーザー名とパスワードを要求するように構成できます。また、グループのメンバーのみがパスワードを持つように調整できます。



サーバー



プログラムのサーバー側を書くことから始めましょう。 Node.jsで動作するコード



ルーティング



サーバーを起動するには、http.createServerが使用されます。 新しいリクエストを処理する関数では、サポートするリクエスト(メソッドと方法によって決定される)を区別する必要があります。 これは、長いif / elseチェーンを介して実行できますが、きれいにすることもできます。



ルーターは、要求を処理できる機能に要求を配布するのに役立つコンポーネントです。 ルーターに、PUTが通常の順序と一致するパスで要求することを伝えることができます。 さらに、パスの意味のある部分、ここでは引用符で囲まれたトピックの名前を抽出し、それらを補助関数に渡すと役立ちます。



NPMには多くの優れたルーティングモジュールがありますが、ここでは、その動作の原理を示すためにこれを記述します。



サーバーモジュールからrequireを介して要求されるrouter.jsファイルは次のとおりです。



 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; }); };
      
      







モジュールは、ルーターコンストラクターをエクスポートします。 ルーターオブジェクトを使用すると、addメソッドで新しいハンドラーを登録し、resolveメソッドでリクエストを配布できます。



後者は、ハンドラが見つかったかどうかを示すブール値を返します。 パスの配列のメソッドは、一度に1つずつ(指定された順序で)試行し、パスが見つかった場合にtrueを返すのを停止します。



ハンドラー関数は、要求オブジェクトと応答オブジェクトで呼び出されます。 通常のURLチェックグループがグループを返す場合、それらを表す文字列は追加の引数としてハンドラーに渡されます。 これらの行は、URL style%20からデコードする必要があります。



ファイル出力



要求タイプがルーターが処理するタイプのいずれとも一致しない場合、サーバーはそれを共有ディレクトリからのファイル要求として解釈する必要があります。 第20章のファイルサーバーを使用してこれらのファイルを発行できますが、PUTおよびDELETEのサポートは必要ありませんが、キャッシュのサポートなどの追加機能が必要です。 したがって、実績のあるテスト済みのNPMのファイルサーバーを使用しましょう。



とした。 これはNPM上の唯一のサーバーではありませんが、うまく機能し、要件を満たしています。 ecstaticモジュールは、ハンドラー関数を返すように、構成オブジェクトで呼び出すことができる関数をエクスポートします。 rootオプションを使用して、ファイルを探す場所をサーバーに伝えます。 ハンドラーは要求パラメーターと応答パラメーターを受け入れ、ファイルを送信するだけのサーバーを作成するためにcreateServerに直接渡すことができます。 ただし、最初に、特に処理する要求を確認する必要があります。そのため、別の関数でラップします。



 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"); }
      
      







リソースとしてのテーマ



サーバーは、提案されたトピックをtalksオブジェクトに格納します。talksオブジェクトのプロパティ名はトピック名です。 それらは/ talks / [title]のHTTPリソースのように見えるため、クライアントがそれらを操作するために使用できるさまざまなメソッドを実装するハンドラーをルーターに追加する必要があります。



1つのトピックのGETリクエストのハンドラーはそれを見つけ、JSONでデータを返すか、404エラーを返します。



 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); });
      
      







後で定義するregisterChange関数は、変更の長い要求を通知します。



JSONを使用してエンコードされたリクエスト本文のコンテンツを取得するには、ストリームのコンテンツ全体を読み取り、JSONルールに従って解析してからコールバックを行うreadStreamAsJSON関数を定義します。



 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); }); }
      
      







JSONで応答を読み取る必要があるハンドラーの1つは、新しいトピックを作成するために使用されるPUTハンドラーです。 データに文字列であるプレゼンターとサマリープロパティがあるかどうかを確認する必要があります。 外部からのデータは常にゴミになる可能性があり、不適切なリクエストのためにシステムが破損することは望ましくありません。



データが受け入れられるように見える場合、ハンドラーはtalkオブジェクトに新しいトピックを表すオブジェクトを保存しますが、既存のトピックを同じ見出しで上書きし、registerChangeを再度呼び出します。



 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); } }); });
      
      







トピックにコメントを追加する方法も同様です。 readStreamAsJSONを使用して、メッセージの内容を取得し、結果のデータを確認し、受け入れ可能な場合はコメントとして保存します。



 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"); } }); });
      
      







存在しないトピックにコメントを追加しようとすると、404エラーが返されます。



長いリクエストのサポート



サーバーの最も興味深い点は、長いクエリをサポートする部分です。 GET要求が/ talksに到着すると、すべてのトピックに対する単純な要求、またはchangesSinceパラメーターを使用した更新要求になります。



トピックのリストをクライアントに送信する必要がある状況はさまざまであるため、最初にserverTimeフィールドをそのような回答に添付するヘルパー関数を定義します。



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







ハンドラーは、URL内のすべての要求パラメーターを調べて、changesSinceパラメーターが設定されているかどうかを確認する必要があります。 「url」モジュールの解析関数にtrueの2番目の引数を指定すると、URLの2番目の部分(クエリ、リクエストの一部)も解析されます。 返されたオブジェクトにはクエリプロパティがあり、その中にパラメータの名前と値を含む別のオブジェクトがあります。



 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); } } });
      
      







changesSinceパラメーターがない場合、ハンドラーはすべてのトピックのリストを作成して返します。



それ以外の場合は、最初にchangeSinceパラメーターをチェックして、数値であるかどうかを確認する必要があります。 すぐに定義するgetChangedTalks関数は、指定された時間から変更されたトピックの配列を返します。 空の配列を返す場合、サーバーはクライアントに返すものがないため、後で応答するために(waitForChangesを使用して)応答オブジェクトを保存します。



 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); }
      
      







spliceメソッドは、配列の一部を切り取るために使用されます。 彼はインデックスと要素数を与えられ、彼は配列を変更し、与えられたインデックスの後のこの要素数を削除します。 この場合、1つの要素、つまりindexOfを介して認識されたインデックスを持つ回答を待っているオブジェクトを削除します。 追加の引数をspliceに渡すと、それらの値は指定された位置で配列に挿入され、削除された要素を置き換えます。



応答オブジェクトが待機配列に格納されると、タイムアウトが設定されます。 90秒後、別の要求が待機しているかどうかを確認し、待機している場合は、空の応答を送信して待機中の配列から削除します。



一定時間後に変更されたトピックを正確に見つけるには、変更の履歴を追跡する必要があります。 registerChangeを使用して変更を登録すると、この変更が現在の時刻とともに変更配列に記憶されます。 変更が発生すると、新しいデータが存在するため、保留中のすべての要求にすぐに回答できます。



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







最後に、getChangedTalksは変更配列を使用して、変更されたトピックの配列を作成します。これには、既に存在しないものの削除されたプロパティを持つオブジェクトが含まれます。 配列を構築するとき、getChangedTalksは同じテーマが2回オンにならないようにします。これは、特定の時点からテーマが数回変更される可能性があるためです。



 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; }
      
      







それはすべてサーバーコードにあります。 記述されたコードを実行すると、ポート8000​​で実行されているサーバーが提供されます。サーバーは、パブリックサブディレクトリからファイルを出力し、/ talksのインターフェイスを制御します。



お客様



テーマ管理Webサイトのクライアント側は、HTMLページ、スタイルシート、およびJavaScriptファイルの3つのファイルで構成されています。



HTML



一般に受け入れられている方式によるサーバーは、ディレクトリに対応するパスを要求する場合、このディレクトリから名前index.htmlでファイルを提供します。 有頂天のファイルサーバーモジュールは、この規則をサポートしています。 パスを要求するとき、サーバーは./public/index.htmlファイル(./publicはルートディレクトリ)を探し、存在する場合はそれを返します。



したがって、ブラウザーがサーバーを要求するときにページを表示する必要がある場合は、public / index.htmlに配置する必要があります。 インデックスファイルの先頭は次のとおりです。



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







タイトルが定義され、スタイルシートが含まれます。スタイルが定義される場所には、特にトピックの周りのフレームが含まれます。 次に、ヘッダーと名前フィールドが追加されます。 ユーザーは自分の名前を入力して、トピックとコメントに添付する必要があります。



アイテム
   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. , . , .



. . , , ?











All Articles